pax_global_header00006660000000000000000000000064152341171020014506gustar00rootroot0000000000000052 comment=f0a8ff0df68fd0902809fde0465845b5d1f111fb tremotesf-2.10.1/000077500000000000000000000000001523411710200135775ustar00rootroot00000000000000tremotesf-2.10.1/.clang-format000066400000000000000000000055651523411710200161650ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 --- AccessModifierOffset: -4 AlignAfterOpenBracket: BlockIndent AlignArrayOfStructures: None AlignConsecutiveAssignments: false AlignConsecutiveBitFields: None AlignConsecutiveDeclarations: false AlignConsecutiveMacros: true AlignEscapedNewlines: Left AlignOperands: Align AlignTrailingComments: false AllowAllArgumentsOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: WithoutElse AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: true AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes AttributeMacros: ['ALWAYS_INLINE', 'FORMAT_CONST'] BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: Both BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Attach BreakBeforeConceptDeclarations: Always BreakBeforeTernaryOperators: true BreakBinaryOperations: RespectPrecedence BreakConstructorInitializers: BeforeColon BreakInheritanceList: BeforeColon BreakStringLiterals: true ColumnLimit: 120 CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true Cpp11BracedListStyle: true DeriveLineEnding: false DerivePointerAlignment: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: LogicalBlock FixNamespaceComments: false IndentAccessModifiers: false IndentCaseLabels: false IndentExternBlock: Indent IndentGotoLabels: true IndentPPDirectives: AfterHash IndentRequiresClause: true IndentWidth: 4 IndentWrappedFunctionNames: false InsertBraces: false InsertTrailingCommas: None KeepEmptyLinesAtTheStartOfBlocks: false LambdaBodyIndentation: Signature Language: Cpp NamespaceIndentation: All PackConstructorInitializers: NextLine PointerAlignment: Left QualifierAlignment: Leave ReferenceAlignment: Left ReflowComments: false #RemoveSemicolon: false RequiresClausePosition: OwnLine SeparateDefinitionBlocks: Leave SortIncludes: false SortUsingDeclarations: true SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInConditionalStatement: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: c++20 StatementAttributeLikeMacros: ['emit'] StatementMacros: ['Q_OBJECT', 'Q_GADGET', 'Q_ENUM', 'Q_ENUM_NS', 'Q_PROPERTY'] UseCRLF: false UseTab: Never ... tremotesf-2.10.1/.clang-tidy000066400000000000000000000027401523411710200156360ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 Checks: > -*, android-*, bugprone-*, -bugprone-implicit-widening-of-multiplication-result, clang-analyzer-*, -clang-analyzer-optin.performance.Padding, concurrency-*, cppcoreguidelines-*, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-macro-usage, -cppcoreguidelines-misleading-capture-default-by-value, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-owning-memory, -cppcoreguidelines-prefer-member-initializer, -cppcoreguidelines-pro-type-static-cast-downcast, misc-*, -misc-include-cleaner, -misc-no-recursion, -misc-non-private-member-variables-in-classes, modernize-*, -modernize-use-trailing-return-type, -modernize-use-nodiscard, performance-*, -performance-enum-size, portability-*, -portability-template-virtual-member-function, readability-*, -readability-identifier-length, -readability-implicit-bool-conversion, -readability-magic-numbers, -readability-named-parameter, -readability-qualified-auto, -readability-convert-member-functions-to-static, -readability-redundant-access-specifiers, -readability-redundant-inline-specifier, -readability-redundant-member-init, CheckOptions: cppcoreguidelines-avoid-do-while.IgnoreMacros: true readability-braces-around-statements.ShortStatementLines: 1 tremotesf-2.10.1/.gitattributes000066400000000000000000000001401523411710200164650ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 * -text tremotesf-2.10.1/.github/000077500000000000000000000000001523411710200151375ustar00rootroot00000000000000tremotesf-2.10.1/.github/workflows/000077500000000000000000000000001523411710200171745ustar00rootroot00000000000000tremotesf-2.10.1/.github/workflows/build-macos.yml000066400000000000000000000057631523411710200221310ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 name: Build Tremotesf for MacOS on: workflow_call: inputs: release-tag: description: Release tag type: string required: false default: '' jobs: build-macos: strategy: fail-fast: false matrix: architecture: ['arm64', 'x86_64'] runs-on: macos-latest steps: - name: Add GCC problem matcher uses: ammaraskar/gcc-problem-matcher@cb2e3f949c41b7818628f35f8484a9b0acf90cf9 - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Use latest Xcode uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 with: xcode-version: latest-stable - name: Set up vcpkg uses: equeim/action-setup-vcpkg@ecba2ac2c8e1317eee041a5188e56f8311b0c29d with: vcpkg-root: ${{ github.workspace }}/vcpkg save-cache: true cache-key-tag: ${{ matrix.architecture }} - name: Install host dependencies run: | brew install autoconf autoconf-archive automake libtool - name: Build Tremotesf for ${{ matrix.architecture }} architecture id: build uses: equeim/action-cmake-build@e6217b34862c420e581c40032d76a767cd343420 with: cmake-arguments: --preset macos-${{ matrix.architecture }}-vcpkg -D TREMOTESF_ASAN=${{ inputs.release-tag == '' && 'ON' || 'OFF' }} package: true env: ASAN_OPTIONS: detect_leaks=0 - name: Archive packages if: inputs.release-tag == '' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: macos-${{ matrix.architecture }}-packages retention-days: ${{ github.event_name == 'push' && 7 || 3 }} path: | ${{ steps.build.outputs.build-directory }}/packaging/macos/*.dmg - name: Upload packages to release if: inputs.release-tag != '' run: | packages=(${{ steps.build.outputs.build-directory }}/packaging/macos/*.dmg) echo "Uploading packages ${packages[@]}" gh release upload '${{ inputs.release-tag }}' "${packages[@]}" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Archive build logs uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a if: always() with: name: macos-${{ matrix.architecture }}-build-logs retention-days: ${{ github.event_name == 'push' && 7 || 3 }} path: | ${{ steps.build.outputs.build-directory }}/CMakeCache.txt ${{ steps.build.outputs.build-directory }}/compile_commands.json ${{ steps.build.outputs.build-directory }}/vcpkg-manifest-install.log ${{ steps.build.outputs.build-directory }}/CMakeFiles/CMakeConfigureLog.yaml ${{ env.VCPKG_ROOT }}/buildtrees/*/*.log tremotesf-2.10.1/.github/workflows/build-windows-msvc.yml000066400000000000000000000114321523411710200234550ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 name: Build Tremotesf for Windows on: workflow_call: inputs: arch: description: Architecture (either x86_64 or arm64) type: string required: true release-tag: description: Release tag type: string required: false default: '' jobs: build-vcpkg-dependencies: runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025' }} steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Set up MSVC environment uses: equeim/action-setup-msvc-environment@433e932212086aed3f1dfc9d4039149f44c201ab with: arch: ${{ inputs.arch == 'x86_64' && 'amd64' || 'arm64' }} - name: Set up vcpkg uses: equeim/action-setup-vcpkg@ecba2ac2c8e1317eee041a5188e56f8311b0c29d with: vcpkg-root: ${{ github.workspace }}\vcpkg binary-cache-path: ${{ github.workspace }}\vcpkg_binary_cache save-cache: true cache-key-tag: ${{ inputs.arch }} - name: Configure CMake run: cmake -G Ninja -S . -B build --preset windows-${{ inputs.arch }}-msvc-vcpkg -D VCPKG_INSTALLED_DIR=${{ github.workspace }}/vcpkg_installed - name: Archive vcpkg build logs uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a if: always() with: name: 'windows-${{ inputs.arch }}-vcpkg-build-logs' retention-days: ${{ github.event_name == 'push' && 7 || 3 }} path: | ${{ github.workspace }}\vcpkg\buildtrees\*\*.log ${{ github.workspace }}\vcpkg\buildtrees\*\*.txt build-windows-msvc: needs: build-vcpkg-dependencies strategy: fail-fast: false matrix: toolchain: ${{ fromJSON(inputs.release-tag == '' && '["msvc", "msvc-clang"]' || '["msvc"]') }} runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025' }} steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Install WiX run: | dotnet tool install --global wix --version 6.0.2 wix extension add --global WixToolset.UI.wixext/6.0.2 - name: Set up MSVC environment uses: equeim/action-setup-msvc-environment@433e932212086aed3f1dfc9d4039149f44c201ab with: arch: ${{ inputs.arch == 'x86_64' && 'amd64' || 'arm64' }} - name: Set up vcpkg uses: equeim/action-setup-vcpkg@ecba2ac2c8e1317eee041a5188e56f8311b0c29d with: vcpkg-root: ${{ github.workspace }}\vcpkg binary-cache-path: ${{ github.workspace }}\vcpkg_binary_cache save-cache: 'false' cache-key-tag: ${{ inputs.arch }} - name: Build Tremotesf with ${{ matrix.toolchain }} toolchain for ${{ inputs.arch }} architecture id: build uses: equeim/action-cmake-build@e6217b34862c420e581c40032d76a767cd343420 with: cmake-arguments: --preset windows-${{ inputs.arch }}-${{ matrix.toolchain }}-vcpkg -D VCPKG_INSTALLED_DIR=${{ github.workspace }}/vcpkg_installed -D VCPKG_INSTALL_OPTIONS='--disable-metrics;--only-binarycaching' -D TREMOTESF_ASAN=${{ inputs.release-tag == '' && matrix.toolchain == 'msvc' && 'ON' || 'OFF' }} package: true env: QT_FORCE_STDERR_LOGGING: 1 - name: Archive packages if: inputs.release-tag == '' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: 'windows-${{ inputs.arch }}-${{ matrix.toolchain }}-packages' retention-days: ${{ github.event_name == 'push' && 7 || 3 }} path: | ${{ steps.build.outputs.build-directory }}\packaging\windows\*.zip ${{ steps.build.outputs.build-directory }}\packaging\windows\*.msi - name: Upload packages to release if: inputs.release-tag != '' run: | $packages = Get-ChildItem ${{ steps.build.outputs.build-directory }}\packaging\windows\*.zip, ${{ steps.build.outputs.build-directory }}\packaging\windows\*.msi echo "Uploading packages $packages" gh release upload '${{ inputs.release-tag }}' $packages env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Archive build logs uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a if: always() with: name: 'windows-${{ inputs.arch }}-${{ matrix.toolchain }}-build-logs' retention-days: ${{ github.event_name == 'push' && 7 || 3 }} path: | ${{ steps.build.outputs.build-directory }}\CMakeCache.txt ${{ steps.build.outputs.build-directory }}\compile_commands.json ${{ steps.build.outputs.build-directory }}\CMakeFiles\CMakeConfigureLog.yaml tremotesf-2.10.1/.github/workflows/cleanup-cache.yml000066400000000000000000000014061523411710200224100ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 name: Cleanup cache on: pull_request: types: [closed] jobs: cleanup-cache: runs-on: ubuntu-latest steps: - name: Check out sources uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Delete caches for closed pull requet run: | ref="refs/pull/${{ github.event.number }}/merge" ids=( $(gh cache list --json id --jq '.[].id' --ref "$ref") ) echo "Cache ids for ref $ref: ${ids[@]}" for id in "${ids[@]}"; do echo "Deleting cache with id $id" gh cache delete "$id" done env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} tremotesf-2.10.1/.github/workflows/main.yml000066400000000000000000000230331523411710200206440ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 name: CI on: push: branches: [master] pull_request: branches: - master - 'feature/**' schedule: - cron: '0 0 * * 0' jobs: build-rpm: strategy: fail-fast: false matrix: container-image: ['fedora:43', 'fedora:44', 'opensuse/tumbleweed:latest'] compiler: ['gcc', 'clang'] arch: ['x86_64', 'arm64'] exclude: - container-image: 'opensuse/tumbleweed:latest' compiler: clang runs-on: ubuntu-26.04${{ matrix.arch == 'arm64' && '-arm' || '' }} container: ${{ startsWith(matrix.container-image, 'fedora') && 'registry.fedoraproject.org' || 'registry.opensuse.org' }}/${{ matrix.container-image }} steps: - name: Add GCC problem matcher uses: ammaraskar/gcc-problem-matcher@cb2e3f949c41b7818628f35f8484a9b0acf90cf9 - name: Set packages install command for dnf if: startsWith(matrix.container-image, 'fedora') run: | cmd='dnf -y --setopt=install_weak_deps=False install' echo "INSTALL_PACKAGES=$cmd" >> "$GITHUB_ENV" echo "INSTALL_LOCAL_PACKAGES=$cmd" >> "$GITHUB_ENV" - name: Set packages install command for zypper if: startsWith(matrix.container-image, 'opensuse') run: | cmd='zypper --non-interactive in --no-recommends --details' echo "INSTALL_PACKAGES=$cmd" >> "$GITHUB_ENV" echo "INSTALL_LOCAL_PACKAGES=$cmd --allow-unsigned-rpm" >> "$GITHUB_ENV" - name: Install Git and rpm-build run: ${{env.INSTALL_PACKAGES}} git rpm-build - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Set Fedora compiler RPM macro id: fedora-compiler if: startsWith(matrix.container-image, 'fedora') run: | echo "rpm-macro=--define 'toolchain ${{ matrix.compiler }}'" >> "$GITHUB_OUTPUT" - name: Install build dependencies run: | readarray -t dependencies < <(rpmspec ${{steps.fedora-compiler.outputs.rpm-macro}} --define 'with_asan 1' -q --srpm --qf '[%{REQUIRES}\n]' packaging/rpm/tremotesf.spec) ${{env.INSTALL_PACKAGES}} "${dependencies[@]}" - name: Make source archive run: | # Git complains if we don't do that git config --global --add safe.directory "$GITHUB_WORKSPACE" sourcedir="$(rpmbuild --eval '%_sourcedir')" mkdir -p "$sourcedir" .github/workflows/make-source-archive.py --output-directory "$sourcedir" zstd - name: Build RPM run: | rpmbuild ${{steps.fedora-compiler.outputs.rpm-macro}} -bb --with asan packaging/rpm/tremotesf.spec - name: Install RPM run: | ${{env.INSTALL_LOCAL_PACKAGES}} "$(rpm --eval='%_rpmdir')"/*/*.rpm build-deb: strategy: fail-fast: false matrix: container-image: ['debian:13', 'ubuntu:25.10', 'ubuntu:26.04'] arch: ['x86_64', 'arm64'] runs-on: ubuntu-26.04${{ matrix.arch == 'arm64' && '-arm' || '' }} container: docker.io/library/${{ matrix.container-image }} steps: - name: Add GCC problem matcher uses: ammaraskar/gcc-problem-matcher@cb2e3f949c41b7818628f35f8484a9b0acf90cf9 - name: Install dependencies needed to make source archive run: | apt-get update && DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends --assume-yes install ca-certificates git python3 cmake - name: Check out sources uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Make source archive run: | # Git complains if we don't do that git config --global --add safe.directory "$GITHUB_WORKSPACE" .github/workflows/make-source-archive.py --output-directory .. --debian gzip - name: Check out Debian sources uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: repository: equeim/tremotesf-debian path: tremotesf-debian - name: Remove everything except debian/ directory run: | mv tremotesf-debian/debian debian rm -rf tremotesf-debian - name: Install build dependencies run: | apt-get update && DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends --assume-yes build-dep . - name: Build DEB run: | DEB_BUILD_OPTIONS=sanitize=+address ASAN_OPTIONS=detect_leaks=0 dpkg-buildpackage - name: Install DEB run: | DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends --assume-yes install ../*.deb build-flatpak: strategy: fail-fast: false matrix: arch: ['x86_64', 'arm64'] runs-on: ubuntu-26.04${{ matrix.arch == 'arm64' && '-arm' || '' }} steps: - name: Add GCC problem matcher uses: ammaraskar/gcc-problem-matcher@cb2e3f949c41b7818628f35f8484a9b0acf90cf9 - name: Set up Flatpak run: | sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get --assume-yes install flatpak flatpak-builder flatpak remote-add --user --if-not-exists flathub 'https://flathub.org/repo/flathub.flatpakrepo' - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Build Tremotesf run: | flatpak-builder --user --install-deps-from=flathub build-dir org.equeim.Tremotesf.json - name: Check that generation of release manifest works run: | readonly archive="$(.github/workflows/make-source-archive.py zstd)" .github/workflows/make-flatpak-manifest-for-release.py test "$archive" echo "Release Flatpak manifest:" cat org.equeim.Tremotesf.json build-freebsd: strategy: fail-fast: false matrix: freebsd-version: ['14.3', '15.1'] # arm64 runner doesn't support KVM so only x86_64 runs-on: ubuntu-26.04 steps: - name: Add GCC problem matcher uses: ammaraskar/gcc-problem-matcher@cb2e3f949c41b7818628f35f8484a9b0acf90cf9 - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Build on FreeBSD uses: vmactions/freebsd-vm@4441bea20b31bfa77329a5af583199362489567e with: run: | pkg update -f pkg install -y cmake ninja pkgconf libpsl libfmt qt6-base qt6-tools kf6-kwidgetsaddons kf6-kwindowsystem gettext-tools cxxopts cpp-httplib echo 'Configuring CMake' # Not enabling ASAN since it causes weird issues with tests that use openssl cmake -S . --preset base -D TREMOTESF_WITH_HTTPLIB=system -D TREMOTESF_ASAN=OFF echo 'Building Debug' cmake --build --preset base-debug -v echo 'Testing Debug' ASAN_OPTIONS=detect_leaks=0 ctest --preset base-debug echo 'Building Release' cmake --build --preset base-release -v echo 'Testing Release' ASAN_OPTIONS=detect_leaks=0 ctest --preset base-release build-windows-msvc: strategy: fail-fast: false matrix: arch: ['x86_64', 'arm64'] uses: ./.github/workflows/build-windows-msvc.yml with: arch: ${{ matrix.arch }} build-windows-mingw: runs-on: windows-latest steps: - name: Checkout Tremotesf uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Set up MSYS2 uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 with: msystem: 'CLANG64' update: 'true' install: | mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-cppwinrt mingw-w64-clang-x86_64-fmt mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-pkgconf mingw-w64-clang-x86_64-qt6-base mingw-w64-clang-x86_64-qt6-svg mingw-w64-clang-x86_64-qt6-tools mingw-w64-clang-x86_64-qt6-translations mingw-w64-clang-x86_64-cxxopts mingw-w64-clang-x86_64-kwidgetsaddons - name: Add GCC problem matcher uses: ammaraskar/gcc-problem-matcher@cb2e3f949c41b7818628f35f8484a9b0acf90cf9 - name: Build with LLVM MinGW toolchain shell: msys2 {0} run: | set -e -o pipefail echo '::group::Configuring CMake' # cpp-httplib doesn't compile with MinGW due to missing function (GetAddrInfoExCancel) in system headers # ASAN + LTO causes linker crash cmake -S . --preset base -D TREMOTESF_WITH_HTTPLIB=none -D TREMOTESF_ASAN=ON -D CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=OFF echo '::endgroup' echo '::group::Building Debug' cmake --build --preset base-debug -v echo '::endgroup' echo '::group::Testing Debug' ctest --preset base-debug echo '::endgroup' echo '::group::Building Release' cmake --build --preset base-release -v echo '::endgroup' echo '::group::Testing Release' ctest --preset base-release echo '::endgroup' env: QT_FORCE_STDERR_LOGGING: 1 build-macos: uses: ./.github/workflows/build-macos.yml reuse-lint: runs-on: ubuntu-latest steps: - name: Check out uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 - name: Check REUSE compliance run: | sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends --assume-yes install pipx pipx run reuse lint tremotesf-2.10.1/.github/workflows/make-flatpak-manifest-for-release.py000077500000000000000000000023561523411710200261220ustar00rootroot00000000000000#!/usr/bin/python3 # SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 import argparse import json from hashlib import sha256 from pathlib import PurePath MANIFEST_FILENAME = "org.equeim.Tremotesf.json" def main(): parser = argparse.ArgumentParser() parser.add_argument("tag", help="Git tag") parser.add_argument("archive", help="Release archive filename") args = parser.parse_args() with open(MANIFEST_FILENAME, "r", encoding="utf-8") as f: manifest = json.load(f) tremotesf_module = next(module for module in manifest["modules"] if module["name"] == "tremotesf") del tremotesf_module["build-options"]["env"]["ASAN_OPTIONS"] tremotesf_module["config-opts"].remove("-DTREMOTESF_ASAN=ON") with open(args.archive, "rb") as f: archive_sha256 = sha256(f.read()).hexdigest() tremotesf_module["sources"] = [ {"type": "archive", "url": f"https://github.com/equeim/tremotesf2/releases/download/{args.tag}/{PurePath(args.archive).name}", "sha256": archive_sha256}] with open(MANIFEST_FILENAME, "w", encoding="utf-8") as f: json.dump(manifest, f, indent=4) if __name__ == "__main__": main() tremotesf-2.10.1/.github/workflows/make-source-archive.py000077500000000000000000000102071523411710200234030ustar00rootroot00000000000000#!/usr/bin/python3 # SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 import argparse import gzip import json import logging import shutil import subprocess import sys import tarfile from enum import Enum from pathlib import Path, PurePath from tempfile import TemporaryDirectory def get_project_version() -> str: cmakelists = "CMakeLists.txt" if not Path(cmakelists).exists(): raise RuntimeError(f"{cmakelists} doesn't exist") process = subprocess.run(["cmake", "--trace-format=json-v1", "--trace-expand", "-P", cmakelists], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True) for line in process.stderr.splitlines(): trace = json.loads(line) if "cmd" in trace and trace["cmd"].lower() == "project": args = trace["args"] found_version = False for arg in args: if found_version: return arg if arg.lower() == "version": found_version = True raise RuntimeError("Failed to find version in CMake trace output") def make_tar_archive(tempdir: PurePath, debian: bool) -> PurePath: version = get_project_version() root_directory = f"tremotesf-{version}" if debian: archive_filename = f"tremotesf_{version}.orig.tar" else: archive_filename = f"tremotesf-{version}.tar" archive_path = tempdir / archive_filename logging.info(f"Making tar archive {archive_path}") files = subprocess.run(["git", "ls-files", "--recurse-submodules", "-z"], check=True, stdout=subprocess.PIPE, text=True).stdout.split("\0") # There is an empty string in the end for some reason files = [f for f in files if f] logging.info(f"Archiving {len(files)} files") with tarfile.open(archive_path, mode="x") as tar: for file in files: tar.add(file, arcname=str(PurePath(root_directory, file)), recursive=False) return archive_path def compress_gzip(output_directory: PurePath, tar_path: PurePath) -> PurePath: gzip_path = output_directory / tar_path.with_suffix(".tar.gz").name logging.info(f"Compressing {tar_path} to {gzip_path}") with open(tar_path, mode="rb") as tar_file, gzip.open(gzip_path, mode="xb") as gzip_file: shutil.copyfileobj(tar_file, gzip_file) return gzip_path def compress_zstd(output_directory: PurePath, tar_path: PurePath) -> PurePath: zstd_path = output_directory / tar_path.with_suffix(".tar.zst").name logging.info(f"Compressing {tar_path} to {zstd_path}") subprocess.run(["zstd", str(tar_path), "-o", str(zstd_path)], check=True, stdout=sys.stderr) return zstd_path class CompressionType(Enum): GZIP = "gzip" ZSTD = "zstd" def main(): logging.basicConfig(level=logging.INFO, stream=sys.stderr) parser = argparse.ArgumentParser() parser.add_argument("compression_types", nargs="+", choices=[member.value for member in list(CompressionType)], help="Compression type") parser.add_argument("--output-directory", help="Directory where to place archives. Defaults to current directory") parser.add_argument("--debian", action="store_true", help="Make Debian upstream tarball") args = parser.parse_args() compression_types = [CompressionType(arg) for arg in args.compression_types] if args.output_directory: output_directory = PurePath(args.output_directory) else: output_directory = Path.cwd() with TemporaryDirectory() as tempdir: tar = make_tar_archive(PurePath(tempdir), args.debian) for compression_type in compression_types: match compression_type: case CompressionType.GZIP: print(compress_gzip(output_directory, tar)) case CompressionType.ZSTD: print(compress_zstd(output_directory, tar)) if __name__ == "__main__": main() tremotesf-2.10.1/.github/workflows/release.yml000066400000000000000000000053721523411710200213460ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 name: Release on: release: types: [published] jobs: upload-source-archives: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Upload source archives to release run: | readarray -t archives < <(.github/workflows/make-source-archive.py gzip zstd) echo "Uploading source archives ${archives[@]}" gh release upload '${{ github.event.release.tag_name }}' "${archives[@]}" .github/workflows/make-flatpak-manifest-for-release.py '${{ github.event.release.tag_name }}' "${archives[-1]}" echo "Release Flatpak manifest:" cat org.equeim.Tremotesf.json gh release upload '${{ github.event.release.tag_name }}' org.equeim.Tremotesf.json env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} upload-debian-source-package: runs-on: ubuntu-latest steps: - name: Check out sources uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: 'recursive' - name: Make source archive run: | .github/workflows/make-source-archive.py --output-directory .. --debian gzip - name: Check out Debian sources uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: repository: equeim/tremotesf-debian ref: ${{ github.event.release.tag_name }} path: tremotesf-debian - name: Remove everything except debian/ directory run: | mv tremotesf-debian/debian debian rm -rf tremotesf-debian - name: Make source package run: | dpkg-buildpackage --build=source --no-pre-clean - name: Upload source package to release run: | shopt -s failglob cd .. files=(tremotesf_*.debian.* tremotesf_*.dsc tremotesf_*.orig.*) archive_filename="$(basename tremotesf_*.dsc .dsc)-debian-source.tar.gz" echo "Archiving ${files[@]} to $archive_filename" tar --create --gzip --file "$archive_filename" "${files[@]}" echo "Uploading artifact $archive_filename" gh release upload --repo '${{ github.repository }}' '${{ github.event.release.tag_name }}' "$archive_filename" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-windows-msvc: strategy: fail-fast: false matrix: arch: ['x86_64', 'arm64'] uses: ./.github/workflows/build-windows-msvc.yml with: arch: ${{ matrix.arch }} release-tag: ${{ github.event.release.tag_name }} build-macos: uses: ./.github/workflows/build-macos.yml with: release-tag: ${{ github.event.release.tag_name }} tremotesf-2.10.1/.gitignore000066400000000000000000000004471523411710200155740ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 /.cache /.kdev4 /*.kdev4 /.vs /.vscode /build /compile_commands.json /installroot /out /RPMS /vcpkg-installed /*.list /*.user /*.user.* /.qtcreator /.idea /cmake-build* /.flatpak-builder .DS_Store tremotesf-2.10.1/.gitmodules000066400000000000000000000003221523411710200157510ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 [submodule "src/3rdparty/cpp-httplib"] path = src/3rdparty/cpp-httplib url = https://github.com/yhirose/cpp-httplib.git tremotesf-2.10.1/.tx/000077500000000000000000000000001523411710200143105ustar00rootroot00000000000000tremotesf-2.10.1/.tx/config000066400000000000000000000007171523411710200155050ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 [main] host = https://www.transifex.com [o:equeim:p:tremotesf:r:desktop] file_filter = data/po/.po source_file = data/po/messages.po source_lang = en type = PO trans.en_US = data/po/en.po [o:equeim:p:tremotesf:r:main] file_filter = translations/.ts source_file = translations/source.ts source_lang = en type = QT trans.en_US = translations/en.ts tremotesf-2.10.1/CHANGELOG.md000066400000000000000000000556231523411710200154230ustar00rootroot00000000000000# Changelog ## [2.10.1] - 2026-07-28 ### Fixed - Crash on startup on Windows - Missing icon for clearing search field on Windows and macOS ### Changed - Flatpak KDE SDK updated to 6.11 ## [2.10.0] - 2026-07-20 ### Added - Status bar displays free space in the download directory (thanks @keizie) - Priority column in torrents list now shows colored icon (thanks @keizie) - Support for mapping remote directories to network share URLs like `smb://` (thanks @Manriel). *May not work correctly in Flatpak*. ### Changed - Improved performance and memory usage when dealing with torrents with large amounts of files - Minimum CMake version is 3.31 ### Fixed - Label/directory/tracker filter lists in the sidebar now use natural sorting - Jumping cursor position when editing paths - Issues with activation of "add torrent" dialogs on Wayland (again) - "Add torrent" and "Set location" dialogs being too wide when there are download directories with very long paths - Peers list not being updated correctly ## [2.9.1] - 2025-12-09 ### Fixed - Changed maximum values of idle limits and speed limits settings to align them with Transmission limitations ### Changed - Increased default width of some dialogs (thanks @glutenmancy) - Updated KDE Flatpak runtime to 6.10 branch - Updated translations ## [2.9.0] - 2025-10-03 ### Added - The first column in torrents, trackers, and peers lists can now be reordered too (thanks @keizie) - Torrents can be renamed using F2 shortcut (thanks @keizie) - Torrent's file list show appropriate file icons based on file extensions ### Fixed - Fixed opening torrent's directory when torrent has a single file in a directory (thanks @keizie) - Fixed window activation when opening torrent's file or its download directory on Linux/Wayland - Relocating the torrent does not save the directory for use in "Add torrent" dialog (thanks @keizie) - Improved support of servers that use HTTPS certificate chain with custom root CA ### Changed - Minimum dependencies baseline has been raised: - GCC 14 or Clang 19 (MSVC and Apple Clang are always required to be the latest available versions) - Qt 6.8 (Qt 5 support is removed) - KDE Frameworks 6.11 - fmt 10.1.0 - cxxopts 3.2.1 - cpp-httplib 0.18.7 - gettext 0.22.5 - Clarified dependency on cpp-httplib with OpenSSL support enabled, not OpenSSL itself - If your server uses HTTPS certificate chain with custom root CA (instead of single self-signed certificate), then you need to specify the root certificate in the connection settings, and optionally the leaf certificate if it doesn't have correct host name ### Removed - Qt 5 support - Native packages for Debian older than 13 and Ubuntu older than 25.04 (and other distros with outdated packages). Users of these distros can still install Tremotesf through Flatpak ## [2.8.2] - 2025-04-16 ### Fixed - Crash when failing to parse server response as JSON ## [2.8.1] - 2025-04-12 ### Fixed - Not working file dialogs when installed through Flatpak ## [2.8.0] - 2025-04-09 ### Added - Option to show torrent properties in a panel in the main window instead of dialog - Ability to set labels on torrents and filter torrent list by labels - Option to display relative time - Option to display only names of download directories in sidebar and torrents list ### Changed - Options dialog is rearranged to use multiple tabs - Message that's shown when trying to add torrent while disconnected from server is now displayed in a dialog instead of main window ### Fixed - Delayed loading of peers for active torrents - Window activation from clicking on notification ## [2.7.5] - 2025-01-14 ### Added - Windows on ARM64 support ### Changed - Windows builds now use system TLS library (schannel) instead of OpenSSL - Various hardening GCC and Clang compiler options are applied: - `-fhardened` with GCC >= 14 - `-ftrivial-auto-var-init=pattern`, `-fstack-protector-strong` - `-fstack-clash-protection` on Linux and FreeBSD - `-fcf-protection=full` on x86_64 - `-mbranch-protection=standard` on ARM64 - `-D_FORTIFY_SOURCE=3` - `-D_GLIBCXX_ASSERTIONS` with libstdc++ - `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST` with libc++ >= 18 ### Fixed - Failures to add torrents when "Delete .torrent file" option is enabled - Compilation errors with fmt 11.1 - Debug logs being enabled in release builds in some cases ## [2.7.4] - 2024-12-06 ### Fixed - Tray icon disappearing in some X11 environments - Wrong translation being loaded on Windows ## [2.7.3] - 2024-11-20 ### Fixed - Black screen issues when closing fullscreen window on macOS - File dialog being shown twice in some Linux environments - Crash with GCC 12 - Torrent's details in list not being updated for most recently added torrent ## [2.7.2] - 2024-09-15 ### Fixed - Opening download directory of a torrent with some file managers ## [2.7.1] - 2024-09-13 ### Added - Dialog is shown when fatal error occurs on Windows - TREMOTESF_ASAN CMake option to build with AddressSanitizer (off by default) ### Fixed - Performance regression on Windows (and potential performance improvements on other platforms) - Crash on Windows - Issues with mounted directories mapping ## [2.7.0] - 2024-08-31 ### Added - Merging trackers when adding existing torrent - Add Torrent Link dialogs allows multiple links - "None" proxy option to bypass system proxy ### Changed - Removed Debian 11 and Ubuntu 22.04 support - minimum baseline now corresponds to Debian 12 - Minimum CMake version is 3.25 - Minimum fmt version is 9.1 - Minimum KF5 version is 5.103 - Minimum libpsl version is 0.21.2 - Minimum cxxopts version is 3.1.1 - Minimum gettext version is 0.21 - Removed dependency on Qt Concurrent module - Breeze is used as a fallback icon theme and should be installed as a runtime dependency - Clarified runtime dependency on Qt's SVG image format plugin - Notification portal is used for notifications in Flatpak - Added workaround for Transmission not showing an error for torrent when all trackers have failed - Networking and some other async code is rewritten using C++ coroutines. Hopefully nothing is broken :) ### Fixed - Mapping of mounted directories working incorrectly in some cases ## [2.6.3] - 2024-04-22 ### Fixed - Qt 6.7 compatibility ## [2.6.2] - 2024-04-01 ### Fixed - Application being closed when opening file picker in Qt 6 builds ## [2.6.1] - 2024-03-17 ### Added - Added TREMOTESF_WITH_HTTPLIB CMake option to control how cpp-httplib test dependency is searched. Possible values: - auto: CMake find_package call, otherwise pkg-config, otherwise bundled copy is used. - system: CMake find_package call, otherwise pkg-config, otherwise fatal error. - bundled: bundled copy is used. - none: cpp-httplib is not used at all and tests that require it are disabled. ### Changed - Qt 6 is now used by default instead of Qt 5. You can override it with TREMOTESF_QT6=OFF CMake option - Flatpak build uses Qt 6 - openSUSE build uses Qt 6 ### Fixed - Clarified dependency on kwayland-integration - Sorting of directories and trackers in side panel - Menu items that should disabled on first start not being disabled - Selecting of current server via status bar context menu being broken in some cases - Debug logs being printed when they are disabled ## [2.6.0] - 2024-01-08 ### Added - macOS support - Option to open torrent's file or download directory on double click - Option to not activate main window when adding torrents (except on macOS where application is always activated) - Option to not show "Add Torrent" dialog when adding torrents - Right click on status bar opens menu to quickly connect to different server - Support of xdg-activation protocol on Wayland (kwayland-integration is required as a runtime dependency) ### Changed - "Open" and "Show in file manager" actions now show error dialog if file/directory does not exist, instead if being inaccessible - "Show in file manager" actions has been renamed to "Open download directory" - Progress bar's text is now drawn according to Qt style (though Breeze style still draws text next to the progress bar, not inside of it) ### Fixed - Initial state of "Lock toolbar" menu action - Progress bar being drawn in incorrect column in torrent's files list ## [2.5.0] - 2023-10-15 ### Added - Qt 6 support (with unreleased KF6 libraries) - Option to move torrent file to trash instead of deleting it. It is enabled by default, and fallbacks to deletion if move failed for any reason. ### Changed - Windows builds use Qt 6 - Progress bar columns in torrent/file lists are now displayed with percent text - Default columns and sort order in torrents list are changed according to my personal taste: default sorting is now by added date, from new to old - When search field is focused via shortcut its contents are now selected - When Tremotesf is launched for the first time it now doesn't place itself in the middle of the screen, letting OS decide ### Removed - Windows 8.1 and Windows 10 versions prior to 1809 (October 2018 Update) are not supported anymore ### Fixed - "Open" action on torrent's root file directory - Saving of settings and window state when on logout/reboot/shutdown on Windows - Unnecessary RPC requests when torrent's limits are edited ## [2.4.0] - 2023-05-30 ### Added - "Add torrent" dialog now has checkbox to remove torrent file when torrent is added ### Changed - "Remember last download directory" is replaced with "Remember parameters of last added torrent", which also remembers priority, started/paused state and "Delete .torrent file" checkbox of last added torrent - "Remember last torrent open directory" setting is renamed to "Remember location of last opened torrent file" - When "Remember last torrent open directory" is unchecked user's home directory is always used - When authentication is enabled, `Authorization` header will be sent in advance instead of waiting for 401 response from server (thanks @otaconix) ### Fixed - Errors when opening certain torrent files - Incorrect error message being displayed when there is no configured servers ## [2.3.0] - 2023-04-30 ### Changed - Tremotesf now requires compiler with some C++20 support (concepts, ranges and comparison operators) ### Fixed - Crash on launch with some Qt styles - Mounted directories feature not working on Windows with UNC paths (those starting with \\\\) - Incorrect error message when adding torrent that already exists with some Transmission versions ## [2.2.0] - 2023-03-28 ### Added - Torrent priority selection in torrent's context menu ### Changed - Torrent status icons are redrawn to be more contrasting ### Fixed - Torrent status icons being pixelated on high DPI displays - Torrent priority column being empty - Zero number of leechers - No icon in task switcher in KDE Plasma Wayland ## [2.1.0] - 2023-03-12 ### Added - Trackers list shows number of seeders and leechers for trackers, not just total number peers - New dependencies: 1. libpsl 2. cxxopts 3. cpp-httplib 0.11 or newer (for tests only, optional) ### Changed - "Seeders" and "leechers" now refer to total number of seeders and leechers reported by trackers, while number of peers that we are currently downloading from / uploading to is displayed separately - Tracker's error is displayed in separate column - Torrents that have an error but are still being downloaded/uploaded are displayed under both "Status" filters simultaneously - TREMOTESF_BUILD_TESTS CMake option is replaced by standard BUILD_TESTING option ### Fixed - Fixed issues with lists or torrents/trackers not being updated sometimes - Main window placeholder when there is no torrents in list is now displayed correctly ## [2.0.0] - 2022-11-05 ### Added - Dark theme support on Windows (dark window title bar on Windows 10 is supported only on 1809 or newer and it can be buggy because Windows 10 doesn't support this officially) - System accent color on Windows is used in app UI (can be disabled in settings) - Windows builds now write logs to `C:\Users\User\AppData\Local\tremotesf\tremotesf` directory - Opening of torrents (local files or HTTP(S) links) and magnet links is now supported via drag & drop or Ctrl+V on main window - Option to automatically fill link from clipboard when adding torrent link (disabled by default) - Last download directory is now remembered when adding torrents (can be disabled in settings) (thanks Alex Bell) - Directory of last opened torrent is now remembered (can be disabled in settings) - When adding torrents via opening file/link, drag & drop or clipboard while disconnected from server, Tremotesf will now show message explaining that they will be added after connection to server (instead of doing this sliently, confusing users) - Status message is shown over empty torrent list when not connected to server - Command line option to enable debug logs ### Changed - Raised minimum version of Qt to 5.15 - Raised minimum version of CMake to 3.16 (3.21 on Windows) - Windows build now uses Fusion style since default style imitating Win32 controls is non-themeable - Windows installer now associates Tremotesf with torrent files and magnet links ### Removed - Sailfish OS support - org.equeim.Tremotesf D-Bus interface is removed - Removed special handling for various file managers when using "Show in file manager" action Now Tremotesf uncoditionally tries to use org.freedesktop.FileManager1 D-Bus interface, and if it fails then QDesktopServices::openUrl() is used ### Fixed - IPv6 address can be now used to connect to server - Windows paths are always displayed with native directory separators ## [1.11.3] - 2022-03-18 ### Fixed - RPM spec file ## [1.11.2] - 2022-03-16 ### Fixed - Display of torrents list updates ## [1.11.1] - 2022-02-28 ### Added - MSI installer for Windows ### Fixed - Renaming files when adding torrent - Display of torrents count in status filters - Sorting of torrents by ETA ### Removed - Donation links ## [1.11.0] - 2022-02-13 ### Added - Automatic reconnection to server (thanks to LuK1337) - PEM certificate can be loaded from file - Links in torrents' comments are now clickable - Torrent list filters (except search) are now saved when app is restarted - Torrent properties screen now shows list of web seeders - Most menu items that didn't have icons now have them (thank to Buck Melanoma) - Hovering cursor over status bar when connection to server failed show more detailed error description - Vcpkg integration when building for Windows ### Changed - Further reduced memory usage when opening torrent files - All app resources are now bundled inside executable - Windows builds use MSVC toolchain by default ### Fixed - Sailfish OS support (this will be the last release that supports Saifish OS) - Console window output encoding on Windows (it now also uses UTF-8 on Windows 10) ## [1.10.0] - 2021-09-27 ### Added - Ability to shut down remote Transmission instance - Renaming of torrent via its context menu ### Changed - Reduced memory usage when opening torrent files ## [1.9.1] - 2021-05-10 ### Changed - Disabled MIME type checking for torrent files (it doesn't work for some files) - Tremotesf now won't open torrent files that are bigger than 50 MiB ### Fixed - Segfault or error when adding torrent files ## [1.9.0] - 2021-05-04 ### Added - It is now possible to specify whole certificate chain for self-signed certificate ### Changed - Ctrl+F now focuses search field in main window - C++17 compiler is now required ### Fixed - Fixed torrent list artifacts when using GTK2 style plugin for Qt ## [1.8.0] - 2020-09-06 ### Added - Tremotesf now implements org.freedesktop.Application D-Bus interface on relevant platforms - Desktop: added support of startup notifications on X11 - Desktop: added dependencies on Qt X11 Extras and KWindowSystem on Unix-like platforms - Desktop: added menu item to copy torrent's magnet link ### Changed - org.equeim.Tremotesf D-Bus interface is deprecated - Desktop: notifications on Unix-like platforms are now clickable - Desktop: when application window is hidden to tray icon, open dialogs are now hidden too - Desktop: minor UI improvements - Updated translations ### Fixed - Fixed support of mounted remote directories ## [1.7.1] - 2020-06-27 ### Changed - Updated translations - Enabled LTO for release build on Windows ### Fixed - Fixed adding torrents with Qt 5.15 ## [1.7.0] - 2020-06-05 ### Added - Added support of configuring per-server HTTP/SOCKS5 proxies - Added support of renaming torrent's files when adding it - Added ability to add multiple trackers at a time ### Changed - Minimum CMake version raised to 3.10 - C++ standard version raised to C++14 (Sailfish OS GCC 4.9 toolchain is still supported) ### Fixed - Fixed passing command line arguments and opening files with commas - Sailfish OS: fixed opening app from notifications - Sailfish OS: fixed reconnecting to server when its connection settings are changed ## [1.6.4] - 2020-01-11 ### Fixed - Fixed compilation for Windows - Fix RPM validation errors/warnings for Sailfish OS ## [1.6.3] - 2020-01-05 ### Fixed - Fix installing of new translations ## [1.6.2] - 2020-01-05 ### Changed - Improved UNIX/Windows signals handling - Command line options that don't start GUI (such as '-v' or '-h' or passing files to already running Tremotesf instance) now don't require X/Wayland session - Tremotesf now shows localized numbers instead of Latin-1 ones (in most places) - Updated translations ### Fixed - Fixed compilation with Qt 5.14 ## [1.6.1] - 2019-07-16 ### Changed - Minor performance improvements ### Fixed - Fixed notifications not being configurable in KDE Plasma and GNOME ## [1.6.0] - 2019-01-26 ### Added - Desktop: restoring of torrent properties dialog's geometry - Desktop: Tremotesf now remebmers used download directories and shows them in combo box when adding torrent / changing location ### Changed - Desktop: toolBarVisible and toolBarArea configuration keys are now deprecated, mainWindowState is used instead ### Fixed - Desktop: fixed restoring state of detached toolbar - Sailfish OS: fixed peers' upload speed label ## [1.5.6] - 2018-12-08 ### Changed - Desktop: Improved HiDPI support ### Fixed - Desktop: fixed notification icon ## [1.5.5] - 2018-09-26 ### Fixed - Yandex.Money donate link ## [1.5.4] - 2018-09-10 ### Changed - Tremotesf binary now doesn't link to QtConcurrent library (but still requires its headers at build time) - Desktop: improved AppStream metadata ## [1.5.3] - 2018-09-03 ### Added - Multiseat support (it is now possible to run multiple Tremotesf instances on different login sessions) - Added .appdata.xml file ### Changed - D-Bus is now used for IPC on Unix - Minimum CMake version lowered to 3.0 (note that Qt >= 5.11 requires CMake 3.1) - Desktop: .desktop file and icons are renamed according to Desktop Entry Specification ### Fixed - Fixed crashes when disconnecting from server - Added window title to Server Stats dialog - "Open" and "Show In File Manager" menu items are disabled when files don't exist on filesystem - Desktop: handle cases when xdg-mime executable doesn't exist - Desktop: fallback to org.freedesktop.FileManager1 when showing files in file manager ## [1.5.2] - 2018-08-18 ### Fixed - Fixed crash when disconnecting from server ## [1.5.1] - 2018-08-14 ### Added - Universal RPM spec file for SailfishOS/Fedora/Mageia/openSUSE ### Changed - Updated Spanish translation ### Fixed - Fixed openSUSE OBS build by adding subcategory to .desktop file ## [1.5.0] - 2018-08-13 ### Added - Server stats dialog - Ability to set mounted directories for servers - Ability to open torrents' files - Notifications on added/finished torrents since last connection to server - Ability to reannounce torrents - Ability to set torrent's location ### Changed - CMake build system #### Sailfish OS - File selection dialog now shows current directory ### Fixed - Fixed segfault when disconnecting from server - Fixed segfault when closing properties dialog - Impoved support of self-signed certificates - Active network requests are now aborted when manually disconnecting from server ## [1.4.0] - 2018-05-09 ### Added - Donation links via PayPal and Yandex.Money - Filter torrents by download directory - Show available free space when adding torrent - Dutch, Flemish and Spanish translations ### Fixed - Checking for Qt version in .pro file - Translations installation - Sailfish OS command line arguments ## [1.3.2] - 2017-03-05 ### Changed - Disable debug output in release builds ### Fixed - Installation of translation files when build directory is project root directory ## [1.3.1] - 2017-03-02 ### Changed - Don't create a new thread for every async task, instead use QtConcurrent::run ### Fixed #### Desktop - 256x256 icon ## [1.3.0] - 2017-02-28 ### Changed - More correct handling of self-signed certificates (you may need to update server's configuration) - Translation are now managed on Transifex. ## [1.2.2] - 2017-02-25 ### Fixed - Windows icons ## [1.2.1] - 2017-02-24 ### Added - Show authentication error ### Changed - Show license in HTML format ### Fixed #### Desktop - Project URL in About dialog ## [1.2.0] - 2017-02-13 ### Added - Torrent properties are now reloaded after reconnection - Rename torrent's files #### Desktop - Command line option to start minimized in notification area ### Changed - Performance improvements for torrents with large number of files - Accounts are renamed to Servers (config file name also changed) ### Removed - Ability to select another file in Add Torrent File dialog (it was causing unnecessary code complication) #### Desktop - Settings options to start minimized in notification area. Use command line option instead ### Fixed - Tracker status is now more correct - Set correct torrent priority when adding torrent ## [1.1.0] - 2016-09-25 ### Added #### Sailfish OS - Peers page now shows clients of peers. ### Changed - Update data immediately after getting response from server when performing some actions on torrents (adding, starting/pausing, removing, checking, moving in queue). ### Fixed - Update torrent name when it is changed on server (e.g. after retrieving torrent metadata if torrent was added by magnet link). #### Desktop - Don't hide main window on startup if tray icon is disabled. - Fix showing temporary window if main window is hidden on startup. ## [1.0.0] - 2016-09-17 ### Added - Desktop user interface for GNU/Linux and Windows. #### Sailfish OS - You can now browse torrent's content when adding torrent file. - Filter torrents by trackers. - Search in torrents list. - Multi-selection everywhere. - Notifications on adding torrents and when disconnecting from server (all notification can be disabled in settings). - Option for disabling connection on startup. - Cover action for connecting/disconnecting. - Tremotesf can now open torrent files and links from console or file manager (Sailfish OS version can open only one torrent at a time). - Several new server settings options. ### Changed - Entire project is rewrited from scratch. - A lot of performance improvements. - New configuration file format. Accounts now stored in separate file (with automatic migration from previous version). - Switch to QMake build system #### Sailfish OS - New and more compact user interface. ### Fixed - A lot of bugs. tremotesf-2.10.1/CMakeLists.txt000066400000000000000000000030101523411710200163310ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 cmake_minimum_required(VERSION 3.31) cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}...4.4) include(cmake/VcpkgHostTriplet.cmake) set(languages CXX) if (APPLE) list(APPEND languages OBJCXX) include(cmake/MacOSDeploymentTarget.cmake) message(STATUS "Setting CMAKE_OSX_DEPLOYMENT_TARGET to ${TREMOTESF_MACOS_DEPLOYMENT_TARGET}") set(CMAKE_OSX_DEPLOYMENT_TARGET "${TREMOTESF_MACOS_DEPLOYMENT_TARGET}") endif () project(tremotesf VERSION 2.10.1 LANGUAGES ${languages}) option(TREMOTESF_ASAN "Build with AddressSanitizer" OFF) set(TREMOTESF_WITH_HTTPLIB "auto" CACHE STRING "Where to find cpp-httplib dependency for unit tests. Possible values are: auto, system, bundled, none") if (NOT TREMOTESF_WITH_HTTPLIB MATCHES "^(auto|system|bundled|none)$") message(FATAL_ERROR "Invalid TREMOTESF_WITH_HTTPLIB value ${TREMOTESF_WITH_HTTPLIB}. Possible values are: auto, system, bundled, none") endif() include(CTest) include(GNUInstallDirs) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include(cmake/CommonOptions.cmake) find_package(Qt6 ${TREMOTESF_MINIMUM_QT_VERSION} REQUIRED COMPONENTS Core) if (APPLE) set(TREMOTESF_MACOS_BUNDLE_NAME "Tremotesf") set(TREMOTESF_EXTERNAL_RESOURCES_PATH "${TREMOTESF_MACOS_BUNDLE_NAME}.app/Contents/Resources") elseif (WIN32) set(TREMOTESF_EXTERNAL_RESOURCES_PATH ".") endif () add_subdirectory("src") add_subdirectory("data") add_subdirectory("translations") add_subdirectory("packaging") tremotesf-2.10.1/CMakePresets.json000066400000000000000000000235161523411710200170270ustar00rootroot00000000000000{ "version": 3, "configurePresets": [ { "name": "base", "generator": "Ninja Multi-Config", "binaryDir": "${sourceDir}/out/build/${presetName}", "installDir": "${sourceDir}/out/install/${presetName}", "warnings": { "dev": true, "deprecated": true }, "cacheVariables": { "CMAKE_CONFIGURATION_TYPES": "Debug;Release;RelWithDebInfo", "CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE": "ON" } }, { "name": "base-vcpkg", "inherits": "base", "hidden": true, "cacheVariables": { "TREMOTESF_WITH_HTTPLIB": "system", "VCPKG_INSTALLED_DIR": "${sourceDir}/vcpkg-installed", "VCPKG_INSTALL_OPTIONS": "--disable-metrics;--clean-buildtrees-after-build;--clean-packages-after-build", "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/vcpkg-overlay-triplets" }, "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" }, { "name": "base-windows-msvc-vcpkg", "inherits": "base-vcpkg", "hidden": true, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, "cacheVariables": { "CMAKE_CXX_COMPILER": "cl.exe" }, "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Windows" ] } } }, { "name": "windows-arm64-msvc-vcpkg", "inherits": "base-windows-msvc-vcpkg", "architecture": { "value": "arm64", "strategy": "external" }, "cacheVariables": { "VCPKG_TARGET_TRIPLET": "arm64-windows-static" } }, { "name": "windows-x86_64-msvc-vcpkg", "inherits": "base-windows-msvc-vcpkg", "architecture": { "value": "x64", "strategy": "external" }, "cacheVariables": { "VCPKG_TARGET_TRIPLET": "x64-windows-static" } }, { "name": "windows-arm64-msvc-clang-vcpkg", "inherits": "windows-arm64-msvc-vcpkg", "cacheVariables": { "CMAKE_CXX_COMPILER": "clang-cl.exe" }, "environment": { "CXXFLAGS": "/clang:--target=arm64-pc-windows-msvc $penv{CXXFLAGS}", "CFLAGS": "/clang:--target=arm64-pc-windows-msvc $penv{CXXFLAGS}" } }, { "name": "windows-x86_64-msvc-clang-vcpkg", "inherits": "windows-x86_64-msvc-vcpkg", "cacheVariables": { "CMAKE_CXX_COMPILER": "clang-cl.exe" } }, { "name": "base-macos-vcpkg", "inherits": "base-vcpkg", "hidden": true, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" } }, { "name": "macos-arm64-vcpkg", "inherits": "base-macos-vcpkg", "cacheVariables": { "VCPKG_TARGET_TRIPLET": "arm64-osx-release", "CMAKE_OSX_ARCHITECTURES": "arm64" } }, { "name": "macos-x86_64-vcpkg", "inherits": "base-macos-vcpkg", "cacheVariables": { "VCPKG_TARGET_TRIPLET": "x64-osx-release", "CMAKE_OSX_ARCHITECTURES": "x86_64" } } ], "buildPresets": [ { "name": "base-debug", "configurePreset": "base", "configuration": "Debug" }, { "name": "base-release", "configurePreset": "base", "configuration": "Release" }, { "name": "base-relwithdebinfo", "configurePreset": "base", "configuration": "RelWithDebInfo" }, { "name": "windows-arm64-msvc-vcpkg-debug", "configurePreset": "windows-arm64-msvc-vcpkg", "configuration": "Debug" }, { "name": "windows-arm64-msvc-vcpkg-release", "configurePreset": "windows-arm64-msvc-vcpkg", "configuration": "Release" }, { "name": "windows-arm64-msvc-vcpkg-relwithdebinfo", "configurePreset": "windows-arm64-msvc-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "windows-x86_64-msvc-vcpkg-debug", "configurePreset": "windows-x86_64-msvc-vcpkg", "configuration": "Debug" }, { "name": "windows-x86_64-msvc-vcpkg-release", "configurePreset": "windows-x86_64-msvc-vcpkg", "configuration": "Release" }, { "name": "windows-x86_64-msvc-vcpkg-relwithdebinfo", "configurePreset": "windows-x86_64-msvc-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "windows-arm64-msvc-clang-vcpkg-debug", "configurePreset": "windows-arm64-msvc-clang-vcpkg", "configuration": "Debug" }, { "name": "windows-arm64-msvc-clang-vcpkg-release", "configurePreset": "windows-arm64-msvc-clang-vcpkg", "configuration": "Release" }, { "name": "windows-arm64-msvc-clang-vcpkg-relwithdebinfo", "configurePreset": "windows-arm64-msvc-clang-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "windows-x86_64-msvc-clang-vcpkg-debug", "configurePreset": "windows-x86_64-msvc-clang-vcpkg", "configuration": "Debug" }, { "name": "windows-x86_64-msvc-clang-vcpkg-release", "configurePreset": "windows-x86_64-msvc-clang-vcpkg", "configuration": "Release" }, { "name": "windows-x86_64-msvc-clang-vcpkg-relwithdebinfo", "configurePreset": "windows-x86_64-msvc-clang-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "macos-arm64-vcpkg-debug", "configurePreset": "macos-arm64-vcpkg", "configuration": "Debug" }, { "name": "macos-arm64-vcpkg-release", "configurePreset": "macos-arm64-vcpkg", "configuration": "Release" }, { "name": "macos-arm64-vcpkg-relwithdebinfo", "configurePreset": "macos-arm64-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "macos-x86_64-vcpkg-debug", "configurePreset": "macos-x86_64-vcpkg", "configuration": "Debug" }, { "name": "macos-x86_64-vcpkg-release", "configurePreset": "macos-x86_64-vcpkg", "configuration": "Release" }, { "name": "macos-x86_64-vcpkg-relwithdebinfo", "configurePreset": "macos-x86_64-vcpkg", "configuration": "RelWithDebInfo" } ], "testPresets": [ { "name": "base", "hidden": true, "output": { "outputOnFailure": true } }, { "name": "base-debug", "inherits": "base", "configurePreset": "base", "configuration": "Debug" }, { "name": "base-release", "inherits": "base", "configurePreset": "base", "configuration": "Release" }, { "name": "base-relwithdebinfo", "inherits": "base", "configurePreset": "base", "configuration": "RelWithDebInfo" }, { "name": "windows-arm64-msvc-vcpkg-debug", "inherits": "base", "configurePreset": "windows-arm64-msvc-vcpkg", "configuration": "Debug" }, { "name": "windows-arm64-msvc-vcpkg-release", "inherits": "base", "configurePreset": "windows-arm64-msvc-vcpkg", "configuration": "Release" }, { "name": "windows-arm64-msvc-vcpkg-relwithdebinfo", "inherits": "base", "configurePreset": "windows-arm64-msvc-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "windows-x86_64-msvc-vcpkg-debug", "inherits": "base", "configurePreset": "windows-x86_64-msvc-vcpkg", "configuration": "Debug" }, { "name": "windows-x86_64-msvc-vcpkg-release", "inherits": "base", "configurePreset": "windows-x86_64-msvc-vcpkg", "configuration": "Release" }, { "name": "windows-x86_64-msvc-vcpkg-relwithdebinfo", "inherits": "base", "configurePreset": "windows-x86_64-msvc-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "windows-arm64-msvc-clang-vcpkg-debug", "inherits": "base", "configurePreset": "windows-arm64-msvc-clang-vcpkg", "configuration": "Debug" }, { "name": "windows-arm64-msvc-clang-vcpkg-release", "inherits": "base", "configurePreset": "windows-arm64-msvc-clang-vcpkg", "configuration": "Release" }, { "name": "windows-arm64-msvc-clang-vcpkg-relwithdebinfo", "inherits": "base", "configurePreset": "windows-arm64-msvc-clang-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "windows-x86_64-msvc-clang-vcpkg-debug", "inherits": "base", "configurePreset": "windows-x86_64-msvc-clang-vcpkg", "configuration": "Debug" }, { "name": "windows-x86_64-msvc-clang-vcpkg-release", "inherits": "base", "configurePreset": "windows-x86_64-msvc-clang-vcpkg", "configuration": "Release" }, { "name": "windows-x86_64-msvc-clang-vcpkg-relwithdebinfo", "inherits": "base", "configurePreset": "windows-x86_64-msvc-clang-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "macos-arm64-vcpkg-debug", "inherits": "base", "configurePreset": "macos-arm64-vcpkg", "configuration": "Debug" }, { "name": "macos-arm64-vcpkg-release", "inherits": "base", "configurePreset": "macos-arm64-vcpkg", "configuration": "Release" }, { "name": "macos-arm64-vcpkg-relwithdebinfo", "inherits": "base", "configurePreset": "macos-arm64-vcpkg", "configuration": "RelWithDebInfo" }, { "name": "macos-x86_64-vcpkg-debug", "inherits": "base", "configurePreset": "macos-x86_64-vcpkg", "configuration": "Debug" }, { "name": "macos-x86_64-vcpkg-release", "inherits": "base", "configurePreset": "macos-x86_64-vcpkg", "configuration": "Release" }, { "name": "macos-x86_64-vcpkg-relwithdebinfo", "inherits": "base", "configurePreset": "macos-x86_64-vcpkg", "configuration": "RelWithDebInfo" } ] } tremotesf-2.10.1/LICENSE000077700000000000000000000000001523411710200212102LICENSES/GPL-3.0-or-later.txtustar00rootroot00000000000000tremotesf-2.10.1/LICENSES/000077500000000000000000000000001523411710200150045ustar00rootroot00000000000000tremotesf-2.10.1/LICENSES/CC-BY-ND-4.0.txt000066400000000000000000000406761523411710200171550ustar00rootroot00000000000000Creative Commons Attribution-NoDerivatives 4.0 International Creative Commons Corporation (“Creative Commonsâ€�) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-isâ€� basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. Creative Commons Attribution-NoDerivatives 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 – Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. c. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. d. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. e. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. f. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. g. Licensor means the individual(s) or entity(ies) granting rights under this Public License. h. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. i. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. j. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 – Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: A. reproduce and Share the Licensed Material, in whole or in part; and B. produce and reproduce, but not Share, Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 5. Downstream recipients. A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 – License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material, You must: A. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material. 3. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 4. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. Section 4 – Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database, provided You do not Share Adapted Material; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 – Disclaimer of Warranties and Limitation of Liability. a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 – Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 – Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 – Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.â€� Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commonsâ€� or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. tremotesf-2.10.1/LICENSES/CC0-1.0.txt000066400000000000000000000156101523411710200164110ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. tremotesf-2.10.1/LICENSES/GPL-2.0-or-later.txt000066400000000000000000000416711523411710200202200ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice tremotesf-2.10.1/LICENSES/GPL-3.0-or-later.txt000066400000000000000000001035561523411710200202220ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This Licenseâ€� refers to version 3 of the GNU General Public License. “Copyrightâ€� also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Programâ€� refers to any copyrightable work licensed under this License. Each licensee is addressed as “youâ€�. “Licenseesâ€� and “recipientsâ€� may be individuals or organizations. To “modifyâ€� a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified versionâ€� of the earlier work or a work “based onâ€� the earlier work. A “covered workâ€� means either the unmodified Program or a work based on the Program. To “propagateâ€� a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “conveyâ€� a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Noticesâ€� to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source codeâ€� for a work means the preferred form of the work for making modifications to it. “Object codeâ€� means any non-source form of a work. A “Standard Interfaceâ€� means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Librariesâ€� of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Componentâ€�, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Sourceâ€� for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all noticesâ€�. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregateâ€� if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Productâ€� is either (1) a “consumer productâ€�, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally usedâ€� refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Informationâ€� for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissionsâ€� are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictionsâ€� within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transactionâ€� is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributorâ€� is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor versionâ€�. A contributor's “essential patent claimsâ€� are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “controlâ€� includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent licenseâ€� is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grantâ€� such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relyingâ€� means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatoryâ€� if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later versionâ€� applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS ISâ€� WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyrightâ€� line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . 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: Copyright (C) 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. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about boxâ€�. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimerâ€� for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . tremotesf-2.10.1/LICENSES/LGPL-2.0-or-later.txt000066400000000000000000000604551523411710200203350ustar00rootroot00000000000000GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! tremotesf-2.10.1/LICENSES/LGPL-2.1-or-later.txt000066400000000000000000000625571523411710200203430ustar00rootroot00000000000000GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! tremotesf-2.10.1/LICENSES/LGPL-3.0-only.txt000066400000000000000000001221621523411710200175640ustar00rootroot00000000000000GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This Licenseâ€� refers to version 3 of the GNU General Public License. “Copyrightâ€� also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Programâ€� refers to any copyrightable work licensed under this License. Each licensee is addressed as “youâ€�. “Licenseesâ€� and “recipientsâ€� may be individuals or organizations. To “modifyâ€� a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified versionâ€� of the earlier work or a work “based onâ€� the earlier work. A “covered workâ€� means either the unmodified Program or a work based on the Program. To “propagateâ€� a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “conveyâ€� a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Noticesâ€� to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source codeâ€� for a work means the preferred form of the work for making modifications to it. “Object codeâ€� means any non-source form of a work. A “Standard Interfaceâ€� means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Librariesâ€� of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Componentâ€�, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Sourceâ€� for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all noticesâ€�. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregateâ€� if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Productâ€� is either (1) a “consumer productâ€�, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally usedâ€� refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Informationâ€� for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissionsâ€� are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictionsâ€� within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transactionâ€� is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributorâ€� is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor versionâ€�. A contributor's “essential patent claimsâ€� are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “controlâ€� includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent licenseâ€� is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grantâ€� such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relyingâ€� means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatoryâ€� if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later versionâ€� applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS ISâ€� WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyrightâ€� line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . 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: Copyright (C) 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. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about boxâ€�. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimerâ€� for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . tremotesf-2.10.1/LICENSES/MIT.txt000066400000000000000000000020661523411710200162020ustar00rootroot00000000000000MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tremotesf-2.10.1/README.md000066400000000000000000000150461523411710200150640ustar00rootroot00000000000000# Tremotesf Remote GUI for transmission-daemon. Supports GNU/Linux and Windows. Table of Contents ================= * [Tremotesf](#tremotesf) * [Table of Contents](#table-of-contents) * [Installation](#installation) * [Dependencies](#dependencies) * [Building](#building) * [GNU/Linux](#gnulinux) * [FreeBSD](#freebsd) * [Windows](#windows) * [macOS](#macos) * [Translations](#translations) * [Screenshots](#screenshots) ## Installation ### Dependencies - C++ compiler with partial C++23 support. Minimum tested versions of GCC and Clang toolchains: 1. GCC 14 2. Clang 19 with libstdc++ 14 3. Clang 19 with libc++ 19 - CMake 3.25 or newer - Qt 6.8 or newer (Core, Network, Gui, Widgets modules) - fmt 10.1.0 or newer - KWidgetsAddons 6.11 or newer - libpsl 0.21.2 or newer - cxxopts 3.2.1 or newer - Qt Test module (for tests only) - cpp-httplib 0.18.7 or newer, with OpenSSL support (for tests only, optional) On GNU/Linux and BSD, also: - Gettext 0.22.5 or newer - Qt D-Bus module - KWindowSystem - Qt's SVG image format plugin as a runtime dependency (usually located somewhere at /usr/lib64/qt6/plugins/imageformats/libqsvg.so) On Windows, also: - Windows 11 SDK is needed to build - Minimum supported OS version to run Tremotesf is Windows 10 1809 (October 2018 Update) On macOS, also: - Latest Xcode and macOS SDK versions supported by Qt (see [here](https://doc.qt.io/qt-6/macos.html)) - Minimum supported OS version to run Tremotesf is macOS 12 ### Building ```sh cmake -S /path/to/sources -B /path/to/build/directory --preset base-multi cmake --build /path/to/build/directory --config Debug cmake --install /path/to/build/directory --config Debug --prefix /path/to/install/directory ``` This example uses base-multi preset in CMakePresets.json and Ninja Multi-Config generator. You can invoke CMake in a different way if you want. CMake configuration options: `TREMOTESF_WITH_HTTPLIB` - determines how cpp-httplib test dependency is searched. Possible values: - auto: CMake find_package call, otherwise pkg-config, otherwise bundled copy is used. - system: CMake find_package call, otherwise pkg-config, otherwise fatal error. - bundled: bundled copy is used. - none: cpp-httplib is not used at all and tests that require it are disabled. ### GNU/Linux - Flatpak - [Flathub](https://flathub.org/apps/details/org.equeim.Tremotesf) - Arch Linux - [AUR](https://aur.archlinux.org/packages/tremotesf) - Debian - [Official repository](https://packages.debian.org/sid/tremotesf), or [my own OBS repository](https://build.opensuse.org/package/show/home:equeim:tremotesf/Tremotesf) ```sh debian_version="$(source /etc/os-release && echo "$VERSION_ID")" wget -qO - "https://download.opensuse.org/repositories/home:/equeim:/tremotesf/Debian_${debian_version}/Release.key" | sudo tee /etc/apt/trusted.gpg.d/tremotesf.asc sudo add-apt-repository "deb http://download.opensuse.org/repositories/home:/equeim:/tremotesf/Debian_${debian_version}/ /" sudo apt update sudo apt install tremotesf ``` - Fedora - [Copr](https://copr.fedorainfracloud.org/coprs/equeim/tremotesf) ```sh sudo dnf copr enable equeim/tremotesf sudo dnf install tremotesf ``` - Gentoo - [equeim-overlay](https://github.com/equeim/equeim-overlay) - openSUSE Tumbleweed - [OBS](https://build.opensuse.org/package/show/home:equeim:tremotesf/Tremotesf) ```sh sudo zypper ar https://download.opensuse.org/repositories/home:/equeim:/tremotesf/openSUSE_Tumbleweed/home:equeim:tremotesf.repo sudo zypper in tremotesf ``` - Ubuntu - [OBS](https://build.opensuse.org/package/show/home:equeim:tremotesf/Tremotesf) ```sh ubuntu_version="$(source /etc/os-release && echo "$VERSION_ID")" wget -qO - "https://download.opensuse.org/repositories/home:/equeim:/tremotesf/xUbuntu_${ubuntu_version}/Release.key" | sudo tee /etc/apt/trusted.gpg.d/tremotesf.asc sudo add-apt-repository "deb http://download.opensuse.org/repositories/home:/equeim:/tremotesf/xUbuntu_${ubuntu_version}/ /" sudo apt update sudo apt install tremotesf ``` ### FreeBSD Tremotesf is [available in FreeBSD ports](https://www.freshports.org/net-p2p/tremotesf/). ### Windows Windows builds are available at [releases](https://github.com/equeim/tremotesf2/releases) page. Minimum supported OS version to run Tremotesf is Windows 10 1809 (October 2018 Update). Build instructions for MSVC toolchain with vcpkg: 1. Install Visual Studio with 'Desktop development with C++' workload 2. Install latest version of CMake (from cmake.org or Visual Studio installer) 3. Install and setup [vcpkg](https://github.com/microsoft/vcpkg#quick-start-windows), and make sure that you have 15 GB of free space on disk where vcpkg is located 4. Set VCPKG_ROOT environment variable to the location of vcpkg installation When building from Visual Studio GUI, make sure to select 'Windows Debug' or 'Windows Release' configure preset. Otherwise: Launch x64 Command Prompt for Visual Studio, execute: ```pwsh cmake -S path\to\sources -B path\to\build\directory --preset # Initial compilation of dependencies will take a while cmake --build path\to\build\directory cmake --install path\to\build\directory --prefix path\to\install\directory # Next command creates ZIP archive and MSI installer cmake --build path\to\build\directory --target package ``` ### macOS macOS builds are available at [releases](https://github.com/equeim/tremotesf2/releases) page. Minimum supported OS version to run Tremotesf is macOS 12. Build instructions with vcpkg: 1. Install Xcode 2. Install CMake 3. Install and setup [vcpkg](https://github.com/microsoft/vcpkg#quick-start-windows), and make sure that you have 15 GB of free space on disk where vcpkg is located 4. Set VCPKG_ROOT environment variable to the location of vcpkg installation 5. Launch terminal, execute: ```sh cmake -S path/to/sources -B path/to/build/directory --preset # Initial compilation of dependencies will take a while cmake --build path/to/build/directory cmake --install path/to/build/directory --prefix path/to/install/directory # Next command creates DMG image cmake --build path/to/build/directory --target package ``` ## Translations Translations are managed on [Transifex](https://www.transifex.com/equeim/tremotesf). ## Screenshots ![](https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-1.png) ![](https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-2.png) ![](https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-3.png) ![](https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-4.png) tremotesf-2.10.1/REUSE.toml000066400000000000000000000023341523411710200153610ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 version = 1 SPDX-PackageName = "Tremotesf" SPDX-PackageSupplier = "Alexey Rochev " SPDX-PackageDownloadLocation = "https://github.com/equeim/tremotesf2" [[annotations]] path = ["CHANGELOG.md", "README.md", "**.json", "debian/**", "src/rpc/test-data/**.pem", "src/test-torrents/**.torrent"] precedence = "aggregate" SPDX-FileCopyrightText = "2015-2025 Alexey Rochev" SPDX-License-Identifier = "CC0-1.0" [[annotations]] path = ["translations/**.ts", "data/po/**.po"] precedence = "aggregate" SPDX-FileCopyrightText = ["2015-2025 Alexey Rochev", "2018 Carlos Gonzalez"] SPDX-License-Identifier = "GPL-3.0-or-later" [[annotations]] path = "data/icons/status/**.svg" precedence = "aggregate" SPDX-FileCopyrightText = ["2009 Mateusz Tobola", "2023 Alexey Rochev"] SPDX-License-Identifier = "GPL-2.0-or-later" [[annotations]] path = ["data/icons/hicolor/**/apps/org.equeim.Tremotesf.png", "data/icons/hicolor/scalable/apps/org.equeim.Tremotesf.svg", "data/icons/macos/tremotesf.icns", "src/tremotesf.ico"] precedence = "aggregate" SPDX-FileCopyrightText = "Transmission authors and contributors" SPDX-License-Identifier = "GPL-3.0-or-later" tremotesf-2.10.1/clang-format-all.sh000077500000000000000000000004141523411710200172550ustar00rootroot00000000000000#!/bin/bash # SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 exec find src -path src/3rdparty -prune -or \( \( -name '*.cpp' -or -name '*.h' \) -and -not -name 'recoloringsvgiconengine.*' \) -exec clang-format --verbose -i {} + tremotesf-2.10.1/cmake/000077500000000000000000000000001523411710200146575ustar00rootroot00000000000000tremotesf-2.10.1/cmake/CommonOptions.cmake000066400000000000000000000253611523411710200204740ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 set(TREMOTESF_MINIMUM_QT_VERSION 6.8.0) if (UNIX AND NOT APPLE) set(TREMOTESF_UNIX_FREEDESKTOP ON) else() set(TREMOTESF_UNIX_FREEDESKTOP OFF) endif() # FYI: # if (MSVC) -> MSVC or clang-cl # if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") -> MSVC only if (MSVC AND (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)) if (VCPKG_TARGET_TRIPLET MATCHES "^[a-zA-Z0-9]+-windows-static$") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") else() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") endif() endif() macro(prepend_clang_prefix_for_clang_cl_options compile_options_var) list(TRANSFORM "${compile_options_var}" PREPEND "/clang:") endmacro() macro(apply_warning_options common_compile_options_var) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") list( APPEND "${common_compile_options_var}" /diagnostics:caret /W4 /w44062 /w44165 /w44242 /w44254 /w44263 /w44264 /w44265 /w44287 /w44296 /w44355 /w44365 /w44388 /w44577 /we4774 /we4777 /w44800 /w44826 /we4905 /we4906 /w45204 ) else() set( gcc_style_warnings -Wall -Wextra -Wpedantic -Wcast-align -Woverloaded-virtual -Wconversion -Wsign-conversion -Wdouble-promotion -Wformat=2 -Werror=format -Werror=non-virtual-dtor -Werror=return-type ) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") list(APPEND gcc_style_warnings -Wlogical-op-parentheses -Wno-gnu-zero-variadic-macro-arguments) if (MSVC) prepend_clang_prefix_for_clang_cl_options(gcc_style_warnings) endif() else() list(APPEND gcc_style_warnings -Wlogical-op) endif() list(APPEND "${common_compile_options_var}" ${gcc_style_warnings}) endif() endmacro() macro(check_if_stdlib_is_glibcxx) include(CheckCXXSymbolExists) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) check_cxx_symbol_exists("__GLIBCXX__" "version" TREMOTESF_STDLIB_IS_GLIBCXX) endmacro() macro(check_if_stdlib_is_libcpp) include(CheckCXXSymbolExists) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) check_cxx_symbol_exists("_LIBCPP_VERSION" "version" TREMOTESF_STDLIB_IS_LIBCPP) endmacro() macro(apply_hardening_options common_compile_options_var common_compile_definitions_var) if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # Not Microsofts's cl.exe, but allowing LLVM's clang-cl.exe on Windows include(CheckCXXCompilerFlag) set(hardened_flag_to_check "-fhardened") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND MSVC) prepend_clang_prefix_for_clang_cl_options(hardened_flag_to_check) endif() check_cxx_compiler_flag("${hardened_flag_to_check}" TREMOTESF_COMPILER_SUPPORTS_FHARDENED) unset(gcc_style_hardened_options) if (TREMOTESF_COMPILER_SUPPORTS_FHARDENED) # -fhardened sets -ftrivial-auto-var-init=zero, override it with pattern list(APPEND gcc_style_hardened_options -fhardened -Wno-hardened -ftrivial-auto-var-init=pattern) else() list(APPEND gcc_style_hardened_options -ftrivial-auto-var-init=pattern -fstack-protector-strong) if (CMAKE_SYSTEM_NAME MATCHES "Linux|FreeBSD") list(APPEND gcc_style_hardened_options -fstack-clash-protection) endif() if ((VCPKG_TARGET_TRIPLET MATCHES "^x64.*") OR (NOT DEFINED VCPKG_TARGET_TRIPLET AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")) list(APPEND gcc_style_hardened_options -fcf-protection=full) endif() endif() # -mbranch-protection is not enabled through -fhardened # It also causes crashes on macOS if (NOT APPLE AND ((VCPKG_TARGET_TRIPLET MATCHES "^arm64.*") OR (NOT DEFINED VCPKG_TARGET_TRIPLET AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64"))) list(APPEND gcc_style_hardened_options -mbranch-protection=standard) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND MSVC) prepend_clang_prefix_for_clang_cl_options(gcc_style_hardened_options) endif() list(APPEND "${common_compile_options_var}" ${gcc_style_hardened_options}) endif() if (NOT WIN32) # Not Microsoft's libc so exclude Windows completely # Undefine _FORTIFY_SOURCE unconditionally to make sure it's enabled only when ASAN is disabled and build type is not Debug # Enable it through compile_options instead of compile_definitions so that the order of flags is correct # ASAN can be enabled via CXXFLAGS circumventing TREMOTESF_ASAN option, so check for that too list(APPEND "${common_compile_options_var}" -U_FORTIFY_SOURCE) if (NOT (TREMOTESF_ASAN OR CMAKE_CXX_FLAGS MATCHES "-fsanitize=address")) list(APPEND "${common_compile_options_var}" $<$>:-D_FORTIFY_SOURCE=3>) endif() endif() if (NOT MSVC) # Not Microsoft's STL, but allowing libstdc++ and libc++ on Windows # Macros for libstdc++/libc++ hardening if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") check_if_stdlib_is_glibcxx() if (TREMOTESF_STDLIB_IS_GLIBCXX) list(APPEND "${common_compile_definitions_var}" _GLIBCXX_ASSERTIONS) else() check_if_stdlib_is_libcpp() if (TREMOTESF_STDLIB_IS_LIBCPP) list(APPEND "${common_compile_definitions_var}" _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST) else() message(WARNING "Unknown C++ standard library implementation") endif() endif() else() # Assuming libstdc++ list(APPEND "${common_compile_definitions_var}" _GLIBCXX_ASSERTIONS) endif() endif() endmacro() macro(apply_asan_options common_compile_options_var common_compile_definitions_var common_link_options_var) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if (VCPKG_TARGET_TRIPLET MATCHES "^arm64.*") message(WARNING "Ignoring TREMOTESF_ASAN=ON for ARM64, it is not supported") else() list(APPEND "${common_compile_options_var}" /fsanitize=address) list(APPEND "${common_compile_definitions_var}" _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION _DISABLE_OPTIONAL_ANNOTATION) endif() else() if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") message(WARNING "Ignoring TREMOTESF_ASAN=ON with clang-cl, it doesn't work out of the box") else() list(APPEND "${common_compile_options_var}" -fsanitize=address) list(APPEND "${common_link_options_var}" -fsanitize=address) endif() endif() endmacro() function(append_qt_disable_deprecated_macro common_compile_definitions_var) string(REPLACE "." ";" min_qt_version_components "${TREMOTESF_MINIMUM_QT_VERSION}") list(GET min_qt_version_components 0 major) list(GET min_qt_version_components 1 minor) list(GET min_qt_version_components 2 patch) math(EXPR macro_value "(${major}<<16)|(${minor}<<8)|(${patch})" OUTPUT_FORMAT HEXADECIMAL) list(APPEND "${common_compile_definitions_var}" "QT_DISABLE_DEPRECATED_UP_TO=${macro_value}") return(PROPAGATE "${common_compile_definitions_var}") endfunction() function(set_common_options_on_targets) unset(common_compile_options) unset(common_compile_definitions) unset(common_link_options) if (MSVC) set( common_compile_options /utf-8 /permissive- /volatile:iso /Zc:__cplusplus /Zc:inline ) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") list( APPEND common_compile_options /Zc:enumTypes /Zc:externConstexpr /Zc:lambda /Zc:preprocessor /Zc:throwingNew ) endif() endif() apply_warning_options(common_compile_options) apply_hardening_options(common_compile_options common_compile_definitions) if (TREMOTESF_ASAN) apply_asan_options(common_compile_options common_compile_definitions common_link_options) endif() list( APPEND common_compile_definitions QT_MESSAGELOGCONTEXT # Needed to silence a warning when using QList/QVector with ranges QT_STRICT_QLIST_ITERATORS ) # QT_DISABLE_DEPRECATED_BEFORE can cause linker errors with static Qt get_target_property(qt_library_type Qt::Core TYPE) if (NOT (qt_library_type STREQUAL STATIC_LIBRARY)) append_qt_disable_deprecated_macro(common_compile_definitions) endif() if (WIN32) include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/WindowsMinimumVersion.cmake") # Minimum supported version, 0x0A00 = Windows 10 list(APPEND common_compile_definitions "WINVER=${TREMOTESF_WINDOWS_WINVER_MACRO}" "_WIN32_WINNT=${TREMOTESF_WINDOWS_WINVER_MACRO}") # Disable implicit ANSI codepage counterparts to Win32 functions dealing with strings list(APPEND common_compile_definitions UNICODE) # Slim down , can be undefined locally list(APPEND common_compile_definitions WIN32_LEAN_AND_MEAN) # C++/WinRT macros list(APPEND common_compile_definitions WINRT_LEAN_AND_MEAN WINRT_NO_MODULE_LOCK _SILENCE_CLANG_COROUTINE_MESSAGE) endif() if (TREMOTESF_UNIX_FREEDESKTOP) list(APPEND common_compile_definitions TREMOTESF_UNIX_FREEDESKTOP) endif() set(common_public_compile_features cxx_std_23) set(common_target_properties CXX_EXTENSIONS OFF CXX_SCAN_FOR_MODULES OFF) get_directory_property(targets BUILDSYSTEM_TARGETS) foreach (target ${targets}) get_target_property(type ${target} TYPE) if ((type STREQUAL EXECUTABLE) OR (type STREQUAL SHARED_LIBRARY) OR (type STREQUAL STATIC_LIBRARY) OR (type STREQUAL OBJECT_LIBRARY)) target_compile_options(${target} PRIVATE ${common_compile_options}) target_compile_definitions(${target} PRIVATE ${common_compile_definitions}) target_compile_features(${target} PUBLIC ${common_public_compile_features}) target_link_options(${target} PRIVATE ${common_link_options}) set_target_properties(${target} PROPERTIES ${common_target_properties}) endif() endforeach() endfunction() tremotesf-2.10.1/cmake/FindCppHttplib.cmake000066400000000000000000000036321523411710200205370ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 # Using macros instead of functions since find_package can set variable that we need to propagate to parent scope macro(find_system_httplib) # httplib breaks backwards compatibility on each minor version (i.e. second component) # Because of that we can't pass minimum version to find_package message(STATUS "Trying cpp-httplib as a CMake package") find_package(httplib) if (httplib_FOUND) message(STATUS "Found cpp-httplib ${httplib_VERSION} as a CMake package") else () message(STATUS "Did not find cpp-httplib as a CMake package") message(STATUS "Trying cpp-httplib using pkg-config") pkg_check_modules(httplib IMPORTED_TARGET "cpp-httplib >= 0.18.7") unset(module) if (httplib_FOUND) message(STATUS "Found cpp-httplib ${httplib_VERSION} using pkg-config") else () message(STATUS "Did not find cpp-httplib using pkg-config") endif () endif () endmacro() macro(include_bundled_httplib) if (NOT WIN32) set(HTTPLIB_REQUIRE_OPENSSL ON) endif() add_subdirectory(3rdparty/cpp-httplib EXCLUDE_FROM_ALL) endmacro() macro(find_cpp_httplib) if (TREMOTESF_WITH_HTTPLIB STREQUAL "auto") find_system_httplib() if (NOT httplib_FOUND) message(WARNING "Using bundled cpp-httplib") set(TREMOTESF_WITH_HTTPLIB "bundled" CACHE STRING "" FORCE) include_bundled_httplib() endif() elseif (TREMOTESF_WITH_HTTPLIB STREQUAL "system") find_system_httplib() elseif (TREMOTESF_WITH_HTTPLIB STREQUAL "bundled") message(STATUS "Using bundled cpp-httplib") include_bundled_httplib() else() # We shouldn't actually get here message(FATAL_ERROR "Invalid TREMOTESF_WITH_HTTPLIB value ${TREMOTESF_WITH_HTTPLIB}") endif() endmacro() tremotesf-2.10.1/cmake/MacOSDeploymentTarget.cmake000066400000000000000000000002061523411710200220310ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 set(TREMOTESF_MACOS_DEPLOYMENT_TARGET "12.0") tremotesf-2.10.1/cmake/VcpkgHostTriplet.cmake000066400000000000000000000016421523411710200211400ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 if (NOT DEFINED VCPKG_TARGET_TRIPLET) return() endif() if (DEFINED VCPKG_HOST_TRIPLET) return() endif() if (NOT WIN32 AND NOT APPLE) message(FATAL_ERROR "Building with vcpkg is not supported on this platform") endif() message(STATUS "Automatically selecting host triplet") cmake_host_system_information(RESULT platform QUERY OS_PLATFORM) message(STATUS "Host platform is ${platform}") if (platform MATCHES "arm64|ARM64") set(vcpkg_arch "arm64") elseif (platform MATCHES "x86_64|AMD64") set(vcpkg_arch "x64") else() message(FATAL_ERROR "Unsupported host platform '${platform}'") endif() if (WIN32) set(VCPKG_HOST_TRIPLET "${vcpkg_arch}-windows-static") else() set(VCPKG_HOST_TRIPLET "${vcpkg_arch}-osx-release") endif() message(STATUS "Setting VCPKG_HOST_TRIPLET to ${VCPKG_HOST_TRIPLET}") unset(vcpkg_arch) tremotesf-2.10.1/cmake/WindowsMinimumVersion.cmake000066400000000000000000000003421523411710200222140ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 # Windows 10 set(TREMOTESF_WINDOWS_WINVER_MACRO "0x0A00") set(TREMOTESF_WINDOWS_SUPPORTED_OS_ID "{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}") tremotesf-2.10.1/data/000077500000000000000000000000001523411710200145105ustar00rootroot00000000000000tremotesf-2.10.1/data/CMakeLists.txt000066400000000000000000000104011523411710200172440ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 target_sources(tremotesf_objects PRIVATE icons/status/status.qrc) if (TREMOTESF_UNIX_FREEDESKTOP) find_package(Gettext 0.22.5 REQUIRED) install(DIRECTORY "icons/hicolor" DESTINATION "${CMAKE_INSTALL_DATADIR}/icons" PATTERN ".DS_Store" EXCLUDE) set(po_dir "${CMAKE_CURRENT_SOURCE_DIR}/po") set(desktop_file_template "${CMAKE_CURRENT_SOURCE_DIR}/org.equeim.Tremotesf.desktop.in") set(desktop_file_path "${CMAKE_CURRENT_BINARY_DIR}/org.equeim.Tremotesf.desktop") add_custom_command( OUTPUT "${desktop_file_path}" COMMAND "${GETTEXT_MSGFMT_EXECUTABLE}" ARGS --desktop -d "${po_dir}" --template "${desktop_file_template}" -o "${desktop_file_path}" DEPENDS "${desktop_file_template}" VERBATIM ) add_custom_target(desktop_file ALL DEPENDS "${desktop_file_path}") install(FILES "${desktop_file_path}" DESTINATION "${CMAKE_INSTALL_DATADIR}/applications") set(appdata_template "${CMAKE_CURRENT_SOURCE_DIR}/org.equeim.Tremotesf.appdata.xml.in") set(appdata_path "${CMAKE_CURRENT_BINARY_DIR}/org.equeim.Tremotesf.appdata.xml") add_custom_command( OUTPUT "${appdata_path}" COMMAND "${GETTEXT_MSGFMT_EXECUTABLE}" ARGS --xml -d "${po_dir}" --template "${appdata_template}" -o "${appdata_path}" DEPENDS "${appdata_template}" VERBATIM ) add_custom_target(appdata ALL DEPENDS "${appdata_path}") install(FILES "${appdata_path}" DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo") elseif (WIN32 OR APPLE) message(STATUS "Building for Windows or macOS, deploying icon themes") install(DIRECTORY "icons/hicolor" DESTINATION "${TREMOTESF_EXTERNAL_RESOURCES_PATH}/icons" PATTERN ".DS_Store" EXCLUDE) set(hicolor_download_path "${CMAKE_CURRENT_BINARY_DIR}/hicolor/index.theme") if (NOT EXISTS "${hicolor_download_path}") set(hicolor_url "https://gitlab.freedesktop.org/xdg/default-icon-theme/-/raw/v0.18/index.theme?ref_type=tags&inline=false") message(STATUS "Downloading ${hicolor_url} to ${hicolor_download_path}") file( DOWNLOAD "${hicolor_url}" "${hicolor_download_path}" SHOW_PROGRESS TLS_VERIFY ON EXPECTED_HASH SHA256=a02db5e1b203d981701481bbef8b6fae1c576baf34927eecbc0feae0d2cb9bc5 STATUS download_status ) list(GET download_status 0 download_status) if (NOT download_status EQUAL 0) message(FATAL_ERROR "Download failed") endif() endif() install(FILES "${hicolor_download_path}" DESTINATION "${TREMOTESF_EXTERNAL_RESOURCES_PATH}/icons/hicolor") set(breeze_download_path "${CMAKE_CURRENT_BINARY_DIR}/breeze.tar.xz") set(breeze_version "6.27.0") string(REGEX REPLACE "^(.*)\\..*" "\\1" breeze_version_without_patch "${breeze_version}") if (NOT EXISTS "${breeze_download_path}") set(breeze_url "https://download.kde.org/stable/frameworks/${breeze_version_without_patch}/breeze-icons-${breeze_version}.tar.xz") message(STATUS "Downloading ${breeze_url} to ${breeze_download_path}") file( DOWNLOAD "${breeze_url}" "${breeze_download_path}" SHOW_PROGRESS TLS_VERIFY ON EXPECTED_HASH SHA256=bc8c2337802837ff1809269a8e4c4311b93e7e90c78ab4fe2a86cf5300ffd414 STATUS download_status ) list(GET download_status 0 download_status) if (NOT download_status EQUAL 0) message(FATAL_ERROR "Download failed") endif() message(STATUS "Extracting ${breeze_download_path} to ${CMAKE_CURRENT_BINARY_DIR}") file(ARCHIVE_EXTRACT INPUT "${breeze_download_path}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") endif() set(breeze_extracted_path "${CMAKE_CURRENT_BINARY_DIR}/breeze-icons-${breeze_version}") install(CODE "set(breeze_extracted_path [=[${breeze_extracted_path}]=])") install(CODE "set(TREMOTESF_EXTERNAL_RESOURCES_PATH [=[${TREMOTESF_EXTERNAL_RESOURCES_PATH}]=])") install(SCRIPT InstallBreezeIcons.cmake) target_compile_definitions(tremotesf PRIVATE TREMOTESF_BUNDLED_ICON_THEME="breeze") endif() if (APPLE) install(FILES icons/macos/tremotesf.icns DESTINATION "${TREMOTESF_EXTERNAL_RESOURCES_PATH}") endif () tremotesf-2.10.1/data/InstallBreezeIcons.cmake000066400000000000000000000120451523411710200212530ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 cmake_policy(SET CMP0011 NEW) cmake_policy(SET CMP0009 NEW) cmake_policy(SET CMP0057 NEW) cmake_policy(SET CMP0177 NEW) set(breeze_icons_destination "${CMAKE_INSTALL_PREFIX}/${TREMOTESF_EXTERNAL_RESOURCES_PATH}/icons/breeze") file(INSTALL "${breeze_extracted_path}/icons/breeze.theme.in" DESTINATION "${breeze_icons_destination}") file(RENAME "${breeze_icons_destination}/breeze.theme.in" "${breeze_icons_destination}/index.theme") file(READ "${breeze_extracted_path}/icons/commonthemeinfo.theme.in" commonthemeinfo) file(APPEND "${breeze_icons_destination}/index.theme" "${commonthemeinfo}") # Keep in sync with QIcon::fromTheme() calls in source code set(icon_files_to_bundle application-exit.svg applications-utilities.svg configure.svg dialog-cancel.svg dialog-error.svg document-open.svg document-preview.svg document-properties.svg download.svg edit-clear.svg edit-copy.svg edit-delete.svg edit-delete.svg edit-rename.svg edit-select-all.svg edit-select-invert.svg folder-download.svg go-bottom.svg go-down.svg go-jump.svg go-top.svg go-up.svg help-about.svg insert-link.svg list-add.svg list-remove.svg mark-location.svg media-playback-pause.svg media-playback-start.svg network-connect.svg network-disconnect.svg network-server.svg network-server.svg preferences-system.svg preferences-system-network.svg preferences-desktop.svg preferences-desktop-notification.svg preferences-system-time.svg system-shutdown.svg tag.svg view-refresh.svg view-refresh.svg view-statistics.svg window-close.svg audio-x-generic.svg image-x-generic.svg package-x-generic.svg text-x-generic.svg video-x-generic.svg application-epub+zip.svg application-pdf.svg application-vnd.efi.iso.svg application-x-cue.svg application-x-executable.svg application-x-fictionbook+xml.svg application-x-msdownload.svg application-x-subrip.svg image-vnd.djvu.svg text-x-script.svg text-x-ssa.svg x-office-document.svg x-office-presentation.svg x-office-spreadsheet.svg ) # Replacement for file(REAL_PATH) that doesn't work on Windows function(resolve_symlink_recursively path output_variable) while (IS_SYMLINK "${path}") message("Resolving symlink ${path}") cmake_path(GET path PARENT_PATH parent) file(READ_SYMLINK "${path}" target) cmake_path(ABSOLUTE_PATH target BASE_DIRECTORY "${parent}" OUTPUT_VARIABLE path) message("Resolved to ${path}") endwhile() set("${output_variable}" "${path}" PARENT_SCOPE) endfunction() set(files_to_install "") file(GLOB_RECURSE all_icon_files LIST_DIRECTORIES OFF "${breeze_extracted_path}/icons/**/*.svg") foreach (icon_path IN LISTS all_icon_files) cmake_path(GET icon_path FILENAME icon_filename) if (icon_filename IN_LIST icon_files_to_bundle) cmake_path(GET icon_path PARENT_PATH icon_dir) cmake_path(RELATIVE_PATH icon_dir BASE_DIRECTORY "${breeze_extracted_path}/icons" OUTPUT_VARIABLE relative_icon_dir) set(destination "${breeze_icons_destination}/${relative_icon_dir}") if (IS_SYMLINK "${icon_path}") if (WIN32 OR CMAKE_HOST_WIN32) # Copy original file with linked name resolve_symlink_recursively("${icon_path}" original_icon_path) file(INSTALL "${original_icon_path}" DESTINATION "${destination}" RENAME "${icon_filename}") else() # Copy the whole symlink chain file(INSTALL "${icon_path}" DESTINATION "${destination}" FOLLOW_SYMLINK_CHAIN) endif() else() file(INSTALL "${icon_path}" DESTINATION "${destination}") endif() list(REMOVE_ITEM icon_files_to_bundle "${icon_filename}") if (NOT icon_files_to_bundle) break() endif() endif() endforeach() if (icon_files_to_bundle) message(FATAL_ERROR "Did not find icons: ${icon_files_to_bundle}") endif() file(GLOB size_dirs LIST_DIRECTORIES ON "${breeze_extracted_path}/icons/*/*") foreach (size_dir IN LISTS size_dirs) if (IS_SYMLINK "${size_dir}") cmake_path(GET size_dir PARENT_PATH category_dir) cmake_path(GET category_dir FILENAME category) set(destination "${breeze_icons_destination}/${category}") file(READ_SYMLINK "${size_dir}" original_size) set(installed_original_size_dir "${destination}/${original_size}") if (EXISTS "${installed_original_size_dir}") if (WIN32 OR CMAKE_HOST_WIN32) # Copy original directory with linked name cmake_path(GET size_dir FILENAME size) file(INSTALL "${installed_original_size_dir}" DESTINATION "${destination}" RENAME "${size}") else() # Copy symlink file(INSTALL "${size_dir}" DESTINATION "${destination}") endif() endif() endif() endforeach() tremotesf-2.10.1/data/icons/000077500000000000000000000000001523411710200156235ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/000077500000000000000000000000001523411710200172625ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/16x16/000077500000000000000000000000001523411710200200475ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/16x16/apps/000077500000000000000000000000001523411710200210125ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/16x16/apps/org.equeim.Tremotesf.png000066400000000000000000000013261523411710200255440ustar00rootroot00000000000000‰PNG  IHDRóÿa�IDATx•�íK“aƯ¹=o››†šþ ±ÆŒþQ‚”27Åo�¾ê€…ï´ ƒF„�ÎI$®¥PR@Do€®A²p/>ϳÝ�û (}kðÛ9Üç¾®û\`8{Ø jö16S£Œì‡�ŠœÍP¥»Ÿï—@þݶ޷µ‰ÝHDü!¾²pw÷·Èf·¹_ïéáYžøÒÙ)H·|ˆ³ >Íb™H�årYñãç7îã<³HÌz¸Ž^gƒsÄ»À"ìIBŠÞn¦Årê 9›%èîWê» `æd¦$]»yC¼|õB<|ô@Èþð<hAßÀ@ìjȼêǹv=r`�àþßyo8d†bèî½¼?=}[Œ��ˆÕÕ”H§×E&“»ºD2µ"ææeO³U‘L&E"‘2¿%zH[c™–jx<ÈårðûýŸ˜D0Äv6‡ÌÆ&>l}ä¾½½ñx�@¿²Û0 7LÒº,ˆ¦*¨Tª¨©q :<„7é4Ìý}¬¤ž ±±ÑhzL× (Š©uY¶ U!ƒj…L*hm=Š�ŽŽNìKlàÖ5P,455¡P(@7tÔzj!µ´�EÕP%16in>‰µµ×è …ø|qñ)¼^/­lBÞ7hƒºúÜs·Û`áÎÎZZZxàt:ñda�+ÁgÊ’Ïç)¿�ú:ßQ¯Ï—ˉ¹ûsrÈݲ†NÐÊÕª@±XD±TBCCmä;Œ`C×4„ÃýüqTEåz„ÊÕ¶-Ž!) à ªÕŠ¹´¼¤ú|^ÿÙä^¢×Êå2ÕòA-AÓ4þ˜º®Aj8!v&4Oûâ�?iÿžA¬b�ñ¥[IEND®B`‚tremotesf-2.10.1/data/icons/hicolor/22x22/000077500000000000000000000000001523411710200200415ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/22x22/apps/000077500000000000000000000000001523411710200210045ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/22x22/apps/org.equeim.Tremotesf.png000066400000000000000000000022431523411710200255350ustar00rootroot00000000000000‰PNG  IHDRÄ´l;jIDATxÚ¥”_lKQÇ¿÷_o«k·µ5­µÂ6&62A‚„�ˆHH¼H¼ ^x A  *ltã$"ˆa³Íbm{×{×^¿s²6¼p¶Oî·§ç~z~¿{r1kÖ¬ {�ÙûEñë!›-žf§Õª�>qØ OrX^¯ªÝÙkö §=À´lWñà}óðáf¤ª*ÃÛE‹Ìc'�š‰D·©iqžïΟßgMcQ‘yhú«xÐv0Ïgqœ¨9´ÏL¥R žwþ²æAÕ>ø«˜Ê™L šéj¤ÙJlßµÝüðñ�Ùúâ¹Éò '{ mèÉn`ÜßÄæ-šW°`Áoš [6š÷Þ3¯„.š,³¹,\rˆÈÎ|ûBg^NX1¤Ï¢„pšx\CôGß:Á²(#œF�ÌotO›Óé¬ÌvÉ™ Ëù0…#£F–©”�Å¡: �çd2‰žžžú ÑÏÞ ­“‰?¾—´·¿< 8íËì8éš™““£²›ê QW[‡zÊ1Ú¥ „Èó•«!OqúÔ<|ø !+rQ8.ýM¬Ú¬î|7ûuô/è�K—.ãòå+¨Z²”Ê�#?/Þx®Z¼.\DMM U”@ŒæœÎ\ä¹�“[,ÊHOÿþÐ4 “&MBAAòHÖÚÚŠÎÎïøòµѨÆ2ŸS�À T”—#‰Âí·ÍÚo|q(²Š²<¬€ÄŠEF9-¶Ûí Öàqc#:»ºpóÎ]ܧ²Y~ÖÔ„ô9jDEF>U+ÉÒhr‰±ÇãqI‚4ØíqÓÎe^²aèüa½jo§Ï^4?Ž–Ö6žÛZZ J’©|^œ‰]�Di8GFì øÆ¨ªUIè:tÃ@wwsæÌµ†ä^¿}‹öW¯ž’®î.„ß9ñx‚÷�]P…S]=žÕOÎ]Èé¶LÑÁ´§�¦¦&³$I%^¯’$¢:X «ÕÊyñìŸ#¸sï>=yÆíW¯^"Ý•ˆ‚Š^ß[E¾„,r¤‚4ÆãöÀd’QP�QÉ6¡µµyyyxó¦Í-mÜnmi�ÙlŠÖŽ„#×g®‚d,`Ë(**ª’M²œ¤‹K$èë‹`úŒ°Úlø�ó o&§„nSAôS ýX¼d ¥Özåp{=�ŒÒ°ââb_–€l6WÛlv„ûÂ�e™ý8wö4–/[†$ &HÄãvsâ±(߸fÍjœolÄØ²2X-V0ɵç¡8&g]2)¼4U…Ëåâåh _·n-êëgaUMäs ± œ={uuu‘¯¥4ñJÊ>ÍÍ­5™ð D©$¿`íg°äX%'Tïœ`pnÞ¼†Òâ"øGâÖí(/‹ÞÞ^â#è‘Áb± ¥¦àム‰äSÎŒÀN¯¸Èï÷#@'•eûÑ6Af›76?mãùs|.cž§µz| ŒF‚A(à > ¨š¦8Ü>ŸV¨’ ~ÙøÞ'©Oè› ²ÞËFý›l#¤ÁÞ¨õôô*cÊPYU5\¼x�RÒ/觤ˆ~ ò>Qüyn``€?4ª@Á’cùâ>ª¢D"Ùõº¹ v»ú[ðx¼¯·‡ýÀËçÝ:.7\N_=íïºõˆCH7þS GŒZ¨©yœ8I#Ø?¢é> G¯Ô}¦8éHƵô2ë%“iÎ?A{u?| Y4jBøO ™>¿Å€|ïxCYIEND®B`‚tremotesf-2.10.1/data/icons/hicolor/256x256/000077500000000000000000000000001523411710200202235ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/256x256/apps/000077500000000000000000000000001523411710200211665ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/256x256/apps/org.equeim.Tremotesf.png000066400000000000000000000651561523411710200257330ustar00rootroot00000000000000‰PNG  IHDR\r¨fj5IDATxìØ± AEÑQ¬Ã&&5‘°`S ¨¢ˆZéXÆ‚ÿ<8 7ø- ÅŸ?@Xµ²3³e«;3KRà°HÒÌÌ  `ž €}ÓׇÓ~<Þ—ñúÞgñüÜÆùzÓn;ù“{ÖbÙQEWÕ9ç¾ú¾z¦'�ŽL2éIÒy� d4 "DÑK@@#êW@PgÕ/ˆ?B¿üòGAâœI„ü F j”ˆÉL™Ñt¦ßwºï©*÷âì¢Ë‹€ äŽíÙ°X{ïªâö9sתšº' ·u41 üúú½ØØØÀƃ‹?ûüùóX]½Ã¡‰M,4ʲ´)ËÖ[eÙŒÊò̬,ß%8?-ËG_yí°?Âx´ŒÑp¼ŒG5®ÿñê{®—å“òw½ß—å»MYnÎÉß¼V•åé­²î”eWž£�ç2¸í£‰ÿi46Oƒ`E°6înY{6—žú™pnLßÖõ!Œ¤7²KɲsáG‚íp}k ívKÝl¶Myïá*ÁW¿µa VË:‚L`¡ÎžfzåʡО¶ ì ï8cv„™ïIo¿¶½¿¶ üùïÀõM`se29@�Ü;ôãÀà,°2T�/Y{_x¨ <öak0ÆäQšFa��@�ŽÙ´–ÑV�ßo€,ŠÕZ‹(â\YÙ«keJ¶ÊÆÚcñ+¬öLÂ6ŠA0 …sØ•~»ÕA·³„<_”xT³ í¢…¡Ñ™¢ÀеXÒg†÷µ˜É‰°½ö½ÖˆµÎa0wì+;í º²îÂ’÷Nq¾µðºæƒzº˜¿=ƒØ^}Ûû_¼&õp2yM4ðo>ÿ4ðÀ�Yö¾å>&Bÿ¸Q¸™ =Kê<ö˜ sGd�uìÅq²!dÅͰ̣!èH‰)aŠàÀ9øÃCüm6C‘·Ðñeº~�gZí6†ã1–{=¬JÞç;Г Å Ý9¡ â¯k0' ©Ô2t¾� ÉÈ3™ãéWÔcš{æ@[êÇ�``Í<̱+WþºkÌ�·¼ÿùUàåç�7å¶; ‰ÆDôÙç€Gï±ö³"ø/[ ¨…l ˆ– 9S�.L[ÈYç9K¦ˆÉ*næQü ë8XSì*ü8F°ö*�á÷÷öÐÞÚ‚÷DÅá…DsÖé sÇXZ^Fi â@‘Þ$S˜<^Uäz­ìë˜#+8æ•ÝÑkyöŠ9M@Ç«ªóŠ�Ú 9̸Ž,¨ïïj‡pqlÌÅ{êÓ›o½ðÂwïýO~¼Þ˜Áÿ¡ðvúCÖ~õIk¿–cŠ4 ½­'Ež×c*ì¼ÕÅJΊ–h·kæ….¹¡�¹FÂHLc�ˆžÁÞüŽîõK]ÉîߺyëÙ($šÀ‚â8yî\„ßZ[CwÖ"|/ì1'$wdš€ ÒùN1£aßò4‚#eâ(„»ï áÙ;�yöÀ7^|ñÛßsî‡b3 ‰ü¤ïø_ʲO�³ö¹ÜÚa›Âçå/³]h!âí¨ÐyÄ%g*r²¦Ð‰ùœG‘+(ìDôÉ @Ù˜Q]Ǭ;¦©*ä øÍö¶îÀ4�» ‹@[Öï#_^F¶²‚BÌ€ïÏÆgKCŸ+ë0·ó³V3 à™ÿ³ b„þ˜kHç8YÇz&9�a&ë‰#ç(~L½Ç!qïYïðc¾ñjY~~s2y¹1€ß,ŠçFÀ3{�—Vy¢CÑ (øo×…m§ƒ\`†"Ovzæ†LH� ØSÁ3�b'@°ž}""�U$Æ9~±ë»‡½=ðó{2Îåœû‡næ°¦>ýôz dbëäƒÏ<é3!2ÅŸYÍ Fr@4…(~áˆ0�‰a2w‚êÖ-8š€Ì;’õ‡ÎáÀ{ï O��ÇŒùåëeùôµÉäG�œÀè¿ôÒý£,{†»ûXD´,Âí º*úB8ëõjÁ+†L‘+(êTð主§âE¯@™‘€÷HEA6*h­ëULA €óýB €ÂÈ8{úØ›?å°Ö0jp }Öš“;­�Í€L#H9ž tç"v!F@1'uGPÉ\šÂ-ᩬ٭*Üä¯+Þãœ1ß¾,úÈd2m àûŸúÄSçF++[²ƒDì§§]AÁÝ‹"—Ü*ÿKѧbgEÎ<=ò&ÿße/�Îaç‘ãܺŸîˆš³ï«Š;]ü2K>ÃZï)(¿ ‚žN(6'ð<�ð’{$&Àš1ÿ®ñÙYGŽ‘®ƒäüwMBŒ;ƒÕÓA& ‡Ä2¹8AKÐä4™{Xßœzï}ëŸÄêêåƒ76ø�Ë—/ÛK_¿´þȃ}æ‹—¾ð•ýݽ3¯Tů~�Þ`€Ápˆž¿-à‘ÕPüÉÌù–< �AA –¨ªZ#€c#PæZ�SÑ«ðä�Šžp®î)œÔ�±ü¢ò€ƒ}ý"„wú ŸgÀÏwáý}ø�¸V Ü]ù>CžGÁë;HÞ‰€ÌP>6IáÀD:9éÏ›ä?Ø»ÖÉŽêüÕ}tÏÌÎŒ_û0°‹±cl³ƒB‡qD2HQHB„Bþ!Œ’�QE Ž,EäO�(‰P‰<,„D@œMÌ3ƒq¼¯½¶÷1»óèî{oU�Ü{tö¨TšNkĺ{fSŸõ¹êöíÛÓ}÷ž¯N�:UE¡Pä94y*Ï9^CÝ¿£ˆºx*®ëN­¯³|Μc‘ø™ß|ûgÞø–;qúôÙÿ{C ÀÇ,Â*ÈñušoÀï7y΂Î9-Q–*ìD„¢­ªâû k±ÿÜxãÍ8tpøs7¾úÆ/\ÿê›N}ýáëÓ?|ú{IvAËÿÉ¿þä'ï¼óëÚk¯E¿×ã¾²­Ùmd·Ð~&?(u­$·îaÿ60z9Vêq–1MXo¹e뉀Ö5~éð°V3¢ÙØàþk–±ЖÙ,†™YV€œãïã67ÑO’@$‚z䳯‰€–Üú ƒ{^ÏÇ*áçeânAÒŒ‹®,ኂ�Àì²,¯¾òjû½Ç¾½^¶g~ñ»_í?üó�?úè%ØÁø£{ï=ð¦[ny÷�ýû1?7/φ…'/cê¶iPW� û +D”ãRŽ¡;?ÌQ©­\Üâ CD]�-ÁYË™pMÀêú±M£Y‚3˜  Y}Ü¢J~B3Àjk5÷AïÓ„{0Î )ãaCmñ¥d± [~€ßüóàîXÉp€r0ÜÀúÆ: ^^\ ¯º>ûá?ºûqàדì`Uu{c›|0€]ÅÌ ©¬¯¯a¸¹‰¹Á#" IÚnØÚs‹#“P`Œº�zhàQ=výA‹7öá�Z³pDÀwÆßñb -MÐ ár†A@6¤ÍMÔl¿Ï÷+±ÿûÇ߃¸ÅE I}MºkˆÄÁ @L8ÁZî²0»:§�u�0†Ì"Gc›·ag# À™3«ù… kXë‚RN‚tŒF5ªÑÃ� ú(³Lóú ˆƒu¡qÇnþÿ]�ßÅ &°GÆà[r"KËf8ä—D´˜3'FÔgÛUÃ!{UÙv~säÄõÐØõõ¸Í=“ŽLp/e$F½Î è iàdRRQä-KýþÎ9ÎØX˜$;à †CþGγ\ûȽ²€!ð:ê@S�ˆ…Æ èñøþ¾ÖÇz,*±[:)Ц©À®3.ç¸ëb� [æ¬@α(U·QY³'al ô†"Ï Ž0âã 0T(d¢Q'®:ó°,ûX˜_€€à‚[ð`‡# À`00kë°Ö ñH”܃®KP55 k¹¥ª�AÀu.ÆÎØÂXͤ׀qÃ[[‹@ˆñ‚Î…g÷´’®‹q™¡Àð ä@÷ýŠ… ‹šÉŒ©‡—ñ¹ø˜‹ð8> BǺ¥ëp9ªFØØÜÐÏòÎc½=^_[O°Ó±¾>0§_<�§žú1æ!À Ï¿€óçWÑsŽ�©Ð\ìB÷xƒÕ26Þí9&¯ €ú⤠¢Ø1Ï„ÀZTY†Rïéd„FîÄ÷)¶yM$:zo›@ž}ö$¾ÿØ÷ÃkÙøWW/$ØáèÜ4söÜ9;zE™C€sgÏam}{ý‡¶ò0˜Ð8'aûï™ü¹ÎëC*ßÙ£sNžú(�ýKz®Qµ"ªÌöŒ?þüm½?:¿�ÏÕ{ë8)O?ÿ<ž|òGPÀ ª*¬o®#K°³ÑT6kê’L¹œí«ì•€É ‹ItŒV¢í¶Z° a™,J66g¥¦Y òßgº(Æ ˜ÌOøùS€zò<¨äE�^¯yµ잫~‘§U�w2œéÔz°9Ï™—‘Éšƒ @téŒeCt©¨æ�‡'Nž ½óº8‡Ó{y‰~#0†/Ý¿™ ˜ÉПŒr`4a°1`ÑÛáH€“ˆîêêjD¿ßc5ßÜØ@ÝXuõ„ s‰Z“— ·RÄ€® 0‹T`ç¹  ÉU.0&ÃÜ X6øÕs«¨ëUU³û?U°Öý$;D´n-+5 Ày^>« ⬡ËxyÔ¢ÐÎXUT° @K7`·ß5\WQÝÅà‚.À©S§@‹‹šT–<0XÇÎFêô�yhs8Z›[XÀË^~m·s ––Q”~X•nR¿»ç  ºâÓ¤µ–Ù°G ´¾›ï-ŒA¿ßÇ•W^�—¿âeØß>C£ª†õî’ìp,//ëjô‡�=ñC_Y‹ƒ¯<ˆ×~ ßz ®Þ{MÐRíNêƒê,\GÏåLhmOØí÷”évÛ®½þ�·â¦Ÿ~5®Ù{5žk‡��;yÂ{ÿçIv®^ZúøÙÕó¿ôð‘o¾øõ‡Á‰“ÏÁ GÙë]6kc\K;#6�娸�»»�.�Þ\Õ¨ÆOü>ô5<öƒ=@õð¶}ËËO%Ø%Ø·´ôo«ÏŸ:tüè‰_ýÚ׎|þ_¿ôàæÿ=¿» ?ô40+r ¿ËEµ‰îïù¾øÏ_:ö­o~÷ÞÏœ9|åÂüJëYžÃîBZìàÁƒ5€û„ÀéÓrÆüEKͬ+â v¶«æ˜§±ŠL9X½O·þ ƒc¶Îd˜<ü(¥P>ùäí >‚²Àå…´*ðÉâµ1(ezhÖÒ„SH¥ ¢€’�Î}O~Р±5<V s¶Ï×àz@2F�¿r•d2r—¦iF¸ü�àd¯wß~ïÉ•¥©ò¹<åEðžIÑ\ôh"ÊØœu}ÏK,ìÁX Îpn&à¼C]Õ øû¼Ä€™4¿¢elðÈ2�NçÖctpbü£¦Á€[O¶2æÇß úÁJ€Ëßüæç/9rwžeÍŠ‚g­5-{ a.4"¦e<§œÆ¼ñ(ÆŠÁ¸sDƒBzÏßÛÙ¦>öäfâÔM E%|<Ùš'u|~K×R¿N-3YÜE‡(eBUMÄ cCc0P�ÅË'zïå»CPê ºãŽ?{ò;ßy຦ùÛ¹<¿­� >ËŽyŽÒ]($�1£�}#6“¦¤Ž€ø½ãf°éßq-sYèÒÊbD4hš¦ed9¯¸›…nŒÊk,Œ5py=¬c²ORÆ[§Q°¯B° ±î0숸´2‚·%×¥t-7�Ï=Nô{7¯¬<�ËioÀ¥7¼á¿?{ÿýoú…¥¥·^åýo÷�ùå’7ñd`-qàsÙÅRZ..;ÈCÏD\òm CK/ÆçPÖ5Šª‚³NrÓ ÞÏ"`¹ `ÊÅâ"ʹ9N¹ž“ûþv ê˜|BAÔß/�ÂÆ.îe ñÀà%S‘�\7m:Ê9¾ŽÈ‰>}ʹOXYùæÍ¸Ü‘@ѺyÀ}ƒ–�9²÷@U½åJ¢ö�ysže,]™b�Éë²Ý73†LʰC6fU nåccˆV»�µ€Acn…M†Ù”ÉzüT@YrÙ‡†oÐäU†™�Ò‡ôžéºRŒWŽÁÆÔmð^{w,À/œóþož޼~eeóf/3ÄõwÜqÀçê–_¿ÿþ«~ª,o^n_~¥4æÎ<Ëru¿…ÖA�’iB×X^cŒq™M$âld-*ï$è�yÌ&è,¼_-ßkàï•ÇF/uŠºK^êÑsÉF»•¡óy+‹�Š„¢ÖÆÈ˜/ ŒùòYç¾½pìèq|üÃ÷ ®e¥`1R&7 ƒ9øÊhÑ�‰y: À^C2ÿi# @B]ª«ZÖ¬QU#TÝqݲ±"Ò-`z6Œ½)"O`‚7 Þ…K‰@iMÀY!y oÎ9UlØyQ Ï3dY®C€Šx½=Oˆ…€œ º[®Ö“g9 2ˆ L ÉH¨*Àq+O`¨F�˜¨ZÖ5{°M£‹‡º`AÏ%EÁB.Cò{Æx:Š0m$$HèË2â†àéâ&5Çj.kðkÖ²Px�ÈBœ€À0õ?]ŸPÎ Bq00üY©0e$HèQψ»hKm­ÇØøØŽNw†Æ€ÐøÙÐ5{0“ÒdšZÌ„�_Ï Ö �� !Ë ò¼�>˜Øc!à:S»�L(ŒfnÁ0Ÿ€3hÿ’$ôz=c²eQ (/Š@¦ãúœ À-¿��GX„` Ï¥ µk�™ Ì IxÃ^‰²ìX ��‰ t«± h{žI²©)0a‹�*"8 I¦ cP°ñ÷P%׋¢@–gá~ƒ-½Š€³.HR`Š7A[ü„ fV@B€ Èd'ã‹^@Á" ûcÔÕ—e¼$XÈ¥Š€Æ(­ˆ¨èL–Á#%%H˜:*TðÎi´>ç]‹ ”EÇ’… ÏUB�@'þ„yý:‘ˆÂ BˆðЄÿKH�0]˜ÚPg´áÔ_#;çE�BÈ"ÀB�H‚Ä e˜ @H (d�Ð &%$H¨Ð46píÅ…7™AQt,�yà d"Ø"÷ŸK Åúå?Ä`û§dþ3A€” Ìëñ�¸´Mg-0ˆ$Q‡ Ÿ»‹ÀŹ-�‰¦³ÑÇ^€2†1-åú ! @‚wNWæm4óÏBºÐŒ½œ…@É]‚¬w :ŠñG ‹0¾W=€„$ Ó»ý0œ,Ó‚-¬® Þœ´Ã­¿ˆ€zñ¬Á8ß? r©þÿŒR�’$T2'ƒ§x€$ütt²Ä02‚,cª(·úà2žÌ€H�0uTb•yn` ‚¡=6þH4õWÓzÅ#PO€)Â-Ä€Ëü!�ÎI’x¢-‡õœçEB•"º<¡¨˜Ìp©³þ"DAœ ’$�L6�ËmúaeÅ`¡×‰@ñ&"Ðà` ŒA �Ç,�� �ˆtHO 4ÒÔ_!¬ V öâÚC ^E äâ„„´&à®Äòòò ý_óžn"Ð!ƒü *Ëò sÞ{Ž®ƒÀ(ŠB÷Úkš†]pH¿ºÈ 5Œº©Õ=60<]·CS7aæâôól$Ïìë`eC ôgo“ö0Æ]7Á0�sž�³Œ@.ø�1³ÿ´‹/¯máHw↯ÿìW.}ÖdeQêõÖÚ`±‘Œç*ð�çÜ�j\–z ßW¬Ì3®³ YÎMÐ{k%)Jн®ŸY7õ:Ïø–ü‰¼(Ÿ±Î?Z ª/¯­­Õˆ‘³²²‚Ë×]w]Y×õ[Ë2{�sô62Ô7dÔ¥•‡lÈU ‚¹<À]½á%¶¼œã8†Z…�ñËМ Jf@"&<l¹z �µ€ˆ��[h¾fssƒ âýï?ן9y‚ÏÅPƒÖÒ Wþ‰7 Ñ­ÃdñÐpÐúüü<ñЃauõöíÛ×­QþLþM 5þR—³"ªlÿYÞ^Ç÷\îAÀS�zNaÀºEšvkd}„,¼N…°×/UXmÝœõŽ>o�ûûÕWÁå�$û÷_óž…=‹Ëób¯L�Õ|VyàÀýi°ñó8 ØÈ¹%²�“×ÙÕ�»cÛØîœ>Üe¯§Fc­ƒ1, šŒC†@N–Ö6€Wö#gCÑ]~Ùé 6Ì çÏãä³'ñ�ÜÕ À:N<3F&D ¸ BýÞã8?× À<Ø `ðªW½’¿;ˆ[wxG`M4Ä“2®|ÿ¼‡h�Ч³.ð™¤.ó½kX<¡âDÚƒEštK4�A‡F›&ð J½GuU…‚ë÷­®®~ Œ$—‘áïß“—Ù_.îY|_k°š1GDêæv­{f ×ëºæÖՈ˽°g¡;§Ûp5�mÙðõlüÁƒ¤.¯ôÍ!k †ÆŸÉB84ôçÕE€Œ\1Ì���Öè�ã®»>ˆ�NNßZ&A\�1NæææY¾ò•¯ÀY�C‡��Î(ä‹õ3õœÞs î�°ˆ§Ñî‹ó>òBôûëð¦Q�›ŸC¯,X¨=ñjÉú¹íkÚ­ªêZ»Ú!Bm]=lþÁÿ²w&Pv–e‚~ï÷­vÈÊ–°I:‡T%$!h+:¨D”MÅF›»iÇvlÜzPeU�Á¡UÑnPTP”nÑAlGQpO„€B BªRû– ™ï9ßë{îüçzsoòߪ¿âýê¼çû—[wù¿ïÝ·ÁCi€DÏÏZ(u~«£½óØt*wòº¸˜;L’Æm¼ŽÉ¦ 4„–­¶mÛ3òØc�™XËë4ÖEù:{?â0¹ä’KdtlTþð‡h€4@L5Éår’Íæå^GúÙ#<Ã8õM”l6++V!]Ý]’Τ$¥Rkq75¢Âþ0夤�ɉÉ;·oßq¦{�Ù–p��T*m{¹íØ\6ë|Ú‰âÒ<÷F�´@8?¼:“Îzõo~³YÄêèèpj… ëvuuI{{»ýo5$®~ÝÆ~ÿŸUåáG~æŠûK˜¸V%4¸ú÷„£3N<ñDžÍ¼zCX»ááaž‹ Ê3Ï<#�<òs~‹,[¶LŽ9æHI¦Súûª¨Ž0p™¤©L6óŠîƒ;/ž¸¡EðÈçÓë³ùÂYÅbÐQö üæ*�šIÅÅo� yjÛ6yð�1tÉš5kä”SNañ�J©!$Ó¬™açO>ù¤Ûà…9÷ö �*ÿ—ioô„×<§1}}}ròÉ'Ë<ãîHF´Nß±c‡<ôÐCò“ŸüDüqyê©§díº>G¸Û”1øº ¤>¶{ƒ‘˼ÏûÜúÞì~ßH‹,БH¥¯,ŠX™áêFC‘ö\^M B> Î�oÙü;ùío+Ç{¬œ}öÙ²víZééé‘R©d.¸f ¸×MLàv5íøŽçßg5…÷PC[íÁwÔ¬Ã|!�D›Ø\åÊ•aºÿþûåî»ï–ÿßû¥·o�,Z|0Üß2*½‹4aèÔäTO6Ÿ¾|r|ú}-°ð‘M'“Ï/òéŒc{·S ¸Vw†ó ˯~õ+�ã_pÁrüñǃøaNÏf‰|sã½ Ë÷—„x—r3Måµ÷Þ_]ºªzÀ±~V̆}?¤´ÎÎNb=¾hÑ"¹ýöÛåᇑӖyûîAÜ�Öï ðÏQmˆ8/‘X€ƒ�°Ú¹Ÿ2èþ‚ÅY�@Z!Ç‚yÜ0«üæÍ›åÈ#�”‹.ºHz{{1tÕ™Ãçs=ƒøî€ $µGXpXŠˆ¹x¨ú<$ÎU ÕmãÆ�¸á†äñ­Oà¶TOÏ]¡‚îïC¢I°‚ÁñŽA”œ1p¬ ¼ÀF*“8N$’èöˆ÷ ?‹ çTöHåÇJì Gýò’—¼ÄŒŽ‘B¡v“Ù¾¾ïPý=ë�Sü. 6Êfs²hÕ�uŽø±'ö¼Ê岬Zµ [Žs™þAí Ë}Èd³V7qvfw"Ï-Y,fOhå,¼RŸà„7ßu�ðÄÃ×8ù'''¥X,ʦM›°øÛƉ~Ž„˜X}?€ ¢lÄÏV ?� #?ÿh’Àdß)îD€u]·nú=ßC l™ …à+fžªb¤6µÀ¤¹ž˜Ï0ö± ˆónQ}þû¬G~€sîaT#`ȹùz@šH:¿s%ªÁΫé¬d3•�á‘ŠÜ 6ï' Y�û„“ }§xÖ™€%¾çäÔ” ÒT�ÐhD"E|NC._À8|r‹,¬�(\N&ÓÇà@#¥|ŽÿÙäâ‚XPsÇ×e3é¿Èi¸–®`õ‡úÊ GäxxxD–/‡dƳHEýÆÁÊ|›>�ˆß¿½_ô‹z5.¤ˆS€û+!`Ne¸ð:ë(¬ó>Ÿé��ËI؆Í‹ WB`‚Hz“¨ê&áÊ ÎÔ)ž¿‡g^þÞ�¾·ºEÈøÖ·¾•‚”3æ äaCbä±ä6.1Dâ˜L2"ÿ–.]7 ù”�ùùùC|Ü–ûŸ7ÈåïøoV”ÿ@•�«§�€´½—˜�×y ÀÿØÜ xäU÷/·|^Þûß? #££'!ä(×ÃÀã�f>ÏÂ0ëÀ*†´PPŽØyÖüÈ�¿ÒŠ÷¿KÎ<ç<¹õË·ù~~â«ö2+¢™tSÙ1W%Î*%+Z?hã¾ÿø9÷õo�[ný"ï!ˆ¥ZB€ €ØOŠ"ÞN俯¦{ûP6Çs<‰kñ$-^˜„Óÿׂ'{P f°ö+÷JK¾P° agÓ"²Aö“ßT× ß),òÿæ·›å¿z‹üÓ?$ĺÛ÷ ,7r¸ã/Ýx@Ô@ò´$A|ƒ 3×+ИJ€ù<Ü+€,»Ï|î_äü ß$?úñý Vj�Öœï_)ö[y5µ-èmDé4 o"ÐRÀ† –RÑÇq6n.[õ[ìØ8'+¤’îîn|Ũ a n"5ב+?t•Cþ‹åW¿þu5¥Ö÷ö÷)ÆI à73û‡4�€”‡dŠ{úZæ½� âS žéï—+Þ÷~¹üÞÉq¥}`ÞÕT=>=5Å÷PuEë ¨JÀàY”Ê%žÕ¡'�tÒò?'",Pî䊹 Å<ˆ®�2f ±Øˆžãˆxv}ttÄéÿKÕ˜!½èyóìa®ÏuÄüW½ö¹ó›ß¬ÉÇF®4`Ì€¾§YíÑõ+‡¦Ý±ƒTπεÀ·G©ñÝxðgòú7½Y>ñÉOñ=«©À\{ðó�á—ϯðxf¡{‰àJöAV©MÜüs!ÁCþ8ë÷:JUa‡ûkĺ½UõÁÈà¤W`&¤÷zÞ°] 6„�ßà§>(g¿î¹þc—q÷½j-Ý¥qI€÷3¿=ç*YpçT `漑x³0× þ—oÿŠœíì÷üï{‘æÓ>`1!¤u�ŒŒbôc•ý^Q7  õ ñ"ù`²dâGZ*@B!2é”3–ÐAt®È‚úVß×¢Ùˆ#Ã#f¬Îý£W ˆ¿}û³òŽw]!o½ômòØÖ­õVÇhï$WnüÔ§ñïK#C¿n@sÇ%5±Å€©¸=˜½ÁædU!º$ÂåCÏÙÿ÷{îqjÁrû_ùçÒ[`W Õ†ÔĦ,©âœù Q(…H¡&;z:6Pˆ‡ÐR Wh[‡/·X(øy«Q/p-.iqáÜ“þþ~Ío·jA¯«Dôêçõ¹ Ãn¾ÇŸø½üõeosÁ<ï”mO?½Ÿ-Â!¢«èŸt„Z~qÍCu‘>  6TF Éê‘€ZÏtŸÇÄĤÜpã?Ëý•<ôÈÏç,šP=p~¾C%a0‚³@˜uO³ì©RQr™Ì‰- žÆ? ȦÓëˆÿ' Ðwxdžž™ö¥·½ó•ÀtØþþí²dÉ‚@¸V�ûGbÐ9¯sƽלw>~rÙ¯¡' ãîÊé“zn.A òZed_‚Y= ÉÚX$àþcÂ\y³¿w.Ãw½÷}2¸s(M9à=É äsðT¦3Ø/N²òU‡�a*•ÊØˆÔ"Ð’! ™Îdו'‡Z#¹ÆPr²¼¬)EÆg™T0¸s �ˆÄ.p×7ï–Wœy–Üüù/xÛDC €T–̨J`ç@ºx0ÕÁ®Uý ?QÖúÁ䂈Þ(7ýëÍ0 ®…gÒi¢JQ…NpÉD™òÇ@ hÙç×uû%³º»» ½ñÎ!ø$~j[*¸<\…�ÎÍ‘ȈŽU€Ú®Â_þú×Î~‘¼ûýÿ$ƒƒÝÐb¦p)ÏÂóÌIŽ“f´sÀüù+pμ7àuF ¢e„¬Dò¼ /”ï:Ibx‹ã˜kp|ƒtI}M-&�Z‰;°tćגbèþ;üðÃ�Ë9 ïhïÀr-S“Ó>ùGëÿcàCtU•€{ )iÀDI­è¸?!»„îžÿ†7Ê#?ÿ…D<¬eÙsnÃRØÒŠƒz°Ößfä0Noƒv¬ç¼®ŽHÀæò ʯºFþÎÙIp‹6+$µ ‰È?ã"CżB@”w%ïš%®û„ƒÂÆ?�Z@î? (” k‹Etµ"Ö[ã�kÇsFÜ‚¾°gÂqþ)®›NŽ8¾dI¥ðÇæ™Öéܪrα„ÎFÝú˜œ´iP© ìoÏCº�}ü¸sßú[c¼ØjóåMÚ\ÓßÅkûëþXïé1àÎþTuâÊïaó¾+Þ…jÀ÷¨§´9£îûÛŸ}d%b/RC ž€q\�ä‹`ñgíu­@xƒP&¦&P‰„Q`üTÈþ´§EæÑý8æß§†jü£÷›¯œñÆCÆÑÑQâÿ�AH=ŒªD@& k{ûÌî £ò¼á2àaãâ§?s“L��€üü&«ÓŸ¬x<úy¶CÁ9©z¼'tâP�T”!WèëíåY«dµgJž×$K؈(gMMù9؉”žs�¨HÉ·¤ÜÖ†GÀ»=ßgW¡žGRmˆ¤ µÅ½i@‰�‚ææ 0@űYù+�;ÖsÀU±�ºj tã-ÒÂõÀ¶@µ`Öu�űîÐÔ�Р1¼,H‰äNà.v†æe®ßÀ²– &À¹tJŽ¢U.µ!¢�ì …ÿLî¦õÇfÆÀ  Rƒ"c;@ø\¢v64ëà÷pUË€ëg†÷¤I(çNVÌI5êqeJ±Þ�$@=xT¸]’TóC‚qï‘:2<Š„G܃G~mϾá¹2C$KÅ¢äsYik+nˆ‘ e\ºtÑñŽÛ��íp|' ÏZb�hŸýË–ã„öQšT•ûW?÷» ž÷઒ç|¶£vÚõR´8`†?››+€gUج™—ÑO�›œ¢A1–R@E4_„窋Æïµ†¨dfrùõ!ÐrΡ0D{ na Å2:(”\å_�­&œª3NôÂP­g5ˆBU t?²¬Â*ˆ�¾¿;ìú«pÏ÷*çàÿ­¹môz-à5ö ¥µšÖ'¡ÙÅAÆÇ&$™IYpêŒW ü`F@j,–È4ÕÔàxÄ´lŽƒ÷•Ûˈhf“eA~ÍXÓ¤ <ÈAûogç¾ãKŒ„T€*ç׿þ×Çýª6~ƒß°Ìá׿Ÿ½—T¨æÔc›¹^;(ääõ Mû$45'€p`�ÿ×(@Ñ0g¼iì�ù�Ô žäóœ= REça´là¬Ï½í�’/´Cì.ŒdÞ%å�c,"«§ðÎ808€(==Ý„áþ�ªÛôõdª‰"²ra…�¨Ï6®œEg… Rêÿy/+°Òôb©Ñç¨'` ±ßgŠ*ö3ð00(SE˜AD`&›N.[¶xí�lŠÐÅñ÷d²™å9DF À­ƒ�Ì|ôî;¼uparj’õ•Á�B0²¹öÊ™é# ÑØؔ¡¶·©.¦›˜¯Ç ±9Ál`*Çõªb€'sÐ-r€�Oœ^ׄ˜•&Q üž¢‘(Lƒ‚³Ùlnƒ¹¡@) X(@��m}ù\Á¹tz؈äpCÑ ¯àXxxñ”†ÿr²sÈô«l[ˆÀU�]ÜN¬P‹[Û}{}Åõêïa¯¯¨¯«·ˆÄÕü–h‘zpš+Pk æ @:€8ÐF5³­ÜF³�<0í©”ÈåòkX�¶¶2!±”Ѩ¯ ¼…_¼Ñ�û¢Ñk#Ãòê¸ã*‹€„%�йÖØù¾ qQªœóù:7±sf ¨t[z\O0�¾O­6à5 ¢‚=7‚&8Íy—ä(… ìÔ'_¨…cˆ,ÉA, Œ…ßÈëÛÚÛQÖ:u åú 욟ˆÀ– 2¹ìÚöŽvÁ:Kh/¢¿Ç_ÏMX¼Y‹`�0ìP¢Š`DcÜ?®B n¸¬¸½ËÙ;0\‘Ü4U'П+ìý™§gLïO§“ JC¢DsTv̹ڃˆ®Ä}¬"¶¤ˆ‚ØC¥Å‹{ŽkÙæQ r™Üš®®n2ýdbrÂ!û.ëõ5WÎþAk¿SÊ©K°Ô0ÎEjˆªÚ��LKz•XuŸP{Â\þôýj×§§&‡Ñ‰ù|8d3[¤5�˜'`|‚øÑ�P÷gX=‹õõ%æ‘f¥P,H>Ÿ“\¡tÀÆ Áè:½æD²În§ÃQÙ–¶ÏlJa‚è,à$^‘EE¾ÏÂùé ¨…t5âÙíLÏ›B"<$Qß›Ò YlH¦ö¦xva¨þÚiæˆªË �pÜl/ÀÞ`Æc]gBɽ{U×ÂÀÀÎ1î˜ †ãb¡H8ùÆjñ-м P €|[éø ·g�¼×rþÕ­ã]V =GÏ>;@€ Å ½>"îßüb8>a…+§g”cOMÎ LOðlQ?x´xÂqó:ïƒ'À—‡åw`+2¢žÑjÒ˜MMLB,a"‚Úé\˃ςx½Ù&'EØ|"€…oŸDŸƒ :O[Uò!|”ã6̘Z éÜ@5ËüEOw Âà ‰þ㸂Â'P À%¤8•óì´:€µãø· Ô°Dæ@ôÖ+D=‚Œ† �&ŸÁ94ÙK\:b¡ (4XhÒú58©ÆgjÙpÑg¯�Dh0KñYÜ�ó�ØŠœäuŒÓÁòÝ=.Ÿ?Ÿ“gwôƒ pxô~M€ô5þ´:€ê`á+ýñŠpõûçÃçÊÝ9‰´Ú\)«ôÈÈV¦Õ5ØôTZÕ‡‘ˆ¥Ÿ˜ùñ Ôƒt5Ÿa3çz �÷ßÿcÉgóB�‘€üVõX”0à Ä B=‰r©(ÙL&l< „1Ísÿr}´nîêì´¦ ¿qÇ„ H¤±ÜÔêD¼Cä#B°qNW!œ—MKT¶�dÊs¬¹øc0)Çw X&*@d¢~ôvƒº%-‰î‘_×RTM@š„Ø�ŒŽâu"1#¢&µls*àÿo+—�5¶î£�0dÿynh î+DUE@°úâÀmÐN  ²jC^@²)—Û@>bÐW­¸GÁZŠ¥Sú+H©\FFµšwQmfà>ÁNäYIû­Ü×ì>—„}$É ì F@²­«m®ƒZ6g�]ÛÙÙ ò:0!À&Â'àŒˆrج00::†øOௌªÄ—ž×0FSmÈ$ôð§¶=%O<ñ¸<úèïd˖Ͳyóoç¶lÞ,¿{t‹<þøãòÔSOšû¬ÒÅn{¨ ‰;yhhR’ ß-ŸËá àX3Mw;Isƒ ”ÛʾPh*{À%qnâ¸^ÁéýÇtõøšnèÁ•5éàŽˆ«ˆq,šFÊ#òã T=‚TÞù !žU€ˆ$³pl}þ“M>‡Ï$ÄÁSSˆšE,')`xç�I;pÿÀ×@Ä hITØšˆ€ùœ;ƒRq–\öÖê|6›ìq 46>ªºÿoÙ×6¥åÌIôµ�`ß«þr½±°®ïc6Ô˜8 °�KN^jƼï£úš„ ��>ö;2!ù\3‰ †"�#ðîA¤ƒb¹ñ]«8sÀ$q.V(ø b©`i¸=žÊ¶fP®©¹Xvñ, ã\d®Âìp ²�¾5@w¥Ê’`±�kAvK B÷ÇÀŒ¤Ã“G%Ð~оd8R€û½H]N £cÐòå‹ç*1¨eÈds}p¿b±„þ� LÒYJ€§áDÚ17Qœe¦  xûÇÏq#*A¸w$Õ†tìR €èµ€©VR¬qÿ/çÕæf#؈àìtH& QŸÏ5ƒ``ôd½!–öáÙ«Ph*Î6€L:Õ‡îI…Öi-þA;«™Y ”¨h†‘Îäl¡ŸÞö´Û°nÑJÚ$Rÿ<çU;°ûÕ Pa]T*Ϫ�[Ð  TD)°ÔÛü£ž×q¬À3D’‹:ÎÀ<¼7cÙlÊý.ˆÈ¿G¥„´¹•§¦|Õ©R±L<€drÙpÇ YȪ@Wäwˆß•ÊdëìèÄ….¦ÑqXggŒ]b°Ñ~€šá5íb½(ª­À%rÿ¼"iEQͤÍác=7Ü×Þôzˆ A@x°ÂÇF†G4Tv·uóe°»è €hÌásfi­r®|}¥tuœ�µ '/`xhˆ,K¿Ž¬MÐ^Ž´gŽ5:p7ª˜8s´c*]’M771¨%(8ÐK€OgWú˜ LÀɈ3Üd“à£VNmý‡\Ý·£Ž:šÍUÓgܸºqŽ©jô ÇwÞu§ îàW™y“øŽ­ý¶"¸ «Ó'*a0á{v@ >hRT)TñÄ¿!D‘ {~_ùÚm(ÇÕï­ßÙGÖ\3BZ{äÅö,Üцu'Ê¡‡µqÀ£D–(�I‰Ùµ[P†bѤú}ðƸ}Ø-]Ý�¨K�±|ûöí[;@*†ÀÈä2kp}ÑÏ�ÅC˜˜ÇPã7b€K'mz¹oæï �Œàyláë)ñ" 5U€¿|Á©rË>ç2Ǧ 9”#¢C2{¿²rÊ A71N¨ìý/Ú ’f't¨�ûŒŒ Û{Çh¸ï7KFJiùïºÛ@Kjù™ßÕè@Ü>~uŸvèá¶v†C„Ͱ}{?R„K2)¯û³À>÷Á×™ä Ƕ·uP$T2ù v€Ç›Ó8´ €²½èÿ®¿±ñq_#ßÂ3Æ[àüÏi‹ppuxxÄbÍHåu ¹øÍí6k¯uË€€@½Èdô„à8܇ßÍb†ˆ˜´µµÇT¢òªLØÀEáŽ=�œóš×ËÆõ›8kZH1¿£´˜¬38cÄž áÒuñq>ÇÄw ¢+u.¬D?Z6§×÷uuu •€'�<£¡›*ªUôª›–Ý\×ÒàäÐscÉ’Å OSóûsOþOÏ—7¾þÍŽð˜Î‚@Á�YšCÑ×ë`fŒ9kõï¶l‰줢.À�ýnan,HigåáGË›ßøVÇeÛ›™mh* btdD'ÌÚÍücAAHqYug¼€¢´Î¶ô§ƒ-MÐeŽJ÷t8K~&—’‘1 ¢H–óç·Ù4z‘s|ÿ@F#qü�º !JpÇ·^|©¬8ì(Ä_vZ™CBà9Œuö1ŒëÌ´©–ƒ  F#Lb1fÓŸ¸V'òïÞ#¯>ãyá_¾8\Á¸i³EÏ÷ªR™ƒÙ™]Œú†›�ߊ1ïG!‡JÊžÌ>Ï�r+8ú`�-dûrù<::¿¯¸k·×³©æ*¾H&Ü~�vb!Y¸Á�A·¸‹ólÑCH}Õ_5„�ú¢—È/5…<*陟ìÕÌ4–©M) 33ñôê˜Añ…aÈ_/ïgý–,>D.¹ømÄkc®²Ù+Ä5Àù C"C�ˆšƒÆçÏëP*µXÈ,1(>Á,—O:BqbPK0È¥s½ø¾‹…<…T‡ ³x@ ˆ‚eˆØy婢¦§ò‚Ì=®nÁE^,m]ÎÏ !à†¾Ê ëJ‹¡l··øƒìFœ ô+Àó„Ø~·Ÿzuú^~ÚòÊ—�a†Â¹Ì„«ƒüxwòXaóÔÀ|´Rˆo=a0Žp�™Î¦ˆÄ T,K€å³}„c˜!‹ðÓt…1fÚ×qWæêã@ß l§lX¿^ €UGDE>ª» 9æ}OéËdÛÓÛä–[n–Ƕ>ª†=±Ù†¦5ëÌ{XçšÉ‰iÊXËÀމÃÀNhõ7ï¼[$0ʾ/çn²ãð€³¾äÅ/•W�q¦y?æºÁHØ8¸cBŒ qƾÄ÷Ô&3Óöz˜ŽÊ*H[{ªYõÄ V $Ã`©l:}<•€)„I­6 ÎSwŸC1Ù:w¡ˆ"%õFGG­X=£f7 ãüu» 9Öñœ,uIÿëÛß!wÿÛ¿É-Ÿ¿=²ñ‡Cp ‘€1DÏmß±CG~„\véÛÄ•xñtoF_mˆý�¤øÀƒ?áõzÒrG H;Üc�ó¹,3e©(NJmBbPKœnv”£´Er¶Y¤i-Ê€�†ÎÏ¢¨TŽXjH755C¥Dk(»Þ«=šÜ ÈŠ“ü—ÓO—뮹NV®X�Hùç¨1Žã¿J>ü¡«äÐC­DÜù,,Š£ð6€À{eÐû}ÃÔj Ç𤯡2³ª” ÏeK===«Z6€ˆK€9¤ï¥ÈG©T†ïhBÜ?ˆ�Õßúãù›T#4˜ÍF'`+ýŒ¸ÖèhVëp› $8ïÜóL—Ö�xÀ£ÒXˆ1öª_-çŸ÷:kÍ“¬A¾¯e"‘tæC€“¸ü°X`öìì?º)Üʨ¨™Ô‚O JÅ®`&µñ¿T*HÂ?toñ§•7¨�Ö‹mþžo 184äõ`ü¼,tt¢eíb=¯BÌðúîYg½FÖ­['×\w­lݺU&µìö0@‹áùË^!]tM5ö'Ç¿iv¾+}IN0 îqLÂÙž$Ÿ­MfÜ9mf÷ѧR,”p²Ï0~r!'±+–É­%ú¯­Üî7Ò¬arÒ7mTlJgR¶9Òü|;à´æh|ÔÎï�¢ ïuÄ+äcù¸¼Æ5ŽÀoëîê–}ðÃréß\*¹l."Q?ú*C*¸¾ã©…4‰¤É¡rêý¼¤Òiß� î¯Û”ýIÃ�\¾°q¡'1+–KgÒÏ£“¶ÇÚ@ ¥ ö1¤¤F3,ìèˆnLÿ—¹t@‚¹ð ÊG¯¿Þù×�•¶àáÔž*7}æ&YÛ·Vq-¢®@óàe!&c³fiâ Äód¥Ø! wìöö²øFµ¹¥NrˆªcPKHçÓ«Ü¢¤¡ÊˆŽDj¡Ÿ±XšÄ¡bœ`ñ7ÿ-QmdtÌb¢ÍlÆ{{ìqòÏ7~R^ùŠWâÝÀú À‘øM±ýŽÔ]¸òÊÈW¼ÛM4�;ÍŸ�ìØ±ÏÜJÊ!½`@ ÇH¤SfŽ�e)BœI.èŽA©¸”²Él_©\äáZÈ®"¿‰™3ؼ+Ð 8°PD ² ¼6‚•«p¯©Å Õe—]&'�t’\ùÁHÿ3Zÿ Þƒçÿüç¿@þñ]ÿ(]�]û¤³ëhN-À:\�¨Œ?üá-•›�ÑO›Î²|ZkB|} ÷:Î9.·ãÌ`¸}ÿ‘¿å ðÇ�pö9où‡Ã'5+ B`$ Òsþ‹900ÀÂ9ÃNW#€ywV†÷õõÉ-ÿz³œöŸOã·Å ù‚¼çŠ÷ÈG®ûz¬kÖr"1j 9\~T_¢F ¢¾÷Ô$=÷§öÌG›Õ°çö/1¨e0ÜCî#þ?�É‚ä&þó°ÕºŒ¡F+¸$X(Ëùß±ctkp’ØÚŒÀ@ÄÞýî+䪫®ÆÝË\Õí·}EÎpþýx‹úµg˜ ê&LdppHJ…’g{(É>kBS^%àuü~î±NT"v`A'q1:jÜîŒ*+Ð'ó9O¸S,ª6™D"Ðl-ÿlÁY€¾@:zи]…º“`�:åä“åË_ºÍyAlŒ|¬ÃÛÿþíò™OßijF¬Û„Õž8>î@ʃQí—¬Ö=´8/X ñ]¨¥‘(X›t{wÝ~&µ$€\.½Æé ¬ÿañÇçª-¿ÉÓÖÂ�I8?©©XÒ�0Œ��Y€æ¶‘n@ï½PX%@¸úê«��íJ)–J„áÎ<ïyÇÉm·Ý.\pA¨ÂoQ¿FR�ÕܹÓâ1P´h �e¶Ïf*r2é´ôÔCT ’Ä‚M âÒ •Ê�_¶X.ûøk(HataàŽµ»K.Ë"yÿ¬�™ #MMò „ÏýŒ4púé§ÉW¾r»l:ñD¬îs |‡K.¹D¾ô¥/ÊÊ•+«-}5¹u\gÍ  ’´Egj‘$Lµ1–}ª iÔ¢„(ûré…Ü~t jIs&—YÛî8^1Ÿ§ ‹ÖÍ NˆŸV}O§ÒS|ÃÝÔb¨4WÀœ¶ƒäÒŒo¼ñFyϻߋ>:'"? <·}ùv‚zp�Uo”zÑò4ßèïßn…\5ü×R �d`MCx ÷ BÝžÅ؇Gm?\�-€ÛԽр�í�€»yøpzQŒ㾕§æxÈYq¹N.>õÝæhDTm¨.»€y?^ûÚ³äë_ÿ†¸¦ùøœ /¼Pîºë²zõ_4Ú>=:m¾½À±âC„ûmŸá Ô}f*‰C öc» .¹Ñ@bP‹„€–8¿êâƒî‘R¹$õyðøÂQ¬]5”—sѦ»[Ô«w†œB;mqç‡4ÕUhÒÀ²eË]��[äÞùN’¢"Õõ;d¸õÖ[]PψÇûØ>�…à´F! šÏ’î«5 6³JÂj0ôêgà“¹pQ··•) ‡´° ƒ‚y.élº· %Àÿyð$ùü%/nj‰­lOÅµÑ ý?kWaeƒÒ‹Þô&¹ëÎ;eÕªU‘pþ³Î:K¾óíoˆ ù̽öJ¬£wbÜ]ƒˆò–Aºsç�Ù–H ¢ �Ù´JPB<òàðF‹eÇ|ˆÈ…ã-ÀÞ²ÿ˜³él/¢T¹­:xΟåð<ü¬ÓL$<ò‹¿Îâ@ä h”ú Bˆ÷Š„ˆêGu”|íŽ;äoßö·<Ð}ÊÙ'ŠïsŸýœ\Ýõp¹?£P/÷—ðy¬]ƒZ%Ø÷ èï7±Ÿ¡ƒ*Uö÷íÿ Åzùñ�éÏŽ �‡‹»kkJ�1ÜD2¯@u9¦\µuŽÁh KpC®ÂªH€8^~ùßÉ7¾ñ Yyä‘ ‰ü§�vº|ï{÷É‹_|*ϽidžìÑ.ägžy†�rž©µN€!¿Í[ÐÑÑålOÝìÝ¥N�X¶Ðƒ‚” r¹üñ”GŸÕ"sñ ùìgo"¢-"eAسålÛö41%Þ`ðÃà÷‚xnÂ=ñÈOxp¡¤X*²ÿ64˜Ô’œñd…{pm%‡ì9ŸÄãE{µÖüŒOʰ:úÔk'[�:îCƒCèf®ÂëÁÌ×ѬjCádlæ÷¿ÿýrç�_§þ^U]ÿ”SN‘üàÿÈ«_}–!g•ïÓ€×"»Àü…[0@Â7hõ�ÞàGì?i4è�’i–ý†û5”¨@þgÁu b`ìËó¤‘Rw�«ˆýZ›�Œ JX3P÷ ­¤öàà Z6Y_ù…A$¤«Ž„ÀÆ�åûßÿ>‘{†øHH×^{�ÜqÇWÙð&òGPø¤‰®ÂæÛ ØOØð,�ŽŽ¨qÏÂÌ)LË1ÒS„g©¶€€tõtáÁ:±ÁÄ – �Iez©²J0Á?èñøW¡¼>ôRìáþË}mÂ@�UŠùhfµ!Ö¨„MüÑ�~D¾úÕ¯ÊK_úR¸>eºš¸´ÐT�šNR”ùÚÞ¿£DU€sÓ÷=a(rLB�¯¬}–:R,Ž‘ƒZ€*@}¸`ò¹K€¥ÓÉ^¨.¢"X ^ìJˆ§Âšp¡Ö§+ò¸8¿÷ yòÉ'e|||¡�ØèáÍ•FâoÀï?44#Áˆk™ý§ (åËÌÞ ¨žÚò½ÒV€4ÚÝÓƒç`m}‰A­HÀ �ÎöQŒ`u�ždcàjõ”·P°B Ô ô�‡aà:sGg›üò—¿”‰‰ �–] a$Ü?¥ò=£wFïdìtµî¿ÿ~9ÿuçÉÄä$÷@xìQèýã!�ݨ^-…Ó›­• G³Ñžn®Ѩ‘Ô²8×] @ÅB� -f5Ùÿ¡ÂPd^®í›ká´[U‚¾µ½rïwï•Í›· ÆI F\« éyƒv�T”h¥‘滑8~øa>SY~_$pëÁlP Þð¬¨H¬ìUÔUþ‰JTt†¡-¤Ä `¾Ò”œÿôȃ\U*…S.Ó™4Ç2«¡Á*úa0•@©õºõ½r— …ݲe ‰ñWšWm(@bà*ln1̆*R¿øÅ/ä�~*§žú"ÒÏmŸ9æd^'8<ûL;RQ��sڼ㭂X -�SA»1â6îù[€í×ä ùËiBÛf³0äZÓ†:¡x—gä�óോRa+¤§»GNØÐ+ßþÎ=rß}ß•§Ÿ~iÀ8Ú�oh.'žÅžAQ€ö~0ˆ'žxB¾ó�ïÈÀÀ9÷¼s5½\�6áwŸ(“IšJp Éý@"@€ hD`ü‚RóỦIkðŸÂ類jt!íR 3 ß‘%`áÄÂ/‘¦&ÈÓö‹’p7³ùœQè+���Ãrë¿ kûÖã�DÁutS,˜Á÷­qî ¤ÕÏýA}çágbçöúf~¦ž×ó›Aâºg8=R$ q;Ñïÿ{g4“õë×cc¢€µ›O¦°úç!_x´ M]"Í FgŽ%«ÄÇŒPÈ“´Ü1 ­q—Ró%ÔÖÞÆÃæAr•‡há¼,”Gþ Ô¬ÁÙ ö ª Däæ|‹pŒ€’ 5SVõÿØ;—IŽ* Gf]zÜV�ÌXØÈ^!, 7<‚wÁ£°ã]Ì ™�½€1 ðÚH0,½`òUb¦«z¦«:“øôǧ¥š™Ý0hÜ­TgWTDžŒˆóŸsþ¸½S.ËßþþWV –‹ÝådN�L|ǼƒÎºÍ ½ˆÎcšãÅ2Ç--y _ägÿ|ÊTÝÁ™Òðâ|‹·¬#{%íL»Xç(]?>�ìüçjPò˜æ³ M>i‹È� מ÷b /#Nçç?)*²Êßoúaû¼ãLû®âæóŒÝþ9Lc!�„ t`ƒÊÆ øÇÂðÍ/3ˆ„#W�ߣÍ|–Êͳz×å‚`ä@Ò(ò¼Qpò5e˜™¦LghÖ�¸pK (^B�¨K6)I îsà°‘2’ÞÈ$]ÍÑza7dåv!“ž¸wo²‘Ï:dÕ P(ÙöŠ2!ZÉÛj§ßίWpˆ­ÈÖö PË™±‰òÍ%JRéanrÓVPeWëö©VÖ‘¶ßç¼GÛ—¹ô*Ó~·ë|å.O´Ë—9ÒQ:”ŸˆåmC9$ÞYE®y1 †˜+¼I¾Zó=*®ûŸªŒg¸÷ÁbÒø¾ž‰ÀPß÷ è}ºq¨ÀÅ*Kˆ@äþiݧâ·ß�€·Ïüöödûö½{÷h,WV9ƒ¬µsÆfý¯RñXºÕ¦qcOÆpñyÇÅ�9xŠ—HäY-t ¼5£Ä~UŽMé(/“Žy@‰Ç~G"N�É.ÅK:©“D“X°£:�5FJä@ù£ä‘Јõ²â°’™E>:›µ~¤Ê€U›&d0B¦®yþ@6å'm 0Dñ¨?‡¿�—|”6ÀºQb”(õθHÚÌ=áÛJOgæ8t€Co ^'1£üû*#ŸÈHÊ ÖŽõG¡kž;¾ëŒò£�”I>Ùñ>Hc©8` é”Óöú RœGáÀûË=ßm“Ñ 8£�jOá ¨_FÞxó ¼�ÿ‹ƒÖÏ!þ_¸ÿÔå꜆`öß+§¢uÜ8V^ùSAB`ÀÒ¤Ñ)¡¬:®k 0ºW£\Á\C<‰4pÍ)W"`à~µîj±^ü†xœæÙCI×…ïÓq/÷ÙFŠ4ÝP¶“¦L@Åm¾±†º¹¤lP¢XÐËÇÙ5ßQù·t6dŽEú[ï/ ß|‚E.CÉiEY<§¤³£�Xãz­ë÷y¾ŠÀ•!ØýÅi  këm*HÒé>£\óÑžx+ZkÞÙ�-·Û]ò>õ’8p™³­û±$� Ù\ñãá˜á�!»Þ÷|N_qØ×<Ô1åpª´u œ¸Ä\o‘çõ§3Ó�4åÐË但[B‚Û¹ö|û­·�Å…A_¿ÐüÒ»ï¾û<À­ÅÃF¯k…ÿúüüÇ¿úù/~V~Tw�ŪH¬%QÁT¾!�Š'W` wq±C¹_ìHçÍç3!�g ¦ ‰tîæÒŽ”……åó€S·D–ÎÞÇéä�/ ÂÝ{OÕá9¤9ÚƒuÇká²×ò¸�/áç=€á™”„ .â½øKd%‹‚-°�þ`¹0è sÞAÁ‰‘uiÈ¥…JH@ƒÅe,*Ý©JDž+�<£Y¹‚kcû~^A‹�‹\[=¹#Q€¡Ÿz` ߬8€%'N0´mÌ(ÀÍ%Oà ²­9ïÖo2Áë$ùˆ’çx4”I`à¢`sÁJÒ‘ñt$øÈC ×ôx7E¹Œµq­Q:¾k¸’X/"À�gpM�~Ô3÷È lÔµr ¦užN€!@㲨È(ß§ žOÝ‘‡Ð@`€T€üD¾û¨t=¨õÀ€GØ–eÞíwôåó Vþ ™Ý)ø¬…}W†&ÎÿÇ8�†BWàñ$Ò /^}Ñ�ÏŸŒ·«"cÑ–¥0Í$_rºÛnæ » c™Øm(¨^ojá7kb‘å*ÃlAŽ'1®(�@Þ”çF¤’ˆ°æ³{$&¤ss/£žÎò‘2Óò+yF™ÈîÁÈ„9–mdç�…/(s”ÜNŸýòN�/ñB�AÚ¡äŒRhYÉùŠk_AK%&- —›|�ë_òÉì‡k¹ž§GVÀ "WIðÆôzv= Q�…ßGv}îHê�{2�Öö(0ð7À@h`@nú ό첣X€�~¡GcÜï;Ûsñ¹«5@·°ÿÔ=÷Wñ$)ï΋¾Gàú9n6ÊTv{TÁ�N´S÷£4ì2$ˆ%||0„±v˜ÐF–ôÛKúDÒŠ$b¦g`j…+—ütj‡Øè4á "ƒÖ‰4râIà�èN’~`ÐíïdÌ× äõ{†%ÜÝ�±¬[\êþôÆý 2  ëIfÎ yŠàeÏógGRwÔABšFÙfÜVJäеõàĽV�|ÆÑ¸È!ΆÙÑ µÆÂSÚEæ -\˜Uü ë5²7£Î°î¢ä»žÏ¨Àÿ*¥Ä‚Ï…bHÀ;Q¤ÙS[¿È�ž‹Ë=äðá%Wàü•tÕyû¢o2þ/v®�}¸ª5ì¤É»……_†Oã T.‡¹<Úk¨’p2ËU†IãsÙq8�ž+X¬AHG�yì§ÙÌ„ÿ t Q”p¥ã âQÈgî8\é„%C-”À`‰e›b‘»�`,’¦«žþÈø;€nu›»€õŒÒ]뇩Õ'�|Xê„%±È-ßÑ�eŠ2 »‹�n»ÞePçž·‡„vYþ� ×? Ù¸‚ÔÄÇHaíQp‡á�!#”×…¶%!íAx¦'į£)á v†’Ë!£ØÈ/W@YK®€Ï9¬æ°0|ƒ×ËÊX«Ãõa¨ÃOZ–Oe¿39çºwýûvrϼZ‚.ñ¥ŠŒ+¾öÄ×'ä‰ò7�@ǹ>0žßsk¹ID.˜v†Û'ÜC·š ùP$îÃø÷\�À08D×m|z’ï…+P9¬­®ÊŒ P?lZa!0ÊTÅjJ"O€•¦,üç"æWqw5 «kœ“.ŸȤ>ȇË�p�ïªÈºÖ`–�µCŽ›QFK1Iž’IEÈ!)é~È0°N_ô¶xÏ.$H=$$˜�� G†%È,0ü×�ÀѤx´}ˆbö³´}[H@½êÈÀÖþþ"‡ãó¶þ„Ìùâó/pfQ’&™·[×ÿ£Ï†›�`¸m•!nºÀ€Ë(0 žI`XßΨ¨÷.<²<âjÒ$£¼·pžI/hÈ(Gw–ý¶Yx”Y>€«ç °†x³\ÁvË,¾ÆDáW�'p4 Ųò#0L×<®LØÂꆼã�UÊL]3Ü'0¤LC Òâ…tSŽ{®à #õÐMö‘+HÛó½ëx ‰Ç¥Aøu\Á�ì5�#0tI{p�Ü€�#)yV¸¾�W g–éÝ3‡8 .<ÖƒVÖ|Oëÿì…A/C°¨ŒýëŸ4ì°*S󜘰Ÿó>ŠçhXk‡Ä=`‹¢¨YȃŠ0` U~<�aoH ‚3ŒjH€Ç0YŽáNÞ)À°“èä’ÀË”ÝÝ…ÞrHúÑîÔ“Šœ„Á<)Oä ç=Ȳ Y„xÜQŽóË„°üÝ܇€¡óM —9›âþý�jþÓŠr$íï*ÿâ*/«°±X³ÿÁ‡ðoÝÚû;å{ßý~ýûfmÌÄâ·„2±O†§s¤én¯ä F¹’3z®àÊ)Å(Ÿ'ã¹`M†„(în·­yñÀ$íx¥ÙqÛ„S{·Ñ )'Ý„£ë£á²;÷>n·´)3é̸T®À1ÛáQ’ž'ØèVFà:(+À€‚Ç·ç �ZþŒ³ë1L¬ü“+ JóÉk È¿ä ³(r7Ê�€áMÇô{®ù{®` ³u\ïÅ=ßé3z ‚di!Aêæº\<ºh²ç�ø/¿ü²|ò—OÊîÿ¾|ôÇûå³Ï?CÖº¿À×ä» ^ÈÑ€õsæœl%à6•+pHåë÷îÕ}ÕÞ,oT0ð$àýåŽÎ]­i¬¶n?ýËítYÐ\Òìob{sî[?sðÕÓ3î»p�<×U9·ìën\ÊÕØç#eBm"å¥-ã2¾Æç²Ètb@!2ºBm½ÊRR-µ– Ú:¶VÿÀÄ“×\¸30¼èX{æí#Ç0•ízë6�çûÈx3 §’dú-�:òÀU:<<šÎ/¶^ñLÈ [×wá[XÖzÏ@æb¯qsÎ@2B’È@h7Dê�÷x2\Qf½Ô�'ô”Gk|^¨¸‘Š1@�6fpÈˇ…D\µ�s_óÕ×_•yfrX7t<²Ótº¡ |`o ¹'W"§Ã~XvËç}€K~™ðhÇ…~ý‡½3ë�£ˆ¢ðém&&Hl‘ñÎ €Ÿ BüH YAȱo±“xì;±ÝÅÑÕ‘ru5ÕËfÏt�>UÕíZ¦kj›êºÕUF1Ú<ƒ@fÄ™ÇÚ“5üøÓøîÛïñõ—_�AÚ`Ck¤ªè�CÏmõXOnÊ´ÿ\¶Éi{“Yœq25ȶ0A6î  Éü™&KÐv^;�Ü_‹r¸øDéOê‡ü$KAÂ!Ÿ§Âû°ºÓP8¸|t]äâDbùXã÷¶:F¤V�sQ¡ª ëôÔpu&Q‡+?ö_¶åIJ c~“SÕ+©lÒ”´:¦ïÈ8v¢°,ªÝؾRй~ PŠ�üÞXoúðáþ%ø+«+jpylïÜšžÓ&¥¡©¹l�$奠[²JÓjÚ¤®)«jÔ•Ôy+³Iƒ¦©E“ÁÂYН4ˆÒµü”W¥=åæ/õ½�Þ¯{н9¿d´»ñ¥€¿n’p ˆiÑVz2rËïâ ÅEÄ…qß¾Óõi…<áÂ+.?±J&[—•®“¹û i™¶`{{»ë4ê† Þç¿õvÆéÿ°£lZýëÍ_ðÇ�Û¸¾x�g¬Ý°=ÖœÎqF° é7¬a£0;S¹-m?âË­ÞÜWrhöðV¦ë¼›F2å¡;q#lëG-G:/—Lñ tZ?ê¶n4„Å]Z’IGrŒF]Œûž�Œ€d1â(e¡<23! ¹±œdÌ­ÑZ#´ür{[£½ìN¥�àä´OeKfß&�Ø�m2­MXµ§VZ×ÈWßëØøe+òüü¨É‚Üy£•úÇk«œ ,ëPŠc[d҂̸sã.\¦Æþ?ÊÌ3V&ùÄá&—É?UÙÅü&j„ž(»°0 Ñ¬Ð�pàf(*Q8à׿a°R~WÜÑr½éÍ,›†\‰{ÄìÏ2�? ¥ûïi\vÓ›Þĵš�f¼Í¼=,‚»*šF…óÛ/§éMo|ýÕò‹Ÿ4ï«N@=m=2H)|öîUHÝê-¹„¦7ý¨¯š¦©5…Úýˆ�¿˜×#Á*Î+îR³M'ê`hþ²˜Þô�^ ß ;î|<Oʸ@>o¯Kb“Úb¦åu¨c•ªÊžÇûÂ$²ÏLϤœ“wk CŸé–¯¾R�´y4Iq(=Ð’þÝ€*ˆSr”RZe™XPxνÓ:Ʀ`»NaZ?hÏå£(§S/ôH0lòªÐJm™:Øc½fý>°îNMš§ 9»oÈKò¸mÛ¾›X_ßÀþh§z;+ ÔíÀ3·ÑÔ�Ùž¦i. ¥Ù�¾G¤>cóï©#güMšˆ+oq–ïQwä_½HĦû£ÑÖÿÙÀöÖ¶)°QöÀ3òJõ¾õmb^fÉqJ^“ÙI)ýÆB:ÜcÁ­»ö)í% ƒO�5ýœŸ&µJ`û@.áo]– N„(ƒúZѯýú-Qì:;Ÿùûš†Þ²t0¡Ïzê©tà,H^û­½7vASß Š.'½Wk�௵«à»t8ðÖ+>‹à»~S¿õà% ïh_íeYô)cÌA’eY‡Õw;u@ èÿëv:B€ @€LÅ*î¼¶)k IEND®B`‚tremotesf-2.10.1/data/icons/hicolor/32x32/000077500000000000000000000000001523411710200200435ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/32x32/apps/000077500000000000000000000000001523411710200210065ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/32x32/apps/org.equeim.Tremotesf.png000066400000000000000000000035351523411710200255440ustar00rootroot00000000000000‰PNG  IHDR szzô$IDATxÚµVçï‹Q~ÞUê‡R´ZP›{„Ø{#_b~ "�˜1¿˜±glµgµ Ý×9'm½(Aâüòüîí}ï=çyÏyÎmѽ{÷¿Æ’å‹ü‹—.TzðÚ¿ø*¸¸0¯¬.¬>¯TK xïÁmå{tWpïámY›Ï{tà³ìƒ}±Ï?&°ؼÕfû|gÒ$õtútõbæÌÿ˜§ïzëÖêb… ê¬Å¢Îy†”@úü‰ÁsYÛ è÷òYñq½M)Éßd`ˆҥ¿nÕJëÒE�êÕ+�ݺI°Pø£zôä�zøØ§Âá�¬mlß^¿WβöÅ>ÿJ„t`)‰çʯDøôÙuöüiuæÜ)õìùÓ_Š0ëcéïDø×à`—®\P{öïT»÷íP—¯^ÿâË€´W¯^"ð&À ù+ü£ýL B… %‹ÊØÖ|U$臯^ üb²¨lÉó4Î@Ó8 z+*]j¯£‚£Ÿ«’£™tét©T ©Lš>§PÖ^=£ÏdùyìKF“F£ 9S*ƒx,Ž'OŸ öùËäP(ºþ—ìv{yh™`ϽáóÝÅåËW@}þÍ!aÄÈ¡(dÛ¶î”9³Z­pUv¢NÝ:(V¬nÞ¼y>ü1Úú—%H!Ö¼L™2(UÊ¿?§Ó‰Y³fcÞ¼ù˜;wž`äÈÑø• >œ÷ä1~üx|ùC0DùòônšÖ„|‡ÎLЙJkÍÝnL&“¤tôè1ðzkÒ<ÃYøÉY<ž@QQô‡QÜZ�7†ÎäÌù çP¼xqØl¶b‘ЧæNÌ€C‹Zµj"‹!­Ò¨W¯>e£äQðôÙsH¹ÊÚ¿Û3zf6›¿CíÚµeL$âp8PZºí/K  Z4¨ßÁ·¸œ.”-[‹…Ùó(¸{ïØÊRP=Ø|¾ûøÑ\®ÊtÞ‚p( we74 ]A%%�ÀY†‚Æã1Ô®S[< òÑOŸ�¢Ô&S™<"ÑOxüè1u„z3R'y<| …P™Z›‚´D¦U¥J�Dĉ·†WZP¬ &ÂÁmI©~‰·îàæíÛܺM¥y�PøŸaÅóتU«Ne ]© ,Å-ÒOÍŸг–O¼zý и%Ë‘€RäP óÛwî ¥óé‹W8xè0:Áá#xB¤¬–âxþâ…>kB¦bÅJ¢ŸÏ”9.+ɹýO] i†·G$I”³Û%p‰%ùMòŠ658ËÙ…¤Þ”Rxÿæ5¿¹ˆ-kœ5Ñ™tS¥ŠN¼~ýŠu°éhBL©eÎB3jÌ^ìØ±]î‚—/_J=[¶n«­ ˜¼yçN�„L�5°páBôèу»@2¡��›ôpíÚ•Öß• XQ±êt¸À‚J ©šœÈ]Э[Wœ?Ž/ÊF |ø¢7xƒ7oÞ~‡'OžáÝûÒ²K–,á[ƒ Ê—ÃIYД�:¡2_ñõéb*ÊgÀ˜Ak¾é× ŠÓ抔&�Q`+V¬X†Q£FbÊÔi¸zå**�Ãb´—‰¾}óï‚ :„Þz}¡9¸¤-ŸúªUªâÖí[”EëÁðîÃ[¾K4›;å)å ª¡Y®ÍBÖ¬YSœ=sãÇ�ÅûwðâéSøÈi•Ê.œ8~ «V­"‘•É 7É#gQˆ¸ÝU¨½ãT é ±ÒÚç2Àm×ÜE©Ñ Ì$¢·oƒ¨Q½Ì ,7ŠæÌ™��`Á‚…3f4ºtéÂo,÷ Ñœ]4*“–bfX©$^¯wïÞ‘û@ž6(4iÒÄRF“‘HT¾ß£Ñ¨þÁt¤øƒâpÙ:¿‡þòk m”Dƒ QÞ^¡PËV, íܾ«,gÀLl¬Ôt&Åé¬%­O$�ˆÇ 4f{\RjáQ k‹nNZÉ­q¶¹Õ™¤•+À™ãÇNÛ¶i³ÙhÕ¨™Œ&�Ô*ô�k™L )•$4WTgz¤q»�~«(®³¢oKB˜Ö•â3ú—]¤ÕÇïÍ~¿¿T6ûfþWšJt9+›Ýõû a)ièó�syþÃ\Fý\4óåËþveÈ ”+W±/ Яê$•Àab o/Sÿ¶.f®N©#9F. �z4"4+4Ð(AÁ–Ý!@‹çÊ']‘¤îðû}‚Wr÷�•úr`‡Ží/öíßçÃÐáƒÕÿÇàXôƒ9v®,;ÁK¨óŸáåXÙ˜ÚWP­�&ÿŠö9IEND®B`‚tremotesf-2.10.1/data/icons/hicolor/48x48/000077500000000000000000000000001523411710200200615ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/48x48/apps/000077500000000000000000000000001523411710200210245ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/hicolor/48x48/apps/org.equeim.Tremotesf.png000066400000000000000000000057261523411710200255660ustar00rootroot00000000000000‰PNG  IHDR00Wù‡ �IDATx^íXilå~fvÖ»ë]Ûñ}ÆIL|$ØI�›Ò! mQB!¢EB**­ú3=¨ jùSHhJh‚�Z©´B´€ú£ýA¸Ò$ji;8Á!vâ8>÷ðúZ{wg¾>zµí®wm§ Š�új}ß|óÍ7ïyÌâÿ”†_ýúù}»_x6öüž_ªdpmê…½Ï=EàfbÞ[ûqÙ¨t<çv«ÝÏÿB}xâˆúWûqâ„ࣶãêÝ£“{OËÞ™8øyöñƒd‰×w˜óÜtˆ¿Wu}ü½¥KÕ™GQ��>ªº{lþÉõß¼´W�Ž…U  ü‚`(ÀµQ±Ä;Û·g|–gÊÙïTW[‡5m‚ýŒÀ\ÐfC0”¦=qÿ’%ÞÕ«V¡nÃTq,mi™�††(þ,“°–iBƒ ëë3>[µzµœ½¦¥Eûê¢E¹~Œ¹in ¸9�ñ+­­êS@uFV+,T/ïQE£Q599™®‰þêpÌv†¼£kíZuˆ˜ º0®qešV/5;ÔØˆŸWtQ«]ñx z M×Å n·;N§$ôÎ|NκÂg®ååaxùr\㚦ÌCav*°¬ÃgOŸþNy}½ÇÝР¹¼^a š†dšäõ¸CG4Åôô4’Éãö@Æ�‘KFSH)D#LONbddļxqJvc>tpžx ØÄñâØ`h?KÇsyy±—îS#á�ºÚÓ�‚±q bõÏô,Ïíç¹Ïë ÌœÜDìÙ³§âÐáý‘¾þ^Õq¶=ƒC"�› }œ“›,ÓS&B#¡Œ��áó 7™LËTáðÂ#©åÚCÓD(ƒ1Á`ð‹!€e)øƒŒ„Ã)ðü_Lq™Èd$¡PŸ˜�j;ýMMa4lT92“«––fLMM!�"‘ˆŒmmí Ù)M�Ò”~\YÚnsú�¶sçÎÙ7hÖ·\÷V×jž\¯5Œ�Ž�­LË‚Rµ.€®é¨©­’=Ùh``ÍVlBöÁÁÊžÇâ¦)……Ð݆ýþc°ô­¢ÿµ†‡‡�²Š’Wׯ»C øýäËÏŸ?/É—"�¤:WÕTȽltìèñ4IÜÀåÊAÍÂ,Z¼UÕ5ˆÇÍ»‚##OHK6 JÏnÑÒÒÒe�Û[R\ ÿð®÷]G#û¡{îÙ$î¢Tê³Ô‚¡!ÌFO>ùy6‰äœ¾¾>¼õö›ÈÏÏG.QRZÖ�;ç@Cv”n®)).ANN ɱ±1üð?‚Ïç—I }v,d¢X,‡a ººéTSSƒéè4NŸnƒ‚BiI).]º´áÏo¼¥Ù™Ô¹Y 3�ÖVÓœº|5(¾¸J,PYY‰ªª*{$PQQ�pxTüšf ÌH%%%Ñ|{³Äø+,*]±në[+Á•M£á¬µuK–°³œ’³¡¾A¬Aˆ–£�+==È>_^F�p­·Ù¨¶¶VFú¿¸UAA\š±Qø¼Atú¢×á0šëë1>1Ó´ÐØÔ$ŒfCÏ5ay>_F�p¹» ÙÈår‰UcѦ§¦ƒephÆ�‰d£ÍW�p¬X±|Enn®›AŒ8}W×�ººº¬ÌKöCÓfw¡®Ïº0 É;è†TÚ…©â{õõ7*€¬{|y«*ÊËY”¦¡alaMmV®ö\…+ljQֈѱñ„¹&�J&#ÍâF‹�K¥�„Gs•0œF k„ç†-�ãt¶ÖÕ-Å8Û`ÓŠ‹,(�VAÓ¤ø¤ÐÕÕ §a`h8€žžë6®^íµç¼á3Õ†™Í †SY(/+‡×ëÃtd PR[<­­-+„× BY0œ9Îæ††F‚ºPœY¡–T^±† 2 Â\¸pžÁÅà°9—.¥WcÞ‹cÈ …é¡5kä¼40pJ)±ZqqüCþ þAÄçe¯×ë¦6›–Þ¶”¥¿_´NÍ$^b L3míí´€fܹópöìù|rñSXÜç `ÝÝÝY]‘–Id-ÆYwJKÊ`¸œk8æcqŸæææåF/ß%½¼$O“ÑdÍËvB¬rîÜ9¬\ÙŒÛêfë…äC°ÿ:>¹pA2µ,Já¹b!Îå•ÒoÅhѪªjP™­ÙYÏœB}­Å%۵]TX$sBºMº�8MÓ‰LÑÌÃ0¨Á�þAô÷eFÀ¢†]´ÆEÑ6ýT”C…ØV(gòÈ¡«A�ó2Ð*µMMMå™â@Ïd—Ûõ¥òÒrqŸ¸iQ€8x�í.º®áÀ�(b\�6a QT\È5àñ枌È%òó Ä,RÂxKK Ž9bÿwD°ªW¤E<¹¹b%Ö­²¦rƒÍïÐbF û 3¯[V\§l“sÄã�'Oþ›7o&ã”3g‡˜&ýÃ�ìð1‰ ˆÖ器»w/:::°mÛ¶”(+-“Zࢵ”²À¦·k]¦8ÐÓ˜×Y¼ÜÌ@�ô=DÙ\†ƒš.FÙ‚”••âÍ7ÿ„}û^D÷åËèd CR�tFÂÈí§Náî�w¡³³»ví²•“€-…=TÜ´¦f!×rVpÎåBzSsÓ2)ÇŽ¤{dACa¶o߆ö¶“ذn.œ9ƒ¡Á!v–1ÃL­Ÿò;¢„.wôè‡xå•ÃTJ¡S„>-o P]½Pz"Æê–ÔqÍhàšKGaA~kA~¢Ì\ÐÄs}šŒ½Œ?¼ö{Œ3sõ^éB˜ã5ö>ã¡ Ø�÷ß{Ë–-KJ¿VdMB¦¥½ŽD&…‡|žM�úÖ¯_»íFY\Èp¹Ü+ËÊK™Âbb^KAL;_Ú¸ñ+ho?‰‡|ýl/¾ÿ½ïâã�Ûqÿý_OhÛJ‚™ RÕ- æv¹Å�‹ ‹QPèÛ d©:AWs6WSzpÁˬrælˆ$+¤¸�Œ�‰AÜá™g~ާžþ©d&jIõAãbŠPÉ.dÐ÷$Z?<úwmËæÍFUuU”mµ&m¯îÐØ>€%Þª”/æµâq‹K?£â¢±¸ŠrTÊRÊ„²ÀÑRàŒs(ðRqŒ›&(¼ŠLr›e)é‡LSqM±úó image/svg+xml tremotesf-2.10.1/data/icons/macos/000077500000000000000000000000001523411710200167255ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/macos/generate_icns.sh000077500000000000000000000025221523411710200220730ustar00rootroot00000000000000#!/bin/bash # SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 set -eo pipefail readonly SCRIPT_DIR="$(realpath "$(dirname "$0")")" readonly ICONSET="${SCRIPT_DIR}/tremotesf.iconset" readonly HICOLOR_ICONS_DIR="${SCRIPT_DIR}/../hicolor" readonly HICOLOR_ICON_NAME="org.equeim.Tremotesf" readonly HICOLOR_ICON_PNG="${HICOLOR_ICON_NAME}.png" readonly HICOLOR_ICON_SVG="${HICOLOR_ICONS_DIR}/scalable/apps/${HICOLOR_ICON_NAME}.svg" rm -rf "${ICONSET}" mkdir -p "${ICONSET}" cp "${HICOLOR_ICONS_DIR}/16x16/apps/${HICOLOR_ICON_PNG}" "${ICONSET}/icon_16x16.png" cp "${HICOLOR_ICONS_DIR}/32x32/apps/${HICOLOR_ICON_PNG}" "${ICONSET}/icon_16x16@2x.png" cp "${HICOLOR_ICONS_DIR}/32x32/apps/${HICOLOR_ICON_PNG}" "${ICONSET}/icon_32x32.png" inkscape -w 64 -h 64 "$HICOLOR_ICON_SVG" -o "${ICONSET}/icon_32x32@2x.png" inkscape -w 128 -h 128 "$HICOLOR_ICON_SVG" -o "${ICONSET}/icon_128x128.png" inkscape -w 256 -h 256 "$HICOLOR_ICON_SVG" -o "${ICONSET}/icon_128x128@2x.png" inkscape -w 256 -h 256 "$HICOLOR_ICON_SVG" -o "${ICONSET}/icon_256x256.png" inkscape -w 512 -h 512 "$HICOLOR_ICON_SVG" -o "${ICONSET}/icon_256x256@2x.png" inkscape -w 512 -h 512 "$HICOLOR_ICON_SVG" -o "${ICONSET}/icon_512x512.png" inkscape -w 1024 -h 1024 "$HICOLOR_ICON_SVG" -o "${ICONSET}/icon_512x512@2x.png" iconutil -c icns "$ICONSET" rm -rf "${ICONSET}" tremotesf-2.10.1/data/icons/macos/tremotesf.icns000066400000000000000000012163551523411710200216300ustar00rootroot00000000000000icnsíic12‰PNG  IHDR@@ªiqÞsRGB®ÎéDeXIfMM*‡i  @ @FQB°|IDATxí[il\×u>÷Í›7ûÂ�Ôbn–e‰–äE¢­Ðu%7q 4†SÅcha (E‹4]ÐÒ¢M¤E“hH[Ã1&(Š4u”È@⨱åıåȲM)v"‰ä ‡ÃY8ófÞÒïÜ7o6ÎF)V-¸|󶻜ûݳÝs…mÛô^&å½ œò¹<-/-‘ÿßžzÖóʹ„[·Í9'ˆ’èÅ"z×?ü}ÛÖÛ”mûxKðE!ú5¢Ï ñ»�‘‘`0/k˜NÕ¶="üµ§ôÔ]‚fgï#J7/ðÎ4Múη¾E}·í:2P6Ú7†7´YÂD)‘__÷n,/ëÿ Ä×¼Dú ۾ܱrÝËžøG!ÂüóÑmÛn¹ûÄ otp�PýõF—0áW1ð�¾*—KÄ3^O,^¯F^M£íÒÞ»î©Ýêš— ›¸È­¯û^^X˜O,.>ô÷BìûÛ^nU©ùYÏ “£ÃÔŽê[o‘þä“dd2Ííµ½7÷ßAÊü£ †0Ó¾–àñ¨pK6YÏø ºpñb�C™j.×ê¾gP™uÜö†(Ÿ=KÊÊÊ–|¨§\–ý[¶I¦e¶@(Ðd(µb¤‹­ømý e­^ _4J~¿ßÊ íQ}#[@ÖKbàE]§ÎZß…s�ƉcNÓ´¤®·²Bðüeq,áØ 1À�åe2šT«[=ñß@câ—¿øôTWU²1S½Òz¥ ë>ƒ`Û�6€� ¢À8¢+ÖV°-Z0Hž«WI/•´Á¼öD[àYØ�äo¿�G"^o,¶%0ÂaÉ�Íâ�£ÙÊÙ÷°9ƒ”@§Kòº—)5�ʵK— Ó¶K˜¬ÿ襗é€OÚöÚ—…8T*—¿´’Jý´•+—qVaŠÑggÒs9YÀU�fÌ›i:î1ŸÏ“µºÚ¹A¼… Y¼QrÆÁ<<‡ŸO}ʶßêZ¹R g¸|¥á‡ÿYˆA˜´Ññ½`Ç騖 £‰=DŸ�`36„ßÃj�¸¢ \DÙ_rŸí}šès ç$޳àçÔÚöÕvåÛ=ïÈt»J¿gÛI¼{ºr´+ÖðüsŸûË�y„øŠeš <›À²ïÈVD¹ôG¿üéoç‹ ¼C7×ÀõðbJ#Ø~Çú|#é†À3ÏvÀ‚Ô»E)7�ÿxö�`¨Ü4ƒRÿþ�º¾áH` ï „™çw7šn8Ž ´#w ã�Õ�-pâĉQ!¬‡I“–ewõýõ³¹cûèÞ;oléyö7IÀ^Þ0™H|èøü£#õõ»_Û �°¾ùÔSßXì^¶V¢gæç}XxÄ“ðÓXoLµ)¡Qk²Ý•P`è„Y‰8h éØÙ Ø?�„çÞWn17'µ ³ŒØPàßÌ?öè?ýÔ×ÿ}õ$J=ðØc¿=AŠúÕ¾¾¸||B¨^¬[%�›žºAaä8³ìH@ó:‰ QGÇF«}p�Ô� £L—/_¡ÄJâo�Ï„—ž_êV‡ß÷€Eê¼×ëõLLN ˆ>e2Y©¯=‚,ùм^ c=ÀzîªA#ƒ¢j666¨TBœÙñ"Š3LA,†ÆÇoζ’H$¾pìwŽ=½ð•…•nMôð___Ÿ—³6™Lºº–ß@Ìž³< n�F$,úòØT)1¼cP³H´¬­­ul’îóû)‚ü"ƒšA�X,J;vì d2©¨%åè*=€†öAa OçÎúZjM2966"ÓX¹ÅKdƒ�"@“ÛpÛ©Öc•uþH…´ v^ †I+XÿgÒëu‘<äó‹ÆtHd³¹ÙjÛ.º¤tê–@À‡¥—(¤x¦R)ºïð, sB¸;½úÊùî…P‚�1±Ì9pç¾®åM€ðâÙŸDž¶cæÙ¶0H»yr¹�™®  @WŽ?¾ ¦Ë燎YÈÁ²¾ñr5 Óþû{éC–yé'¯ôT–�|ë©|  ¯íR 9¡ŠñS("{yyW/ t�Üʃök> Ü�ù|V…ÑÑêïïë¥Y&“Í$µT€]éµí0’'Led§Ø&X�  ó,rìØ±�………މ…®Á ŸiMÓLFW®Ù†Q‹!#´Zƒ®r¨Ëu™Y÷0ø€ò=ë5‹q$›lD›Î4Ÿ‘ �²”N.†´2©ªz›¼èðÓUšLCÌL�âÁr^‘"f ³(,n¯”‚Á,é%*ŠR}` $ÃõõU„Bxh£P�ï’«)êbÝú}ýýT@fXæaHC¡0Û,‹8ã–kuî €âQvÁ£bT²¶±66bñ¸T·Q6âÒŒ»êΗ¯,à *€�|n.'Û¨+‚M¯Üa�Øc,/'z`ph€..þ\òÇ6@E’“VF<ÑÕt@�½' IU�ÀèlâGääÀ›‡^{ÈWÉD’|P!\4“ÍJO `šßû4¿œI�W¥ÕdŠg±£¶jÑ^^Ó/óǼñ9‰z ÅÂõ«Â÷ )¤Aê+sÅ�zƯE *Ç_{Îâ¬x`ÜtH 븎,.“«ë>YÄ@‡d±Í`ãëªX6î<ç_Üb›Ž#GÙ#c†# +"±rG­`뫎pòäÉA0ˆ¸–1²¬ÿÌX»0 y¼Ž3ùºúÓËÒáU „03` vº ˆÁÃÂV‰¶dóµ>ªW¸à �,RâÌ~A†ÎA9PɆ#æ¿Aùh.PG$è�š NEj²äöåðï>„±ä0½Co,ѰÀïÄ1Ôv·¸#&)ÓAÍk!PØ7{Ð ‹ûh£†€Ã�û ïTj]% n.ÑO§3„¨³¡«N 0ðù|”^OS&—¥x,Âé! 6ÖÞ•ÑVa•÷Õ;tÆ’Éë½X�³1¹ås�’Te†2ð„­¯g(„Ýä9Ö¿UœV††‡)³¾.'†Û Ã,A-¤+<]k£ñª£@}¦cáK¯D–«êðýضrÅSÎz…™ž*@¤×22ŠÄ’MA+ÉU⣙\ ¼pa–e�k^áíDŒ_OU©©ˆëa°¼¼âð F˜?k&m]aGà»÷Dc1LŠ<ÎEØ‚�¾~€êˆ]ö븒ïlZÇò™­Z(¢¹©Ii¡9&hEÆB/hii…4Õ+U¦ t¥/çT¹qÁ-ÀËs擹a�ÞµôZz·[¬Õ¹#6‰©H$*u•õŒõŸC׸¾w‰ÙñB¤ƒ×Úà8ͽwÆÙšÂF‘®\fU¬½wëן6´N� ì��««ƒÎ2¹u'�bÀ@ü;22"#Hüip©œYê‹ÅÅÛôÖŒ,Ôæ§-H€†I±úÙ�p÷rÈÄ~×�‡ÂN2 ÷N“ •ʺ´ÎÌ+¶nñ¢±ŒS²ñײ�%7ç’ÉDÕ^8¥léA‚ðLL®ì�Ž:+-ö.lsXU ¼„ßyôèQõôéÓÍ98Y¿-ŠbL#(}3Ï:ÇÙ:|7ì  ®Šî+ã CÄ_{í<�9sw€b&¸<»ÁHF –¿â…Q«Ç"Œæ¿?ù¯² ®‡ˆ”𥩩)ÙL J�Y?f^ƒ›-`‚ÅÉ�:2ǹe¶¸-&y¦šf‡‚A‘Éf¤pôÆy=^�9A«“ν{ghbbŠžûá÷iñÍ7)c÷³ó :e*9OÙ;m`ð%¨ÎÞ=·SÀï£ýû÷Ó�wÞ�õB�e €‹¶×‡†¡jyÉ+ON<î¬XM’«Â­€oßó•=ªªql\xÅÕ?Ðßq¼4ýÀû¢Ûví¦gO�°R;÷³óÒ°5 Õ¡• Døî»ö£nMNNÑÜÜ€GlP¥šäUábll;]¹zÙ1„˜?Úñù}e½¨s,ð_õeÝëœî“Êj8�Åa°‘u­¼ÄkÖ¶§ÇN~ŒžþGrF¶#òâKçê"ÀÍ­hš—n¿mMNìÄàè�~ù¾V™¡FÉs[â$Ío¾áð‹Yã` íØH—·u…mðÀÄãqÕu)ÜIQ‡ „ì•X'ï¿ÿ×èÖé[é»ß=E�žW^½@¯¿qQ)·c§§ÆißÌí2¹qï½÷Ñž={«ºï–ëv¥Všlµ™oþê «---ïmW·-BñLrÌ�vª°�…L7hÕÏâüGOÒK/ýXÆì;wl£çþç¬Ü_¢ÙƒwQ$¤;föÑ¡{gIÓ×Bì 9£ÄÆšUFºÂ¸LÛµ�Ú€E̶rùŒ&Ü0»^_ q’âàÁCïI:õ½S2Þ_ú ¿/F““4÷¾ûå îZÚvëp0Ä”‡!”.³Çy Ó4ÆüqÿO<±éãÖ<òÈ#P …9ÇÒ”m,ª:,úõ«Ð‰ãóôòË?¥ÅÅE:|ßaÛ†Äó¯€x©Î¤y}’_ÌõÅûY•DVÏNãöU~_O›3Ê*>ŸÂ‰P,D�[ƒqÐÃÇØØ6:…ÏXyár=„>èÀ�;騱�üÊÏ1Ç÷ðl�`S¥n­‹Å™UÞl˜á±5óÝ \`÷Ü\(»aˆÄ4òÃ¥ ræ¹"ÏÖ?ómúÌgþÈʆ›ÛÛÒ=VjÈ m’Ê-µáfõd`>ÌŸËÊǼe¦ù¼XÅ�ÕÕäÝ÷ÜsÏ¢L`¹ÑCãÆÈ‘#G´ÕUcHé³vCä±åg*œÂrË3ºüàoÒÕ+WdnÏíüzÎŽtmš˜-7ÉYeÞNãµ óÌÄmóèÑHDAP¶7­+43Ã_‘;¡*.ªdÄîÝs^%lÆ!(ƒÕcærY%µ–ªÀ�râ"‚äC¬/.CS~ön"ÛsùœÜ0åœÛ-kl�ªŒ†ol?mOa¨eW ªð Êá¢â³mD~¶ÂKÓ,Bàµ&&Æ'�¨H#ƒë“ËbÈÝ»iür²Rk«à=‹ªÑ+W±0æ¢ìµ4+—o·x$¶­Ê±þð'']à÷õÛÖ—~~‰½k‰÷8Ó¬#WÈî ãØ4[›pGcU¾åãÁs p3‹?ç$š¿L¯Kƒ8T_ØLä?T±Î߬Ä0±4·£–õò%l6ªi$%ûñ?>ÜlC'#K¹ùj”yùØò?Ê6©€¦˜V*�>ß/½yáÂð̾;”m€¸¡›‰xöÙÙ///±[4K†¹Pj@e0 ŠÓôæõ²¥è‰µìçQá³/œ=ëTý¾J~þ&AÁÀÇy¸ÃõtÆ€=[O¤Ö4£ «aµÁ U`¿¸k×®rD‹¬!¦ $ÒéŸêzù“Cý‘—õò[l¶ ï~,ì ]7_^I­}UÏ®(˜~½˜1x¬.ï ÿ9ŠW�z ÀꘊZÈ‹²›\‡[ùf8³ØóÌcÇ1ýz0˜BÒ²XÀÿ–«$�­®žIEND®B`‚ic07+š‰PNG  IHDR€€Ã>aËsRGB®ÎéDeXIfMM*‡i  € €HŽw*üIDATxí} �\Çy^¿¹÷¾°Îű � \ˆ·)êL…¦Èˆ‰UŒS9ä(r¬ÄQâTR%±*‘K‰U‰Mَ⤘b¹TZ¡«\Jœªè HQ–xS I€¸± ì.öšûåûþ~ý^ÏìÌîÌì›Åµ?0ûÞ¼×çßÿWÿÝ㸮«ÖàêÅ@äêíúZω�5¸Êé`�Öà*ÇÀUÞý5°FW9®òî¯q€«œbWrÿ¿ñû߸þåº78*’ª§¯Ž*æ]åp�â_þî¿ø·Ïד÷rJë\©Ž oþþï}ÿ‡ŒÄ ž2÷[ÿê«ÿæ«+,ç’Ì~Q àIlj *µ>«Ô�£T;0K¹_»c[î¾{ÿX9Žp¸H$¢œj¨81Š…¢Ÿ#þê_�?û½—ü+¸AKŠ(y��Œ*uì¹nnÅ­(몊€?vœ‘‚R÷‚-ß‚iõ‘>¥¶æ1CCWDZ[_³uÛV544¨RÉ”ŠFcŠÄ°14…B^¥3i55=¥Þ=pŒÄUn*ùõúHh©Z´¸Pø¿í8Ç��7ðõeÄÿû’R¯J¥KÊÛ¦ÀSŽ“Êÿ.:ø›hñ^"÷M…"œÀY¿mëVÕÖÚnè¡æzÛÛ:TkK«:þá 5;;« ^™5P{BRäàe ®€3üûo+u ~lá©æºçk/ªþ”M%€?rœO cOcÀ7³iÑDBµoÙ¢ZúûU[G‡¢VÆðÊ ~98¤Î¡´ˆQÝݽþ¬w1»‹nÀÖ+U©ä§�£½±˜nY¢½]ÝòéOWÊÒÐ3¶ÜO‘÷Cª¹…µ01¡f�S¹ÙÙa<ú:”—ßwø—_vÝÿ‚ïM�¦þ÷ÑâÿŠO$�«Mwß­¶Ýu—êhk­,Ì/ÇÌ!2VUÄ$rdó}±XPÙlVE�åkNÄâ*ÕébQ­–ÄR)5üÉO–WÚw‰a>ŸWG_yEýáUöÂ…<úàòFÁ?­2« ¦|êø#|dðo}â µ~Ó&=Ëçæ¬ê›t›D·bá%!û ”ëÔj�(ߤeTÛš×vÊ�¤÷éÚ³GmÙ¶Mýìé§ÕÂä$«ÿMàô%ÁŸñK˜PFê¯ñ!daԷݦ6ww+Rå‚ÍeÞxCNŸVÅùùúK­1Gñ³÷+µ}«¤N%¥àq|–fÿ¦x*ŠFY4Wí_øÓ§M’ЯÄM¢1qã�*ÑÛ«À½>þ©O©¿zöYð2Ñ›Çå²!€½Ä©zÛð°r0ë]|ÒßûžŠ‚–gÂÌÝ8D2?³ñs¸�ýüÔPú‘Ú‹¾œBA¥Îœ‘û¦ý�ü/¾ù¦ŠßwŸŠnØ ú@ƒ™§€;´ææfÔÛ, cL�t øá‡*†Á_ 0CǺ ‹°ëñ© àtJÓ²LOÔVD£©À¥œÃ‡U¤«KJˆ·´@CÑÓ”yÓ,𻿠¯ dÿþÓæÞP4`f½Øò pryǬÜ5ÿO,—S ˆB¾F±Õh«šJÄݼGy°åt£­¬3Í+$€Ú9€ïG2ňïbµ ȼAô4šJløœGɹtzÕÀFYÀêÑœE:Àjr€ÌÕ¼‡·‚¡à&QAS €`æÂ(QåB«�£{ÖðÔ&uˆÅÚN\©©HPcÝ~ùÇŒž�ëjq€LϬDz–B+ ùOSH;D¥‰³æìø¸j�×�PܸQea×Á Ü&²¶à)~ôòS �X]x< h6p¸`S0ÑÓ£è{äÁ æðñà ¹ óÚ@ÿ7>_aC��ú1óRžgMuv*‡¾lx¶?‹÷­€z8ÒF|n¡)ÀAû#ë×7«Å~¹2Üüê?^&‘ôC©ïû‰B¼i ü–ëþž«o£�_¦óå,|ÜI Jk,&Þ!on…Ø�Ò¢lîbD@}V€v©Ìoä²ý<Ì{’-¥àl¡”K¾wÖ ³.SVS€…c1æ·{•:…™þ$Xo, 30MÀJã¸ç•Æ­1pù6 ÎP´Ä‹ÑüÝ:üôøù<@ñaä2š¹"`‘üPTQ´PgÉ’Àpð¹�|{>Ó/à ̥‚Сið5­~ãça€ÿ5Zþyðc‰ÎaGó¥� µcD¦\=ÛCŠêkÔ\ð(?­.‹9ÇkÔ!Bè +ý&ÿû'®ûBåU-¢i`jüm×}÷¿ñŸç+¨ì>Üÿm|nÃg;>MbÑhþœÑfV/Wi„À#"=üËååýY ø/@h?À„ù‹/¹î±PJ]¦�¦€©ÿ+®;…û?ó> ±}èìtzÏÖ dwà³â6¹]]ŸC™;Èg�ÈÙ_3n�ÌïRL&/àöOôÕý™DYÔ÷Æq¢àô¦ã++µ±Ü+FvcÕ*Ö5Aâ6^n´Œjù¾ù¾q=Þíà{Cõø˜Öäó) µeƒô»Õê¼\Ÿ_4X-„™�¤X³²Þä[­v^¬z®(™ÕK£›ƒoÀ“Kg¸Œß^ùàiî”ÿµ„PYô¹…§ ^Æc¼dÓ¯|ð´8{V/‰¼”´Fû[.ñeþþŠ'cÎ1x¹ˆ`3–Â-¼™¿& V.Ó+•?‚VkNá^>°ƒË´çµ5ûŠçF™Óf`mž@r “¯64^¾©®|ðÖÓêr‘L¾Ëwlkjù•O–¨ÕLÃÁˆŽ5P]º‰ +¯›\á²ßŒØÏ+ öÅ Î~“ïÊVCXx1”´ÜõñÇïY.MXïoسáX^¢-yN¨—˜|R†” "«Ñ‡\.7÷ì³Ïúq`ºêæýmxä‘GnÄý{ˆ­yÍ¿¬r®\˜¹ º¼�ÆšãÕÌêåZ!* ™úÞK7ç ™¦nÕ–vETö‘Ç>÷Kˆ®?/äÜÿþÜsÏqѬi€ÕÈðƒƒŠßÂàs[óø¬ÚàÛ½áØ™™O×n]1I « šÙöÍX"røáÇþÍlAèà±Çºó„FËÞ&Ù¢�;Z°Å‰»n5knf—°+µBP(•Áš9Òåш€h$Š“F†šÖpG“ËçT&“Uéô‚Y·èÆ$úÓG{hïîk÷üÖ×¾ö5-ÏBlE¨ðÀt$[âa¿½½MmÞ¼Eµaƒ£‰²n¬í”çÔ0%e¦‹üÇðùf æ¦˜¥®%^C¯¾Z7oÞ ^*çrï‚Ì ±Š‰óù"ÂçÔÄĸš”�Èsd”«ÇÈ –ýxyJªdPSã»þç7¸ì†'’tuuªÍ›6«�›6úÛÔ‘ìŸ?òù‡>[–|Å_CãdçáGú‡lYýöí[U³†@všÁ²¸Z—d%cƒZR-þÑ.F›7º@MEÒrð¬=ÜP ¡CÌÎ5ooP"†¢+‘Î0Qx?0Ð/„zòø Ýl×ùèWÿVBhÛ¬C#€‡~!XE’ýhx"¡f( D{fffUÞ n ¿ƒqÔcZ2ŸMòEW-¼Ç~„mãø–æAGØdpªI \³CÎ7Šã¨šþuëÔÜ̬šžžfÕ›aYñ ˆÿV;B#4ì:#ẻŒÉïÊ [KÍúL:#ˆõñBÏx*IÀ"°¾‚j]f“O2³è³³áÁée<¶.],�s¹¼ vwNU�âÄ#n†Ôì±$a{fõ¥G¥ÞŽö�õb÷€r?�Á%¨¤èIì,@é : |¦p¨Áp^ÍýrõÙi)± 2uGÛ„ ­°Œº{{ÄB²Ëå„™™�Á.º.Q:;Ú;U iô±:·=ñÄmÏ<óL(Ô¸˜í–Ôqüngrμ(�VÞHÍ•à,ŽZ™ŸhÊà/ª�ã'Mfp—»ê<‹J õÁù$EÉV6†ÀRð06Ì\˜a�}·ìS­­8© À!ÊŒôBN-œCôöõªOÿ­O¨ïÿ¯ .õä œi1Ň҈ck àù¥Eï¼óÆF¨B¢ÔnµÛ/çwÄ4œ¬ŽÐ%mÇöm+b£¦ÌòëÔÔô"yZž¦‘ïºi7l ÷Œ2Ë›öÞ¨^yùUщ2 ÞO�•vÂz‰à´Ò�­…%¯•jŠ€P P($¶cñG<Õ’„÷Ê�ãÖ“ ¡±£GGw¨.‹Õ…Ò¯�ãÇO-"¼°ÊŸ…nVÃ*“å øÅåÁ k¶““Æ@|q›lIáŒQþ/-€`;)™&`­­�%�ss¡p�PJ‰Gàb¶DÄõkw$où¸ØÑ,8yâ”­¨úlÿ H bü1ºNŸnÐn÷Ì–r ÷’ÇÞóÓÊÂð R G E@›„RÉV(*Mcl´Ê6]X)lÐì<‰YºyÃ�(TùhA XWõ¹ä倄K…‹�ÏðÉ£¼<–hÏ‹s˸™—+§ž÷}à#áE¡`ӤޕÅ{´ýÒ €b¬¸  ‡ ƒ .5Ù ³.@·¯¶ª¡Ÿ©Гµ¶´Té*挡ÒŇH ûÏxĔ޻ Ü€b,ç¹`)ºÈ øPÉ=¿}3€Gà ±:�ø¤OMÜÏÀnk[;ú"ÞÔQ$õ­6ä˜ÂË®+æ³£ ô¡ ˜�5Pn³œaNfU°ѺÚÌÁ{¯é¥=(ý¦‹ Ê’ö#I�*¸£o]Ö!@�œµ°ñf !›A¼]Úò–iá1*Ž/žì?ƒH9;Þœ ]z ðxX.LAÃQÛa Liwzëƒ�?¸yŽ›|�\WLFa�vqÒ`8—p4W’€[Ø-'5y mÙ$ RywHLùÌYJ…)¹ÉG}Ý#m릸ŠWD”ð9V> }‚J ô –=!À:·®bq5>ìêî’ò9YÒ fÒ0ÄÎ@´èR \|ào+Д¡Œ©Âv (–�¥<6ÐƒŽ–P·5”’&Y¼ñ�ìãÚbŠó¯…b^‚(ɉÈ9kI”F÷Èå2`�Œ«,«BïU&ž9˯$$ŠM1uþü»è‰1/Úç7ѺóÞV½h¦£sr†Ó;znâœè*fÆ3³²xU[{` ºÅ(-�U­ †+æüÖC¥%á)NÔlS&‡Ùg «‹žg‹ÏH<ü …ÞàXcäßZ,›ùOsfâ¥p̘g-X¶Ñ=2i[xµVºPgü"õ/‰Ép^/ÌÌ©nß—�v£^¿]æÎëNÐ+ÖTú­\¼Q 04ŒIýß+¢N…œ8mÐ @KX±)¸b@CGÙgaM‚ÌR Φ`j­z»{„8Ȧ|Ìù7Q˜\:m ú4öLUL`ðuw…P$ÀÇÔM"¤)X $‘Îäã•~µ)ÿ‚YzÁÉ4TáJÒ�úÄ[ß:,7<$ø£ÒʽÔ ¥FœAÄ-qŒ+¶VDž (Q Œý'ÅrµÊ–_ì€P4®lx'”@^ù¯üa^ô ðrÑc`‰Ú9g?Sñ¬`zð„8“Ìd"•€WKB&‰Yï‰-†¬Q.Sû€xý�6«P¨®¢¬ÅþW‹0ü¶8ª·7ˆ7 Ðj-=³~ŠYâŽq‚ *\\@Èþ¥[$´N|×ÚÌcû4Ь!�KhW±~®ÿ4~|,ù‰äµ<ö±eåÑ驜QK'pcåv3&°JýKYOY Øä À3%5�)uþܤ°c«5Òw¿Á¸)mn�RßY}´nû,Àiá6IÐ,•ÐÑÑn`û¿øÅøw¾ó�€ÅÚ�¨á~E«€£l?g×°£PT²â»¶z…÷Æþî†ü·@Ý>¤ÅÿE^«åA9º‚r-cA2ŒŸP ¹P³6 3аrÑŒ'Ы‚ÃA¬G*LŽA Ó膜Ÿ¢"ˆx°3ù÷V‰~ÿ¦¢x3}_GàA`ãŠDmV;;:Õ8~‡G„Õ0®‡½lu_VD°ôFI˜DL!^Ä5V[�uEL¢]È:M�­æ.’¤(Ëb”eyJÞ»f´‘ž¡èöêÄŠÏÊÉÒ¹9E†"¿¶X Ë7kÌGnÂ,‰dBf íð@‘µ:#·•„›i>”× J6�tô/Øû~_ £%x@§lï AÇ)R \-”²©%Õ 68ûè™,Ä®½ÓƒŸCë PýZ(ŸÒÈk#è ¾„ááž1Ô?Ä ?z.O@ï~î_>§BÅ€U£°m‹�§)ÛŠ�Eä$$®¿“›€óªvü‰@Ì,`€¨TêM¨AÃw ôÚ¨SiMÝA�pb%Žc'Ð<ê°ñh[VÜ8bùWd ¬ˆ R9ã%h&QN0ìŸß»¡9ëwÁ{ÿοш°g¤M ,ÇO*7.L'Zzõ.`g1á BÐ×aw a?ÍZ 0Û X»˜ MŽ�æÚÛÍnÅ=E“¡g¥»…K¹’®|Ío¬W³tÏ£ < ^ë»>´•@ÿ…ñ=0§ÖúÙŽ¨âOÛž."x&`Ö²)ŸtßèºeÿêÝ�œ�„`€dèNé¼Vú±ä)OËWŽš€Rfäs6íŠÿ߈�§NI€jOYZÐB¿øà/½| Ä#«˜l,„«©grrJmØ Wu«¼Ëš¨ŸZåA™À³^÷÷kbec( ìà™b^Ê8|¦¥“K&›rVd 4̼­à² (›Ø1 È–[ìcÛ ôv÷ÊLâ÷`€Kgá©J¿ÙœÅ”É$Ük@ ³„³—>„YìAxþ‰VfHÅÕR@™?�Ø¿4”0Š~ÜÆEýÆÀÔôôA³×]l¹x“.”¤�tÁ$¾ ôÜ"˜z]ÝAì÷8¿ÑHLB.`¿ ³2_@ÃÐÒÕ¨a”/V„!ÿ)_ R46H�ó¥òM7˜·ÖÕCœ… Œ¿þVJÌc=AÎVš€Œ;Ôæ¹£’½ò¡ÐÓnžšâ~{«N¹5‰ÊŸÛߌO aQ3 K€ÜL×g§ Ê“»à+yBIJ@L¢óóP-}ŠÄh¾wvu`¿ Þ0 LK£;†&üºö)—¬¨+Tœ�D†Íþ‰š3*Lfw‹0À$5¥ˆ�LÞ£†)ùô ÞÒǰ�™cœ7mPâ@SÓÕ·Ï™�«P«TmÿiCy½=ZîRg§¾Ãw­”•€Fé®é¿v9œê’º, Ó˜öôƒ .Ù/ŸŒ4kl\U=~ì8³‚ó;pý¿Ô Z2ÂéD–*� $OY²AVJè…°p`  òUpVŠ8ýÍJ\v{¿­G"$pŠÐ…�µÃ[·¢>Ì8%¸Sy}’p™? Ç"�9�CѤh(º•Äj!·¼“›�Æ ;ëÑM´êuÉ» ÄàN*`»âŽ)†ÌMa—Ðv ™çL§9€.·F ‡ŠàêNÜàïý¤ ”P6Ñ[ØÇšª ôP´Ø™i<‘á£Cúf!‰,‘Šo:¥ŸOÎOjùO´‹ÃϨ$i|Rsל)¨ jùhÍH!X9F˜Ã�ŒpÒP§JkX$ÞP × »/:“ = _`Ž»�Ñ~ÓÁ¯t .u+²ÆiÊ`ÃÝÛÁ–%˜€&~-h î u£Dõb¥«\AÔHðØ¢…2ʹ˜·¿ßzÜzX£­Ïýtf@(XÍ7;‚6ÈØè�>€egaž™ú¦§g ³‡°�S†¡K¯0¤x4Ÿž‡Ž¢SI't^&ø$«#'ƒ>|X´|Ö'»­¼¦C @ ¤;8ààr ›‚ €*:;Èë:Á®´.ŒBK¶eVÖŠÁëíYÌJmdq¸ÐZ Óf9 4eŒÂÁIÃâàlánšYïÇ——+£Ö÷*8•Z‰Fßé­[×ÛïÏÐ¥ÊjÁnŸ‰sAX¹î­PA�Íû:08è?ã™tL ˜‹Pï’‰ç/w7Ìü±3Ôr½÷Þ{±ýGm`ZR"Ù>å!9g¯ùðçO p7�yÎAâ‡Ä|tì;ËbºZ€eP3 íd~`-5åòæÓBp´:¦q ¥kÔÐX±ÙÙ7ïCÝ�"1øh\°O<ÈÀü¬¶ îˆgÞS¯éìôw¯.èìLmŽe”¸2ÅFóH•rï-YJ_Öõ­C£5‰—0>=L%øòù~¤R‰à•²y"i9ãO!B—lŸ€~~zìhëÇä %¿ÈpnÐÛH1*áZà¬óèGÔŸï*.ÎøBÝÔfõ‹Š(qsÞ=âjöµž’Žá ù´ƒ‰EÍÍÏ–àÕ^k¡gõÜ9Ù6S° ¦ Ø…¦ µ\Xgƒˆõv°¢¶VM4QH6Ø€>3³ƒT^�½LÄ—‹oºi¯zûí·Õk/¿â;tìrí{ÆË¨ Ñsh'ñ>‹ç4Oy²G'¼Ÿtr=zdÉR`ßcûÆÔè(ãftÏÍÕÎ(ô"¯©ö‰©ÉX„¹¹ù¼NB–koT-F‹ä¯ÚeÖrßhP[-Ø ¢åþb€±H(\!=5M H�Oüo°ÚnºéF5·M½ð êĉã&ƒ\9ÐD„ ÅfUdÑ&ÌÞõb\ï•výÇ?þIµcdT½øÂOäüAº—÷Þx¶k�Uû¼]ÛÙ�w®ÞZ–NOųN¦Ý׌*{u‘íÛ·O]·ûzË3è�$Šôïü=Øò×§ÿ¥4bhàÁ¹sçaùf¨Hh×\s­úÈGn‘�¨½N]‚ü5…y™ÅB@ó‡,_Àì,,Ìxº{ßÔ­H€Õ!€ýû÷o‚(Z9]L % <öÏGv´«•¤á[î?¸ó®»À FÔ ?ù1ÜÁ“â’ÞsÃn¬Ñ©·~¹Ž ­4\‰—‘ƒ¼ux³ºv׈è<¦¼�›6©;ï¸ } ¶t™w+¹Š…T¦ �Zöä"w5\®aö'Ožd•]>ñàÀsϨøõ×ý3‰ à@~ü£wªw½}á]aªZÝìÓu×îT#;†}–KG'Îí·ß!&oµ¼Íx>„ÓÃHô§0ÆÑD3Q�Ø�í jĨ›PÉ&ªX<ʼ�fÀ–QDY�FäôPùÖÜ?l�/ü•’`);ö¡ ä5PÞ6A7xùÕ·Ôlë*ΦÑ[Õ ×íò‘Ì46nTwÞ~—’-[å™Váû€e P´õ Z$4{JÎ~SñŧnE°n€üß̾óôjºFµîg¶/X™÷VãšÉþƒÚJï¤úë¿~Ÿ8�~úÓEKæÏ×|죷«÷>øP½þÖ¯d¿s ö«}{o(Y_ç¦[n¹Zÿ®Ò‚WùÛ EÔì�#&>�{&>ƒ…)@ó •lbMô…ó‡”h¶�%•š).D€æ+u±®F�ŠÚ† ÔÏ^zI:|HfÊÈöaµaý€z"a^<€²Ø›o[U9oê®t¥j@A˱ ñKà$ó`„fz=?.U7@}BüM ²ʦröÏU@í`(X HIkWù]ÕŸøä§ÔÈÎ�¾‰+†·î»Éo Û¿k×5˜õ·AóÖÞýéÆ6Ÿ3. ¦6E€ùÞ È‘£°•-b¦+õa­M®‹î¿ÿþÖTk¢—ëPp4Óß4ÄT*1xæË%r¥iãŸW¿øÅÏÕþý¿™É¦ ­_¯î¼ó.ü4KÀ .‘&Ci ¼ˆSÏ2Á<‚èA�7*–@ø€Ê�‡zHL@²J.sšõ}c’˜FPéãBcó�•­ä™4ãÊvßuׯ©‘‘QõÊ«/«káÅãý¥ g-…•Êt ž1÷8þ$ þêˆnqžý !uõe¡Ôv«�œOÀ+¹3Ì$4—¸Bs�?¥Â×ö‡Úé çÊ<|è�úðãJ¾x�è@úÌ}÷_ÒƒOì¼ñúë>’¸½ÍƱ¼ î…A`ª˜‡Tá6�1óóW»Y’¼B56KF Åm,H8€,ƒ.؈2à÷Ý×î–:IˆO}û)õÎ;ªµaíyˆ³7Þx]ýøÇ?”7\ðájh9ž� ž†; Äìš{î¹'®Ô=QoüÊJ/ýZUð2;×]w],�‹œë¶¸naÄÀU(þ.@åuV±exXmß±C½ÿÞ{8ÖlJ}ë[ßB¨Ó`ù�!–¶ßȸŒ±èe=P�ñEÔ“­RÚù�ÖøË µ7†š½�L¿oßG*z©r ¢ˆO$Ç=…}�XqE¿ÛÆÆ ¯©±Æ1¿”8¨JÒб±h~6•Œ¤ÓíÙ|´Ã�8"È触\ êÓß‚¿»çcâ0:xð]Qºè¦åg jÇ8/V¯©˜�œÂ¸ˆ»±(DÀ˜ «DG_q.;9™Ÿ{ s DP¨F À›ý‘1pû\ÒmуŸëD,êF¶„•ò·_Ùûÿ+µòÖ[oEh×vr¼«Î"žŽ¿¾É†Wê¯öºæç\1 £œÚc”Ýš+¯��žS{A§BÿhF[oôþ1àµ�˜ðyçzMµD®Ig3o�Ï¥ ###ì1« °^  @’��Ep i¼+ºÐ‚sj[݂ӂˆg‚,‡ŒÅV$ß*ÿiGãn%¯Aý°—ÕËsS7ñ†qˆe©‚CÒÅ÷Û‰ ˜UÀd XDÕÀ™žŽ`¡!>WŒ'ÅJ�ÅýƒÓAä.NÚ𨒛#–#Ó°µk¸p±w1ë ¼}„},߀³š’Iü|Ë×¥ï¼ö„A¨F8{“3±°MæÓñ<»b0þÂûES3gî1ô{)à¯\Ð·Î­Ùæx–vvh‡‰Wh×Ú# t�OŒ›Èÿ w*¥²ÚsiÎC�—7špœޱ‰ $ú¢ss§«ÊÔª¹ãà …H>ÖqŠi=ø^Õ4?Œ\b8ÒR°‘-ô ´Âlô~YÑ+GGÒÔŽFVs'´ äèfÌ•ï)ð³ˆK/FTÑC‚T¸ˆI5‹ÎV¡š˜ BPø9X·èSÜþH¿ÿRpîü9„…w!lëœÖ}úô)D󬧯º5`€3ýÌ™Ó>þLêÊfÌ™Èú§¿ëd®ƒíì&iUTWå,ëÀ›`êó¯psòϱ¡2R Ð@‹�«†>À3~Ö`e ^Í8ðÄF`I¨V " Åñ0)®Áêc€¢ÕŒCÞ:’¿ž–,é ®§ µ´—'"{ÆWó^žè¸úZ]ðÐ@ÆsñÞ>½‚ß×àòÂ@]€Å voñgF¥cü].×Ë m—FkÍéèlÍR–ØR­­‹àx’•nÁævl]‘\ˆXƒÕÅ'�9v�“Ñ8Ùt+4§®¥EuÎá{Ózüø‡¾öÏcÍô ugŠZ»Ö‰jÿëÂÆýŠž#d/ƒ=ëˆÜÊ­jbf»þ¼Y?€ü?75õ“Ö–¡ß@¹ñqüBgþzD×�éî%EÊê שנ)àJ!?FìòÀ •Ç5™|þï‘FÜ/(1Û$ñ¯U €) á»ùtÌ�Ä3nK3³¹ üüêÿI%Ð÷|äƒ÷e]```@ÅebC•~ k7MÃOFåÁ,¶�7;æëäøää�|Üa€‹`R÷ÂòªAUÀžw^Àb"¿PÈFð un T”UÇNNü�Ã{Qðê,œîÞ�þAüv;ÎÌKøL£Z¥kÏǽ®ä²\ƒ™Æu1¾Yé�›�&’s I¬ å�b!�IbY0IP‘ p‘xQk@9 C\™Âˆvöâ þÞå¶Áßw£Q§£=µnã`ßï�Cl^”yíÁEÃ~søûß?ñX7pŠ388è\&™ŸžW¯½V14¬ªÖ¶ÊÍ´·ç[£ùL"ZLçŒ ¢*gfg'Þ;z⟢¿DosB_44]yc—й™Ù…ÿt胓ˆóµÉä0f‘X,û,‡Á¯ª”Uå@S„Ñ¥øµì6„iu"É®¢‚ò1Ú!@”ÞàÜú–T__wÛ­ÉXt¼¤*A]yh¿¸=ò1Ýó Ùì;gǧßÌ Y &""׉Ìáðþé¹Bá‘\n;a8û+AE`×(Z¼gÏùd4ÚÕš�¥;œL¦5‹%l�-âg °äxqѰV;1@…�2?só¹b1�ÌFf§Uzv¤¿þùçŸ'‡†WAÖãEU%�@…x| 1eónq>]È�¥8±X €ñb¬áz3°#ü:@b·�É! ,=çæÒ#ÝýéçAhYÕÁg«ÿ?1ìˆò™¥!¦IEND®B`‚ic13eÁ‰PNG  IHDR\r¨fsRGB®ÎéDeXIfMM*‡i   gêI@IDATxí½y”e·yˆûÖÚ×®^ØûÆ}oî¤$ÊÎØ–dQ%Q’ÇÑDsÆ#�—Ø™““Ø3gÉs$ËdƱ3q29™Ø‰¬(qä(9:¶å…’hË”%J¤ÌEE²›ìµº«»özë�ß÷Ý{\Ü·T½µê¢ûÖÃúø|øðáฮ+“` ÁÀÎÄ@jgV;©u‚�Ä@B’~�``c !;¸ñ“ª'H@Ò ì` $`7~Rõ Hú@‚�Œ�„ìàÆOªž` !IH0°ƒ1�€ÜøIÕ $ é v0°ƒ?©z‚�„$} ÁÀÆ@Bvpã'UO0�€¤$ØÁHÀnü¤ê 2 Oý³§&r¹GRŽØïVÝ™nÔ&•r×E5u®Zužûå_þå׺‘g’Gg0à$‚t±�†úéOÿê ®HýŸÂ?�¼r�Î/¾ë~ËA9þÁ?ø•?ˆ�“ô-зM_°Oýú?ù¨#œ�Ãñ±º‚òü‡õµâÿøä“O®u7ç$·­` ![Á^Ò~úÓŸü\Çýÿz�uÃ,q¹Ü k¥÷ƒTFN"ô!`_4Cs…øõ_ÿÕ“ü¿Ý\ìîÇr„x,?’ýÅîçœä¸Y ìh!à?sœùOŒÝž^^-Z#ôÀí¸Œö,âwÙðq™2�ï4¿ŸsÝ+øÝ‘¦:wÑÿ”ã¤v q:Á}è÷#«Û`?û>|øïø&ð |zkqs9á:)D&“GŽS“2,�N‹l6'èŸJµ‡Á«V«¢\.cÐE¥R‘ù ñúoˆ•å½L¹lÞ=~üïˆo¿ û÷Ðå·«ÿkå7‡•8�ï{ˆó ´ý_ñ÷\wÉŒ»ÝÜÛV€ÙýòhÈ÷b܇_Îæk*'Oˆ�Ÿþx¤ü7Üxƒ˜žžŽãˆ¡ü�È‚PtÒpæßØXÜ="1øö·¾á²ÏþµÈýþÀo PŽñ ¾?CßùO{…xæC®ëQ¿N"¸Ë°·ð/gÓ�¡Á>ˆÁÿp‰a!ÜoÜÚ ¯V„³ýÜÜœœéG†GD îNry™µõ5IfffÄÅ‹µl«““š{@dŸnæ‡>ôó¨áÅßrœ?@_úÝŸwÝ¿Ð:Eн-À¿pœÝe!~µû%|Û¢÷EZÊ2³sÀçÀî‹tº‹M‰¼˜ß:ˆ@6›�ÕÍw– ‰dØ�=üŸ@VŸÀ’á/`ÿµ_â¿I*Ø�ü;’K{MûË�’¦_äŸÆgŽ5›+É=çOõ—vrý`VQJ¯Tƒ½w9øó9Ê.»k25‚c“3°Sõ&ßN.“„ü¥{‹¼üÃè_ø-!¾‰¥×/ý¬ë>�iV*ü(0þïðí�Ãüèu׉é›nGŽˆáÝ»Efd3fVªÍqÞâ€W¿88ýàÿЍŠgSìÆ¡Éåòâ±÷>†%@”LU+ÜI��ž&LݬÍp©4É¡n¸ðÍoþµª!»A˜ÞU�ÆWãôÚN¬��(ø�Û%È6Šëë¢xíšX>sF,¾öš¸úòË¢³ë�$À‚ø¿„ø‡Oº.Á ”!±8ßßA—ÿ—(x”ÿĪ÷Ýw‹ëÞñ1¶¿ÜÃãüH¦”‘û»[¢€1Æ©¢oý+‹õ¸�¯ n™ñ£t!z=oWÂÊ¥s‘eEË€’Çæ_/›.‡5ì Ñ"€åV rŒ�ÇEñmoŵ51ÿÜsâÍ/}I”VŒ]€¬ÿ‡0îøçŽóà"º\µ–²8€™ÿÃ@ø¿®µŸVÙ¡]»Ä‰}HL:$F2\,F6ñ·:jvÑ õJg²Øî#£›J¡,²k·±åe#@Û†n¹oÔZF?êY³¿¬ƒÐŽÜwŸØuûíâôþ¡¸øì³ðÕ úäcÛàóØr~8��©ü@€ßpœCèîÿ*: ½s䈸é#Sˆ�cào;“FËéƒ= ¶Ü_‹ûõe‡Íd¶¸Âõ�¿ü¡šÉÖdš'_œÞ&mÀúŽÔ¾ èUŒ¾ûÝb;/o|ñ‹Â¡3Ìßšâƒß?2üûÖ9P,Û'ѵ"ûùÃØŠº3ÿ4(56¥ûÙ[)˜H+B(Ú¤ÿn&Ò1ÃD[°™Ä† l²–Dj¶¥Ìsø&ï¼S¤°øÁÓOÃ1¿‚]©õ3®{6Ò‡CþÇ™þ7q˜Ázó��=&&¸ão™u¤ÂÊùó¢|ö¬¨.-�589�¬réúBÜ{·Y}©ñ§z’p%ãªú¶Çž�59ÇѹæT½rE¬éÏÛ“i·¡ ¥ ,NShŒ¥dj(ºÃBŸS÷Þ+Êž† Ð09ð_ƒß' ÿ¾t Àœv0i�û�B vjÊ:øK/¾( _ÿº¨.R|ÀÍ(V¤&Àh´mù8Ð “¶¨SÑ4îêªpŸÞô·¿+PÁËÜpƒzè!áŒRª ï=uJ̃˜çŸA„cö·m`�zÈD%Å\‡n¼1Ên‚-üÉŸˆò÷¾gaœM(ƒáf‡‹ …ËmÝ`þ�zêQ6éòàêÞ–ËÚ~ä& ½•dä9‰¼ñ†—™ÂRS5Cûö‰#ccâ¥èÎÀa5^?Û†‰EvÅ  e°þåW^ÿ¶è„µÞ7§Û Í¯�°Y¸l—í„{ަúÇ,²�GV b„Üg”Dúj;ðß ƒD"õ—-\ÀÅz_Ÿ§"IÎ#¾¡t€®NÉÛiB¾@Üo;e`È5Òˢ†ô±�5–ƒÖ±âûÕÔ„Rj»ÕލúE.�uëÌF˜ŸC÷›]z 3ô(€›~†¡×6܈&Ç�ƒtf¿Å@h�Mˆ—’1ûÓ¯ˆ5ðv#qõ±±å6?âe«Æ7J8(¶#àRÓ$žºõV1Û»ôMØõ¬A܀骷–s`c÷m~[ËÝOÝäíʤ¡Û!Qe Yñ;Ѐ¶]MÅqf ZÅRÈX‚™€áÑ¡]J„kJW"À±¥íÉd, ’Koí’»ž•@£l7WÛX·ùµ£Ilp£Ã_’‰m¹È’zþiíÀo/` <P´É°ˆ0½@r;òŒãlër›_;ÊÐ,\…í(�À@d"0ÒM§ä,:çîàÍ·!Ö‰“æVêašfm„mnÚòÚ®K€9YÉÀ@H·�œÕvxH1e¸·Û OÐ8,#)€á§oÉÊ5€9,YÑk»áŸÂ¿"––Ü TM ·#²ÑGN×ýζëÐÂJãð†j\Ü’[‚âÆ üQëâÛíKÌ¿‘H¿ˆ§fK¿\“DAnG ¿g�(Ú¸Í��(Èg`:Õ¼Ùõ8(VpÐgØrûL'¹ 8XÁõNÛÁÄq¶¡nókLئ›ùÐOŸ'Û•{÷ápæÏáü¿;1!¼&lÅBP\"<f`ÿwm(={õªØ?>Ž{™Lò€“r{÷   .¡ V­ÊÚmek§Ÿ�`uÌ!ØA@nµŠä�ãÀˆ2ÈÀሸŒœegMª ƒõ'–Ī¥þq}ÕÀ\_8†à.öïà:°—€5ÞÕ˜ ¬Ë.ÌÏ‹]8”%ˆÆÆÄýnÁi:KƒÀúØ’Ž»kßV›_;êÖ$\ž.dHcL$|ÉfV1û_Æý6ƒ~ø{6ÿ~ôPCÞ'ñû»&"WqZ« *=‰ëÀxTÃJÌDæ¶\?%Ym�á+C~À&™clÞFØæ.€™?¡“+‘ÜÖæ³êÛ”äÄ–1é,ƒóŒr_²ØÏ^âÏû¶FÁŠ€ øwx�å tÄ÷õœÔºªœÃ . òæ�íD¬M ‚ƒR5œ¤›œ¨ÕdMÙ%\©fþ €…en*“>�D>@¾Í“/<°˜UøþOOº®5пç^Eˆ-ô¿ŸD§û¬|úK3d6ÐñŠ\¿�õgåHÈK-Åà8\ãÄ£_òÈ`‡GÌÌä'Ùô¯· À2X$æ›Î¸G )Çà&¿ ‰êTç}€"úØ1IýM�Š»©lŽðÞuÜ¿þß�Äþ[Ô8rC)3 Ak¹–øhXQ^Rá¼`ÃèÊðé_G,<*a�—ÛP9ܨÛ`Ð8NÙ¼NÖÿ8ãËiœÄ õ‰™ñ}œr‡êƒè›_ñ=åwà+_pœþ¦÷VÛ¯ÁË{[Å:³¦?pHÍMÉ4‰?’´û„Á#ðè§rj›ím~m©qj2Ý ®"ÞFq>?βúý¸ž÷?[=ÜŒù‹Ÿý%×=ßLä~‹ã÷û~+Wãò —ÿ¼ÿ¯ý>ìÏ!Á/âÃýLÍ¿ñ›OÑ»˜vY4Êcô\ÙÁ ¿v•š`ù>‰fL7YÖ«Z¤méðuÝÿ:ȵ\PÃú/¸î<¬ÿèŸ:Îo€½Çƒ­âgðá9÷�aÌ9“oge>{TC¯eüogÄ“¶}(øÔϹî×¶CEžø�ð÷\ï3Чð4Ó?Þ%Ä#°�“2‚~œíùk‚6¿vÕÞ„mºÛ•OßÀáÊñOPËßÇ’ð¿`à_é›’µ¡ Û†ø¸xÒÛ‚ù Ü_Ááí_ÂÎwÁþ ð>Pîûñ{=ÜÆ<æ§¼_Îøšá:=â©ÅشÃk ÎÌÓÐû&á5”„�ÿ}5ý:„‚Ï`}O¿mi¶ÐZ =Â�çàÇ´ï8;η�š� è0¼£¡ù{w )Gà¯~ÂýhlcÝæ×Ž²w n;ÊÖ ÊýøB ‡ðÏ%|§ýtì4¾ïý]!^íEfýf¶7°`»FÍ¿Š ~u Œ@7 znÖø�?�BpÇÃ0è¶š¡ÛôÓ"lÁÑÜÊ¡ƒ_ÒÞ³…ŒÚš#}œ!×ï‰10°ã€QÿºNíÊÞuSu&ðÓŸþ'æ T2#»oókK©À˜$ÀtË|Òéòÿâº;`# -Xí)�„ôý[ÍÃÏàË9 ;E›/ÿjÆtk�‰£ß1�€~o¡å3†c èÒ Ù¦‚ͼÄæ·)àI¢ž` !=A{3åŒÜ©Y™p�㲬Iº H@�ÜÉ,"ì>iįM°ÂMX€6a·7`м·%WŽ=›X.ê×–ìbskôN÷1�€îã¼½9ì¾$ †_»2$`¨µ t§GH@�ß¶lm,¸Í¯]š°Mw»òIàt è š;—‰Éîsò·®ÕÛPÉX4s° y% ºƒ�„tÏËÅœ€IL¢Ð®Ì=¸É" ]øì8 è‡VØB"³=¦éˆßà«IípM¤¦HìýŽ�„ô{ 5*Ÿ9þè6ýÁh6Ü׿×,¼$^Ï1�€ž7ÁÖ `c÷m~[ËÅO…œŒ7ƒù›€Ál·°ÔR2§8aµ³êaœÍÚ7‘l{ý™.!ýÙ.Í—*2Ãà Ík"¦-¿&’%Qú èÏviºTæx¤ÛôkXƒˆ6¸6¿`’à>Â@Bú¨16S”Ȫ¼Ó»É`3ÍÔ·iзMÓLÁ0üMv¿ÓÀ,–™¿ž¸û èëæi¦p6&Üæ× ¬Fq:·Q¾Ix§0�€Na¶[p�1IgÇ&e#/VÑâÕ­š'ù´ h{ Âì” D–m* ×$$ Mèí ˜„ôííÉTÎöÆÌAÚ1 ‹­x“µ§f ”na !ÝÂtÇòѤǔ›~íʼSpÛU¾N«H@«ë³ø‘Ùc4âצ2Ûá&D¡Mèí ˜„ôímÊ”cÏdÉ;Å–nD`äߦš%`º„�mE>þñ�-•—¦Ó…4Þ¹.á°+ÙÜ|óõ‡G£¯•Q觺L?5|köæ W*•±'žxâÔÖòê�ÔétñÒïýÞ¼Ù¥i)š<þ±Çw§‹éÇáþPóÃøðîæ¨Ÿ�Û^æò•+â …ã• è$ å"ˆ^__¿ mð�HÀzTÜŒxâ#\Ë(Ï9"õ§Ž[þÝÏ~öó?ÀªX‹<�³Ë^'åþÃŒH}½}ØZ³âiÎÉ\§Û×ê[Gˆ¤+æ•`:²õLúˆp�G€Y|éÿá�@~ë?~ö?¸h à‘ÜÁ2O|ôƒÿ3f— 1~ %ßуŸ-ç±üá¯ÍÏŒÓn7óÜA†cæ] ÿíCþÀŸb2:1ÈuD�}è£ü züoáÑÅð ·ÂVÊÎ ¨[Ÿ¿¼PóÛJÙ?í;1½ðá�~èï;0ƒX�� üÃ@ô÷ùÑADr'Ëì³üá/é�· èÆo'ë6 °‡�ç_7ðo?ñ‰Od¤ÌA1û^€ÁŸÃàÿ}”øÑ Ô†%“Ɋѱ1::*†††E>Ÿ)â@åTj ³QC!Ò)­&3¯ }ùO÷m�‹�C-��ípûí··'Ó.C©V+¢\®ˆR©Œß’(•‹bc£ VW×Dac£^iþöÕk 3�=öØ_øÂ¬O¹×KÜ«°¾'üO9ï²!(“Έ™]3brrRŒŒŒŠ\®ÿ«£×ÃBœl#Š‰Üª¨’õ6ŒéŦŸ‘dóN�” â�M'%2ù͵EïiK[S%€R „`}]\½¶(VWVì8tÜ÷ �ä>óÔSO=þä“OÄVÔæZÉ^ý¶ûbö?%RâïÛ� ‹=ûöЉ‰q üNmù[:…¥ÃÛÊ×5¿Èhç(íP!%X¶éFÍ�¶ödK[Ô�LÎdëÆ#“Ɉ±±Œ¨V†íù¡!±¶6.æ/̓C(Û²}ßß¼ôÂ?FÀÿn ì7?_Ù?eLW)ð‹©‘‘qàÐ1==�ÁŸGvŽÚçâ·mÀ²_¨œýd|–?øÅàïèÚÈÐþÙÇ P¶‰Š¾ÍÛµ\µ2t£=Ré”c£cr‰sðàAö½¨ÁÒóWžøèÞ é?Ÿ¾%˜ýA+G´É8Û8x@LŒOb]œÖglgìÄsVÜìWsX!Ýüç5@´tj¢¡ýá³™¶ÈåÀuNˆl.+öïß/†G¬;ÑÐMKýæOýÔOMôGMãK™]ã£v7ÄMWÖÔ9a öí»N6€)ŒRKW©VE©XÄš¹Ú1nXͯöL:-²Y½¹$½3ˆ¹ÿN®lmbÖªÀb}½®ÀÌLÒnN*à9“Áä"¹˜hÑ({šŸKËKbï޽⭷ÎÊþ¦Çt¯+• OÁïïéþýåÒ{TŸ”í�ï|gfnÏìO˜Å»æf d"{5%¬É † �M)8è>à|L «dŽvÓ¯]u·Á5³rA€ …Á#*šÒ ¸ù|^ cWÉfRØ•!X\ZĤ"ðæ[¢Šz«\ÐÏä#ïÿÍ~VîË%Àì¾Ù›�Èæ9ûK¶_Å2ìì—kkëbiii{~£¾ªSŽwâ øT滽væææ©–g»ØÉŬ­­AêUîØêE"0"ÀeéôÌ´-J¦*Ò¿` 迾$銸ËD)òääx¤2ÞêÚªXßX7“ìwDàÇA ŠØ™�ÄV‡í‘�í‹jÎêœX6 `“d±T Pš[Ñ1»QïgY@_0R·š]jjrJ8eÎü…BÁŒ¾3ܘŽ#32ý:ù³jž;ÑÞ$³ Ž@ÝÝðíÃØÌf³bjzÊ†Ž‰B¹ð[@?øõ¥ ”ö¨Šjô�a F5¤ÆÔØÚÊÌOêN™©{°*Ðã ×�f[žÃÐ}ˆ³¼þðŒÆÜº�™—bØsçÎÙ»í‹-átš¡PN å>þÖöt·jØW¸% Ý8b|l\ê,\^劮€-�Eü£§éW_ F#¤®9l»pЫf³’æ2„„W®\…zçJßxµž´—°ÿl3‚…1ñ³%Ü„Ÿ”˜4ÀB0%agÖ¯†ÊÔÆ¹�7b߯‹-:—›i(Q)H5),Q©“B•ôÅÅ%5ˆŒÂß‚v`ªµõZèÅî¥ëˆš9¥ßDºTð©°“S=³Ã4 WÐ@‹$­”§-q�É:vŒv½í×¶Ô§ @ˆªóò£2Ï®]»p†ÄœÕíaÚ••e1=¡Ÿ1!‘C†< B\1û�ï~ç&@|ѵw¾­óA.+€&‘ÅŒšM&“Ü{UM¥Zn©£sV:þ‚¸víZKéÔ<ûöÃ@zþg±�??¹é~Á‚ ¹ÍI ~äT¹Um3lŸ´ù÷Ú¯ïrÔD Ù-“åâú¿YCª}îÜy±.…8ͦJâí$ ,�+<{ö,dAúú=”Hƒ¾Ê|ÃÃC�9P‰H7PI»^÷é—>­öC™Ò•£*«Ï"¡™,�÷†äŒÞ¬¹tñRÝ£œ\ÎÍí†z'.ÃI6ÉB+y5›O»ã±,�Q+xö§…íuÓÍ7¶­p\³oÅ”Š%©'rñÒEQÆ1ß8#¹Lû÷CßÄ2ˆÕtäÊ8*œIëWd³y©°ÅpÕàTz_rýG\GãØøYp)H_Õån¥ÒX^^ÆšÍ~|sfvFÜyçbrj"îd—Ú†=µŸ~ýLOóo%s¶Í 7žh%Iwâ¢]šŸož~Sr„¶L©B~žDàÀ~L8õ O±PÙQ“P`�“GˆUø�žô% L1jq{j×€dÿ x–Ž742UtÄ+—¯X£;vL¼ímK©m̱Nkº^y®¬¬ö*ë–óí[UlŒçÝ»çÄ©{ïïxÇÛ¤ðÏV9ê•PXÜÈ¥šD"ühã¥4ãö'ÐÀàÈŠq€ÄXý¸ …®÷QÚo²blR÷Ûn¿EŠ4TŸz,ˆg³û½àú|è~qóÍÎG …ÅÔg¨×¿‚¥b8þ%-°êk8b_?jöpÜ£jsðT¥«<䔹Ð4jœÅÅk^Då/·{î½÷Ô@ ~�8§0W–€cÁÀ½ñ¦Äí·ß¦ô�кxõjÃ>ƾȭQõŸ•l¹¼Þw뢾’`@;¸\ñHØf¥ @’VpìW±ØÈ¬,¯`ö�Ê N�ºKî(`_¡ˆ¾ '·³º:*À˜#÷Âg!cÃ%Á…‹…ŪYFš›+{:(j€b¯BØ—6–¨Ã#v�‚ŠHQ𜒼çÖ¾ýèc»ËUýÊï@@þ†9Ãpö¯gl3&/o8~☕0ÔƒÕë°ËÐZ¬VklO¯ ÓDþlšk8@Ãã´ƒ`¸KðÎw¾]ü‡Ïþ§Hq©jÎq†wN8ên "f©·‚e«)[‚Jpß ûŠT«™£&¢å·þªÃ½Z©¯‘Æs *›¦¹é¦±Õ‡sõf@Ÿ»¯ÖXÑ>/¦V¼«×–ÄöÄÅÌà8ïøø˜à¬¯šR±,Ù{ÕOµSÐLö_5”[q'À$8–Ðw[�}E\ \r–çá ^¿¤NøU;J<ù´ó&W) 1Ž;>0³’ZôË8`ÒˆãQã÷ÜŽÆZX¸6P¸æL>;;!Å"NšÖékò™qM郎‰÷ ¨&áTlXìЖ:®RSŸý×b�Nr*E0`ñ>w›Ù½g×@uJ¿óØÊ$ÀñBÕZžº$#�ó¾qZ+rÇêážÜ¦í†*« Ðí?]€¾â€Éª¶Y©,Ø2üªñ„{ºŸÎGLÃÓ„ã¸B|Íù óblx�“ƒ3KØiážxn`PnZ±(5Scº›§’Î@=YÌκÌ}îsŸ›·„õÄ«¯¶Á"W±@€'9ÙÈ�+(xQü »MÝsr‚gŒÏP�á¢!�„Zp vé’.Uï÷rÏÎDw-¸Æ¯TÉÄ÷7>Øb†ÇÜ,Ü´ÛWr€¾" ´'ÔN’Æ @ Sxwƒÿ5£dã&p�Ø š ü¦0iPêqîüÅA)ª,ç´…0 HA FxÜÇ­@É€Hø¿#€Ú–ލöÕN@߀�}ìc£@ß"Ü7ž €,dˆÙfT€ùž›i&pgÛ š³gÏb±e™/\,`jzÛwÑ“|RX§È¥™ ñ"Ó@Ö˜p&Rè^+¯‘ýçHŒ\p@ùWÁÂ2Žûþܺ1 ŸDóÖÙ ƒXlYæóưÿLNEïõ£ž€ß·l¿RoØAW5øØwóòÕ*½ù§¯8€¾¦\®ÿµñ/�dæ T¡úóÎyu§@G/•„\°ÌÑ]€‰•œ}+äÔ­MÚ¹â6”fào;ÿ ÅÙ¤ƒëa7¥+$1–C5~9/\¬%ë0 }óY[�õú\Úkf8 �™óö Í8‰ @ÃGà0Öÿ’ýϤ f ªŠ…Šÿ•-ë-I7j«êØÌ ÊΞ=T‡Ûÿ8èä‡AÉ�é|É�÷K™BýÏ{"›nõóà~øqK–B0õƒSšóX˜Ä!¨LŸZlr€BKÊp±S/ÅöåȨu'àzÞØ/Õï€;êvŸÇþG)�Ô€yá§ÍLâÈA3; K„µ§ôYrDJÅ$háº4ÂmÖðêô´¡÷î¢ .8Í�2Õ åƒE¢2â”r€zgÈ�ñýFÕŒÙuDÅIQ˜Y°k[€¼” ìJë]t4;@-;TÚÌ=ðÐYTöŸxðg{¿8t›~ì¬ö{í ÎZç—�r®Ð0/rlŨn€ÛnµŸ¹W’ô�Õ¦ ÄÂñn€¸çÀ.w§0i©f·sò ˆx-ЩHAàŸ©q{e×KÜ£R@; ›ýâ š=/Éæq.SóM©DV+tûþê/)µi‘ýgΞSv@¸ÞV ¤yäYc@ªiêÙÍŽjÆ•?�$“(Ç ™~жù^õøùXŒjøØ©ãÄ/#½ÉIï›r'êÐ&¬~:Ô •*�FµÆ?yKª¿†H•3�âVÈ·Û®£X î¦ mÆL ·TB #a¶QPn¶m�ÃW Ø­$6)½žðG%M,§Ïq `ͧñ Óùó*î© D9@ØM q'À4Tdã+B&@¼¾Ù ì €•%©]HH…Œ8å”¶6`„wO›Þ|¸±ç §¨¹P$¯TzÙ¬q;àyöÜŰܨ8»Z�Ð~œÅ[¹2½^±MâÁ]×8ùæï¨p˜ÎçÜ.âÎA33³SÀƒëõ=¾S¡0«µ*»b Å"®3Lß(õs{B¥®ò@THAU¬{ÒV•$èheç·/,@Iвòr‹ÏS/AŒ =¨ 온¾7g�Åk‹ÁLJÁ'• TÃKBlK�(E—O{©Z´“Ý7õ8Û›œ‰Êðf¦U<6Úâ\-­­Ñm;¼(´Þ$¾g^ ÂBÅlûÄ'>‘ýíßþíèzµ­5i ¬/@J¤Nª�Ócÿs„{·â(=Ú¨%â¦PŒQ&¦¨l¤ œêæ#ct46ʘT¬µt5€ô 6ûÖÿìpò#4$5ëFŽÈßró ÊíBóqJ?l+¿lÉpzW ýLÙ„ƒE®<Â]«.¯Ú:zô°’ª¿­6].-Il99Ù û&é³z4˜8¡†{Äd¯¬\9ßïGBºìÑ}ê¤:î$À1RíWt*JZë±aqo†K½óz¸n0(�$†ÓÙeBoÛ+@ø·œ?(e!†¢¹Yy=ت~=ШMz]«œ‘QÏ ÀââüQ¬œHC€\‡€j£„?DnÜË´ÞVMFZÈþ› k`õˆ�Ëè€ÒiOÄ´ûÅ û]\ñ€UIÄþùùKÚ "/`®µmƒOÌDæì¥ç¹9—w[)$ÀPîB9€jH”üå ýy=˜ÏËè©Zó ¨¨µ†R³ˆWó¸bh‹ªï3»kFœ9}F‹$•�èZ ærÌç“á—/{g:|?ü&‘A ÚLì8d›Ò¸ P5’ýU#ª�5»íâ†)œïögŸ “¨i1²¢�)ŒÐ Ë0b„�0(L-ó�aºšM :W[ÿ‡‘PFDààV w¬gŒÙ#rj*”æí¢—�Û_„¸P ýpµùî¹]õ33Âpzie5Ú+pÕ åå:e ÓH…ž:h”ýÉìQ£ö�€ƒÐ€þÜç>W{gÜÌ­;îžsò0©\åy ˜ÁK±Õ�É�/êk›†/ÿ”׬JFJD»¯!Ò#ëf³+éam˜‡õ¼( €¾Ø–Ke­ÍT lJü© ¨éøp•ߦ˂4Á –ùAåS�· ¨/H¨B! {þÒ±gnNM*í4£`¡S‹…taH =Œè†ÓKRm5/`v×l4ëJîŠ}4Î�#J[…£cÖ­@6ð |߉ƒÕ ÿžŒ�’7CZm4v~¯£Ç7<;O«™fr§»�I|J¤P33Эh¿yQíÐì¾ÀH^†ÊzéÒem% ßLI2¥íìpª!âXQÆ3°š¶‘Ý\‚0¾ÌDG5ÜÿV9†Í“FÝLõ1ð!(±lÁˆ£y+Ñ™z.ì)P;xHmH© ê�4˜ÃE±Ý$\^ôh[:„MÚ¼rÄädFS íÛµþµyBÂkÀÍD~‘,¿j¤ÐàzZÒÔÁ© ­v¯ô Éh„Ê,¿Ü Ô"ùŽi²8¡Ó�üÖ B£2� ¡Zú^üì;$\ªY‡ŠvL‘ÑÈ¥™ËWÞ@}€RiYEZ§M~Z`—=%ÕLõN—ibeÞ¤šËóM9¿)Ðñ¹î¬7ý#¶m€�86†Ë@ƒ´a' ¡ÃzÇ£=L Å “†6/BL71£iÐð¨&ôÿ‹Ph’�HÉ“Élì~dö¥`°E #ÿzNۀ܆I˜°2ð ïýúϛի¾Ÿ¢^d˜!‡ðâë©ëŒ¥gÙr K8;³+B6  ö§hzey�DTŒŽðÝA�ìx ]'͆"«”WtˆGÚfÁV«Z‹PÒ0 udG ÚTï$Aüo?r^¸VàŽÿè#­ãÙé�(v¹½â=)­o�ùû«zjÝeÛ” A#ûPÂr-'#ÔºZÃçgªþl9iYè@ç-;ŒÁòq  j®ÉM! ÀÖ—ÈÑ2;šy±vä |=¿¶Ü­´®§�9âGÃïæå$ÑÁ­€•VBA­‡rÅC‰n÷b×®¨Þñ¿nfkú8Cœ˜ŒŒŽ[·Ez(}p¾«Óþ=%èÔšDêPh´ˆwp}Tð¼¶i¦g¼-@v¤Õëa„ø˜~¢Z ß©ýn•�\Âáú™ Éû`ö‡-²Þ‡§¹þ楩Áí4JUT¡¸‡·¾U€²L�9¨B@H§œlé W®~1|]=ûåÕ‹a!%zÝåÁo�¼lSr'`÷ýÅr@Ž£-Êl&åíìL„hÀ{ �‡€B#g4RLW "Ú€<Ì‘´¾­µƒÀ–-–MÁÐZª 0°IM‹™ée°ÉæìéÈÙ8ºàà«@=W5„Ñêa 5½Íî¹½hºJ! ¥~†q@ u/�påê‚Ç:0Œ¤J{é!a²ÐæÇˆíѨ~ï7ÒL> b7IS%ßPRQÀ™Ž$ bxÛÖzxº9LnæR»]=Ý ÔÉTP…Î[ÞýîwçÇ&F¨9É3x[]_I“ɨ‰`§ÔÜï¤jÐô4Ô€µvÖ~Qó c©ÐjvhKmOe‹)!!ÀLÁ;ô©Ìd,,%›m²à¼ýÈ”6ë]„¹IzR«N³?<™Èv‘\€’ˆgš‡”XîbÈrjÙògá kE�äYíøò|Ãd5›=²3Lø‡ÉB[è•0pîšÝ…ËYÏnZÖÖ¡½K$Ç“Sc4pãÝ&@¿Õ&Á�óî™9Aa±Z³ €C9<¢ôÛÞ³š†vÛìOÿ)êsû e´µµù¬ž„d$VÊg†0¶=¤ ÐSÏÛvŽ2=£1×ÚÜ“ö—*œœ˜‚ÄÙ9Ë’éb §G£kfv·u¦_ÛX‹SöÚ�‚@,h�³ZZZ–„Éöh¦Œ'£Öp¢¡&¦ 1Þ„¥%÷�×|Ãd5›=²LÅe€I6pÅY#XH”ƒ]ÐÀ×ãØ’¾Š3†Ù™€SåSà:æ%@áŠ?h�)*¶¨nyÒÉ·ÛLÃÙj jÀ1©×bZb½L âD`Œ`%$ˆ¢F=i3ç/ûë*~A+yö_ß$XÇz”O¨«†� ìC\¾s”Pczv•xø¡~ x0k||\^né±¶~ Å–’XÇ`0/*qØûÑn>'#aÕ^—³{÷îô®E¬ý„¹Em¶Ô~¬f®~\?ËÀ­Xf-‚@½z}Ò“•hs›„ó8ÍÎäÐGNª¬(l @iãyf ˜Ô%�Ò6�•Ç4MCýks`˜qÔt&cŸ›i´¾¨9¢CŸbà¥úQæç£ëêûKƒ¸&»ŸrÒâ ÒìÙ£½ª.—üÚe‚í- µûä}a"¿B¢B`¢kaá*Êë3T^³(Ò"ÈÔA„ÀR‹SƒÝ8 ³´<æ,‡‚¼¥Wüý€¬»·ªé¸áu*ë Õ“?ù“ïÛó™Ïü—&X6­hmqôl $i”Ï»9E¿€5lj @í,ÃLÉõ¿ÞúÑ™. l�ÅØÐéuÀ 6"$„gäuùJô�ÜW*ܪÀ®SW¤rM½m©†EW"„"°hù”hrÙueá ˆ·u΄›¸¦ P^ª$¼Š»L‚àL‰'­0PT‹¡¤"ŠÊêOO™2XdüØ­@Èêé4�+rô§.°°o–ËyŽ…�EPa�x[€dÿuÖi³K€iË;ïfÇ“-¼‰?QBB ^ ºa`i."ŸmSyƒ‹ëSˆ[Ž¥JIœ~óŒ8|è08žl˜¬fS¹¬Hà&=ÈúŸ>sFn¡v"˦ˆ %d–�÷á©fátë¹ÄSÄ¢J(@ö¶˜2 ¦"™tî0ð Z�=,ë$ö]FãM–[¡Òx0GFÇ­°§J9À3µÈ]ýé€`Ü  îïcßð g½¦t°-c¾î²©_ëEag m~›‚ë'V~UBB 9Ý&ðgW®å}6\-·¨¸=÷ê~ ®Û·O®×•,¶dµ�••ÜX|Þ“- ‚<ƒaD,¹Þ gÛ™õ‘‡£ÒÎz!ÕÚ1D�h†6OHËL­Ãf 5_öáLoêñ¯­¯i}ÕKþ•í¦vfQÆ5 xr!ŒÚÓCA=!¼ ËÄëHäP48:·—!žÙ°o£2*YªÒºŠ5„Èi &ì¡-ŒÔ¤  ôž—×€¼Rø‚7îtÈí%£8¼§NÞ „ƒOo¼q·ÖŽ@ø9‰N; ¼¬¤Ä7ÆiS• Q"ñY]]×å/rÎçñVòˆA¢á 0 À5i¼ºëø #9‰RFWÊs£°ÕȶP¿±Ì<æÀ˜€@½¾Ie,3<�%Á(ˆ‰IPäžíô„ Â'ði¼!Ï )Z€è¹•2šIíÁAã†î�—ÑMC@mð \iÝ@RÒ¢DÐü ‡Òƒ«‰e7¡Åþ@}Lƒ€Èò\¤¦e°û)c]É!S)Ö…È��‹ÊD¾á‰ÈÖMXÎP÷@©¥b5a“0¤kÉ© äµeä"øb³íì³9âaæH·­@FëQ¬`­ž®T ~íµ×µŒ×°ûQ¯oÊåšÞÄÄ.I�\“¾³oB1q“{4@¥šÍ7Ö£³?[jfz¦6˨ífô‚¸ÁnFˆˆ—RÏjÆP"¨E0íµd<.Ì–µ¤…°�`«o³Jš£+´ÑåeNþÂÌVºÍ"[#µæI‚126"±ý‚:)`¸È�W%jŒ5,¸´Y®L¨¡IsÄ€•Þ!l/–#(0Í:.m­2ì»z,n©ZÌñ§žz*õä“OZØ)Kì6zõ„0·YJ±Mv^•M¶Õ»`aÿoZ>î`¦@c˜í«D ‚ô6“1t¯ ¦’Z�!,Ñ"^µd –ÍOXÜ…ŒdUÞlŸÕuQœR´6[ã–äVøMNxÒn¶ŸÙ†\9r¸©R�;ĵ´…N-VcGØÒ&ÿD�ÍíŠÞeÈe_¢Êá›8C<™ËºñqëãÃ/|ï…ƒ€s:V§ü{Bp¬ K€ÑÞ ®Ä Û)&"ä †'÷ÿmÇ/cÚ7H–(ð‚E¦R=`¯Å´©),u/;•vLvÙ|ágnפ(ž+àòSÜ�@e›–L«ñ[,UöÎÍh'áØ†æ%šWñZn@€�º+ˆ×Zq¬±£Ä$ĉ´…N-ý\ ¸¤yc¹µQ»¼F÷÷]r dì‚Äp"íÊëÁNûi»õÛAÏqµ‚ì Ã8`öi*¾Ä´I�¼m8ÓÈõ¿EvЖ§Öƒ~hO%ƒíAuÊ­Ù OÉÁÇd—M@ýúýûæÄå«K kÁ 3ëow›yÛcµêë*¾ã°ÏjáMGÕÈeÁ¨æÚ"�¬;Ê„�ºKMW×Pz uÓn–˜ì‚2ÛÆ\Nñ±Ûv³_N¹ 0nÁ+É6Xnïcñ%?m·~{BP9�ð €<_­4~��lÔhò‚[û¯À2ÂÞí$~wŒYV�ê@¯Å©ÁÖ²ˆ¦b°í �˜§ú¦ðÊ1÷ž�ãÀ»œéÙ¡¼{è¢ð §†_žDR�K¤!ÓåÓlW¡˜Deß°!ì•oñÚ#c4Y^‡ŠÕÏ¿ð(šîR"×·Ö2óñ¶fgpKðeCx·.·ãóáN•i1cr»|2œÏ¦«†/d«înÙ»Nj§÷«äÚ?�€Z¯�AͰÿŒh%Ô0ºƒ—_­YkmÙrò"É¿ñͪD zI`Qkðc õ#2ˆØfuÆ“šr]DÂ_/'?Çöüró²Kß0ç,‘ævω³gÏz[–ðã�Y7*IUmK‘#^5T2/ÏjÆP"0RœQ’)V=vÌ6051 ÀÚÚªÖgu@¨;Õ¸ ¶–ýrbl"B =éÉN@× ”*Ž`uö`�W€y;! «¼0tÆÚlÀÛð’è ­CÔ]–,ôÄJ„Z@ @=BÂD�ò‘[€˜ÅU³aœóç#§ƒ`x@':9ÈiJen]êu£?/?•‡‚,Èѽl� Ç BUMή˺ΠÂ�×€ñ €Ñ<¶-Y´ÛÔ€''&åV FŠ]ohñR©FìO<«Ñ[¶“óá5Yá6A8�klh{Îò’”5R¥–sìR”•Ç€}¹¯Ñ�5œ³Þ\öµoð‡u£VéÚ:$ôq&hØÀR‹vÛܰ �o½õ–uM ÃtZ RÒèÛ¼ü–Ö\»¦_†<»¾ØU! Ø\bꈊ$©Šè ÿä,‡ë¯³Ñg#Rølu£ßz™È´„%?þm¯!üÅåE98B @A®ÿÉñ˜�¯ ‡eöËÏY3¨Ø'Ë+úSYJò-Yyj’ŠþÏoǰ_5l{´�Úlr€µÕ•z`@ȱ ·iã#&-r\“·½9€ÇÿûÇ÷‹²€ÎohHòÐŽòšÇóvÂhV›ªpâG ÂÍ7&�÷ýå/Ø~ˆ‘¢‹ëu²íí4œéç-�²^ÚRu%UªÕÎb´?·ÿü:ðè¬?èÕÌ.ãôž¹=mÅ+ÙWakF[:꣔.LxîÙ³7°û–È‚Ì|OåW.e-½l[�ãè·dEÂ;àÑÕ%@º˜>.ð¨jøÖ:Y6RkßðÕíû›¿ìX¦™™™ �B5cÂ]SúÐC¢)¨¨óüóÏ‹ý×�GCÍc»2½¥ÃèpCÕa_ã59Û›ðÈÙøƒ‡©8xüõt¥m¸ÄÇ=Xb)ÜÑ`›š÷=~ÿûß‹80‰Ë[mg7𝡗µy­—»±o¿gïÞøöoÔ^A7ð"òï.©  —JÞÓ€SšæK@~,â�Æìϼ°Öb|ìcý�ßù�:ëKª-xu•PÀÄ;·µ†° èSlÖE€ âk·ÁÛY C [ƒaæÌ„Z„Zw5Â9�£ÅËPÝüã/ý‘<ËĨ%ס¡MY¼õ¿ˆ:æ>…Ú(å¾4º) }‰»—qÇ#ëB�íxVçM\+Ư�†ºöwÜq.åà¯Ó2– Ÿ`éåñ"òox™iCÖ ‚À˜­=‘�·PÖ¥y‚êáÙ�bqå¬ÏGƒ:ãÓU€yAÓàÌ6Ì-@M]²öø„J,uç¬"_ª1Âfgü%€9ཆ4}ÕQoét>|XìÛ÷añõgÿJçÈA¦%IDAT¼ôòKµ\àý7J±y'Î*¯ÚÆ !~¸óàƒÍCí~J> Â{ðyi) uÆ> ºùrr;ùÖ[o·ß~»'Ž¥·¿îŠ =XìÙ½Ç •;8§g¸`öñ »2�¨:òPÐö$X›k;\ÿs ÐD¼¼0›5ÿ ûÏ ]�x„#Óh@†ÃW7=XÂg¢Þöö·‹“×_/¾ü•/KöÕŒ½U7oô‘{éÄ¥�¼äÃR�­æÓ�ôéŒw=8ó’7èt(ÓcÇŽ‰{î¹O^ƒ�*/$hné \ö’ÁÃx�™,A)K5¼.͈ª{7[ks¡Ð•—Ÿ©ÉÚí§ÂºÊK'ÔA)w¨ãnÌö¶G(5ŒÂa[ÿ3ŽúÍQ¯UL€Ò­µR­¨5 JÐÞ½ûć>ø„xá…çÅ7¿ù ¹Õc· Ï••�ýç^:¹sí¼ °=IÂ7ô|î…Ëšv›Y<ÛõÀȵ¾;hrJ"5„x¾†S‹Zs˜èÍó &XY©/”‡‚Œ>ÎÓ›äæ úõ`ÀWW·»K\qLE4L‚‚�ÊøšdªŸšÆ·Û a;±žZ¦Ÿ6ü­­ú‚–Ö{…æÒTxI‹»îº[. ¾üå§Å¥K—B°16¾ûW,cB=ï%èÈ{’cÏ=�»÷)0Êæ2>Y«›¾9öÖðb/õ ÀfI¾bg°9è��‹ øs&¾çž{ÅÉ“×cî`à Æ2\Ú,©Â@Ød“„„�°¼úê«Z¼ÕÕe­k�px‚@£ÁŸÏ×ó5(Ãtu+°kfÐÆPÑ ��Rh¬�€l¹oÅU˜¨f³q¶G" 5�™i"?-™âà,ôþ÷?¹ÀË⯾ö—í.%ª¬çùó— ý? 5Ü´³sófäe(Í Ìè÷+�ÑExr14«˜%ÿâ/¿î‡j¿3¸Áùú“'…~mˆE:(¹é¦›Å©{Ná©®Z�íùÔäÑM Klƒ�„Ïš…9’0¢…�°QÈpu{šx*Ðb¶'�JUŽssO5Þ rÌ•ž·6klkÊYnöÀPàuËÍ·ˆC‰¯@6ð&ê°vàcG ^3ýòËß‹Øu õþ³¸@BK߬“´/‚X~×ÍÊ‹Blñ¿þä l­6n·C‡‰|È»LÔfEMÍSïqFˆ�íç±goTH•ÞωÄÉ ýz¾8ÇÉòsŸûœ'=�Ø&ÿ®q¸;>½ÒóBˆ!°sêôÆ%@#öŸ©ýóæ$ï/×ÿ½4<äñž÷ü¸øÁ^_ýêW¡7ngyùDÔ}÷Þ-Þ:{^|÷{¯ÆÊH0 ´—ÕÛTÞÜæô—>µ£Gá5£ƒ‘YÑ�ãÿò…çxP8È;3£t%ë°�x* 0z$[ ~L›2ã¬.¯Š¡Ye2 òð,TmÏ =U3eÑdx5]%ð¬·Sö®tm€ìE˜·Ú4»°qR ¨S˜jîñã'Ä�ijÏ>+^zéÅØ”öï3¸½ø¥—¾+÷Ì͈¦²z§1ãô»» %y e›ïG�¯?y ,¼¾/nÖƒ�ÄÜ}ꔸõ–[íD‘fBé¶ e/A$Dñˆ©Ìáµ`öYêø«†rÞg*¸à&+ôá6ΫÑ-&]•g¶PoÎáì? %—`ý_CD3K"¿hy|f&z»¿]ñb~ûÛß!9*¾ú•§u"¥#عçÔâÂ…KâE, Š8:ëê:� 0‰¤Þï¿TèâÝäfqIËM7^]€úêÔ¬ï�7Ü(î½ï>ë)Q{�•QŒA?Ó½eRe,{ "f:î\ÍÎÎ@è« ï–ìT¤.€NÌ ‹BkêH¨¦›‡‚t’¤–¢Ív4ŠÆø:æë0Íž4ÙJw Óoæ0Ö®û?ò“r»ðÛßþV„öË»wïnpSâåハ;õÎKov=ê¤ÁFSÎ0H†û+œù)±¿ñúã‚õkd8(~èa¼éP¯½©/�ôQ¸K£œýp}°{.ѽ¡¸/BøÚQ½¥+ï0 9¢ÉÉé·Üµ�€®T>BFÐ9€ƒÀpÆ�ô[ñ ‹mýÏà]ÐìGÛ`ï¿ÿl_ùòŸ‹œ-°rEwÜv³Ø·g·øÎK¯Ç�ËòYpý …-}?ù‘k9~ìˆ8qüˆõ �ZVns>ˆ�OmËÆÆëj¿iœ&.F“ÛÀF¤Jðw¾£Ã\ÅÉÆze’K[=‰tÍÌLŠóçÏj!˜,·øøÇ?NéÈ>µ–‘J @Ü¢ÆÖí¶õ?;\=}lBo\{q8åƒú0}[|ãYGú¥Ú½{—xÇ̃âßxãŒ9ùÑúò—c…ëü›oºÞ¸(Z\jÃÝyç]Pß½£G˜ڜm ðhIuŸ½àLí@oË,¸Äà&&pшŜwà€Ë5b[bnÑ«+T%�A@ãaåúW]Ë“pµJÈ€zd´ϦÀÇ@¸~ìwÃ2R�è¸�§Á \¼xÑZdž.»õæðä÷ñü /âš«®³–§Ï]hÞrË�8¼d}ÿ.AÖ÷$öý¹­Ç³ ƒföX–3ì»\®�¡OÇÆQïd¼˜‡EÆÿ©ÇyöØ[ ÆlƒW€í ¥À\ºÊ^lÜ‹ªf=å}ì†çl�·�â4tr�ûþ÷@î|­ŽÑÌô”xÇÛ¯½~ZÊ؉úÍðÊò›n<)\·Ï.±W ¼ÿ¹Îïõ–­R¤–­q[�+XðYö8ÇT¸T'vë½�.¤¹ ØÀ¼ ˜3@¾v/šŠ,ªLÖ¤øqù2«iúe Ð,W#÷ÍP :rø¨øÊ3_o¼þº5:¹†Ç�Ê3îÏ=ÿ7xH„OkõÞPÑçĉ£âlë5⾨õvß}÷ n‘º¡ /—/æAž¼ïèàœHœ¡€›ë9µ�óÁâÎ$ìµ B¿«]þ]áÀýSÏZ“ýçMª*"X!RF‡F†/äšfÐ8µü|*êÇ~ô]R�è™g¾{ÐibbL¼ã‘ûÅé3o‰ç¿ór¬ A…Ý ;ÛˆK“Ûn½·ùP‘+ÞpÆã:ŸËžAÝÎ4kÇúóhðCãSîÔé¿ræ7Â)øÅc9‘FÝÚ è À¸>¬ÊöHl:ò­¹ãŸH4÷MÙ@| hÐ�§@t D¸s F�ˆ�ïÈáƒàfÅsß~Q\4ö£;�ƒi,Iî¸í&1‹õ~=Ãrr�ÿÀµx@«Ôþ ãmC&X ös³´ìß¦Þ ãðKó„!¼»²Ð€«·«ÈàŸSÅÛØÍ,ä…™–uðL-,5ï~·“µ¤Ñ‘£Ç°eH"t*‹¡É#Ý+Õ‰¿õí¿Ñã°Äߪ×Öù·Ýrƒ8t`C.�³ãC�<{�ÆVËÒém·‘˜\­ZVžµÍoÓ¸Æþ4þ¦+‡‚ºCp¡ŽZ9)äMÀ 6$uTÜj|Õn2|¦OuÔ²·b?Ý÷�|ä£ ˆ¨Ṅ+_xñe Ï´’ESqɶŸÄ^>·õ¼GIâ“�Bvÿ}ˆë¯¿!>Ò6 ± ¹;E…Ÿøû °,s)S°˜ã8”Æ¡ ¨‘%òf½:NÞ÷¾÷�燳Z å! ã9pO_¼1àuY6Ó«“€¶²´ËÏW âÒàé§ÿ\\¾¬«Ÿúùðq•{îº]ìǺüßúNÛnßá:ÿî;o­ûò Ëà¯óï¼ónØÓ~±¶õ/ï0 ¹X^u>Ňib ¯,'¾T£¿ú„äa;„ïõÀ§½$È`x8}ØÜ¸’2 •}{®™uÜ¿fžÂ#QÙ®†÷<þø*íÃQÕwÿÈ.ñ78\ôÊ÷~ m7µ‚›Üow'þî&–U$NÜϧ k'*uÙ /�å%²q†Ò~³ŸóVd›©í 6À%‡‡Õµ>+Ê‹@øÌ’*p]¬��±j¹ZjP·m�çç+;v÷>-Î�ÕÕGýtdÙ©N|ຽâÙ¿þvÃ;ütüå:ÿv(òÃQ]³“ªñhß�×rzøèøÛ‚»¹½û'!¼[ÔªF™M½~L•`S0†É�œ“yÉmm'à�µ Úìè8®è°:®9ûóŠd UÓìÀ&Ønë/¦�ûé�½÷}r—௾öµàÆ3O¬ýØ�<*9�ï¼øÝº[†$.\çßqëMxª½~—à¾õ©S÷È›y ³LÛÍMA I–å…¡j�×kmëç)ÜœÄËA®\Ñ"cìt\X¿µµâlÒá:˜N´dÕy€Çý‡Í.l—K²@ˆ™ÖlT âQãgžùŠxýµ×¬‰9°o††ÞÁ׉¯ƒ¸89o?8…SwÝZW…•‰(غý¶;ÄÝwŸ’[Fí@ ¿ÿýïk5_âN@€Ú®¶àÙéÙÀŽBÇ·;NRؤ~ŸoøNü°ñ…'U,”h~ôÈïŠåÕWÎv;ÑP™êGäÇj DÏXoI"^xÿ‡}Hü»Ü2,ᱎI(Ý}çmbŸE¯ÝÄ%Oé=òÈÛcŸµ6ãï·m+Pîç‡Ý=‚ îv±¿›Db GÁ#Æéü±àŽ\n ÉäúŸ��(£]n*î"j|e¦ˆ§¦M³”€Ì:µâ¦ Ž:öù—!¾÷=ûóòìp'Žáq°­—ð&ß‘C"�ÐÌs¯á>Ìuþ¾xõV3ÍNrïµ¼ÈK\ ÅšŒË‚ ìJu`ƒ  .€Å~÷»ß�ÿâ¿X°„µÅ«ã@¸©Ãª°Ï_¨ÐFmµ³±ÿŒ×��ØÊßI?^‰þC?ôÃòÑ’¯|ùËxÊlÉšÝèè°8:zÐæ{ò¦æûï{P\à ‰„Ÿf'þî¶âaO“ó€Tœá2 íèÛ¥1[‡é‰‰¡c€ór¬­úw”@‘À»š˜XÞ@ýq…M"PÝq•Ú(Ù a õŒ³­ýâV��|ø£âßükQï"(3¸åæ[Ž÷ßg\»m‹�øQÿÂj8»+ýÛŒCŽ7ëèiã=6˜ š­Há^KÜ1”€xÇŸº$Pã«öœ¡@á‡ñUØÄ„ ÀŽ7Å“YT 2¥ËaÌÐÆ¸BoŸO_%¦9 𾛑O¹×¡¶‡B8&l' ;ýTXG9€t@e­Ã=j~ÜCU¼­BbsYêD ufûó6°ha»èýú~àCR�è›àÌ}f…©<øÐ#xÓ þ² ‹Å˜¬l7S±ð¼ôC]âšò”�t_'�3S3âüÅóZ¸ãŽnj³³–ó@€ƒ{´^Åõ?ßDËa½ªó,´¦ÚóP²!¶ÝOL«yº�l=�â>ñÄG¤ Ð¯o-¦€�þÉà÷±ÒÚïEËspìŸ<Þ]ÏÄõwëe¨Ž{ÇR=x[ k+)¨ë@Ô�ë²9|,Â7\ÿ7+dZ*Â\»vÍO.ù\÷Ã8}–˜x LLLŠ÷¾÷1ñâ‹/Jå•S¸s?Wç%›xHIˆ��3§OûÖà—¢L]ÿ °f‘€b/e0hW§GŒ›’€?¶0V¸�Ð6Ó6À/ JÆÑͰ�ëÔ’æÁð& ®÷ý~uHäšùöZa<÷ÜsâZŸÜ’£Ö·í·Ür‹ÔÝOÿÖ[‡—»š†l|3ýXNzÁ(ðFÃÒF�»ïÑGå#‚rÖä8£‰ÆÛœÏ– €Wœ @Þà?u*�Û!Ó p�‡€0‹«FêF« ìaWÑ0ÔøÝÿ»›>üb€Kœ b€ƒßö"ôA¼ÑŒáN€ib.uÆ÷Ì€ x”c•_@Ìô›qo‰(”ˆ…ò ˆ�ÿèøxúÔÐPê½ÔB‘ä!í$ó?_Øw7úå“[¶m¿ž^|æ3ÿ>r·ššbO0Ð ,AÇⳟýlûî¡ÃÞAªFýX^~£ŒÆçV M“+Wo8qâ­4aø mD`ÓõKÊ_‡…{×»Þ%þùR){¥RÉá4“¦B–Ëã& žôRÈ_ÿ ¡5óQ~pÇ·›8’î§Ÿ~ZüÚ§>%^ýþ«ÖðÄ3ÁÀV0Àíêç_xAö±ËЦ4Íql¥ú�Ý4êË�ê»2ò#–kÒ«)çÆ|þ@öÁ\.CÎùz“-,Ê84‹Ó”{SB@%Ó`ðsÖ?sæLf5›ÍÌnldRétñ‚[¼-ÀŒ”xÀ+”¢`¦ysüÄIqúôñú¯G½öúkâ×>ý)yL•Ov8x îsä@66¢—ŒF7ððà¬7ˆÕ8˜plï4N©Çà³=pªm�Ã7ÛQ¬ó¬wBêµoì¢ìÉv»t\Jêšð¨o©³÷�íí»qJ²Ù¾Ìv6ã:Ð<€“¿ûÊ+Z1R8¸(æs…êXœµ³xâDåÕW_å‚„ºB›DdS Vº`ð“2aÖ§šNv,ŸÏ.®¯g÷왡` îDöŸ†*«ï·w:ª5@8�¼ímbaaçÝõóØ £¹pá‚üÉösæ_XHeV¯]ãS§9w¥œÏgF‡rnF“†¨@­_Üž¨Çfç–à�¿÷Ǭ$&Á@/1À£Ù·Ýf_–Æ•‹ýÞÓÔcŒŽF_UÂ�;VM§‡–Ëåüdu$"�Åvx;:þR@ŽIelê@ë¸Z&5XÈðQç]»w§æçEf5s-;‰ÁŸC+™ôPµì€^e÷«ùú€ QXûç±A i )›ùFFFÅ{~üÇÅ�7Þ¨f•Ø t\ÖRåúá‡ÞTÿ•Üo0¼Q1ayV Ãs~fo¾ZͯV˹Éj5·e¨ms·�cØŸ”[®wKK€…afޏe>uéÒPz5[ÉLåDz˜ùsë™l>›Iç‹ÕrÞM§5 _þMC+�üƒ¯»OP*•Z.´™à�'O\�³îÏáií³Év ‰ ÄÝV P£•Û}·ßvFæÔ¦û°<Ú®ïŠC‹Ðþ´ØøÔÄõ ¥õÅáòŠXKeÜ×­‚óvÝ7ç>í·¥P�_K¦% @N=:7—:¾”žËf¸æ‡3'J+üÃyŒñl&• €LW,¥ |IDÉj;”›&q‘å£�¾S šNCCëòåy<¡uÇb—n Rs‹*³[5iè€g Ó½UC8©Là¤2u®¨n¾”hKÀÙlW óI7æã˜ahó6ÿLIó)ì1Ùæ¼ž®Ã<)¿âc¦»pY*Ý4ÔAÙ¬áD˜.ëÇ‚‡kÛäæ˜Èç2Gónæù‚HUGÜJu 0¼ZY^NWÀT¡âɉ¹eY@Ó­ª¬/0ûßâ``¥ÖsÅL&5”r²éÒFnƒç#«8é(Ü,nÚ­"†ˆâ6 y‚ŠŠ@$í2Ø„€€ä¸üÚ3�“`@ÅÆ¾Ü¾R½[¶³ß›ã�nž%X5^ƒÎdSGÀ+ã> üj¾R*¬—gÑÙWV†*�î¯> y: ‰@+išÔ€"¬ý ;gά§æ†Dz±°žÉ8n6�Â*¿\Í”3•t„ÍIev™!ëT6;¯§j'0óLÜ ú’¨è@ €W¬›Zµ‡p�n¶â8•’SÌŽ¥R¥®�–\À©S^‘i™ h•qjÙ¹v­�Z*¦�B&=–)dVS©48„t%“ÊTÊNÚÅ¡?žn4OvËìe쫚~fºÄ�``;b R^5ôŽÊpƒÐ7]MåÊhr2%g(SÚXËŒ–Ë¥RµêÜR©8/ª€š´·JœSŒ{Ïœ dXÀÁ>ܛΔòé|ª”.a%�¯–SÅ2ýºú™_¤ãºÉì(įɲ'Ñ 4Èd*ú¤—±¦Á.®RM¥«�¤;ÉTŠ¥t¥šK�eR+CC©c΋/Î�xº¥e@4'3g¸•õ?´æ6œtz¿S©œMU\bý!ÌÿeÐ&ü«â°|ÚÅ* 1Üû4—¼¤‚•ÞŠ!a¡ð„Äe}#^�BŸñ±qÉžl%¿$m‚ÈÍ®¬®4-Ôf¿�ŒøU0N¬Æu�t¦š*ƒŒ¤]Ü®#R¥Òpª’);вtÄ£Hõ´5e¬gSÀL]©�ÑÑQ´À)Bð(²®S,Süí:eŸ™†n€Š!5%°I*‚®Û»/�⯮®Š /D²§"Ò±£Ç°=ÑœÔ7 ñH0ÐVñtÝ믿Ö¨pù �™jèÇFm†ãÊ €[莛wRÕ\Å©–Ë^ÍÆÄ[´Ž;,߯eÀŒx¢·RA!V�j¾šJ¡@Ø%rÊ%Jd‘±S0êD— ‚Qð ×Ì/Ÿ¢¦^7]¤¡¢…�kkº~?¯pæ]m$B‰I0Ð) �—yõêBÃ,8ûGÆüâú(8]‡¤a êfw}Ùqq6£å�ï® e‚í;Û!Ò{–Tåó`’ Ålaó@ˆÉòp`+ת€e2)¦#¿ð+É_NŠg­Ôˆ‰=ÁÀ1ÀGoÙ¯™¸%@ <×-aÜ¥°à:‘Ã$ì�AÊåjùñ·i… VáËLÀ¶;¹œ7Ê9ø‘ Åa&f¬ãq:b¶ºXYY’ÚX~–T X†áªæÊ•÷‚­ZÿÄÞ °ÿ-,\Žô?[î\GôàW�ØàH ¦˜d ¯ç×*'Ož¯½ft‚D@H&Åž%9€ „~ª¡àd+Àùóç%¸1¢ ;uñÒa{@äâ‘RÍÍ͵E«M­CbO0@  qîÜ9±¼´ÜB(�ŒøÕ#rŒ:%YO!çÝTŽÑH-D.W…Ðç<Ô»�3×<[]0ýëo¼!c�úÖ3gÞzSðKL‚�~À—æx�~ äT$® ¡Œ¿Øl}¶DZÍ”§Ÿl,�É®· 7‰Ÿ``1¿йäNÖ­«Àº À½Ð&&�DB;Á@/0ÀÙßäè®·hw9»J(›4ÙtºM¿vW2�—` 1`ëûr,Ô_ɶµ*1vmÍ#–` Á@Ÿb k n–‡šqŸ¢&)V‚�í��®€¸u~;.ˆØþÍ”Ô0Á@g0Ð5 ·-ÛþÍ*�©^5Á@‚�zè`!l\Àž OL‚�½Á@Ç€+RºÎ/ê·†“R¦–ï$r/‰{`À"ÿžXW+Þ1¥\_®›¥¥¨ 1Ïè��ŽéW‚�€Þ’móÒ\3¼Ýîh Ú•CŽb‚Z^Y‰(>0¯VnÇ­¼f~‰;Á@¿b€»_™ÚkYj£'[ÕÐöÛ;F*n5B 8©=(�» ÅÌLä Áö×6�˜` O00”Âéú… ,Z;ÞÉh¥Š#ë…ÈgTs䫪<h.¦¦¦MïÄ�``Ûa€³ÿôt´¯s§Œ¨ZåÑ!Ó¼tŒ\^X~Ê?AàâµEqíÚ5ká§ð9�D9ÈŠžÄs`€KÝÝs»å­Ufuäà§j¼|9XÅø_Ñ}Úãê(•+ë¸Üðe³˜òÅžu,lfb|Bìøs0!6%~‰Njär¯Û·¯ E·¾É_½æÉÍÍB¬0–Ôög°·ˆ�-*ñry>äMPÉÐ�‚­ :12r›ZF²9×@øˆÐ¾½×ái¦è:ˆwúñÕ_ê àb<+¦ßî£ÂKì ú\çSØÇËkmR¿ìœ}=r¦W ©ǘ÷åÀ`ã¡0ÜÈiÆmÅÝ2øþ÷¿�ûýµgÿ8SËByW‘q@{ož¿põÏÆŽ ÿmÜd>«ŠK>tþÂ99Ûóm>›¡?_IIL‚�íŒ^`»¸è-‹i÷ �Zç�õâ ª›Sb¦6áâ¾ËM�V—2#¼ðãâ1iWg}¹àƲ¼í¤VÿójÁ}û… ¸º ”îü…ó7 ûÞÉo‚�…Ž�y­Y.¬eÜt:çNL,‹«€¾Jªê”ËüÃý$Ò8.¥ù+ ¿ ©…gA.Ï_W�Œ³gßKËK]½ÅDvâN0ÐI ðÕŸÅ¥Eqî<.E_§ác6o�ySlØÖþ7ó—þ_ád0–¼Ivã+=ŒWÂð* $Xp\n¦ÜÃÞJâlöŠ»¾^¬âÍøjÚª¦�g�«U¼Væâ­3·: R–…k«oŒŒÿsùDÿ) dl|k¡%‘ƒÐ„’R>²�ÇYÈZ)^7Á@ß`€7ÿâ <ÚS’{ü…Âf|��‚XÃ3àEˬïWú4Ÿ[¼ºz÷ð»xØÍ8Ìúx’·‡gKéjÎÉU³#µ—xÆÇ[&MÌÞd;8 ݇†ÜQ˜+•Š; PÆ+�C…Bu¥ÊàiPùM0°“1€wÿâ̹‹ŸÁ“ànã­äàQÀ*žä¬b¦­Šj!½Qͪ™k«î[…‚+^}µe NÔÍáT†ìFf%_Í•‡*™ÕJe½š©€!wC.Å©fac¡¹V‘Ky#¿g.ÌÿáÊòúo £d_¯9l'±v(ð^Æ×^ëÂ'¡JWv²Ùj3dÊMW géÊF%[]/ã½ËÊ…õõêÐÐPUY´Dø¢OS(®qœ¹�[n¹÷x¤A”*ùR&3œM§‡«e”"]ʸÕ<.5Îâ�p¼eèr‡B¾äeâ13ScGwÏLýr*ín*ó$R‚�‚Œ™âÚúÆ¿9}öÂ8˜í+ÙŒ›+–*¥´[ÆH-¥ªé�R¥º‘­Šµt:¿±6\Ù80<\|zy¹"¾ùMÞ'MjDn•°RâÔ©–™‹Åbn¬”ϧÓëÃk•ôP&�ÊT«ù2Ê Vëä!_ Ö�yˆ-÷Îͼgdxè .»81 v*0f1é—Ÿ¾ruù÷®-,½%9hÌüNs l¿pJx~·˜¯`úÏd6JÕêz¦X¤:-$*”^}õU~*àt†°a. uâĉ4ؾP”’ϤRÃëåòP®šÎg\¼Ã�•@:çTÒe7�µ@Ú.e¡¡ÊPÚ>{ç¦ß‘Îß ­À»xN"Œ‘Ø lk ¬@HøòF±ô�k‹‹Ï\[Ú˜—[éaºãJ¶?… s7#g'•.àuÝB©â®Od‡6SkÅ=¹\ñk_+bëí›üR¯#›¡Fh ¸€ùj5;Y.ç–Ëå|.3:´Q]§„ '‰^ “®ºéÜr)À'Ž €¦Ì·ÍCnazzü@V¤¢ÊÒJ¼Äš``�1PÕõµ¥ÕKÅJUjÀù}”—q«¯&?«B×sh³?dfN¾ÈÁ?œ*J¹ÜÆÒÚZqÊqŠÐ,ovö'›^øW¸) €"%åìdu$·\)æ‡ÝµÜ†›Íçs" ²”ÍW@¶ 6˜NWS`Ny¨S›~ÖÉo‚�mŠ*øPhžÅX‡½€i?‡«A ðÃÄYÎ §XÀ€ƒ¿˜É`ïp�:5EùOŒÝž^^-Z#ôÀí¸Œö,âwÙðq™2�ï4¿ŸsÝ+øÝ‘¦:wÑÿ”ã¤v q:Á}è÷#«Û`?û>|øïø&ð |zkqs9á:)D&“GŽS“2,�N‹l6'èŸJµ‡Á«V«¢\.cÐE¥R‘ù ñúoˆ•å½L¹lÞ=~üïˆo¿ û÷Ðå·«ÿkå7‡•8�ï{ˆó ´ý_ñ÷\wÉŒ»ÝÜÛV€ÙýòhÈ÷b܇_Îæk*'Oˆ�Ÿþx¤ü7Üxƒ˜žžŽãˆ¡ü�È‚PtÒpæßØXÜ="1øö·¾á²ÏþµÈýþÀo PŽñ ¾?CßùO{…xæC®ëQ¿N"¸Ë°·ð/gÓ�¡Á>ˆÁÿp‰a!ÜoÜÚ ¯V„³ýÜÜœœéG†GD îNry™µõ5IfffÄÅ‹µl«““š{@dŸnæ‡>ôó¨áÅßrœ?@_úÝŸwÝ¿Ð:Eн-À¿pœÝe!~µû%|Û¢÷EZÊ2³sÀçÀî‹tº‹M‰¼˜ß:ˆ@6›�ÕÍw– ‰dØ�=üŸ@VŸÀ’á/`ÿµ_â¿I*Ø�ü;’K{MûË�’¦_äŸÆgŽ5›+É=çOõ—vrý`VQJ¯Tƒ½w9øó9Ê.»k25‚c“3°Sõ&ßN.“„ü¥{‹¼üÃè_ø-!¾‰¥×/ý¬ë>�iV*ü(0þïðí�Ãüèu׉é›nGŽˆáÝ»Efd3fVªÍqÞâ€W¿88ýàÿЍŠgSìÆ¡Éåòâ±÷>†%@”LU+ÜI��ž&LݬÍp©4É¡n¸ðÍoþµª!»A˜ÞU�ÆWãôÚN¬��(ø�Û%È6Šëë¢xíšX>sF,¾öš¸úòË¢³ë�$À‚ø¿„ø‡Oº.Á ”!±8ßßA—ÿ—(x”ÿĪ÷Ýw‹ëÞñ1¶¿ÜÃãüH¦”‘û»[¢€1Æ©¢oý+‹õ¸�¯ n™ñ£t!z=oWÂÊ¥s‘eEË€’Çæ_/›.‡5ì Ñ"€åV rŒ�ÇEñmoŵ51ÿÜsâÍ/}I”VŒ]€¬ÿ‡0îøçŽóà"º\µ–²8€™ÿÃ@ø¿®µŸVÙ¡]»Ä‰}HL:$F2\,F6ñ·:jvÑ õJg²Øî#£›J¡,²k·±åe#@Û†n¹oÔZF?êY³¿¬ƒÐŽÜwŸØuûíâôþ¡¸øì³ðÕ úäcÛàóØr~8��©ü@€ßpœCèîÿ*: ½s䈸é#Sˆ�cào;“FËéƒ= ¶Ü_‹ûõe‡Íd¶¸Âõ�¿ü¡šÉÖdš'_œÞ&mÀúŽÔ¾ èUŒ¾ûÝb;/o|ñ‹Â¡3Ìßšâƒß?2üûÖ9P,Û'ѵ"ûùÃØŠº3ÿ4(56¥ûÙ[)˜H+B(Ú¤ÿn&Ò1ÃD[°™Ä† l²–Dj¶¥Ìsø&ï¼S¤°øÁÓOÃ1¿‚]©õ3®{6Ò‡CþÇ™þ7q˜Ázó��=&&¸ão™u¤ÂÊùó¢|ö¬¨.-�589�¬réúBÜ{·Y}©ñ§z’p%ãªú¶Çž�59ÇѹæT½rE¬éÏÛ“i·¡ ¥ ,NShŒ¥dj(ºÃBŸS÷Þ+Êž† Ð09ð_ƒß' ÿ¾t Àœv0i�û�B vjÊ:øK/¾( _ÿº¨.R|ÀÍ(V¤&Àh´mù8Ð “¶¨SÑ4îêªpŸÞô·¿+PÁËÜpƒzè!áŒRª ï=uJ̃˜çŸA„cö·m`�zÈD%Å\‡n¼1Ên‚-üÉŸˆò÷¾gaœM(ƒáf‡‹ …ËmÝ`þ�zêQ6éòàêÞ–ËÚ~ä& ½•dä9‰¼ñ†—™ÂRS5Cûö‰#ccâ¥èÎÀa5^?Û†‰EvÅ  e°þåW^ÿ¶è„µÞ7§Û Í¯�°Y¸l—í„{ަúÇ,²�GV b„Üg”Dúj;ðß ƒD"õ—-\ÀÅz_Ÿ§"IÎ#¾¡t€®NÉÛiB¾@Üo;e`È5Òˢ†ô±�5–ƒÖ±âûÕÔ„Rj»ÕލúE.�uëÌF˜ŸC÷›]z 3ô(€›~†¡×6܈&Ç�ƒtf¿Å@h�Mˆ—’1ûÓ¯ˆ5ðv#qõ±±å6?âe«Æ7J8(¶#àRÓ$žºõV1Û»ôMØõ¬A܀骷–s`c÷m~[ËÝOÝäíʤ¡Û!Qe Yñ;Ѐ¶]MÅqf ZÅRÈX‚™€áÑ¡]J„kJW"À±¥íÉd, ’Koí’»ž•@£l7WÛX·ùµ£Ilp£Ã_’‰m¹È’zþiíÀo/` <P´É°ˆ0½@r;òŒãlër›_;ÊÐ,\…í(�À@d"0ÒM§ä,:çîàÍ·!Ö‰“æVêašfm„mnÚòÚ®K€9YÉÀ@H·�œÕvxH1e¸·Û OÐ8,#)€á§oÉÊ5€9,YÑk»áŸÂ¿"––Ü TM ·#²ÑGN×ýζëÐÂJãð†j\Ü’[‚âÆ üQëâÛíKÌ¿‘H¿ˆ§fK¿\“DAnG ¿g�(Ú¸Í��(Èg`:Õ¼Ùõ8(VpÐgØrûL'¹ 8XÁõNÛÁÄq¶¡nókLئ›ùÐOŸ'Û•{÷ápæÏáü¿;1!¼&lÅBP\"<f`ÿwm(={õªØ?>Ž{™Lò€“r{÷   .¡ V­ÊÚmek§Ÿ�`uÌ!ØA@nµŠä�ãÀˆ2ÈÀሸŒœegMª ƒõ'–Ī¥þq}ÕÀ\_8†à.öïà:°—€5ÞÕ˜ ¬Ë.ÌÏ‹]8”%ˆÆÆÄýnÁi:KƒÀúØ’Ž»kßV›_;êÖ$\ž.dHcL$|ÉfV1û_Æý6ƒ~ø{6ÿ~ôPCÞ'ñû»&"WqZ« *=‰ëÀxTÃJÌDæ¶\?%Ym�á+C~À&™clÞFØæ.€™?¡“+‘ÜÖæ³êÛ”äÄ–1é,ƒóŒr_²ØÏ^âÏû¶FÁŠ€ øwx�å tÄ÷õœÔºªœÃ . òæ�íD¬M ‚ƒR5œ¤›œ¨ÕdMÙ%\©fþ €…en*“>�D>@¾Í“/<°˜UøþOOº®5пç^Eˆ-ô¿ŸD§û¬|úK3d6ÐñŠ\¿�õgåHÈK-Åà8\ãÄ£_òÈ`‡GÌÌä'Ùô¯· À2X$æ›Î¸G )Çà&¿ ‰êTç}€"úØ1IýM�Š»©lŽðÞuÜ¿þß�Äþ[Ô8rC)3 Ak¹–øhXQ^Rá¼`ÃèÊðé_G,<*a�—ÛP9ܨÛ`Ð8NÙ¼NÖÿ8ãËiœÄ õ‰™ñ}œr‡êƒè›_ñ=åwà+_pœþ¦÷VÛ¯ÁË{[Å:³¦?pHÍMÉ4‰?’´û„Á#ðè§rj›ím~m©qj2Ý ®"ÞFq>?βúý¸ž÷?[=ÜŒù‹Ÿý%×=ßLä~‹ã÷û~+Wãò —ÿ¼ÿ¯ý>ìÏ!Á/âÃýLÍ¿ñ›OÑ»˜vY4Êcô\ÙÁ ¿v•š`ù>‰fL7YÖ«Z¤méðuÝÿ:ȵ\PÃú/¸î<¬ÿèŸ:Îo€½Çƒ­âgðá9÷�aÌ9“oge>{TC¯eüogÄ“¶}(øÔϹî×¶CEžø�ð÷\ï3Чð4Ó?Þ%Ä#°�“2‚~œíùk‚6¿vÕÞ„mºÛ•OßÀáÊñOPËßÇ’ð¿`à_é›’µ¡ Û†ø¸xÒÛ‚ù Ü_Ááí_ÂÎwÁþ ð>Pîûñ{=ÜÆ<æ§¼_Îøšá:=â©ÅشÃk ÎÌÓÐû&á5”„�ÿ}5ý:„‚Ï`}O¿mi¶ÐZ =Â�çàÇ´ï8;η�š� è0¼£¡ù{w )Gà¯~ÂýhlcÝæ×Ž²w n;ÊÖ ÊýøB ‡ðÏ%|§ýtì4¾ïý]!^íEfýf¶7°`»FÍ¿Š ~u Œ@7 znÖø�?�BpÇÃ0è¶š¡ÛôÓ"lÁÑÜÊ¡ƒ_ÒÞ³…ŒÚš#}œ!×ï‰10°ã€QÿºNíÊÞuSu&ðÓŸþ'æ T2#»oókK©À˜$ÀtË|Òéòÿâº;`# -Xí)�„ôý[ÍÃÏàË9 ;E›/ÿjÆtk�‰£ß1�€~o¡å3†c èÒ Ù¦‚ͼÄæ·)àI¢ž` !=A{3åŒÜ©Y™p�㲬Iº H@�ÜÉ,"ì>iįM°ÂMX€6a·7`м·%WŽ=›X.ê×–ìbskôN÷1�€îã¼½9ì¾$ †_»2$`¨µ t§GH@�ß¶lm,¸Í¯]š°Mw»òIàt è š;—‰Éîsò·®ÕÛPÉX4s° y% ºƒ�„tÏËÅœ€IL¢Ð®Ì=¸É" ]øì8 è‡VØB"³=¦éˆßà«IípM¤¦HìýŽ�„ô{ 5*Ÿ9þè6ýÁh6Ü׿×,¼$^Ï1�€ž7ÁÖ `c÷m~[ËÅO…œŒ7ƒù›€Ál·°ÔR2§8aµ³êaœÍÚ7‘l{ý™.!ýÙ.Í—*2Ãà Ík"¦-¿&’%Qú èÏviºTæx¤ÛôkXƒˆ6¸6¿`’à>Â@Bú¨16S”Ȫ¼Ó»É`3ÍÔ·iзMÓLÁ0üMv¿ÓÀ,–™¿ž¸û èëæi¦p6&Üæ× ¬Fq:·Q¾Ix§0�€Na¶[p�1IgÇ&e#/VÑâÕ­š'ù´ h{ Âì” D–m* ×$$ Mèí ˜„ôííÉTÎöÆÌAÚ1 ‹­x“µ§f ”na !ÝÂtÇòѤǔ›~íʼSpÛU¾N«H@«ë³ø‘Ùc4âצ2Ûá&D¡Mèí ˜„ôímÊ”cÏdÉ;Å–nD`äߦš%`º„�mE>þñ�-•—¦Ó…4Þ¹.á°+ÙÜ|óõ‡G£¯•Q觺L?5|köæ W*•±'žxâÔÖòê�ÔétñÒïýÞ¼Ù¥i)š<þ±Çw§‹éÇáþPóÃøðîæ¨Ÿ�Û^æò•+â …ã• è$ å"ˆ^__¿ mð�HÀzTÜŒxâ#\Ë(Ï9"õ§Ž[þÝÏ~öó?ÀªX‹<�³Ë^'åþÃŒH}½}ØZ³âiÎÉ\§Û×ê[Gˆ¤+æ•`:²õLúˆp�G€Y|éÿá�@~ë?~ö?¸h à‘ÜÁ2O|ôƒÿ3f— 1~ %ßуŸ-ç±üá¯ÍÏŒÓn7óÜA†cæ] ÿíCþÀŸb2:1ÈuD�}è£ü züoáÑÅð ·ÂVÊÎ ¨[Ÿ¿¼PóÛJÙ?í;1½ðá�~èï;0ƒX�� üÃ@ô÷ùÑADr'Ëì³üá/é�· èÆo'ë6 °‡�ç_7ðo?ñ‰Od¤ÌA1û^€ÁŸÃàÿ}”øÑ Ô†%“Ɋѱ1::*†††E>Ÿ)â@åTj ³QC!Ò)­&3¯ }ùO÷m�‹�C-��ípûí··'Ó.C©V+¢\®ˆR©Œß’(•‹bc£ VW×Dac£^iþöÕk 3�=öØ_øÂ¬O¹×KÜ«°¾'üO9ï²!(“Έ™]3brrRŒŒŒŠ\®ÿ«£×ÃBœl#Š‰Üª¨’õ6ŒéŦŸ‘dóN�” â�M'%2ù͵EïiK[S%€R „`}]\½¶(VWVì8tÜ÷ �ä>óÔSO=þä“OÄVÔæZÉ^ý¶ûbö?%RâïÛ� ‹=ûöЉ‰q üNmù[:…¥ÃÛÊ×5¿Èhç(íP!%X¶éFÍ�¶ödK[Ô�LÎdëÆ#“Ɉ±±Œ¨V†íù¡!±¶6.æ/̓C(Û²}ßß¼ôÂ?FÀÿn ì7?_Ù?eLW)ð‹©‘‘qàÐ1==�ÁŸGvŽÚçâ·mÀ²_¨œýd|–?øÅàïèÚÈÐþÙÇ P¶‰Š¾ÍÛµ\µ2t£=Ré”c£cr‰sðàAö½¨ÁÒóWžøèÞ é?Ÿ¾%˜ýA+G´É8Û8x@LŒOb]œÖglgìÄsVÜìWsX!Ýüç5@´tj¢¡ýá³™¶ÈåÀuNˆl.+öïß/†G¬;ÑÐMKýæOýÔOMôGMãK™]ã£v7ÄMWÖÔ9a öí»N6€)ŒRKW©VE©XÄš¹Ú1nXͯöL:-²Y½¹$½3ˆ¹ÿN®lmbÖªÀb}½®ÀÌLÒnN*à9“Áä"¹˜hÑ({šŸKËKbï޽⭷ÎÊþ¦Çt¯+• OÁïïéþýåÒ{TŸ”í�ï|gfnÏìO˜Å»æf d"{5%¬É † �M)8è>à|L «dŽvÓ¯]u·Á5³rA€ …Á#*šÒ ¸ù|^ cWÉfRØ•!X\ZĤ"ðæ[¢Šz«\ÐÏä#ïÿÍ~VîË%Àì¾Ù›�Èæ9ûK¶_Å2ìì—kkëbiii{~£¾ªSŽwâ øT滽væææ©–g»ØÉŬ­­AêUîØêE"0"ÀeéôÌ´-J¦*Ò¿` 迾$銸ËD)òääx¤2ÞêÚªXßX7“ìwDàÇA ŠØ™�ÄV‡í‘�í‹jÎêœX6 `“d±T Pš[Ñ1»QïgY@_0R·š]jjrJ8eÎü…BÁŒ¾3ܘŽ#32ý:ù³jž;ÑÞ$³ Ž@ÝÝðíÃØÌf³bjzÊ†Ž‰B¹ð[@?øõ¥ ”ö¨Šjô�a F5¤ÆÔØÚÊÌOêN™©{°*Ðã ×�f[žÃÐ}ˆ³¼þðŒÆÜº�™—bØsçÎÙ»í‹-átš¡PN å>þÖöt·jØW¸% Ý8b|l\ê,\^劮€-�Eü£§éW_ F#¤®9l»pЫf³’æ2„„W®\…zçJßxµž´—°ÿl3‚…1ñ³%Ü„Ÿ”˜4ÀB0%agÖ¯†ÊÔÆ¹�7b߯‹-:—›i(Q)H5),Q©“B•ôÅÅ%5ˆŒÂß‚v`ªµõZèÅî¥ëˆš9¥ßDºTð©°“S=³Ã4 WÐ@‹$­”§-q�É:vŒv½í×¶Ô§ @ˆªóò£2Ï®]»p†ÄœÕíaÚ••e1=¡Ÿ1!‘C†< B\1û�ï~ç&@|ѵw¾­óA.+€&‘ÅŒšM&“Ü{UM¥Zn©£sV:þ‚¸víZKéÔ<ûöÃ@zþg±�??¹é~Á‚ ¹ÍI ~äT¹Um3lŸ´ù÷Ú¯ïrÔD Ù-“åâú¿YCª}îÜy±.…8ͦJâí$ ,�+<{ö,dAúú=”Hƒ¾Ê|ÃÃC�9P‰H7PI»^÷é—>­öC™Ò•£*«Ï"¡™,�÷†äŒÞ¬¹tñRÝ£œ\ÎÍí†z'.ÃI6ÉB+y5›O»ã±,�Q+xö§…íuÓÍ7¶­p\³oÅ”Š%©'rñÒEQÆ1ß8#¹Lû÷CßÄ2ˆÕtäÊ8*œIëWd³y©°ÅpÕàTz_rýG\GãØøYp)H_Õån¥ÒX^^ÆšÍ~|sfvFÜyçbrj"îd—Ú†=µŸ~ýLOóo%s¶Í 7žh%Iwâ¢]šŸož~Sr„¶L©B~žDàÀ~L8õ O±PÙQ“P`�“GˆUø�žô% L1jq{j×€dÿ x–Ž742UtÄ+—¯X£;vL¼ímK©m̱Nkº^y®¬¬ö*ë–óí[UlŒçÝ»çÄ©{ïïxÇÛ¤ðÏV9ê•PXÜÈ¥šD"ühã¥4ãö'ÐÀàÈŠq€ÄXý¸ …®÷QÚo²blR÷Ûn¿EŠ4TŸz,ˆg³û½àú|è~qóÍÎG …ÅÔg¨×¿‚¥b8þ%-°êk8b_?jöpÜ£jsðT¥«<䔹Ð4jœÅÅk^Då/·{î½÷Ô@ ~�8§0W–€cÁÀ½ñ¦Äí·ß¦ô�кxõjÃ>ƾȭQõŸ•l¹¼Þw뢾’`@;¸\ñHØf¥ @’VpìW±ØÈ¬,¯`ö�Ê N�ºKî(`_¡ˆ¾ '·³º:*À˜#÷Âg!cÃ%Á…‹…ŪYFš›+{:(j€b¯BØ—6–¨Ã#v�‚ŠHQ𜒼çÖ¾ýèc»ËUýÊï@@þ†9Ãpö¯gl3&/o8~☕0ÔƒÕë°ËÐZ¬VklO¯ ÓDþlšk8@Ãã´ƒ`¸KðÎw¾]ü‡Ïþ§Hq©jÎq†wN8ên "f©·‚e«)[‚Jpß ûŠT«™£&¢å·þªÃ½Z©¯‘Æs *›¦¹é¦±Õ‡sõf@Ÿ»¯ÖXÑ>/¦V¼«×–ÄöÄÅÌà8ïøø˜à¬¯šR±,Ù{ÕOµSÐLö_5”[q'À$8–Ðw[�}E\ \r–çá ^¿¤NøU;J<ù´ó&W) 1Ž;>0³’ZôË8`ÒˆãQã÷ÜŽÆZX¸6P¸æL>;;!Å"NšÖékò™qM郎‰÷ ¨&áTlXìЖ:®RSŸý×b�Nr*E0`ñ>w›Ù½g×@uJ¿óØÊ$ÀñBÕZžº$#�ó¾qZ+rÇêážÜ¦í†*« Ðí?]€¾â€Éª¶Y©,Ø2üªñ„{ºŸÎGLÃÓ„ã¸B|Íù óblx�“ƒ3KØiážxn`PnZ±(5Scº›§’Î@=YÌκÌ}îsŸ›·„õÄ«¯¶Á"W±@€'9ÙÈ�+(xQü »MÝsr‚gŒÏP�á¢!�„Zp vé’.Uï÷rÏÎDw-¸Æ¯TÉÄ÷7>Øb†ÇÜ,Ü´ÛWr€¾" ´'ÔN’Æ @ Sxwƒÿ5£dã&p�Ø š ü¦0iPêqîüÅA)ª,ç´…0 HA FxÜÇ­@É€Hø¿#€Ú–ލöÕN@߀�}ìc£@ß"Ü7ž €,dˆÙfT€ùž›i&pgÛ š³gÏb±e™/\,`jzÛwÑ“|RX§È¥™ ñ"Ó@Ö˜p&Rè^+¯‘ýçHŒ\p@ùWÁÂ2Žûþܺ1 ŸDóÖÙ ƒXlYæóưÿLNEïõ£ž€ß·l¿RoØAW5øØwóòÕ*½ù§¯8€¾¦\®ÿµñ/�dæ T¡úóÎyu§@G/•„\°ÌÑ]€‰•œ}+äÔ­MÚ¹â6”fào;ÿ ÅÙ¤ƒëa7¥+$1–C5~9/\¬%ë0 }óY[�õú\Úkf8 �™óö Í8‰ @ÃGà0Öÿ’ýϤ f ªŠ…Šÿ•-ë-I7j«êØÌ ÊΞ=T‡Ûÿ8èä‡AÉ�é|É�÷K™BýÏ{"›nõóà~øqK–B0õƒSšóX˜Ä!¨LŸZlr€BKÊp±S/ÅöåȨu'àzÞØ/Õï€;êvŸÇþG)�Ô€yá§ÍLâÈA3; K„µ§ôYrDJÅ$háº4ÂmÖðêô´¡÷î¢ .8Í�2Õ åƒE¢2â”r€zgÈ�ñýFÕŒÙuDÅIQ˜Y°k[€¼” ìJë]t4;@-;TÚÌ=ðÐYTöŸxðg{¿8t›~ì¬ö{í ÎZç—�r®Ð0/rlŨn€ÛnµŸ¹W’ô�Õ¦ ÄÂñn€¸çÀ.w§0i©f·sò ˆx-ЩHAàŸ©q{e×KÜ£R@; ›ýâ š=/Éæq.SóM©DV+tûþê/)µi‘ýgΞSv@¸ÞV ¤yäYc@ªiêÙÍŽjÆ•?�$“(Ç ™~жù^õøùXŒjøØ©ãÄ/#½ÉIï›r'êÐ&¬~:Ô •*�FµÆ?yKª¿†H•3�âVÈ·Û®£X î¦ mÆL ·TB #a¶QPn¶m�ÃW Ø­$6)½žðG%M,§Ïq `ͧñ Óùó*î© D9@ØM q'À4Tdã+B&@¼¾Ù ì €•%©]HH…Œ8å”¶6`„wO›Þ|¸±ç §¨¹P$¯TzÙ¬q;àyöÜŰܨ8»Z�Ð~œÅ[¹2½^±MâÁ]×8ùæï¨p˜ÎçÜ.âÎA33³SÀƒëõ=¾S¡0«µ*»b Å"®3Lß(õs{B¥®ò@THAU¬{ÒV•$èheç·/,@Iвòr‹ÏS/AŒ =¨ 온¾7g�Åk‹ÁLJÁ'• TÃKBlK�(E—O{©Z´“Ý7õ8Û›œ‰Êðf¦U<6Úâ\-­­Ñm;¼(´Þ$¾g^ ÂBÅlûÄ'>‘ýíßþíèzµ­5i ¬/@J¤Nª�Ócÿs„{·â(=Ú¨%â¦PŒQ&¦¨l¤ œêæ#ct46ʘT¬µt5€ô 6ûÖÿìpò#4$5ëFŽÈßró ÊíBóqJ?l+¿lÉpzW ýLÙ„ƒE®<Â]«.¯Ú:zô°’ª¿­6].-Il99Ù û&é³z4˜8¡†{Äd¯¬\9ßïGBºìÑ}ê¤:î$À1RíWt*JZë±aqo†K½óz¸n0(�$†ÓÙeBoÛ+@ø·œ?(e!†¢¹Yy=ت~=ШMz]«œ‘QÏ ÀââüQ¬œHC€\‡€j£„?DnÜË´ÞVMFZÈþ› k`õˆ�Ëè€ÒiOÄ´ûÅ û]\ñ€UIÄþùùKÚ "/`®µmƒOÌDæì¥ç¹9—w[)$ÀPîB9€jH”üå ýy=˜ÏËè©Zó ¨¨µ†R³ˆWó¸bh‹ªï3»kFœ9}F‹$•�èZ ærÌç“á—/{g:|?ü&‘A ÚLì8d›Ò¸ P5’ýU#ª�5»íâ†)œïögŸ “¨i1²¢�)ŒÐ Ë0b„�0(L-ó�aºšM :W[ÿ‡‘PFDààV w¬gŒÙ#rj*”æí¢—�Û_„¸P ýpµùî¹]õ33Âpzie5Ú+pÕ åå:e ÓH…ž:h”ýÉìQ£ö�€ƒÐ€þÜç>W{gÜÌ­;îžsò0©\åy ˜ÁK±Õ�É�/êk›†/ÿ”׬JFJD»¯!Ò#ëf³+éam˜‡õ¼( €¾Ø–Ke­ÍT lJü© ¨éøp•ߦ˂4Á –ùAåS�· ¨/H¨B! {þÒ±gnNM*í4£`¡S‹…taH =Œè†ÓKRm5/`v×l4ëJîŠ}4Î�#J[…£cÖ­@6ð |߉ƒÕ ÿžŒ�’7CZm4v~¯£Ç7<;O«™fr§»�I|J¤P33Эh¿yQíÐì¾ÀH^†ÊzéÒem% ßLI2¥íìpª!âXQÆ3°š¶‘Ý\‚0¾ÌDG5ÜÿV9†Í“FÝLõ1ð!(±lÁˆ£y+Ñ™z.ì)P;xHmH© ê�4˜ÃE±Ý$\^ôh[:„MÚ¼rÄädFS íÛµþµyBÂkÀÍD~‘,¿j¤ÐàzZÒÔÁ© ­v¯ô Éh„Ê,¿Ü Ô"ùŽi²8¡Ó�üÖ B£2� ¡Zú^üì;$\ªY‡ŠvL‘ÑÈ¥™ËWÞ@}€RiYEZ§M~Z`—=%ÕLõN—ibeÞ¤šËóM9¿)Ðñ¹î¬7ý#¶m€�86†Ë@ƒ´a' ¡ÃzÇ£=L Å “†6/BL71£iÐð¨&ôÿ‹Ph’�HÉ“Élì~dö¥`°E #ÿzNۀ܆I˜°2ð ïýúϛի¾Ÿ¢^d˜!‡ðâë©ëŒ¥gÙr K8;³+B6  ö§hzey�DTŒŽðÝA�ìx ]'͆"«”WtˆGÚfÁV«Z‹PÒ0 udG ÚTï$Aüo?r^¸VàŽÿè#­ãÙé�(v¹½â=)­o�ùû«zjÝeÛ” A#ûPÂr-'#ÔºZÃçgªþl9iYè@ç-;ŒÁòq  j®ÉM! ÀÖ—ÈÑ2;šy±vä |=¿¶Ü­´®§�9âGÃïæå$ÑÁ­€•VBA­‡rÅC‰n÷b×®¨Þñ¿nfkú8Cœ˜ŒŒŽ[·Ez(}p¾«Óþ=%èÔšDêPh´ˆwp}Tð¼¶i¦g¼-@v¤Õëa„ø˜~¢Z ß©ýn•�\Âáú™ Éû`ö‡-²Þ‡§¹þ楩Áí4JUT¡¸‡·¾U€²L�9¨B@H§œlé W®~1|]=ûåÕ‹a!%zÝåÁo�¼lSr'`÷ýÅr@Ž£-Êl&åíìL„hÀ{ �‡€B#g4RLW "Ú€<Ì‘´¾­µƒÀ–-–MÁÐZª 0°IM‹™ée°ÉæìéÈÙ8ºàà«@=W5„Ñêa 5½Íî¹½hºJ! ¥~†q@ u/�påê‚Ç:0Œ¤J{é!a²ÐæÇˆíѨ~ï7ÒL> b7IS%ßPRQÀ™Ž$ bxÛÖzxº9LnæR»]=Ý ÔÉTP…Î[ÞýîwçÇ&F¨9É3x[]_I“ɨ‰`§ÔÜï¤jÐô4Ô€µvÖ~Qó c©ÐjvhKmOe‹)!!ÀLÁ;ô©Ìd,,%›m²à¼ýÈ”6ë]„¹IzR«N³?<™Èv‘\€’ˆgš‡”XîbÈrjÙògá kE�äYíøò|Ãd5›=²3Lø‡ÉB[è•0pîšÝ…ËYÏnZÖÖ¡½K$Ç“Sc4pãÝ&@¿Õ&Á�óî™9Aa±Z³ €C9<¢ôÛÞ³š†vÛìOÿ)êsû e´µµù¬ž„d$VÊg†0¶=¤ ÐSÏÛvŽ2=£1×ÚÜ“ö—*œœ˜‚ÄÙ9Ë’éb §G£kfv·u¦_ÛX‹SöÚ�‚@,h�³ZZZ–„Éöh¦Œ'£Öp¢¡&¦ 1Þ„¥%÷�×|Ãd5›=²LÅe€I6pÅY#XH”ƒ]ÐÀ×ãØ’¾Š3†Ù™€SåSà:æ%@áŠ?h�)*¶¨nyÒÉ·ÛLÃÙj jÀ1©×bZb½L âD`Œ`%$ˆ¢F=i3ç/ûë*~A+yö_ß$XÇz”O¨«†� ìC\¾s”Pczv•xø¡~ x0k||\^né±¶~ Å–’XÇ`0/*qØûÑn>'#aÕ^—³{÷îô®E¬ý„¹Em¶Ô~¬f®~\?ËÀ­Xf-‚@½z}Ò“•hs›„ó8ÍÎäÐGNª¬(l @iãyf ˜Ô%�Ò6�•Ç4MCýks`˜qÔt&cŸ›i´¾¨9¢CŸbà¥úQæç£ëêûKƒ¸&»ŸrÒâ ÒìÙ£½ª.—üÚe‚í- µûä}a"¿B¢B`¢kaá*Êë3T^³(Ò"ÈÔA„ÀR‹SƒÝ8 ³´<æ,‡‚¼¥Wüý€¬»·ªé¸áu*ë Õ“?ù“ïÛó™Ïü—&X6­hmqôl $i”Ï»9E¿€5lj @í,ÃLÉõ¿ÞúÑ™. l�ÅØÐéuÀ 6"$„gäuùJô�ÜW*ܪÀ®SW¤rM½m©†EW"„"°hù”hrÙueá ˆ·u΄›¸¦ P^ª$¼Š»L‚àL‰'­0PT‹¡¤"ŠÊêOO™2XdüØ­@Èêé4�+rô§.°°o–ËyŽ…�EPa�x[€dÿuÖi³K€iË;ïfÇ“-¼‰?QBB ^ ºa`i."ŸmSyƒ‹ëSˆ[Ž¥JIœ~óŒ8|è08žl˜¬fS¹¬Hà&=ÈúŸ>sFn¡v"˦ˆ %d–�÷á©fátë¹ÄSÄ¢J(@ö¶˜2 ¦"™tî0ð Z�=,ë$ö]FãM–[¡Òx0GFÇ­°§J9À3µÈ]ýé€`Ü  îïcßð g½¦t°-c¾î²©_ëEag m~›‚ë'V~UBB 9Ý&ðgW®å}6\-·¨¸=÷ê~ ®Û·O®×•,¶dµ�••ÜX|Þ“- ‚<ƒaD,¹Þ gÛ™õ‘‡£ÒÎz!ÕÚ1D�h†6OHËL­Ãf 5_öáLoêñ¯­¯i}ÕKþ•í¦vfQÆ5 xr!ŒÚÓCA=!¼ ËÄëHäP48:·—!žÙ°o£2*YªÒºŠ5„Èi &ì¡-ŒÔ¤  ôž—×€¼Rø‚7îtÈí%£8¼§NÞ „ƒOo¼q·ÖŽ@ø9‰N; ¼¬¤Ä7ÆiS• Q"ñY]]×å/rÎçñVòˆA¢á 0 À5i¼ºëø #9‰RFWÊs£°ÕȶP¿±Ì<æÀ˜€@½¾Ie,3<�%Á(ˆ‰IPäžíô„ Â'ði¼!Ï )Z€è¹•2šIíÁAã†î�—ÑMC@mð \iÝ@RÒ¢DÐü ‡Òƒ«‰e7¡Åþ@}Lƒ€Èò\¤¦e°û)c]É!S)Ö…È��‹ÊD¾á‰ÈÖMXÎP÷@©¥b5a“0¤kÉ© äµeä"øb³íì³9âaæH·­@FëQ¬`­ž®T ~íµ×µŒ×°ûQ¯oÊåšÞÄÄ.I�\“¾³oB1q“{4@¥šÍ7Ö£³?[jfz¦6˨ífô‚¸ÁnFˆˆ—RÏjÆP"¨E0íµd<.Ì–µ¤…°�`«o³Jš£+´ÑåeNþÂÌVºÍ"[#µæI‚126"±ý‚:)`¸È�W%jŒ5,¸´Y®L¨¡IsÄ€•Þ!l/–#(0Í:.m­2ì»z,n©ZÌñ§žz*õä“OZØ)Kì6zõ„0·YJ±Mv^•M¶Õ»`aÿoZ>î`¦@c˜í«D ‚ô6“1t¯ ¦’Z�!,Ñ"^µd –ÍOXÜ…ŒdUÞlŸÕuQœR´6[ã–äVøMNxÒn¶ŸÙ†\9r¸©R�;ĵ´…N-VcGØÒ&ÿD�ÍíŠÞeÈe_¢Êá›8C<™ËºñqëãÃ/|ï…ƒ€s:V§ü{Bp¬ K€ÑÞ ®Ä Û)&"ä †'÷ÿmÇ/cÚ7H–(ð‚E¦R=`¯Å´©),u/;•vLvÙ|ágnפ(ž+àòSÜ�@e›–L«ñ[,UöÎÍh'áØ†æ%šWñZn@€�º+ˆ×Zq¬±£Ä$ĉ´…N-ý\ ¸¤yc¹µQ»¼F÷÷]r dì‚Äp"íÊëÁNûi»õÛAÏqµ‚ì Ã8`öi*¾Ä´I�¼m8ÓÈõ¿EvЖ§Öƒ~hO%ƒíAuÊ­Ù OÉÁÇd—M@ýúýûæÄå«K kÁ 3ëow›yÛcµêë*¾ã°ÏjáMGÕÈeÁ¨æÚ"�¬;Ê„�ºKMW×Pz uÓn–˜ì‚2ÛÆ\Nñ±Ûv³_N¹ 0nÁ+É6Xnïcñ%?m·~{BP9�ð €<_­4~��lÔhò‚[û¯À2ÂÞí$~wŒYV�ê@¯Å©ÁÖ²ˆ¦b°í �˜§ú¦ðÊ1÷ž�ãÀ»œéÙ¡¼{è¢ð §†_žDR�K¤!ÓåÓlW¡˜Deß°!ì•oñÚ#c4Y^‡ŠÕÏ¿ð(šîR"×·Ö2óñ¶fgpKðeCx·.·ãóáN•i1cr»|2œÏ¦«†/d«înÙ»Nj§÷«äÚ?�€Z¯�AͰÿŒh%Ô0ºƒ—_­YkmÙrò"É¿ñͪD zI`Qkðc õ#2ˆØfuÆ“šr]DÂ_/'?Çöüró²Kß0ç,‘ævω³gÏz[–ðã�Y7*IUmK‘#^5T2/ÏjÆP"0RœQ’)V=vÌ6051 ÀÚÚªÖgu@¨;Õ¸ ¶–ýrbl"B =éÉN@× ”*Ž`uö`�W€y;! «¼0tÆÚlÀÛð’è ­CÔ]–,ôÄJ„Z@ @=BÂD�ò‘[€˜ÅU³aœóç#§ƒ`x@':9ÈiJen]êu£?/?•‡‚,Èѽl� Ç BUMή˺ΠÂ�×€ñ €Ñ<¶-Y´ÛÔ€''&åV FŠ]ohñR©FìO<«Ñ[¶“óá5Yá6A8�klh{Îò’”5R¥–sìR”•Ç€}¹¯Ñ�5œ³Þ\öµoð‡u£VéÚ:$ôq&hØÀR‹vÛܰ �o½õ–uM ÃtZ RÒèÛ¼ü–Ö\»¦_†<»¾ØU! Ø\bꈊ$©Šè ÿä,‡ë¯³Ñg#Rølu£ßz™È´„%?þm¯!üÅåE98B @A®ÿÉñ˜�¯ ‡eöËÏY3¨Ø'Ë+úSYJò-Yyj’ŠþÏoǰ_5l{´�Úlr€µÕ•z`@ȱ ·iã#&-r\“·½9€ÇÿûÇ÷‹²€ÎohHòÐŽòšÇóvÂhV›ªpâG ÂÍ7&�÷ýå/Ø~ˆ‘¢‹ëu²íí4œéç-�²^ÚRu%UªÕÎb´?·ÿü:ðè¬?èÕÌ.ãôž¹=mÅ+ÙWakF[:꣔.LxîÙ³7°û–È‚Ì|OåW.e-½l[�ãè·dEÂ;àÑÕ%@º˜>.ð¨jøÖ:Y6RkßðÕíû›¿ìX¦™™™ �B5cÂ]SúÐC¢)¨¨óüóÏ‹ý×�GCÍc»2½¥ÃèpCÕa_ã59Û›ðÈÙøƒ‡©8xüõt¥m¸ÄÇ=Xb)ÜÑ`›š÷=~ÿûß‹80‰Ë[mg7𝡗µy­—»±o¿gïÞøöoÔ^A7ð"òï.©  —JÞÓ€SšæK@~,â�Æìϼ°Öb|ìcý�ßù�:ëKª-xu•PÀÄ;·µ†° èSlÖE€ âk·ÁÛY C [ƒaæÌ„Z„Zw5Â9�£ÅËPÝüã/ý‘<ËĨ%ס¡MY¼õ¿ˆ:æ>…Ú(å¾4º) }‰»—qÇ#ëB�íxVçM\+Ư�†ºöwÜq.åà¯Ó2– Ÿ`éåñ"òox™iCÖ ‚À˜­=‘�·PÖ¥y‚êáÙ�bqå¬ÏGƒ:ãÓU€yAÓàÌ6Ì-@M]²öø„J,uç¬"_ª1Âfgü%€9ཆ4}ÕQoét>|XìÛ÷añõgÿJçÈA¦%IDAT¼ôòKµ\àý7J±y'Î*¯ÚÆ !~¸óàƒÍCí~J> Â{ðyi) uÆ> ºùrr;ùÖ[o·ß~»'Ž¥·¿îŠ =XìÙ½Ç •;8§g¸`öñ »2�¨:òPÐö$X›k;\ÿs ÐD¼¼0›5ÿ ûÏ ]�x„#Óh@†ÃW7=XÂg¢Þöö·‹“×_/¾ü•/KöÕŒ½U7oô‘{éÄ¥�¼äÃR�­æÓ�ôéŒw=8ó’7èt(ÓcÇŽ‰{î¹O^ƒ�*/$hné \ö’ÁÃx�™,A)K5¼.͈ª{7[ks¡Ð•—Ÿ©ÉÚí§ÂºÊK'ÔA)w¨ãnÌö¶G(5ŒÂa[ÿ3ŽúÍQ¯UL€Ò­µR­¨5 JÐÞ½ûć>ø„xá…çÅ7¿ù ¹Õc· Ï••�ýç^:¹sí¼ °=IÂ7ô|î…Ëšv›Y<ÛõÀȵ¾;hrJ"5„x¾†S‹Zs˜èÍó &XY©/”‡‚Œ>ÎÓ›äæ úõ`ÀWW·»K\qLE4L‚‚�ÊøšdªŸšÆ·Û a;±žZ¦Ÿ6ü­­ú‚–Ö{…æÒTxI‹»îº[. ¾üå§Å¥K—B°16¾ûW,cB=ï%èÈ{’cÏ=�»÷)0Êæ2>Y«›¾9öÖðb/õ ÀfI¾bg°9è��‹ øs&¾çž{ÅÉ“×cî`à Æ2\Ú,©Â@Ød“„„�°¼úê«Z¼ÕÕe­k�px‚@£ÁŸÏ×ó5(Ãtu+°kfÐÆPÑ ��Rh¬�€l¹oÅU˜¨f³q¶G" 5�™i"?-™âà,ôþ÷?¹ÀË⯾ö—í.%ª¬çùó— ý? 5Ü´³sófäe(Í Ìè÷+�ÑExr14«˜%ÿâ/¿î‡j¿3¸Áùú“'…~mˆE:(¹é¦›Å©{Ná©®Z�íùÔäÑM Klƒ�„Ïš…9’0¢…�°QÈpu{šx*Ðb¶'�JUŽssO5Þ rÌ•ž·6klkÊYnöÀPàuËÍ·ˆC‰¯@6ð&ê°vàcG ^3ýòËß‹Øu õþ³¸@BK߬“´/‚X~×ÍÊ‹Blñ¿þä l­6n·C‡‰|È»LÔfEMÍSïqFˆ�íç±goTH•ÞωÄÉ ýz¾8ÇÉòsŸûœ'=�Ø&ÿ®q¸;>½ÒóBˆ!°sêôÆ%@#öŸ©ýóæ$ï/×ÿ½4<äñž÷ü¸øÁ^_ýêW¡7ngyùDÔ}÷Þ-Þ:{^|÷{¯ÆÊH0 ´—ÕÛTÞÜæô—>µ£Gá5£ƒ‘YÑ�ãÿò…çxP8È;3£t%ë°�x* 0z$[ ~L›2ã¬.¯Š¡Ye2 òð,TmÏ =U3eÑdx5]%ð¬·Sö®tm€ìE˜·Ú4»°qR ¨S˜jîñã'Ä�ijÏ>+^zéÅØ”öï3¸½ø¥—¾+÷Ì͈¦²z§1ãô»» %y e›ïG�¯?y ,¼¾/nÖƒ�ÄÜ}ꔸõ–[íD‘fBé¶ e/A$Dñˆ©Ìáµ`öYêø«†rÞg*¸à&+ôá6ΫÑ-&]•g¶PoÎáì? %—`ý_CD3K"¿hy|f&z»¿]ñb~ûÛß!9*¾ú•§u"¥#عçÔâÂ…KâE, Š8:ëê:� 0‰¤Þï¿TèâÝäfqIËM7^]€úêÔ¬ï�7Ü(î½ï>ë)Q{�•QŒA?Ó½eRe,{ "f:î\ÍÎÎ@è« ï–ìT¤.€NÌ ‹BkêH¨¦›‡‚t’¤–¢Ív4ŠÆø:æë0Íž4ÙJw Óoæ0Ö®û?ò“r»ðÛßþV„öË»wïnpSâåハ;õÎKov=ê¤ÁFSÎ0H†û+œù)±¿ñúã‚õkd8(~èa¼éP¯½©/�ôQ¸K£œýp}°{.ѽ¡¸/BøÚQ½¥+ï0 9¢ÉÉé·Üµ�€®T>BFÐ9€ƒÀpÆ�ô[ñ ‹mýÏà]ÐìGÛ`ï¿ÿl_ùòŸ‹œ-°rEwÜv³Ø·g·øÎK¯Ç�ËòYpý …-}?ù‘k9~ìˆ8qüˆõ �ZVns>ˆ�OmËÆÆëj¿iœ&.F“ÛÀF¤Jðw¾£Ã\ÅÉÆze’K[=‰tÍÌLŠóçÏj!˜,·øøÇ?NéÈ>µ–‘J @Ü¢ÆÖí¶õ?;\=}lBo\{q8åƒú0}[|ãYGú¥Ú½{—xÇ̃âßxãŒ9ùÑúò—c…ëü›oºÞ¸(Z\jÃÝyç]Pß½£G˜ڜm ðhIuŸ½àLí@oË,¸Äà&&pшŜwà€Ë5b[bnÑ«+T%�A@ãaåúW]Ë“pµJÈ€zd´ϦÀÇ@¸~ìwÃ2R�è¸�§Á \¼xÑZdž.»õæðä÷ñü /âš«®³–§Ï]hÞrË�8¼d}ÿ.AÖ÷$öý¹­Ç³ ƒföX–3ì»\®�¡OÇÆQïd¼˜‡EÆÿ©ÇyöØ[ ÆlƒW€í ¥À\ºÊ^lÜ‹ªf=å}ì†çl�·�â4tr�ûþ÷@î|­ŽÑÌô”xÇÛ¯½~ZÊ؉úÍðÊò›n<)\·Ï.±W ¼ÿ¹Îïõ–­R¤–­q[�+XðYö8ÇT¸T'vë½�.¤¹ ØÀ¼ ˜3@¾v/šŠ,ªLÖ¤øqù2«iúe Ð,W#÷ÍP :rø¨øÊ3_o¼þº5:¹†Ç�Ê3îÏ=ÿ7xH„OkõÞPÑçĉ£âlë5⾨õvß}÷ n‘º¡ /—/æAž¼ïèàœHœ¡€›ë9µ�óÁâÎ$ìµ B¿«]þ]áÀýSÏZ“ýçMª*"X!RF‡F†/äšfÐ8µü|*êÇ~ô]R�è™g¾{ÐibbL¼ã‘ûÅé3o‰ç¿ór¬ A…Ý ;ÛˆK“Ûn½·ùP‘+ÞpÆã:ŸËžAÝÎ4kÇúóhðCãSîÔé¿ræ7Â)øÅc9‘FÝÚ è À¸>¬ÊöHl:ò­¹ãŸH4÷MÙ@| hÐ�§@t D¸s F�ˆ�ïÈáƒàfÅsß~Q\4ö£;�ƒi,Iî¸í&1‹õ~=Ãrr�ÿÀµx@«Ôþ ãmC&X ös³´ìß¦Þ ãðKó„!¼»²Ð€«·«ÈàŸSÅÛØÍ,ä…™–uðL-,5ï~·“µ¤Ñ‘£Ç°eH"t*‹¡É#Ý+Õ‰¿õí¿Ñã°Äߪ×Öù·Ýrƒ8t`C.�³ãC�<{�ÆVËÒém·‘˜\­ZVžµÍoÓ¸Æþ4þ¦+‡‚ºCp¡ŽZ9)äMÀ 6$uTÜj|Õn2|¦OuÔ²·b?Ý÷�|ä£ ˆ¨Ṅ+_xñe Ï´’ESqɶŸÄ^>·õ¼GIâ“�Bvÿ}ˆë¯¿!>Ò6 ± ¹;E…Ÿøû °,s)S°˜ã8”Æ¡ ¨‘%òf½:NÞ÷¾÷�燳Z å! ã9pO_¼1àuY6Ó«“€¶²´ËÏW âÒàé§ÿ\\¾¬«Ÿúùðq•{îº]ìǺüßúNÛnßá:ÿî;o­ûò Ëà¯óï¼ónØÓ~±¶õ/ï0 ¹X^u>Ňib ¯,'¾T£¿ú„äa;„ïõÀ§½$È`x8}ØÜ¸’2 •}{®™uÜ¿fžÂ#QÙ®†÷<þø*íÃQÕwÿÈ.ñ78\ôÊ÷~ m7µ‚›Üow'þî&–U$NÜϧ k'*uÙ /�å%²q†Ò~³ŸóVd›©í 6À%‡‡Õµ>+Ê‹@øÌ’*p]¬��±j¹ZjP·m�çç+;v÷>-Î�ÕÕGýtdÙ©N|ຽâÙ¿þvÃ;ütüå:ÿv(òÃQ]³“ªñhß�×rzøèøÛ‚»¹½û'!¼[ÔªF™M½~L•`S0†É�œ“yÉmm'à�µ Úìè8®è°:®9ûóŠd UÓìÀ&Ønë/¦�ûé�½÷}r—௾öµàÆ3O¬ýØ�<*9�ï¼øÝº[†$.\çßqëMxª½~—à¾õ©S÷È›y ³LÛÍMA I–å…¡j�×kmëç)ÜœÄËA®\Ñ"cìt\X¿µµâlÒá:˜N´dÕy€Çý‡Í.l—K²@ˆ™ÖlT âQãgžùŠxýµ×¬‰9°o††ÞÁ׉¯ƒ¸89o?8…SwÝZW…•‰(غý¶;ÄÝwŸ’[Fí@ ¿ÿýïk5_âN@€Ú®¶àÙéÙÀŽBÇ·;NRؤ~ŸoøNü°ñ…'U,”h~ôÈïŠåÕWÎv;ÑP™êGäÇj DÏXoI"^xÿ‡}Hü»Ü2,ᱎI(Ý}çmbŸE¯ÝÄ%Oé=òÈÛcŸµ6ãï·m+Pîç‡Ý=‚ îv±¿›Db GÁ#Æéü±àŽ\n ÉäúŸ��(£]n*î"j|e¦ˆ§¦M³”€Ì:µâ¦ Ž:öù—!¾÷=ûóòìp'Žáq°­—ð&ß‘C"�ÐÌs¯á>Ìuþ¾xõV3ÍNrïµ¼ÈK\ ÅšŒË‚ ìJu`ƒ  .€Å~÷»ß�ÿâ¿X°„µÅ«ã@¸©Ãª°Ï_¨ÐFmµ³±ÿŒ×��ØÊßI?^‰þC?ôÃòÑ’¯|ùËxÊlÉšÝèè°8:zÐæ{ò¦æûï{P\à ‰„Ÿf'þî¶âaO“ó€Tœá2 íèÛ¥1[‡é‰‰¡c€ór¬­úw”@‘À»š˜XÞ@ýq…M"PÝq•Ú(Ù a õŒ³­ýâV��|ø£âßükQï"(3¸åæ[Ž÷ßg\»m‹�øQÿÂj8»+ýÛŒCŽ7ëèiã=6˜ š­Há^KÜ1”€xÇŸº$Pã«öœ¡@á‡ñUØÄ„ ÀŽ7Å“YT 2¥ËaÌÐÆ¸BoŸO_%¦9 𾛑O¹×¡¶‡B8&l' ;ýTXG9€t@e­Ã=j~ÜCU¼­BbsYêD ufûó6°ha»èýú~àCR�è›àÌ}f…©<øÐ#xÓ þ² ‹Å˜¬l7S±ð¼ôC]âšò”�t_'�3S3âüÅóZ¸ãŽnj³³–ó@€ƒ{´^Åõ?ßDËa½ªó,´¦ÚóP²!¶ÝOL«yº�l=�â>ñÄG¤ Ð¯o-¦€�þÉà÷±ÒÚïEËspìŸ<Þ]ÏÄõwëe¨Ž{ÇR=x[ k+)¨ë@Ô�ë²9|,Â7\ÿ7+dZ*Â\»vÍO.ù\÷Ã8}–˜x LLLŠ÷¾÷1ñâ‹/Jå•S¸s?Wç%›xHIˆ��3§OûÖà—¢L]ÿ °f‘€b/e0hW§GŒ›’€?¶0V¸�Ð6Ó6À/ JÆÑͰ�ëÔ’æÁð& ®÷ý~uHäšùöZa<÷ÜsâZŸÜ’£Ö·í·Ür‹ÔÝOÿÖ[‡—»š†l|3ýXNzÁ(ðFÃÒF�»ïÑGå#‚rÖä8£‰ÆÛœÏ– €Wœ @Þà?u*�Û!Ó p�‡€0‹«FêF« ìaWÑ0ÔøÝÿ»›>üb€Kœ b€ƒßö"ôA¼ÑŒáN€ib.uÆ÷Ì€ x”c•_@Ìô›qo‰(”ˆ…ò ˆ�ÿèøxúÔÐPê½ÔB‘ä!í$ó?_Øw7úå“[¶m¿ž^|æ3ÿ>r·ššbO0Ð ,AÇⳟýlûî¡ÃÞAªFýX^~£ŒÆçV M“+Wo8qâ­4aø mD`ÓõKÊ_‡…{×»Þ%þùR){¥RÉá4“¦B–Ëã& žôRÈ_ÿ ¡5óQ~pÇ·›8’î§Ÿ~ZüÚ§>%^ýþ«ÖðÄ3ÁÀV0Àíêç_xAö±ËЦ4Íql¥ú�Ý4êË�ê»2ò#–kÒ«)çÆ|þ@öÁ\.CÎùz“-,Ê84‹Ó”{SB@%Ó`ðsÖ?sæLf5›ÍÌnldRétñ‚[¼-ÀŒ”xÀ+”¢`¦ysüÄIqúôñú¯G½öúkâ×>ý)yL•Ov8x îsä@66¢—ŒF7ððà¬7ˆÕ8˜plï4N©Çà³=pªm�Ã7ÛQ¬ó¬wBêµoì¢ìÉv»t\Jêšð¨o©³÷�íí»qJ²Ù¾Ìv6ã:Ð<€“¿ûÊ+Z1R8¸(æs…êXœµ³xâDåÕW_å‚„ºB›DdS Vº`ð“2aÖ§šNv,ŸÏ.®¯g÷왡` îDöŸ†*«ï·w:ª5@8�¼ímbaaçÝõóØ £¹pá‚üÉösæ_XHeV¯]ãS§9w¥œÏgF‡rnF“†¨@­_Üž¨Çfç–à�¿÷Ǭ$&Á@/1À£Ù·Ýf_–Æ•‹ýÞÓÔcŒŽF_UÂ�;VM§‡–Ëåüdu$"�Åvx;:þR@ŽIelê@ë¸Z&5XÈðQç]»w§æçEf5s-;‰ÁŸC+™ôPµì€^e÷«ùú€ QXûç±A i )›ùFFFÅ{~üÇÅ�7Þ¨f•Ø t\ÖRåúá‡ÞTÿ•Üo0¼Q1ayV Ãs~fo¾ZͯV˹Éj5·e¨ms·�cØŸ”[®wKK€…afޏe>uéÒPz5[ÉLåDz˜ùsë™l>›Iç‹ÕrÞM§5 _þMC+�üƒ¯»OP*•Z.´™à�'O\�³îÏáií³Év ‰ ÄÝV P£•Û}·ßvFæÔ¦û°<Ú®ïŠC‹Ðþ´ØøÔÄõ ¥õÅáòŠXKeÜ×­‚óvÝ7ç>í·¥P�_K¦% @N=:7—:¾”žËf¸æ‡3'J+üÃyŒñl&• €LW,¥ |IDÉj;”›&q‘å£�¾S šNCCëòåy<¡uÇb—n Rs‹*³[5iè€g Ó½UC8©Là¤2u®¨n¾”hKÀÙlW óI7æã˜ahó6ÿLIó)ì1Ùæ¼ž®Ã<)¿âc¦»pY*Ý4ÔAÙ¬áD˜.ëÇ‚‡kÛäæ˜Èç2Gónæù‚HUGÜJu 0¼ZY^NWÀT¡âɉ¹eY@Ó­ª¬/0ûßâ``¥ÖsÅL&5”r²éÒFnƒç#«8é(Ü,nÚ­"†ˆâ6 y‚ŠŠ@$í2Ø„€€ä¸üÚ3�“`@ÅÆ¾Ü¾R½[¶³ß›ã�nž%X5^ƒÎdSGÀ+ã> üj¾R*¬—gÑÙWV†*�î¯> y: ‰@+išÔ€"¬ý ;gά§æ†Dz±°žÉ8n6�Â*¿\Í”3•t„ÍIev™!ëT6;¯§j'0óLÜ ú’¨è@ €W¬›Zµ‡p�n¶â8•’SÌŽ¥R¥®�–\À©S^‘i™ h•qjÙ¹v­�Z*¦�B&=–)dVS©48„t%“ÊTÊNÚÅ¡?žn4OvËìe쫚~fºÄ�``;b R^5ôŽÊpƒÐ7]MåÊhr2%g(SÚXËŒ–Ë¥RµêÜR©8/ª€š´·JœSŒ{Ïœ dXÀÁ>ܛΔòé|ª”.a%�¯–SÅ2ýºú™_¤ãºÉì(įɲ'Ñ 4Èd*ú¤—±¦Á.®RM¥«�¤;ÉTŠ¥t¥šK�eR+CC©c΋/Î�xº¥e@4'3g¸•õ?´æ6œtz¿S©œMU\bý!ÌÿeÐ&ü«â°|ÚÅ* 1Üû4—¼¤‚•ÞŠ!a¡ð„Äe}#^�BŸñ±qÉžl%¿$m‚ÈÍ®¬®4-Ôf¿�ŒøU0N¬Æu�t¦š*ƒŒ¤]Ü®#R¥Òpª’);вtÄ£Hõ´5e¬gSÀL]©�ÑÑQ´À)Bð(²®S,Süí:eŸ™†n€Š!5%°I*‚®Û»/�⯮®Š /D²§"Ò±£Ç°=ÑœÔ7 ñH0ÐVñtÝ믿Ö¨pù �™jèÇFm†ãÊ €[莛wRÕ\Å©–Ë^ÍÆÄ[´Ž;,߯eÀŒx¢·RA!V�j¾šJ¡@Ø%rÊ%Jd‘±S0êD— ‚Qð ×Ì/Ÿ¢¦^7]¤¡¢…�kkº~?¯pæ]m$B‰I0Ð) �—yõêBÃ,8ûGÆüâú(8]‡¤a êfw}Ùqq6£å�ï® e‚í;Û!Ò{–Tåó`’ Ålaó@ˆÉòp`+ת€e2)¦#¿ð+É_NŠg­Ôˆ‰=ÁÀ1ÀGoÙ¯™¸%@ <×-aÜ¥°à:‘Ã$ì�AÊåjùñ·i… VáËLÀ¶;¹œ7Ê9ø‘ Åa&f¬ãq:b¶ºXYY’ÚX~–T X†áªæÊ•÷‚­ZÿÄÞ °ÿ-,\Žô?[î\GôàW�ØàH ¦˜d ¯ç×*'Ož¯½ft‚D@H&Åž%9€ „~ª¡àd+Àùóç%¸1¢ ;uñÒa{@äâ‘RÍÍ͵E«M­CbO0@  qîÜ9±¼´ÜB(�ŒøÕ#rŒ:%YO!çÝTŽÑH-D.W…Ðç<Ô»�3×<[]0ýëo¼!c�úÖ3gÞzSðKL‚�~À—æx�~ äT$® ¡Œ¿Øl}¶DZÍ”§Ÿl,�É®· 7‰Ÿ``1¿йäNÖ­«Àº À½Ð&&�DB;Á@/0ÀÙßäè®·hw9»J(›4ÙtºM¿vW2�—` 1`ëûr,Ô_ɶµ*1vmÍ#–` Á@Ÿb k n–‡šqŸ¢&)V‚�í��®€¸u~;.ˆØþÍ”Ô0Á@g0Ð5 ·-ÛþÍ*�©^5Á@‚�zè`!l\Àž OL‚�½Á@Ç€+RºÎ/ê·†“R¦–ï$r/‰{`À"ÿžXW+Þ1¥\_®›¥¥¨ 1Ïè��ŽéW‚�€Þ’móÒ\3¼Ýîh Ú•CŽb‚Z^Y‰(>0¯VnÇ­¼f~‰;Á@¿b€»_™ÚkYj£'[ÕÐöÛ;F*n5B 8©=(�» ÅÌLä Áö×6�˜` O00”Âéú… ,Z;ÞÉh¥Š#ë…ÈgTs䫪<h.¦¦¦MïÄ�``Ûa€³ÿôt´¯s§Œ¨ZåÑ!Ó¼tŒ\^X~Ê?AàâµEqíÚ5ká§ð9�D9ÈŠžÄs`€KÝÝs»å­Ufuäà§j¼|9XÅø_Ñ}Úãê(•+ë¸Üðe³˜òÅžu,lfb|Bìøs0!6%~‰Njär¯Û·¯ E·¾É_½æÉÍÍB¬0–Ôög°·ˆ�-*ñry>äMPÉÐ�‚­ :12r›ZF²9×@øˆÐ¾½×ái¦è:ˆwúñÕ_ê àb<+¦ßî£ÂKì ú\çSØÇËkmR¿ìœ}=r¦W ©ǘ÷åÀ`ã¡0ÜÈiÆmÅÝ2øþ÷¿�ûýµgÿ8SËByW‘q@{ož¿põÏÆŽ ÿmÜd>«ŠK>tþÂ99Ûóm>›¡?_IIL‚�íŒ^`»¸è-‹i÷ �Zç�õâ ª›Sb¦6áâ¾ËM�V—2#¼ðãâ1iWg}¹àƲ¼í¤VÿójÁ}û… ¸º ”îü…ó7 ûÞÉo‚�…Ž�y­Y.¬eÜt:çNL,‹«€¾Jªê”ËüÃý$Ò8.¥ù+ ¿ ©…gA.Ï_W�Œ³gßKËK]½ÅDvâN0ÐI ðÕŸÅ¥Eqî<.E_§ác6o�ySlØÖþ7ó—þ_ád0–¼Ivã+=ŒWÂð* $Xp\n¦ÜÃÞJâlöŠ»¾^¬âÍøjÚª¦�g�«U¼Væâ­3·: R–…k«oŒŒÿsùDÿ) dl|k¡%‘ƒÐ„’R>²�ÇYÈZ)^7Á@ß`€7ÿâ <ÚS’{ü…Âf|��‚XÃ3àEˬïWú4Ÿ[¼ºz÷ð»xØÍ8Ìúx’·‡gKéjÎÉU³#µ—xÆÇ[&MÌÞd;8 ݇†ÜQ˜+•Š; PÆ+�C…Bu¥ÊàiPùM0°“1€wÿâ̹‹ŸÁ“ànã­äàQÀ*žä¬b¦­Šj!½Qͪ™k«î[…‚+^}µe NÔÍáT†ìFf%_Í•‡*™ÕJe½š©€!wC.Å©fac¡¹V‘Ky#¿g.ÌÿáÊòúo £d_¯9l'±v(ð^Æ×^ëÂ'¡JWv²Ùj3dÊMW géÊF%[]/ã½ËÊ…õõêÐÐPUY´Dø¢OS(®qœ¹�[n¹÷x¤A”*ùR&3œM§‡«e”"]ʸÕ<.5Îâ�p¼eèr‡B¾äeâ13ScGwÏLýr*ín*ó$R‚�‚Œ™âÚúÆ¿9}öÂ8˜í+ÙŒ›+–*¥´[ÆH-¥ªé�R¥º‘­Šµt:¿±6\Ù80<\|zy¹"¾ùMÞ'MjDn•°RâÔ©–™‹Åbn¬”ϧÓëÃk•ôP&�ÊT«ù2Ê Vëä!_ Ö�yˆ-÷Îͼgdxè .»81 v*0f1é—Ÿ¾ruù÷®-,½%9hÌüNs l¿pJx~·˜¯`úÏd6JÕêz¦X¤:-$*”^}õU~*àt†°a. uâĉ4ؾP”’ϤRÃëåòP®šÎg\¼Ã�•@:çTÒe7�µ@Ú.e¡¡ÊPÚ>{ç¦ß‘Îß ­À»xN"Œ‘Ø lk ¬@HøòF±ô�k‹‹Ï\[Ú˜—[éaºãJ¶?… s7#g'•.àuÝB©â®Od‡6SkÅ=¹\ñk_+bëí›üR¯#›¡Fh ¸€ùj5;Y.ç–Ëå|.3:´Q]§„ '‰^ “®ºéÜr)À'Ž €¦Ì·ÍCnazzü@V¤¢ÊÒJ¼Äš``�1PÕõµ¥ÕKÅJUjÀù}”—q«¯&?«B×sh³?dfN¾ÈÁ?œ*J¹ÜÆÒÚZqÊqŠÐ,ovö'›^øW¸) €"%åìdu$·\)æ‡ÝµÜ†›Íçs" ²”ÍW@¶ 6˜NWS`Ny¨S›~ÖÉo‚�mŠ*øPhžÅX‡½€i?‡«A ðÃÄYÎ §XÀ€ƒ¿˜É`ïp�:5E”–””Іˆˆ†x”“–•™ÏïòáˆïéˆÅòñÝ•’•¥²³¦ˆë−³³«”™••““‰ˆÉ²ˆw“”•“� ™ŸŸ½¡ŸŸ’€^]�[k½¡k�[\`W�†¶ÆÈO³¥O¶³Ÿl…XR˜qêM6{w6EÕVŽRMŸnìÆ0//0¾à^“MH¥kéñÚ&$ÜñæfŸHCªiéîñéGEðý÷ìo®C>´fÑééìïòõøøÝx¾>9Ájkt|†Ž— ž”Š‹Ë94ÉÊÉÎÔÙÞãèíéãÝÒ42z�ˆ „{sjbY`u}260‰.04ƒ„ŠŠ„… 9lldŠïéŠXllM…Šë㊉ŠËµŠ‰�¾£�ƒba�_n¾£n�_`d\�‡¶ÆÈSµ§S¶³Ÿn‡\VšrêN<{<EÕW�VQ oìÆ5445¾à_•QM¥méñÚ&$Üñæh MH«jéîñéHEðý÷ìp°HCµgÑééìïòõøøÝy¿C?Ãklu}†�— ž”‹ŒÌ?:ÊËÊÏÔÙÞãèíéãÝÓ:8|�Š †}uld[bw8:6‰46:ƒ……… 9lld…îç…XllM……éà…‰…Æ®…‰}»ž}ƒb_�^l»Ÿl�^'_bZ�„µÅÈS´¥S¶³Ÿl‚ZU˜oéN>{>EÕSŒUQ�mêÅ�7W¾à\‘QM¢jçïÙ&$Üñæd�MH©gçìïèGDðý÷ìm®HD²dÏççêîñôøøÝv¼D@Àgir{„�–ž�“ЉÊ@<ÇÈÈÍÓØÝâèíèâÜÑ<9x�… �xph`W^sz9<8‰68j ñu–| ýùäI¿÷a½þPÔ¯imÄŸÝ(*­¨Øèã!¢Ðv?ÑÖ×Т Ì•ãÍ›óÍ;ß³ßúcŸ}Î"v¬8gŸyE~'ïÞ+V¬øcWD¬Â0TA@A`¼¨ŒWu¥¶‚€ ‚€ D�ç@A@CDÃF—* ‚€ ¢È3 ‚€ Œ!¢Œa£K•A@QäA@ÆQưѥʂ€ ‚€(ò ‚€ cˆ€(cØèReA@A@yA@�1D@€1lt©² ‚€ €<‚€ ‚À" À6ºTYA@@žA@A` ` ]ª,‚€ ˆ Ï€ ‚€ 0†ˆ0†�.UA@D�g@A@CDÃF—* ‚€ ¢È3 ‚€ Œ!¢Œa£K•A@QäA@ÆQưѥʂ€ ‚€(ò ‚€ cˆ€(cØèReA@A@yA@�1D@€1lt©² ‚€ €<‚€ ‚À" À6ºTYA@@žA@A` ` ]ª,‚€ ˆ Ï€ ‚€ 0†LŒa�¥Ê‚ÀÀ ðè£�V&ç&¿-¨‡ß§‚à¶@…GH¸è750B–'Ȫ“*Pgƒ0ülNüáÛÞö¶o–Ç^8 ‚€ô†>ôB+% ðÎw¿óæ‰ýàm4 ÞGâ�%°N¤�âóë³Ó~ùÍo~óöpVB¤†Q†³ÝDê!Eà]ïz×re¢þ¤w¿™ª0=¤Õè†Øß ÎèßÜÜûOïxÇ;êÝ(@x ‚@QÒxÈ� Ð5~æg~æéaeÿ#�RÇ»VÈ�3¦)�?믡©�ËC^_xdàÀ7‘8 üìÏþôª²ÿ2ø·f¨‚—©`ÿ¯úÝ?ý„bJI�N @§J~AÀ� þ÷…Aø{DFã¿&'ökêÙoËÛO3é…L< €'`B.ø ðs?÷SwÓàÿÊ#ƒ¿pJÝTÝUD»$fý² µ p€‹”Ð ž<úžGh5ÛG(ÛœgV!�xÖÌlí A@è¢tWá*¨©ÍÚ� ÇŠö MÊÿì]ÿæ]ÏjŸƒäâȆŒÄ7xOLу2»¯ÔLµ±um�LÚÔ9/5‰ä"…Àî3Ÿ~(xõƒÿR,ÿ)XÚ¹ *ëë¿øï‚àMíd‡<ôN†ô%w)®+½—4ß´Mñë?†»q¼ü²ˆ�EdLîßÉ<};u7Òß›éïõTõ›¨ó¸ž~‡¨Y¤{ý£ëiüuˆÿbB? f‚#G „ª•ªšœªQVÿ¼fI'¶¾_W[Ûþ¾~ÂÙÙo�ûÿª�}sp*3@’Ðûªâ÷2)â~16è¶TÆ¿5¢?MO×IŠ;C×'éÝ?YSꫤ,€FÂ! Àˆ7öÏÁ-ÔÈwÓ‹þzéï¢êÞÙø­`ˆAçÿ¥Kp/¡3öžú$/33ÓêÆ#GÔââ‚uðŸ¨N¨‰ZMU+ЯBJB¿8­‡uÖëjŸ~{{»ô#û�% mmí¼:{ú,Ñ›†-sÆý§>Y‰`ÆÆ‹E”ø5½M&ßïøÝß!RV©9¿Dq_¦[üýÝÿÏ7…á)º—0‚È6ÀjÔ_‚ë¨ë}!½´Ï#­þô÷nªÞòUq(ªR¿ñFµùV¾ÅzyiIÝvÇmªBƒz6T‚ŠšššRµÚdßû¬l¶{¸ß%E`{{KÕI)0…­ÍMõ�_ü’ÚÝáY¨+çÏ«™Ÿy·‰•Äu�U*â³ôû õ)AjÛ_¼% ›SÝ/^Jè¢t Ùð…Ÿ^Èï¡¢^L¿Òï[è'¡ÏìÞó"µó½ßÍ’bffF=å)OV•jzðÇ—ýÔ䔚¤ß°Îг©vvv´"`:sdýêºúü¾™¡hÍþì»U@Ö }GZÝßÑï¿’•àSôûÄ�„ázߥ¼�)oÈú›�C=…†ŠWPçú}ôâ=—¤¡µ> �p…ot9vìfúº§ØD¨Ð:€ÙÙY2ñ§•‚ÉÐ\ÎLͨéÉiµ±¹¡ö÷ÓS‹‹‹êÐÊAµF_÷œP_YQUQ8Pu›æÓñ£~èÍôÛ¦éƒ?§¿¥åÿþá0üZ·þå @98v•Ë/ÁÍôr=L¿×Ó›÷Fƒ¿„E ~àK2|å¯Ð –4ýOLL¨™i𲡦• j~~^mmmªÝÝ´ÉÿÐáCl \àáÊ_ˆÊDGW7=²ßMÓ¿@ÊÀ_Q_õ[ùÛ´°p­Ì‚„W¹ˆP.ž¥q{4&):*V©¢—éEôwø?KCg°qª©išÛŸœlV&ügè~„Fÿfíh%ÚlC  õq˜›ãûH ™ŠUÌ[þö �o§'øÛiaá»išò�©ÿz/­ˆùešꛈR0`Àžƒ_ ‚EzqÞxP©·Ò‹s|ÀÄq8$õ"ò*­êÇŠ~Xfff‡f¡_Q½ŠÒf©Ž›[˜ˆvÔjü.(dâZT¾¤õ�õa¯¤_ù‹ØYïÝRêW, ¯õT )ÌŠÿí³²�„2 3ÿ<½,?J¿·¿…2x �ÁFßù˜ïG˜��‰9]™¢ÿ¨Ò³3szM>+�,a)‚k„Ò°ýøç§•ú?È*ðsd÷ù…7†!éú‰€(ýDŸÊ†—=úó&ê‚þÒ‡¿„±A€ÖÀ€-~Õq«jš¶6b‡@rýÃØ´ûxW;—ÞE[~„Ö ü$-ÿüõw„äHBB_ /°G…ÒŠ~ì×ÿUº{bÅÈ�Yi<’âîa¬ÆW+þÊã¡Èý÷UŠáø»†UZô75=ÝÄ5ÇlD#&§¦µã ìöÇ¢êÂ-åuEcœ†ý4°ê€¿Q‡‰üp�_kõÝô7£âÿ#­súçdýüç´�ðú+Îx–.}xÚ�æùgižÿ§h@}3Mw G•æFç�SŽWs䄦F «&hYü«$¶™Åw<ÀÇ!D<Øw Šd%NRW»Ý쎋 hŸÿd{_Ád>¯Ã ~ý ô@é튞ΠðÂÁÑæ<Õò‘—¾T=ó¥ßÅ#*#±r¥ VbE!V’ëd¥Ù£6Ú½vMí‘ó¦Ý«WÕµS§Ô•o|C­Ÿ8¡ÂÆZŽdŸk’çyôû YBÿõ¥~–¬CB� G@ÇÅÐü×qzà?B÷ØGÛvÀ@Ý·}›Z¾ë.5G.diÉñB v˜Ç?4¶4x¦ò#v¨{e Ì3'Éüï³ßnw±•.^DW¾ðíq¬ÒüøÔ¥2åÇcêƒÊ(�„ö€â…~ÁÖ@AˆX vIIÛ£Ý&}�žÃk'Oª ÿðjõ¯ÿZm] !¼½0Mr½“æ?ïù·Aðê·†aÛŒÚ+~|sÙžƒñE¤‹5§9¯çûÓ¯-Kæy�;ü¬g©ëiàŸ¿ù榤zw iâèJÑ=Æ?Sãâ—0`ÐÄNn¨Sïh×¹´xΠðqùtƒÏ æôaÑàÖ ƒ¶>²¹îô‹“]Ö¢Eð£{Í¥ÓZP ð Σ@?„ß±ïú.ué+_Q«ó7jíoÿVA9h#|'}¬üwšø'2%ÐzmdñxûÚà.YšÐCý2ºùÏôâ?¯€íúg?[{ÙË´y™ãŽƒ=^T0Å—¾„@€ñõ)1÷ÏÈ1Àî‘'½jÆeðÔ6%,üÓ ±’€k‡&gvYB˜B V [ °„¿x¨_¼í6…ßqê§ûØÇÔêg>ò„�g"ÜAÊŧiJà%oCœ? ¡‹ˆÐEpcÖdö½4¿K÷Þƒÿâw¨[_ùJ5{à šN]Ã@w18â+iø—¯{ Qÿÿç-#L�ù›«ìîÏŠy(°p‚Ïî(A²`œƒj÷i0pÌ7~°lÒ«9  ÔhÊàŽ‡R7<÷¹êëôGêêc�Q¬WX¢Wèã¿/üaøE¯œBì…€(^pùÓ—ÿi`þåÄûÂX¸wË+^¡Íý:uªð™†Ÿ4Æþ2óßü9óÀo¾¼?5o–Ð9=¨â³ý�#„›ÂÈEWÀGHÜ7A€—(X›ôäüAuⓟÔ?κʇÃ4¥ù1RžKJÀÙ8Rþ–‹³«*·Ðqáö»AP%íøT_þé0DŒUüw½á úªÐÀèÙ¯ýqÁp(ë 3=cô«NTX|ùV°bÐs¥}?±«hq“ö)³4±'Dsj&G ï‹�+ƒÊ@ÝÂ:¹D¿úÑ^u•~G_üb½;éË¿ó;¾kn%%à—ˆÅýô“ÐDè¨1KR[ßJ×ßßsþN.,¨oùþïW³ªÅÆàÏK8¬…¦g¸>ˆBßôz!•K¬€f]½¾”] {��u‡¹‚O½èìrÐ;!aðÀ‚dømÀÇ �€¦2ïzýëÕ—~ë·Ô>-õ÷Ñâéû߆¿ï‘GH™ˆÀÊ—ŒLÿX¦ÿ¨O>ìç¿ó5¯Q‡—–Ô2ut¼aħ¡í !½VŒÁV‚ >•! aþ®§õb)œb|È @ Æpü�ÌNßt“Ú¿ÿ~KÖ2y„÷PúqÚ]BB‰ˆP"˜IVôxÿ3º‡åžžpï½êöC‡Ô´|å°1fB“Ëò…�ÓŸýò êÅQh–Œ/-ÖÈ»ÁÇk€(ÑÊØûþ‰^UAÊÉ ÐΤ*mGž /¤ô%?A€8mœa£ý–¼à%/Qÿõ_W°êð hàšø �\¨8ˆÀAÉŸæÙÜ,pâóòî‡ù_ófHþ¹·ÉóÖ¹âÄ:ÓÑc.ò·]ܳú1gÌ“Ç×ö¿XÐNGkçØýÈË©–Br(åÙæ¢Õ:òîWüqµƒ¹þ­Ð‚åɧó™*`ú}ˆ¥š^YQ/ü–oQF}Óù@ÓªèWEˆA,á¯(%€˜eA:í-Ù8Ûý“ɤ6÷ä'û-n¢/þò²µCƒˆ#UmÌ%~ÀÀ—=GDб9ÌzBÉËÜùõ·á²ƒôêþråŠÚýô§Õ¹þ�zÞóTíIOò’eé©OUw’À=Ч_õdŒ‰EèNãßÂa ð�ÓÊØ*Mp¿þÃímµõÑ�ª}:‘K~ÊB3*ˆ0 -I–ÈÝO|B…ßø†šþÎï$-�§¦U®»Nݶ¸¨þáòenÅnå Qx8ùRÝÈÉ@KjÔäÑ£ì¯ÿp�Ž™ýƒ?PáÚš þ€ûDÃùîí“hC],p¥wp›°þå/«m꣦é,n˜Ã)ïÔþ¬~•[¶Ð‰WÙn=¬1D‡³€½¿üKÐàÏÓ¯»U5á[&<ÓþðMàÈά¯þâüåÙ/óé믯]íÿÝß© æt@�¶=³:ËHTÒ.ÔmYŠ ��Ч¹JnÆÖ?½��VAKØÇc-½(O2ý¾xæòÞ#~ö³ªr×]Ø@^Þ6@wýãúàù—w FcÀÿb[ò,Ãj‡Ž……�O€>7Q�ùU:ÎB™>WGŠº†¼ÇË;Ð5xKe �¦œsBÎÇO©’ ³$¢$Ñèñ5¾}êÌ`_€·NûÅñ¿iQ—šK×¾ÜåæäÊË¥‹� ÜVê7ôW8ÊÜ<r±“tODð¬lr® €²‘ ~¼•ò4øñÇÉ�¨W^.*%€�hZ–º_c(Üþ�U¨y# €7dåe@Ÿ¾�—„1YºO=¥[Ÿ.O6áÔ>C6V·_ÑçÔïK�Ë”âÚC NýçëžCÓž’‹ƒ€(”ºH£5`†:QºØ}bMݤ³dPp蜌zHÀ•×]û–Т´°ô+|Øp§|ž�A¯÷°É' @[ >w ¬ècCyíÕ¡qˆAá󔳫ä\y¹t$¬LtµÅJe®û5™(Ón0 ¨zðÔS z™`€4²$è”ß@A¬`åãÚ Žø`á¬ð—)€n Ïç) «Ò)u‡ÆÐ’Q°L”×z|Ô²?ì}xv­bŒ¹òréP´X< Ï¤ø°a)Ìþ¯ÏÕÙâEèsÓrWÁŠ Ï Õ­â¹# —®[rúòåÊË¥£òA*߆è=,§Üþ¯?µýREècë / #ÈHBÂÓ@É¡æÒ Z N½@È¥ À µ®K½€Ñ· ›‡KW½‡-]€>·{ ½LòõÓçÆêBñÜá�K×ÛbÉ•—K!@+ï@[ÍÑóLì52Ðó¶I( @�_ë�ùˆ Ç�ÓAq>ƒûØ‹i—••+/—Žä’5e5N÷ùÈ€îc\F ¢”�b<¸s`¢tò@gu�€ àªºëÉÊ¥ Àà´­[ôW¬5DÇÜå …¢øáU*5ü=±”Š©0]ð¾ÈÀp´/{ €±N`8j<œRŠÐçvÛßÙQªZuJQ§c3¥ósÂ4~_4 j�ø8Í*õoÔG¦¢aYB ¿âl¬£ÿ“Ð7Dèô‘Isow—Ž`œr>3£ê¤(p^ª>VIŠö@@�ë z.ƒUÏHøÃ:_$ð%˜�W¿(+ÓÓº¯"7§Nö©ÿ“Ð?DèöºäÝ� 2¸- ®^w�Ú>}ºÏKñ.º1ø¹Ê‡t± G+OÞpCtÈCÜÝÍM•�t Qº€, uÆ7½^ü²síššœ�eIQYYQÑï]¹Â¢¢GšG[€ùئ´¼Ü·€×N€J,<¬úE5yè�RsslýçhÔÇmRèWŇ´\QºÓp§ˆíÓ\¬÷ˆ`ûêUU¥��jGŽ(̯í^ºÄÍ"t=FÀ£Ccw~><{\]cq\y¹t(D,F¨#’¦11øWdþ|s}�ý9úU %" @‰`Ƭ*J=ÆéØÐ¡]$XmkKµZœÝù·zø°Ö²wÎ�S²ˆÆ —Œx§Ä0x�Y¥õIü1÷ÏÝÕ„ZìÓ×ÿÅ=|±Ã×Ù”BÈB@L~DÔQ}ƒ›ã"Î]¸ f`:ó SSjò¦›ÔiИØ'%BÂ` ÀQþš’²LûÄ‘E×äÚÿ  I\±ÄH À_úâ¯ÒÔeíÀýÍÈõjKõâEå3™IJ߈óÊßr S\èeø Îì'2¡S;MÓGñ"yXšÒK8A¿*­¦…PßÞÖV��4k9j³‰ÒÀ^pÇ?.Ý T”+/—õ­XúÐÂ4Ø4íÐbå }xTéW¡¯~Ü£Mö«ý³Rï�åó ý<Ú]å_fùÈ}gˆÐ~ÆÜoÃÿöÞ ø"=Üw 2‘»ôuwêüyu=™öÛnzA+¤à— ¢$ÑèÍu0áÑŠ¬/{z’Xt½©«®¼\:*´JJòÔÑ£¬â…¨<0øgîv·$£¿;KVOŒèá“?†�yÐ )�žŠÁMHšÐÃý²¼³á¸Ø¡¯÷sdÊ_¦´SZI/tg;dU†Ì+輂‡ÂàÅWˆ­´;ЛbÓß…Ë—Õ®ÿþÿ÷›øI\gˆÐ~ÖÜ´³ÿ?’ðãD°h%Ê$lÑ‹q�Ìm3db;@iy½;“An�qB€LÍeFã]¿ëŠ©›«ôÛ$¿'»´øÏ3u”ÿ�®ŸãÊã•îxIãBØB¬@•À5þ¶ÔŠôµ�ǸÆ_~à • áÐñKí>%Wf~½ È^u<Ûݯ×`–�D1¾ŽßsHŒkn‹ô¸†%¹~ˆ¬£'z\®GˆÐÇÇ€|[þwƒàùg”ú§4¸þ$‰r¼�⤊Nv©¹q"wÀNB0ˆA2lãäM*Œ6,Õof…Š]0F�¿§Zü+øÿp$j3¤•ÀÇ�„>"ð`îÓáA¿Fá]Ôa¾•D9×Gq¤èž# ‡w*uÔþHN�z¸Ø_¾NýÜÎ+õtüûÛ(]€þ·�–€”€m²ü´SŒ�{Ný{Û"R�kÔ´Eƒý£¿Ÿ¼�;ÃbfF>!0D°ñç�awÜoüÔ¿ ‚jÐgÓýóiŽàôr>ƒ®o£½«†Œ¤ø S€¼òØ S‹YdÅÀþEjÉÏÒßÏÐï/ÖhA-âƒï CŽ€(CÞ€Iñ߆8‰óÏ?�DS ¤ ÜM7O¥.ùNúÿn¡kx –ÐG8Ã:h8t}¬†±hŽÌ#s‰,,>þ*µÇ—©¿ø -Ñÿ2Ýÿ-­<þœÌã— õàð`pÚ¢+’ДÁbü_¿f�ÁÄuJ#5þ(½ðG)�,yê&ü¥Nàý]Ìüæè^BÙplà2P6êcÁ�¾Ú± ÿ2Uö2]_Æ5ýè^�Št�ëtæî ÙšGHŒY`Ì<®nÄ÷5ºÇ�°æ€L 3 &Åa‚ž¬ŒcFT?¸‚i™;ƬÚ]¯îþ³žùÞêÇ?ñî®4„ÐûeRäé�R«t8(½ßÙ#@†°V"r· Û�ÿÆš¬;ˆ$JÈ ð3?÷SØ å ôõÅ6íƒv˜B7äÝ_\¸ôÃaÈVX‡ /‘Uè¢ôu)SˆÕ)¨6ܺÅXÈ_A@è)d5’ ‚€ ㆀXÆ­Å¥¾ƒƒ€žpËCúš9_ÓƒS3ýñ°&8Üõ j‰(‚ÀH! €‘jN©Œ ‚€ ÀC@NB%‚€ Œ20RÍ)•68ðÈ ‡rpj�)‹0 ÝçŽ0\µrTF’�!C@,CÖ`"® ‚€ Pb(Eá!´‰ç 4º6EèJ6®ÌÃV¯®€%L�>! @Ÿ€—b�gu40\€qeæÔ¸j.Ò Cƒ€L MS‰ ‚€ ‚@yˆ <,…“ à�öø»‚öÀ sñémz$µ«LNý]<$]ÚC@€öp“\‚@9¸Çÿh‡®‰Êáy92shÊ‘H¸‚@™È"·‚€ ‚À8 €qhe©ã#Àùæ™Ó©’�˜ã ˜SûAª—È"Œ¢ŒRkJ]†ÎÝ Õž#3‡fÐê%ò#‚€(#Ò�R�áC€;®sé �a”y�ðY�^ @/P–2+œOàaN¹2sêoOA Dè<É*ô �a'!¯û(€áœÚèY£KA‚@w »ø wA θšaÛ/É\Xu�8lõr×H(�áA@¶O[‰¤‚€ ‚@iˆ 4(…‘ à‰ÿ3yøLåܺyB&ä‚€ P¢”‡¥p¼à˜ÀAáó.¼‹†Qæ.Â!¬��D@¦²YD(A@A »ˆ »ø wA ²“sŽÃå­[PRÏ“¸2sêßsá¥@A`<`<ÚYj9 `ªÜ†q:�+3§þ.|$]ÚC@¦ÚÃMr ‚€ C�€X†ºùDø¡G€c'š�C7H`@^†Ìö¸q� Y�N @§J~A@A` À6šˆ8ãß0î©�dv·‡L¸1 A [È@·�¾‚€ ‚À# €nmàØÆ‚ºÁC‹Q7ÉàÕK$FQF£¥CˆÆ>ÎøÇ¥$†QæAÂOdz�€Lôe)CA@0Ä0` "âŒÓ>sOý@!Ç•™Sÿ�ª˜#Œ¢ŒN[JM†�qŸàÔ›UD†™Šf!A@�r @¹x 7A€�ÓL7ÀÃæ XËË0ï‹€ÿ¸¥ P6b(Qá'‚€  b‚FÇ�aÜTÇý¶çÒ�û3 õÊG@€ò1Ž‚†•\; `ѱKí!�ë,™eüïAcH‚€™0ã"±‚€ ‚ÀH# €‘n^©Üà#Àù ‡n�jË•yØê5H‹,‚@gˆÐ~’[èÎðS:˜ޑ$åfÖ23X[½UA`h`hšJM8*jÎ¥$”82sh©N"‹ 0:ˆ0:m)5B8Ãh8tƒT}®ÌÃV¯AÂXd:E@€N”ü‚@'pG@.]'²”�we.á' 0¢ pãˆhã€g” ‡n�ðâʈàƒV—hzè¡Å°~w ÂçSÏðÕ÷?U©ú”.®^Qߥ’…m7ØÛÝU,ÀÇ[ΰyÌa×�ßWׯÞGïÂ}üBÙuÂ@Uö•ºxùüîC¯yàëtû?*uõ©0¬|êCúÐWº^¾Ð¢t_û™yä‘ÚÅ+k÷©0x­ª¨—JE~û,%§ ýB F?!ÕH x­ êJ+aøŸÂjð¿÷ÁßûÇ~ &åÚÀŽMWR^õªW-MNO<†á›• Žv¥a:4°l;D4l­8uãÐ McŠ Yn¥ÙÌ·“.ðö‡^{ÿ_õàçÉ2@†�‘Í@ @�Z_ü.¯ýPmºúüö¨X)fàà � áÐ Re¹2[½ ã!’% žM–�’eà_?ôÚ~vyáàûeÍ@ÿÛOŽîA<øÚŸGsdŸ Tð益Á¿˜Kñ09·¥­DÎR¸�úÀ÷Qø·¯~õßY GaÒ6bh:wÆ—¿üåSsf õcDÍ[îf+£„Ƕ¯µƒ!ûRÖ‹2sê?Jí-u‰x"Y>Aë>¬ê•¥i�3q‚üíbèÖ?üð¡ùÅ™�Óàÿ6*Bÿ.á,lA`¨¸OU꟣5ÿd¨k1¤Â‹Ð…†{àµxð�ZXXä’÷”ŽÖúŒdد×U½¾¯öööÕþþ¾Ú%/—´�¹Q×�â÷ÔÎÎŽÚÞÚÖKáU ÿü�‡x©8* Q Q,ÀøDÓài­¥FVVYWÕ‚…¹D(„§8‘Ê»i>ìÑbª|*æño^Ìmö7xt@m ÐT;n�Õ_ z^:Å¡VšžƒîU Ç»ÑÖàŒ¶õÿçU…^\S¤,-.Q¿6Ý,}Ý‘£GÕòòr3Îë‚|ª¾þàOyåb'¢8!2<ðê&Sò Ì©æX ôG�Q´¢vfVÍÏÏ©€Õ�� Ðfð2vX‡ì2åî´a<Þ�¶èœ;…Ú#@‹˜çæfi=À‚^Û„¬øfZ9¸¢­ŸXäÜFø±_óà½mä“,XÃ�%ïØF“éŸ6á…?é¶ñ9rToçÃKs.šJâö¹>B m@›r·í�Ž[7]ƒ&_E =àë9)¦8a € °~½éf½­9Žcþ%"ü?|ïõLz!s € SrÔ¢ø;Li¦8ìãÇœ•Æ m~5Zé/aðèt~9ÎïS³ü€Æ×õ9/0àÊ烗Ðö�øùöþK£Ìüü¼¶zÆÒOÒ.§#7ñï é4Õ½zí½1ùÛ¢´�YÙÜ'Ûáëë½ýpbR­Èàïƒ]’Ö»ã!+£OždY½»æ “�†C7¬4½C{”KòyÖÛ¡í;”73;­æçZ~Òjää J@Õÿƒè~:<è�N䑼2y> ä˜âéJUîæfƒæí��§Á¿$}+iomôãÊ1ütÜzÐYu=×ÌäÀ©V<®3YYd®‡´Ÿ| j7B`ÎÖËöÞóK4EŽœâ¹¾~U{„§Óo¼Q�:yB÷il`ÃàßÐTìGéáN=±‹EBQ<[u?¬¼žûBàÂ^Øó:þò߯G.7ww÷´÷­–;NÏ y×€ÂÇWöÜ@<ËÝuÁK, ª¯nÜbánvk{‹K.t% +“pE^›Tø«õ[wÓZK�yêc%‹£¯»þuæôikCÂ1rÀö/)þ�†4‰b"PÒ')³´ £1�mz‚ƒ¸÷…w?W€R‘ý!ül_¹zE]¾|EmnnißÛ2ø»Ð”tA@(ô58`skK]¥~è⥋êÚµkjŸÎè$@ ˜KL`Ç@[ßvßî»®9Æ=¯{dw„õàuÜFÓ-‰¨Â˃ä«ý1°û†]øÕÞ¸¦|ß¼B?<p\áãc‹C7Hµ†¼™94ƒT¯q—n}·HÀ+û±�Î~Ú S”?$'hP(°Ep“ÎØ"K3¨íVßB´ogÒ Y±d)º­ÔÃ{ŠÒ“i˜û_" €ïà�qcs“|h_–Á? èX_k€¶¿œFC�$ #°N^¦~jccƒÄo¯qîIÒaÐá믣>“ÿÅDŠ}Õ«^Õö!lÈ{™2‹à�f¨‚çpÉáæÇöFfx Ý? þWiqÌ&)A@t0E°¹µIS—ÔîÞn[âÂüO“NÒ:Ï©€…Éé‰GÚ*X2)Q<Z|‡&±•å%Ò‰ùÿ°ÂÚ4ÎÛ–0èïyê@щŽÜÏ>£^¬y‚ñx†º–˜ÀɘȮerÝWˆ gã/ÿ%ê;c…€ =g?¨Oeå M QRp8onwRÁ⹿d,ü‹y¡£Ä,¶‘ ‚À°"p�Ö-­Óœ>ui^»fà!ŠŽH÷·}þ‹ûbz!m óQxùË_>E¤7¹È±ov�Ÿ°¾¾®ÏÏöÉ#´c„€6P}‡í/§‰< K¡é/°¬_[Ï(î)ÐZ0 —éèXt/�©õÊuFùÏ Q˜p-,L'R'^8»JÎ-¸/‹˜ý¹h�oŠ#ÿ¨þkwñب= £VŸíímm @½híSw ´§Àx*`qÉkmß+yä~Ç;j€·YÙÈ®^Q·)rDæ 33¤Å’ÏNɛߠ,øÃ4Äîή^ÈS߯+¬I@œp:ß�?ÿqÍ'@—h±™„<xW±öÏ]µ:¡O�`öCynåÄlS{UéÔ¿™Ù96à Õafz†vBmh+Àùóç¹ýÐòÅ«ç^L}‚]˜ú�e?Îxû•[pÒnQÀÂø·ž˜œÐ aŠh‘¶NÛgú5È¢ÜÍ�Mý¢A ÙÙÞq‰+éLn¸þú¦)Ó™…£c�†Cç,¬‡\™=ê…çõüÚùVb¸‹‚B€½öÓ³3j†öëÃã^üuÝ«šaK3’É)Ì ò¬¨±ÇGžvåÊU^ÆzåûˆPZšJ,\°‚àVW/y9Qu[¢°àog‡íð‚+¥“nwoO]¹t™®Ó¢Ã='½t˜ö]!KÝt.>½M�&-\e[­\õ¤tô1€ñSê‚¶ÌÏÏi7¼Ód©ìUÀz€e:8ÐÛ¢¥Ò"@8Pƒ�9rÌVõw!I àœÓNÐŽùeH @q˜šŒ´ÜbÓÌ��·«âB™©{äYðÜê9õøcßÔfTü™À ™ 0"@!€Kñ“'OªÇ?¡Ö^X QÆUZèì"ªí ˜Ñ±ò†êî{ï½÷‹Vˆ4b`>ô¥r‹‹g\Ãö_�¦[ðiƒo#xÑêU¸|ù²ºpþ�¶ÅXÄÐ+¡¤œ�‚ç$†h/:긼2þjy9u+£0áá�À-Ò;·ºª.^¸H.x—õ>|o&°Ðy‹,34 sBTh-Yy®­Gî‚y«“³“Ï&š?sÐIr±°…b æÖ`âÂñ–®M¼ƒ1¾òO�<­Öέõ¤m³ëfÀ‰Z§NžR{²È¯›0 oA`ä¸L „OÐú€nMQâÃPÒ2X¿)Ú=€£ƒ=‚(`ɬ ŽÅ!VbK@µ~Š:H־ӧΈɿ %« 0Î`ð?IÖ€�Æ1½eb+œŸqªT+úÇÌó&��¢°ƒºSˆçþ±ç5´,@ûÈþíÖüÿ6�£}æôé®™ðâzÉ_A@mÐG�>}†v \.½¢°®’N”×êÚ¿ )ÌpËC=Ä&fòY2QMKËûnq‘M�Ó ô“µè/®M–¯n(°*œ9ƒ/ÆÒk“`WÒ¥A)ŒúŒc§@™ýß¶‡ã1X&É�3€ÐùÁÆä5òdlTG‰¢ †4@g…Øô_u<¨õp¿+_ègϬÒ�Bí;ö�c�ãÇŽé-AðF<)´Ž |y±cš}?)u´kâ¥u(ØÚ9ñV×!„Æì·ß~›z泞aLë4 ;¶žõ3àXÜNžÉ­­Mµ¾¾¡.\¸@[üÖÔêêY=ÕÞ©÷?ðÃûº¸´X<Û4Í€ù}N˜ 55¾€Ú±Ži€/rx�;�(œ' (Þñ  a¦Ã¿vÂõ7\¯žþ´§ê�+t9žéÚ)gœòìÓV§*Í[J(�Mš;^ª”7e¥ 9‡}d3•x¿�oŒNÇ@¹´¼¨·Ý~«þ 8}ê¬:Eóù§hj0þHiGd옠�›¹9¾_ÿ¢rvw·©¯9ÐøÐ(¢TúT@íc¥˜¬™J.Ûe!`�â QŠñ‰So‰/Lã#,'hþ¿ØN@ï7VÀ–°ÒN~|vTÿ¶6¶Ô—¾ôeõØ7‹&±8¤‰Îž9«n¾ù&U£ó: ˜ …Å’cÚ¯�¥Îk+ (ìæ‘ÏT?ü0öÿº—ôÙPöü?Ì;<_wH}ß+¿—ülÏêÁß�${"·ËÊG`PNÎ,¿fÝç8=;­žv÷SÕw½ì»ÔM7ÝÔV�XÁæ,¦\Ÿ9<ö{{±7TLÅÿp@7Ю-LH` €¤z}ûVIÓ´–Ü`ËS¦‡e\óô±}äÈ�êE/~Q×öùÚê=.ñ8J¹Ì6Ü8õÜ ¯X �!0==¥žñ¬§«|Çóé`Þ›N mÇâh*hoþP0ðçTŒäÖUŽ�•ˆS( @RcŒ(FXZ‘!:BµqÊU�LnÑb¢ÈÊfº†ö¬ÝcÁO‡¿ ž«s:¤žÿ‚çÑ \éHMÚvòyÏ6i» 1Ì…WB9,//©—¾äÅêØñ›½^ºtIÁ·§ýW’€ñßðËö�˜Î`; ÔÑ7¼á å,VðFg¸2ˆàj¯Š[ˆ×p4Ô²¾q6º�ïnÌù¿èžïhž³íª¶¤·‡À9Ú6%¡;` s‘MwŠI®!ílúöç>G=ù©Oñ®ßùvºøô…Úfí±ö ØÙY¿Ã»Rc˜AW£‡'@ÑÞÿÚdË@VƒÅ=æêËš?»tÑooîó_ð\1û»Úº„ô³«²°�,°ƒæò•+Æ4‰l¬«xÎs¾U=íi~JÀµ�k¯òé'&*Êg'@=d'ã‘�]�È`k Ì`ÐNà ûËm!$'= ï4À‰†Ïª}¬Ü=xè ǹU(_§rI~¥÷__ø @Ñðæ\8Q--vo+`Äx–ç×Ϋ¿äEäCàšúÚ׾Ζ÷2}„ÌÜx#›ÞDˆ53±Õ”ÇU«5Rx~�‡zeYƒWðW€pI·‘`õje‚ö´T|WëÛʽ|ù’-)˜÷‡o΋–c ^¬Qg*¡{\¼xYžã.À‹©«W¼òåêýïÿ u•ie�€> }L»>3jg Õ 9+ã²´Š€y›Ñ$¡�=xðÕ÷+ª�ÞHËU'&ªÎéIxÕ+#¬_½Æfó¤§x„fî ÕÎø«^²ðp¬zuÑåGÿí3�ùEYw?ý)úÅŽåtñ—ôö8{V¶�ž;§ž @že7VíP\¡Áüvò xÛ·©¯}åk,X˜¹,±hMD° ´úÄâþî€ÑöØ�à²Ð�}¸rˆÆ˜æVWÝãÎ(> °ˆÞgÕ«�ϺǾÿ[éež&>oØÊ•x7p—ufÅ™›“P€€¦XâwÎH#‘!€E�ÏÞ·³€]šZì$D}"ï}‰N2€à� ¸ò¡}ÈßMj'²¼¢5XuÿVEŽ¥‹BË  Û,YÆ€õ«W‹ÄI¥ÁÇ?VNã¤B ÝG`•¶FA(~bº/Ǩ–\¯Òô×>™«¹ÉŒ*ݪ,ŒGo:ª–Ȭ‰1�…C}: QŸ—?î·ë¬b·ëœ2ëÕ:Öüwí¸ÒˆPÔò '@ñ×HMOØ™aæ®Zæ.;­-e‡|öïRçÇ “ä¯ûÈ‘#´øgoKè«´�J·¯»/ë…8£W”+úÁÙÒ1ÚÙ"¡|ÐGa^þÐáC,8~ñ‡P;Õijƒ»+ ¯�œšt‘5Ó«u}(�(MDònÔóyÆ)æVWe£]tbþB}“5ìÑKÒiØaÍ}E¥Üù„ÛI©Å\Ú)èù± Ï£¨±$Åë|×h+àm·Þ2–ô¢Ò˜c_YY¡¢¾Ê*î�gfù¾ú³La¥¬T`(8*͸ANt#% @F8¸` ƒ?ÎðÆÈ�E€E¡ŒùÞâ—HŠ[o»Mw–²bº¨UÊKÛÚÚŽ¾˜Š´ÀòŠON�w `ÉsݽGÖÊ••evØb܉€Ã³¸¦}ŸC�¨w±+1¦„Å£Ö˜‚Ò¬v=¸½hj*2{ÑàOU�ã‰f6ÓE;ô–sCÜÒâ’|ýpéVÔ™³'4k±t áˆ/ðÅTK<õÖÝÒÆ—û²‡Ÿ Ð.Z!YGõ‡ƒ,x˜[Å€SQ,Ñ ÒÓ�Z’utÜ M$Î~·²zmX\Z�N²¨ñJN;{öœî˜"¶sA%—;NìøZ†³@IDATÐñk€¦Zâwoœêß˺Ⱐn€ “à㣠šá˜¹³éNz^RlÝr'b�D^Q,ÍXŸ¨ßV¡-ª–d�­x²°Ðõ„•£ðœ677›�Šj!ie pæìªà]�Z ­€øÛ‰:G1cŸ|àÀ½àóû®Ð©�¶lv¹äv�v�K`¦pà¾×ßwÉÚU‡qMÀÒòA=¸Ãuô¼ÿ!D ‹\(¡è´: {»<`aá€tŽ�ÝFÞ“'¥�i¶¶²`ÀX;A]Ýá¶òK&ËËËê왳Nâ�ÝúR¢¾­Íî }#õެi€Z­J[@'Õ:s7tu» �€òrZZ±xâÚ’i¢i–‰–Ñ‡Ø ™œ0åÀÞ©ðØÛãxÀRjaaÁ$†Äu�“'Ou‘»°Î"pæ´{`Êæ‘{?ÄNwÀ¢AŸÊ&Žu¦›ô mœ$�€ÜVäL€"¬D° ÃQªdŽBp­`­—à‡Ïc àÍÿKèðÎxåÊzï ”’Ôé3«‚B—ˆ¶ò ÙÞíÌßwŠ‹}vÐ'  šP¦,à�IЦìAo¤}©“´ qḭKû\;õÇ5ÿC€…¢¢K‘'Nˆ…±KÐZÙžf˜¦­™%�…ÀÊ !àîö® æÚœ i¸ ÷QhrS,ˆ`†¢ §¢-€t˜|L?øz•k1‰]ŠFÊÓüò…EQœ€–HpBæÿKD“Çê´Lð€ê€jY;â1؆ÇÑú8=EÊÌ�­€•jEÕéÊD(I¦ àÜsÏ=PŒ �Žçÿk“ Å“kÿu:ÿ}æØEd= ²°gP7   E×.z(;dè$Ä[±NÚõÃà?Y›âw;méŽVkssŒ�(†Æ>|øð-]3$5£àüƒ~m‚Ψvüƒ¦ê¢q¥ïíðv@@YØl¦ž\ˆ '0§ Y_¿¦|NÆLe–ð»?7?Ç¢Ý% €«+Jßß‹œÑÄi5šr�š*ìž“2CS(ü˜K�Ûµ(†‚z¡ùYªt0B�œR¸Â>ΰXi\ ¶80ïIÒKDàÔ©3%rV\NŸ–…€\¬Ú¥;x�çç”àPŸvû9Ú'¥öi7AŠ;Ë*,ä €d'€)QLØTÔ¦èd\s á0™_G[÷ãÛ¶ÿr×̓ƯOh»0ÉÈFà�L·±±Á¦6B½Gû´½äEƒ“‡v€³èˆW6È4@‘òï}Ötê(Ú-…Á¿8T*ä `š¯ÐŽ.Y`�4úŒµ$Žk4}°ßŽy¨¢)8mÞíÄÑü¿=½¨Œ8 ä9Ûà™ÿç T�Íü_§6sµ:Æ4´­+ÀM ‡ÎÅ'N/“WÌ3û—+s'²ÈN€,êåß{­ —ÀÓÓíŸ ¸GÓ5Ç‚jÔVΙYÞÔèé“­€ÂD0€BsM4`ï˜#7¤Q÷y40iDA«u)öÜQ ÈÔ›æ±bj™ÿ/ƧìT«ùŸzûI�ÁO›MBá°”¢…¤� ¢Ž¢Éf5Ïk…Ž£¶†F¶ƒºƒ,•øH€Ó¥KŸC�`褯 C~_9;ÃW4¨? €åùÀLNÄŽ`jŸ ×”E!^ÝZDãJÛÝᯰ]X\t±“ô0íÀ Æ”5M›šCk¸Ã`Z¤˜ó÷*¶%gªDšÃÖ £ ,�…‚à Yº3gdí·Nh¼œmwæ Ç›‚é ›œœÔS�,ÿ�L˜pE\ë µQŒY<}ÝãÛäÖ¢jcàU¬Ñî뽪`ÙþÏK s$ô“ó?:­äqÙEXÌ¿ ½V  ΋[í¿tÝô”!’ýGè$hÙëL³®®ž§í±üÝ1™ìrË@§r]ŠÖ´ßÏAÉÍ69îMS°SSìuÇ_þò—³‰Må�jœX2-{ßëî;ªöT¡})Ú`2aòƒŸeL슠<°ƒë”Á!�6Ù3œ”D÷×/|­÷Ä@ IÌϲä�9Ü�ùäDÝšå&è "d¹¿ÕsçÔÑ#7&2Êe™`ð_"«â…‹�l··h+ ¡Ýœûû{ú@ ›µ(ɱA`.À­.,LßFùÿ!ÉC®É‘�€�F ºS½]Ñ9ÀE!Zès©£/{;u·Ÿ@¼Ú[£Ü”sç.б¤y³gdÈWù²c‹@>%[Ò±ù;Sßk[ó9³1nÙ³9²÷!, £V‚LBšD0Ða+ (Yô˽ÇNŽs<~�ìB”i%p¦¡˜xXT=°P€Ì£! @UÝ¿C…¦®´EØÚ8I‘Å´­)€V~ß«�5² ÀÝöém'ôüƪ|A4ÈÁ$îDÕ`ÈâëfØu (7†°ûDÃY �MHÉVÀ®7¥Z!__ý*¯œ­í57;Ë#6Pi‡@´®ÊeI¨U5íµ°"  x‹�…N¥¾ÝÕ)ÇN€&É@ÑðmÖõ gŠ7Þîîñ¶baŒ�Vl,L"ÙضÒ�¦ÈfïæÇ)d&w=o°"q�K5Zô”šst‹q_¯U`M+fi àŒ Ä¯}²•ž3 ”°»³E‡µ¯Ôi'@¥âv®†²fg¦ñ‡H�–­€¤DÈ€BËPnwu—˜B˜ /€†>©É‘yXE“ÞvÁu°±(ÇÛ ìJü©S§�|ù¦ý�Æ÷臥QàÙ~è$/}y·‘SZr­±bj&EI¼:.!nØ& @'a_ïpõÀQ ð€5Û±uÌQ®X ‰�…¶.ß‘‰JÝjó?uˆÍAÅç¤7Úp¡� Al¹Ä×_ôRXÑ d `Ž®_ž8iÙ††pE£f£ÓZ]\ð£_™Á4˜–ÊŸê%À€ŸÓ´<¢Í;6*‰ï�••ƒììÑšÞnbm›æåŸ¤EØè�™nÒÅ`\€<(X-j ­ù2ÿ'ìû‰Ëf^˜my�r3Kî‚ùpë|‹K²0`—"°ØéìY³/z H…_ì�±³-ÿçvAaN�”É$�áÉêd$M²f\JOåÒ»Üã¤ØXäÎEç"”Ú¢•ç—._QËK¢ü¦ Ä¸Ÿ$k'g-’Þ ÐAÙÑÁi<X8ÍUŽÜ{ï½>ò‘�\åq*QíüðâۥDTî {œFåZ¬ªµYWO™+!±ËtŒ\ Ò ¦ÑëÞÝü÷öÌ{ÐõnfÃ`•“†h ¿Ä<´9�³¦ W@÷"\ö½ k0`¢×70ëf³ªœ>}F€î5µþÐY^^¶*¼É¢1]‰g:ùq”Lw]£Ï µIwÇY¡Y§¦'éTHW�Ôfk°î~–E=&D¢$º^ߦ¤øÁ«Õ"È`~ʇtÞÞo @¾Mºcr—„qÎj±O ‚ø²MþÒ�‘6qZâ‚ûñ7QŸdñ¨[Å B‹Ò¡µqEš·i`9{æœzÒïJÓÊ]©À# Íâ•,}¬–X�ÜnÀ”g¼ÐºˆGì  ˆ&™V­ë3DH€" @ ZJ ‹¿m&ªX¡DN€ÊBdÎÊôæ‰ò8—^S‹ð�,¯¸.œò…ÆŒ€uþŸÈõ{öë×À‹äÒ{£í2ͦWÔ3SCÞQ¶¯l_FzqŸE9HòŠœe*œ$H^™I8m™ŠIf•ëÎX9¸Âf€…€SÇõfC‰à L�š›áï8 Ëœ,Ì€ÍÃ9“idoÃúøÊ( ñ€ÚT±†‹½ÍÚ¼[Ì®¨(�¶³“w4cË´@ó iñÛ,œ:Út—œ¾³•?Nñ§h i0@�5�b€L ’fœÁ+ëßkýúd(�–­AQb(KZ$2 ›0?{Ƽ£„j‹>[···h>²}kd´ÊžþÙ9þ©€•@NÌ"* @ê\è@{@盦à (”¶�¹ž±ÌωÄO£/ýî¥ïØŒãYkXk]±y (!¶š#ˆ«ÇuÆ;Hðuï Ù¯ä~ è.³é‘Õ"›¿‡«€´$Oƒý’ƒ%ægΞ3g’ØÒðÙ ¸ÓáV@½xšÙåL“/<…›ÔG±džQÒ€*ú tEôÀéc€3Ãd’ÌXÔ]%£Úºæ¬¼ã]h2†—¶äbejT¿…B늕?&jT¢U—ÖULÒË¿˜–9·vÞ8AýõÛ´¡Ô™ªÑÌ ª¤]3Êõ‡¢² °¬ +¨{\FLbR.]º¬à�nÚa•‹yÉ_¼NÜ"g@ô}8�²±„ćÖ˜\rˆ�E€4 …{E±‘^­3�7uWÆ0äž �ÆCTJÎì·~31S¹Ìm“¬uá¦hÑz^5*ѪKëÊ“FÜL»ùË}êöÿÃ�-=ŠÇÖW­�?w`Ç× ×]nV¢ä}NÜd¢ãšó…•b�)0†i_ÓéÌÒš”»JV€cÇŽ¦D�›ò€‡Q˜Û7®]s2…2ÖÁø=ëææÏ•]¡“Y§¦§¸ ÀÊ}÷ÝwðÃþðù£1� Ñð=ôÐ")�‡‹žƒø�‹)ã€tÎ2¦0øs;Þh `ö­±>ii wV™[w†˜"#Bæ6¦JüuS$ˆý/©"麤ï8 O“ûYÛ@„üh3ŽãMÇøüÅ—4ëk:ÞR¥,²Œ¢cŽÞõ"@F®è,aVZ¢AÛëèꢘp,/î ¹æ(X· (÷ᬄȋç§bnìy•¶N“/€+ô�&'õ¡@¢4À D½Z¿³bès’ŽÿE˜$mØØ%ˆá(¦3Xí�©™–
:ä¥L%s™g¾™ÙÙ@˜!sk`e¡0nˆÊðsSd2°nO�*V4\œ¢¡(0Fa=˜2è´ðœrYµìŒH׋±¾�(Æ Y²Ø/YÐY{qrcÀã�Ÿà�ª]LÉxÖ“eŠu²¸ºÂD…šæŸ @\1 ðW.¾ã’îFxL�ÀQÇ!€äv2: F^±²[ S¬´–1À=e/Ä«n-ãgS¾øÂõ7Ùñ*›Jvñj¦·—Kg7ÔËÕ,)º0PD0Deø¤)àxÆ6!# iØà—á‘¿Õ»cn%Ãü¯ŒÎsÌ-³É´Ÿ†2:4ˆ­Ü˜ÎõÁDjx¤‚3 nØ¢c²§gg¸ä9:<ïœÍ€ôŠÏÌñË‘�i¨EhàAƒáü?Èb/€Óðh[bhÑïq¾ìbzÛ_/€‹‹ðX ”­Û×½ƒ•#9]Z³ÓÏçj&¥s8îÚË¥™æE` –Îtê4ÃPðõÛ”^/”kÞYëâÄ@ÎÉzV‰„tU í_šj 2v½¶Á¤(˜Ä±léê¹5S‰+�•ƒü3¶°°ƒ *7ÌÍòw?‰/€4ª¢4ð€ =P¦ñIÝA@¨MÂä”íI[÷ûu¸ˆmÝ#O;Ág; 6•S�l€–²YR|£ -(¢ )/@³s®fr>gAŒ_®­Í-…UçE®É:h]%8t.>:Ý]\)Ê+æq�^³Bz¸-ÛÁ ”î¢vÉ-÷~` 7àL€NúÀý=ûtOV†šjÀz±É™„5DYX“Ù »>ÇkÖ�ÎA»f5{ªL4KÍT¨qëHneJwÆli’VVËÕ)‡ 8[´ºß5©„¾ÿÜSØÞÔ™# cícqSù”©l¹›H¹ÉEG‰çI+ ‰{KŽFtz @VÖuRÐ6h›æ,Û�ç�.[8¥¢’9ì12·¸uS2ñ¢•hÕ¥u‡õ)8lë @Ôü… @´’ûÿq@ñ—]æˆÄyÁâ'wq™¼šÞ”Æûmý:?êXºÄ_“Œ‰dÎ¥®K<Øp2Xhüê!xæ4æ˜íh6‹"ù8&{ÈÀ±D·¥ Y~ mCÜV›ÿL4NÌÆÀbLà_Ô««PŽid­01 LŽäÛ”Üù\©äV.ÇU{¹4ÓŒ™[C¹ ƒqðÇ4ÀÙ3«~é¨È ¥Œ4©ñ.²¡Í�É©È â+“×_¿tœ|5Åd oÆ^xøá‡Q»º¸Š·²LÒyã®g>\4œçl—¹²ÍÎZÚ8qD¢6Þþb긋p׎OiáÅé,Yãh]ÆÓã«,@/îc˜i±²Ÿ3Oš¼—³âÖHÊ_wŒ{̈ñWOn0 „Àñ™€"ÁÎ¥œ[;¯ß¿äÎ †¸$•p4�#¹Ufªˆ|®Tr+—㪽\ši^„TŸ¶B;8 žYœ 8ENí†:ÎhìÀ*â1AVZŸC�ê}* (E ŽCíW½ÓñQßÜË @òM0½b½žX\X 5 &«„Iº¨Eí)–7tyJ‘îÅ‹)ý¤ó£ÎHí©HœqÜä¡ Û@«?¾„"ËR³äƒdÄ@]à pXu;ÄÒÓ©zØëDÛ·œGĞÙô€?c|ÀÊS$Pš¥naÉéj4‹Èçj&¥s8îò¹|¼- €uSíø¿` ¨Ÿ‹Ù9ˇ�¡ð Ô§þ±!i¬¢ÆÞVÂ;ó¯JúÐ(½Ðdó¯Cš¶Œ) »Ì£€É=§¹·kÔÊ °«¾éáÎÀÄ›Ï�Šqî$ˆØ5D²S›eNÉ’¸qQ_ºtIaU3k €ðbŒëzüÆ—r*dn‘) ½�HÉÔÆ ¾ê1®ª“â†5+2=`otuަ,/DªÜè�qIÇàäfÑ,·d+ á6ö uÎNM°V‹:mÊ*xug^�ÎxŽ5‰Ïí˜õŠ˜J�zVßSB½ô÷”¡0Ö&�êÙSLõ§8gáÁ™³Ž#€ìa¢ãF1æK¸ÁM+æ<®ÁÔYsñÍØ´ ÍèŽ.€äv­GE€inÖ&�wðü…KêºëÒgäX›¡Žä(øßœ-ǽ€ƒ%©ÉÂ\B2W“4_»³Ç”4drjÒe/z ÜéN€=Z�³’ejÜ£®MÔÔîÞ®…"íì÷SÔ#z3ö µkდoµRU799U+| "ßçí½TIÆX8à KK¶)€k§ÉÚ¹Ä(‹ùš–RXƒ‰¡^~ì °eS°è‰gþ�ðU ’Ù’$þ€¦ˆÎÄ¡ˆ>Áºk—‘ÕÂÍ®^šÓ%¦Š4YDë Œm�it8ºî:¬í-™Àü2ÄøD¶ÓÄ´E··y :Ã…%Zè*ÒüfEȳÊÇ3"óÅ6bÚg™.%_@"½Î€p\º¤ñ»k€ækït½°±€©©é§Y£?y&Ìì ·9²½™šení\Ó)6îíP¥D°2NQ¥‹1Ýy�ç‹ÌǘŠXÕ.€‘â¦Ç‰T,8ió·~nò¥&scà°-Ì[-’5jÕƒzº×A �å( ][»ÀSœMê$@qM ’�Ú\ÿ(sþ?ê|~ׇ�!G.J×Å¢H¤óRv9Nœà €óîähD` €›Û±J 3ÔåXà±Vè%+ÔÑÙÀÇ4ôù£´¿Üå8»ÃüúíRá)€iiM¤àá™®(sëÎߢHKÔŠO_¹©R"XÉSTé"LwägW‹¶¦€ÀüQŸ.wú4À¸ƒM³II˜LSÉý¹‰å¶”®=ü™�HåÀ×¾EJÑáEr€s´àÖ[Žçx¤#Òm’L³§$©×íQ9 "²FÁÅÔ|éø”‰º$/-‰*õ‡K‹KêâÅ‹IjãuçS ÑñÌ¡p}* ÍRuãë_ÿú…ßüÍß¼b| "ÇZ ö-ܨ¢7qš4JrP8«¹ 4Ynƒv±` [Vq‡’¥NÞ§s¶¯H€g¦;ÊÜ&K-ºNKd£tS¥Š·�£]ñ5Éx´õµd |�:²¸§h ) ¼¯dœøœ 呈 #·ØÅŒ4 [  õšËF‰kt(P¡Æž”ÝPq;gS} döŠcÌ"Ò¯[1¥ŸtYjœ ÀQàÖ–ÍøP5KÍ­ÑPCz‚tãéá ~ .Só‘{{›wPìgò)ã3¶ ÀýÿôþéÛ¾pÅHüÀNM'}Q›_©Òb.Äã¹´L‡Z4tãã›}ƒ³²1S:Ò\û4�ý®•[‹Òºµg1¦d*’¹5f1DrŠÇžr˜ß9 @Üz`gÈùËw¶ë ˜ñÁd@Å7Ê,?0³Íå'KÐÇsØj'L¤4€$«ÆuD} 7´ àæÈÐ5 ðcm`Ð(Éž’%yë,ÜIqkž¥>xè�úÊW¾š,Ñz�­€jŒ5{¤ONºÚžÖà�5çq(m0ÄNQ,¸�lôÄ��}¢3µÅ|Tr}  cJy[sW 9—Éç¨BºF âè�ND¤)‹ïr½Q.¢8?¥¶Yrƒo:·«íÂdäÎÜ"Ÿ×-¾Vì%&S0@³IâÓÅ“|ñrr,!ÑT�øD¥âTüå(bȺF¾ÊÙ uŽÒ�%3S’¬ÁÀP ?k*Ê›”Ápm)ÜçTÀÍ Ræ ¿· ·¢ÌëÌ‚ÍÍ{œ 8æ[ÇÖ@ŽÚît½  çXh~ÔZ¨6QµnÛ¾âú@Ú °U²âWÝUŸÂ $2G¥$" 3f�"#3Ó·m–NLÒ9MŠÄê¹¢ùÿ¬‘ì‘€Q$£Ž-Í'{W§Erp†ÒIH×´ˆŸÒÄ%¶:DS&Št×_€ÎE�ê5™vKsŒî._¾¢}7Ôȃ§36U9jD<ô ÚY4¸Caþ¬ L%øìØÞêt+ M0Ú¢±v4ê@ï|á:°ÙÈþ_ Ü›lm­yÄ€ÉìX‘ËRÚó€IrL43ï„¶`yÕíï³…QqK)^™èDæV)‰È yám‹Aƒ,Q˜‰¦’WÏ®±¿:É€.#ZÙî,®±Ð-gô5í¦+*‘Ÿ;OÙŽÕ!ú²w+->u‹¿Å¹V€ .ªn gŸ�`¨Y”bjø8“ïß|! Q!å#á®”ÂL õ:tˆï `ssÃü‚9ª'k˜YŸY:§Ïç¹¥ó¥DˆA§¿ô|8> F×hQ‰áùoÂ¥¿b4ó mæÌ_pψÎÈçwÆXD´×Ï;ƒE{ È`)ÅÂË�`•”ˆ0�[£2bïÈp‰óâ3˜3 @4õý8S†Wâ» œtÞÕu—›Áû’;°kºÂ·«Ut<ìq,ÞL*yˆò1­ÒìWyä1í±Ì–`™Oä~ çs¦cbDÓ±Æ;C½è(rXXvw܇ïôr+à-œ¢>»a¬½Ž­€ ·@[]*t`�(Ãóß|¶ðõ_”Þ$t\`Eôï(Kµ¼D ;.µÕËxËoÉÐ☭¬w†,ƒÄ½½YJJä/¸LdÞ¡ÕË—.]æYbÛ7±Öþí Šˆ“hw?ë+J¨Ëa”P{HTÊNTj ÄÑÛ·\áä…³[l¢/:¾/€ó8;Á—©CÍ|L&‹ñ6_d"¦=–ùr,ó‰�)PÄ.¯Ð:™³yÖ™˜Í޽š­Ú½[qöSX~øá‡}ðƒ\ˈ<·c©Ðƒ<øêûo+jamþ§—pŠÌÿ®©í¤¥„v{g‹9 `þŸNÌ”Yø®Ç•MeÄ4Þ[L½9Z2´8š„1d*Î`bBqÅ™ÖέQ{`@œ½yG4ÿ¤$¹qÌ�Ø•ÎÕd§/bñoû΀’’¥ËèÆä�Ðg"dÍ y9pðg±`œc<\²Ÿ?‘”A{›¥¤±’¥¨\Eº©Tþ|‘ù˜T†‚›¼”�˜öY¦KË ÓÒ4GÀ³‹cÎ'iKu»Êbµê®Pä  Øq[R†ÝpVQ’ Œòõ}¯»ï¨ÚS…KE±Ÿÿ4mtu8ÚUeAGÃÅÒo`Þ�~5,olšÈ(š%gž¶IÈ`šÏmˆi2ôGÓ"B‹£¡8[)�L««4ÿ ïTM&Æv$ Í‚³ƒýÎ.y}¤N�f蔳«WYÇœšÙYë`&çÄÆ J–v޾¾€[±U"Ê…ýá1nY>¦{`¨«ÒÒÊLd:‡8$nªÔ3f%OQ‰ÕJóÈ’/6Óbl¿ÊÙˆ±°ƒÀ äpÒáQµˆ”ôËîÐB@þŽZ»àÓn¾£GÁAsäj=±ËØXÃü?,Ø`yúÈÀYL1EƒÐñ‡{Ø,ê)€"†íK”Ιï𥦠›ÑÉ‹‚Ü-²&ƒa+—ã*bÚÇF¦Ü 3D'ë‰6+e•s—¾ˆ²J��x™,>W¯^IXˆ,ÂØ¸ÅÑ9mƒº�­)~™ÌÂIf÷�<“‘ë×6 ¿dQØõqÝáà ‹£AÚÍe•1 'ev]>tX[͸fýõkël›MAU7ûJ"F‡s‘uáê•«j‘«™CšÞ´ Ôœ/›i‹Ìm–ÚvŸ–¦3ª”VÆ)*[�­xù!ŸS·èL«,­blWÑNŸSƒÈ�­Ø‘ŽK€³÷3ö7À®§¶¬)€­-¾ ¥ehÒÅ/ƒ5µ™`x£ �»•Êš``â�Õ”#×YGZ N%³7¯-9›éIöçl>å4/“6*„ã h‹:Äjó î¦�•fM,J]%%`{gÇ”¥½¸f%ÚËŽ\5²˜AAÁÕI(Š8bñì-c[@N1³´FV»�ÎÔ> éœýV$P«´Déz¶îÜT©gÐ@~ˆÚœ°�+™‰§Ù�‰R©š†ªÒ9.,ÿA(3Œ#ëlpþƒ€“¥ Ï}t^8ú´ˆ¦Iì¸àžP¡ƒ Ø�v“HæIm¥j&¤º‰ÿü¥•Òš�çÑI'ÒY³µJDh‹+W×ó€X¢‰yeçé¯n†€�òÊúUµÒœÎ‰9Æ…äÿÎLϨãÇŽkù #wðÌs*'†NÔ$O™“ôÕ-ºŠçý“[IWèë|�Vwî`håŽ[+Z˜éÆ 9Ï_¸ n¿ýÖ×U\H“.ÑL±]ð$3åNçl_‘ïŒÜ™[S馸´DyŠ™™i5;;«6ȼï �9ÚÇzfÀV@œ €c¡á �>úhåïxoAƒá°�Œ¥€Ç©L�`½°±ÀÔ èèËrþúÜlSA™¸……U¥­.Å!ÿƧbR7Ŝܩ®n¢ÅÁJ©øBY)­ -ZWViè+;Z\ô¥©sëÿÒý†žà|ÓÀ—µhO ¤>¡69A–ƒ¾¾\}ûóTwLŸá'T[À|€‹dðB6AU9Á2¦Éá•‹ˆ)­Û,¹Á¯•[—ܺµ–gNÈÈ�¹E8úæ7Ý Ö8¡½ñÓN€R©Ûž‘¿Z«ªiZ8ËTf>÷åÏ!™N´#×0çéa20AÓ#I ·Ât„އ÷ã´~T½´ýv5A�2±Ã9ÚrDæÿfVËE^¦TLêÆÂBGçßøTLꦈ'�-”½So²à f£Z]=gÿú§ª4‹¡ëø«7®!î9sïx¶àD뮬ÄÙ{ö·øÙNˆ‘¬l"º“ËK—.©Mš‰¬%ÿùÏßDE>tõ|&jˆùÿô㟯w�V¦ºhò¹ò1p:“5%ç©¢˜NVÒÚxÚãóµKŤnì\r&I"Mue©›">Ü4ž`6ªstš\Ñ “Ì—S¨f-Ó|’2-;ö´ƒ.¶@ñì$p”»””£ä¶Ás~îÜ9=u¦•%Ë@¶Ø ¶Z6o'À…‹´ÐZ9CB²ˆfY®‹âL†R\ [é‰ÌQ)‰ˆ•ûÊ(¢1²�—©tŸ3¶È%0ævC´ãªæÎNpó>‡íëS?éfLѹµõkÚÍÔ$ÓM¾X]D1>me2ˆÞV)>†¨„ˆ­R‘†,Ö¨Mrˆv~pÃ)Û,eøô·>§r†së8Ltc§Tê¡û`=ï46ÏW•µ`˜!:�I\@V†öýV$ CªOIÝ Õ2=‘Å1çΙ-iÖÑ�#ÙoÚpÌ([tS1€cSú×p„�êôé3êÆnÈæè{ÝöiP å]¥/ÿ+äÏ °˜¡ÊÇbÐ�0ž˜à|þü%uäÆvq�äà•K×økÉ”¯YœÏ;Cœ1ó×^-¥dxÜ& ¤Ã‡ýœu"Á>)Ë…ËeUŸ?`Ûþi¬›s]˜1×�GŽ�ÀÑô"´`Ò} pYS^Z5;'J<ï™GÑž’!ìøv8 T;Ñ“5P@Ì&­VƪfS½²9L‹ß°]‰¥Ñ Ú¸-±£áõ×_g”­!bþx>;Lòs4!‹_2 CÏN¡$Ó“×û‰Å‚¡Ç:€‹4 pôH¤°DMe[<)‘Ïu‹©7GK†ǬÞ² ÷öR@„’0€…}œ)Ì-¼W î¾—ú}3-D00‚Ö\ímÒ�ž‰Â€± c§P ;5½‰ÆÊ¸. èä“_%E´®4`é ¹n¼Eö—ÉžÒ”%óngndæØ&�/Ln;ìYƒoã³–ýÿ&D±4rSY‚Äý¹†ïò8àì¹éèQUe¬vŽóýí] š¥ÀÀpúÌuùrëË?¦Ü¥­]ÜöJOhÚ€ÖÞ¤$u³×/\¸D£ô4•™4QŠW|cÉ'GSD ¦éÜ–»SoŽ– -ަ" ™2°cjaqQ]ºˆ—Å[; Ø×ÐnÈPpÅììŒÂvSF¸í‘G©½ï}ïsmÈ`6,$¢ZªF2¶ÙÅŠ€�DGí×i_jº²‘:ã·ÈO67”¶0óngnâ˜cs²&:…Äe‚Ì› (í²SEb�eþ�Ä5}õ`Zˆ+Ã9ô R4Ïü|åk_W×]wX-ØÌ˜ÉѰ4äÊg´¾¾® Pe\ÍBM‰Î¸tnKÁi"²RñOÄV@ãî”,"ˆú]ñò²É ¤^:ׇqy ÝX)a=¸3óÜæÚ ZÐùjb߯˜\a�˜{¹a™j–w3ù%a%áèÒ˜’|,xY¢“9× J‹Ð–h>e63N�Û¦czc HöØ �å�/óí�ÍÆ"¾"Ùœñ=9Å×EiAcaL•Ý’Çì_F=±URíg* q;¹êwñâeµ’ò¶h.8›ºq•àJç=Vªf_(+¥5ÁT‡fÁ¦ÄfœÏ±À/Äù¡Ù?Íš»Eü›Ø7–º¨·¶ò–{•/<“º)*9�a*&uS㓯ÊÞe6YÀ-3Ö¬¨3§[ST6)6i�(ªÒl;¡%ëHô6À¤¦n¡ÅB@œVÈR”ºù¡‡šùЇ>ÔÙ^E‹,ƒ=V €bœû ¹E‡•Ó¤8%‹"7À jÏN¡EÞ|{ÌÚ½lpe²d‘µÍÕÊm%Éä0ßšsÃ/ÿÿßÞ›Ir�çb'³öÞ§{ºgß`0€Àƒ•$DJ$¥+R" �”d9üd†q#|Ÿïƒ}!ÝGûáZ![–íP„­k),Éa…}©+$E�Xˆm;³÷LOoµWeúûNVVeUår²–s€žÊ×e "Zw Ý9°ße‚ wä{`öê¸Òvêyt=‡ž"þ¹ž;ŒÄ,FOî}[\d+µ$ƒm©õ-ÅmµàûÑ^e:Ž7@-holÎlãdȆ°œ&}ÿSrH1!¶ŽÚSǻ鈠TÁö¦¼g$þî äÍ÷;¦€_�Có«ê“çÓ’O–OEŸ¬—Ƚ H¶]µ‡AúVéîéÖín€>“•Y\yª&zî;rh¯XƒRàÄ)WàüwC±Ùž+*¶¿5Å:Ÿýl&-þ¦áúX%Õ`v¦BÌø^¯®®áý��W¥aYF½¤R“žçÐsè©êŸë)àz†å9ôóÏõè¡‹?ÝUó/بT„€üØF ¸îHÔðJáýËÂ¥v .ˆ#Ó]æ ஑@° <¹®U3Ù>Ð!¢Qùù€oÝÿj1†é¸co“­b8©+£ãzðio5;°ê8 î­óÊö2·o·{ì�÷œÚýa‰EjS‰vÈÞ­2�Ò¶�ânÆ“@Y7!qâ±ûA k¯¹ÄÞ1ã¢r¤I‘ØPï‚Û&8o>Ïv®×Ö×|½-ºóWÙ²qÇUXbþ¯˜%^Š€ì€¶ã•hÄÉöLÀsè©âŸë)àz†ä:Zì¥@IDAT9ôóÏõè:Ü �€ùü¥®üÎŒ"‚Ÿñ=èÕ“%ëòyRyWPÀ�Ä»¨Ä{Æga ¸Ö9æq<¿kܼH€iá¼E~¸øð à‰(lj(·|:íHW†O%Ÿ,¼ïí5ãÜV{«ÙÞ{Ç`ܦ~;î�â`Vîа³ù® Y ÁìÞ½ &D�üè¥ê·s´;ûÜÁÒ ~Œ—]C&ï�J§ÄlzV®(oܸ¸åÂ-‚0BâÞÁUOXà(i`Û ÜÚ2;ߕދ]§h$à9 ÈîjÁɈW: 'Û3/Ï¡§Š®§€sèM?ÿ,šà=+•Š Ì“]Í©fðÞ;ŽÛÂk$ñ=g?·aâ«’¬„E�€¯ª”õ2w �œ$‰VF ðáeT U@¶´ÐÙ—ç}ì¼~Þ5¥®Œðúž«ƒ’Jô6—Žqwœz†Ù<$SÇ=b•…Œb‡XPš�› $þAut¾?dÊ©\víÚuÿÈ%3–èpäöÞz*¢ù.• ½ä;øNº­„ýƨ팧ɀlO ïa£t@·ÙÞÇê%}*·gy&°Ã €­‚É uW½í�ÒL”$Oç�Üs™Ž¡h:zšpñõß‹/�`¨j?‰?WÒL(â™’âÿˆ2ª˜•Ê ûR�Ƥ/óî%ªsÕó>« ÍS!`°Ñ%*2»k]!•[—¶Š‘ £U‘bXà&2³1�´fª�‚Hƒ¸s;…[~© } ?ß�OïïÚb,Ì·5ÓY®í¢{ø2´jqÛˆü�ÑB«Û¶V²ÛÊ8'�’]dû´Ó^’[ª‰!£ÕÜÕ,Mk»¿3]ÅdFœ @pÝq×ĸ+$"Y;)¬N­¾ö¥i¨b@Íãöê=Ÿ•·HXÚâtö{@<ïs@‰Îžqî©äs•YÑ%*v ¢+# b{v#±BÀ¼« ͇)’Xw¥x¿ïAŸ‘1bjˆ¤é'¹ñ»•wx~aAqxž§Ö¯±ŽVŠD¼N ž^;zP=�ÑBÀ ²}Ð(Ùèriq�Oÿ¬~-qSuœ³1œA%E3þ·lDs-3rÿße2r |ž’ó /¢|U•˜šœ‚=t¨#OŸí€ö3O1¿CÕ7ª­®O¥ŽN}J´µàœtTê©„O%fu  =ã6‚Å´‘öËm�VaΔjQJ‚ªëüÂöø+ÕŠ”Þy«JfΛÑ8^ƒ¯‡�[+KµžÂ¨’>f5ZUhR¡ˆ#Á¡´æ8°� 1j7½{ÑqÚDiKT*äa ÐGRñÅá6O}>Cζ�›ëÿ ¢»ÆÐ]!ÀÇàô­ýïv#×õ¶àJßkÔ^vOB[�¾XRŒ°‹b˨/CsŠíÛÏ¢ÇÔ*ÑlPfµŸµJùõÔi@%OÇ%:†à©ÐqÅ=í,±âgÐY'(U«á¡Jƒêéüpj4� H50dmÌÊuß:'‡¦€Î»Ûù´ï®Û¸\¥UÙsÔ^<°UO •ÃF«ŠÍ)k¨g­Ú�E<¥y˜Â"…‘U\/ú4¤5‰ß�î’ŒVC�ù¸À›Ùù'ïžS©s® Ió�³õž³=¤ úàÛã÷GÁ0 Aì¹ë r» …1?¼™ÇÀ2ÞûD“Bï¹sì<­ 0Îs ð0á4Êòþwýµ.»ÅÜß®¢nõà*nÕ¶ßöG ‡q·µæÖ÷´êf…üÎuš×óZTT,—+R�3¤[ß‘»åù@pEý¦HÑ·¦1ûìú¬ºW#”£vxW00,ŽÞ‡˜L¤ !œÂsg!õzËUi¿7[UüÏ~d@üv®^Î�ZÈèÃÞ¤8×ÚºhUìeÌ­:ÎèÝÖÚºˆ:q+uüÖàUlccÇh4zÅÎ%(UWœ+ÿ?D+47âå€2:ß» \xoªÕr(žd¼Ü{öË-ŽŽG ðœw1<©¶ä)פ˜Èk;FOžbîq[oÿânµ®ßöyøtÔUC¥Œ§ÕFñ¥¥81ü;=­†ªºd§‰é)õ @ÉRò®�Œý€Š Ÿ0šÉý¾m!Iî!E” ©Þv©¨(þg%Ç P[õîItº³;sœ†Ï³³Žß¹Ó]H§ýwátÛÖE{£íg~£ Êk5ºº_ý Ü$¡ —é †«Î°T€bçÍ›7}„í]‡µ¡¯Cl¬¯‹[Øž‰JdÌ2TÀŒ(H Ž<ÂMøhh¯Þ›?‰ö6Zg� F�¾Õ–< ,îßSÌâ�µŸ¶zènUUÀé5pvv¶½ñgRàÑ» Ó=ˆÈ6mJ�c(#Yt옄 \û'*Rl˜ÍE@\‘SpkêWÔ´í¯TÊ`¸¸÷ý\�1S‰âH@Wa ÐÉ䲂R¨û¶³ÚÖ%L— )%S…"Ÿ£vSáh4bN °xpOaU–bÄ KàÀ¶§_ïãáýFvVŸ›�éÌ <¿[,ÆžP1tãJgé8"I @DÕËt‡©švíŠc»ï•ò}Íe¾W”†œIgÄÔÄ4œ+¥”ÊoU!ZpäñQÍS©ñåX�@•@;±‚GËvL0éFßÓ:$%ªõs“9o¢ÿ é®`Æ_ ÐŽŽ@¿ãL×ò} û”ûPÝ`0™©©ÉƸŠi8óþ×5_&¥¿ˆ‰ârÜÄ9¥ÁLíTâï�%®Ò•†®Ãþ,|l¨l&¤ï Ó%þÞžôñ p¶“ ´ìÀ�çå’#»î5©ˆAQÜÏö($>«d.ý¾M½�¿£%¥o†ß·¥ãsƒÁp+f÷n5�€ROÇh”Ϲ Z—ÁÞH1�lè�)Íbã¿À0ÀÝnàÚî–+¦–>Ú®tŸ * [.ª„§D9ǰ{,Þ¾ñRp ~_å[¥Ô ,Ý,™Ë)¿`Í:Ûu@IÅH ›Ø‡ä0"Ñl)HͪÔ4¦¸Z§á!@Œk> sµÉ?'8Lø½ä™çl&zËox3‰ß2£”Ö Ý"óîÏ[oaoµ:°nœ.aàúõàø îì)Q¥®‡Š6·Nç/#k2àOTr¢NŠõõ�¨¢¼ž“úcB\R)<ªeÆŸP0LCÀp¬Üs KýJç Ä"¬^£.÷“UÒ®y:êxÑÂ*ú| Êw^‹Ñª¶—N%Ã1ììk;Ϲ¢Ìƒøs $Q‰«Ì°�¤éÑFŽjK_ï bt¨ ÉwÙ?µî/4?ÀT þmmOn EIkj��ue( x�ŒÄbS@XÈìJ£ì.$õ²°º�Jdò§aqpM�1a[†aq`¬€±ÞÀ ç›pœ73,Q@@~XäÇ…µ|þê¸C³7l| ^â3œàZoˆbÿ‘ïwd9<ÅÞšSá*L=hQ³Ú¶”AnÝºÕø¦FÏ”ÑËÂtÈü˜F�z[¦8v�ã {@Ïšª¨µõ5ØÙWabW EÀ|qÓãâã¶F?ºxPÉóY(ÑÑ–§‚ç uT“ôÏïÇØ(6 -‚æÓÑF’ nŒukLîɨ»×ò b““¥b@ls¿æ`V£ö2zjwTè~M; xª¶vWl»Ü¹’(WJ"SÍîx&€û†ù‚–„ùhaä*™uL¾í”e’03 Ü«m+­Oâ"À{FŒƒ•jU€õ†"àÆæºTÞìG -h<ƒÊ§€ÍMlU)Z/mëHq4­G¾¯ Sp»;�û ÎÞÆé °Ÿg €ŠÞtü&­y¢:5ÆßÀX3`ŸˆºÇn@úˆzö]À¨6U®aO®šæç)N‹�·µî×>NmoK�ݪ´¼‘_“аߩZÖTÛćŸ.aiׯB4Š…bàÊÓ‹W R…$̵ïÞ+ú¸W¸åÏ{¶úgÛ|§(PaÀÖ‰åi_O³Pê%pïvF²%³S,¥–¼SØKÜöb¶ ¶Žz™U[£ ›ž>Ð1¥Ÿ}ö™'Óÿ°Ç\¸Jï¡_ ü.3©ÌUZLMÁ$}*D¦c?üáSú§ÿ;²‰�]`§<íÃAIÁ°å€&Aá�P�a€#ʨNDU€Äi×®Y¼NËí¯dPo(¬V0¨�F~w#AQüú“þ‰øèÛmRÜvìèq¬BÕ0šå{:ë¼D®ÿò•Ë0OºÖìJ…ø³p¡èD£kVì8 . ™Hi†S5jO0pTç:ªéÓø&†HÛiQƒ'@>ga«·¶Ö&¢¾ÝY]ñÿsDï;ï2Cí>zá‚Ø¿êMÞ4¨­í•oo´ýÌÛ¹ÿ1ƒ©0–eCñàë¤×$M@ÝÂÚ s>5©Ì$×Ö–�¡½ÂÚåkãÍ2 Cèý¡é·$#ò„ó½ #˜A}ÑUÝ3P†ë§€QVË ™ Ûi{Ï™á›�(Î"Öö¯ý7ÄéÓ÷ˆŸüø%]�QÜÞzûMß–ãeFL†�ué8mëN•è{+9÷ËŠ:gÁì¸Lš·ž>,Ó î\鯮®ùJd(rVa;ªá·–ΤŹûgï;+M–{í±õ·ŽÚÚ {qÚ FŸ,-í‰.Ô(‘‡ƒÉéÞi ¨8öYĸ|Ymhu‘ €fÔàÚq¥"M¹DÑ_T >`ŠÏW$;vøªÄ @íi0£Pf$ØyH—�Ÿ‘Ç‹ï}ÿâµ×_o½ñ†ïGº}>½œ È“í9쥃®:y¹Е-rØSœs5‹;Áè.®s€@.}�9CÐ…sgRu®ÕYo§žs;ãÔ©S⡇–žJCÇ©ðüµÞ‹ÖQh›!�î‚;�`ssSìÙ£Î0t+ŽIgE@SaÙ9–Q:[ ÀsôÜ>„c µòáËÅ¿‰ ìÿG,ßÿÑý¿4|8*]ªª¸2¿À€nƒ_½Æ#Z  Ñ˜O¯#A—›�_xL;zT¼ôâKRÑ.f³=oÌ?�£Ú®R�ÁƒÚ¯ó™ájB§­G —É`ß>-Êx—¼‰¡˜Ç%탘ÿ1ˆû;�q~¾¯¶|ð}¯ÄƒD± ç= ~ÛöIJ "`p[Q 7@¿ú~ßΙᇱVkSÀ±eåÄ a†kísõÏ$ñuÔ3?Hñ¿êêŸ}˜`ðëÒ˜Mp�®÷)tþ¡ƒ;Ù³´W<ûìsâ HÞ|ó—ÊLO×à¶9ƒG¾3ÑÜQºÿí¼ Ï·†ïf o*ÀËߨ'n']}ðСÀ©¿uÁWüó}µ›Mø^õkÆ7�J€d’ƒÌ6½•dL€f¿Þ+jÇü>Šž8§§§•Ç%l㵌f©±eD¢~7/ô®8>àô>ÁÃKÒu¯t"´AÅ‹E(”©¦yºŽ�¢fÓÝ �ÆÀÿbw#Èéüd$’ qþ‘óâø‰câÇ/½$nܸá[o'gÒL©s^¯‰¹é´}Pc¿ó¾ä! Á‰’èmߨƒ{æ÷èK_ú’8s&hŸ¿s¶>m)ñÖ µÃ¯zÛñë6…ùÌíšw|¶j¼uyL—Üê½uÖÆ÷™Þ÷îK]9”e A¢PdÒ[‘íȈÊ~4,l+M S[~â#y±ñ5U£Êxˇ«*² †n}Ìü^³VO¾W}3[uÂ�T^§öükØÒ›áïüÎïŠ÷Þ{O¼üòÏB5¹Û[Üþ3騗Îdƒ)äGG§íA€ž9;ñ¯ÃO·Øø�•Ä÷ûø‰â£ S Èÿíâ<›Oap‘8š5:ò§—;+uKE@€¡ÑkÓëúeél?ꜯ¡ÃøE{·§RõÔä¤ ÄÁï|ç;÷w§¾j‹ìº>¶< ‘ €®v½T* z‚7Ëñ8˜;GQ‰qZ)|�¾W}3ÙbøÞuµ+£5ªè£Ö Μ¹O9rTüä'/‰Ïl„£Û~‰ü&}t÷S*A—ùŠ’ÇîtN_Ðç‚ß}¡Âæ¨0ûöî�=þ¶úvõ…Eë SmÆ¿Fó1÷¿ìÓx³†Ï5!öÀðWï¿ï{­3“ŒöìÜlg¶ò¹tCÐ��qÛn Û,·nßViÛHO¥O¢à[*…G­ÌØ2¸‘ €Ô€Ðˆ‹°ç��¸ô7= »K§2ª©Û @µfT¹°ûÈ:‹‡º®"ƒfsßøÆ·Ä'Ÿ|,~úÓŸ(‡EŽšÑ°®o@ÐOšD¦�­©>ì—‡5æqo—{gk¦ë “Þçæ!zÞɉ+ЇyDœ:ÙÒ1ëžI3ðãˆb4øš¶á_Ã�K,S@XÌõÁØ”)zâž›Qg4u©¨€Àg`'^°Áø?›ÍÑ2th–Á†%' `Dca t\£:•ı‘XÆKî«çYã·²â5¥T:—®«žŒ'NŠƒ‰_üüeñî{ï*õ¶… tU€!c˧  °“]ÉnfÃ쓌×Êí;–ÛrÝ=Ñ©ÌNMqŸ;÷€xðÁáà¨]‡Äójô>ü†Ú{Àã¯Ý>˜ w.q€M©ÀéÖŒ7T–f (Ÿ¥‹oC31 „ŸÛ˜c)øÞ÷¾Ã~±×÷Î72¹ïÆ�¾¡£…AŠÿÙ½ªàÂîNa3r¯µ^ æQóÀ-£úë|Fš“æ�jýèr”¾|ù+_G�?þñ‹Ò�Pt-µ·o¯Àò€…Þ“ë‚6¨î;.£Ÿ‰‰œdÖè �~u<®'À� Æi¸õëe�~nÞ¼åwI)� ƒ~V¢~�ð{söü�<ú("�0õ‘ïWd�f×y1fõ›]·ìÙ«nÛ߯%@œï4--T¶øZbÕJ#Rn,+i‡`è;@�[2ËtäÂß°4Èô'@%%•´°«@¥eÕ20MxšªõÝr­ÏˆqâØ‘�Îiq÷¢xüÉ'±¾ÞnÇûÕýštj­»bGÉÈÍòŠ=6Ëwì†3¥aAœÜ:NÀ#÷,þ/@£õqœÅ1„m�–Ä¿ÛW{6Ç#, ÊÀ0Ê\p�1â(.ìö*nžý÷ÜúŒÈ£Öi[ÓÔÉ BÔ ì‹¾ðÂ?!TïrÛõ¸'ÜByèKç°\ïýêáXdá•Îl~uÔ?¬qç¢Ë·#�Lš�à9~(…é7ÍÏω{NŸ“±·Ü‚{žD ®øO�R¡ÝsxM‚;t¯tTì¹åîŠnžßðB”|,.î×®]÷Ôñ?Ü„) o%ëô’h©%½µ7ßÇàV’‰””Ää}|ùÔR¹�>Õv~ÖXJ@וy{(— 6€êþ?Ç6ß嘹ãŸvïÞ-½ IÀ«¯¾Ò·!%Ùµk—øðãOÄ•+­@@ý"I{béB:üØo7º~ �KgL�΀ŠûfO@JtÏÉb÷™m®xï¿ÿ~Øô?$j�Gƒè©ö�‹“ÙÝ@oä­ùTì¥õ%¸øUa(%(Wˈ"J³ìÞ�5Œ¬Lÿ$ >¤È,þáþᮿüË¿ìö=ÙÓÎ.0– ?{+ p˜Ý­Ó µK}Þ…¨.|¯—b9Úî-ß)lI&ű=ô°8~ü„xñ¥ÄÕ+Wúê—{ó÷Ý{Z,A¯‚ÛJN@Bzä*%™€òh÷1¤–¾4hPàå»W Àçáø±cØzÚßóŠÓo.Œ…ñä“O Š˜Ýk'=äC£ö¨5P+ìÑç׿��áù´ádí‰ÛhÒ,;°µð t±Þ©\éWÃÀ÷envÒÁ›~—»ò�’z¿èº0âãÊDK èÃÄ@@¾¬®¼êüG»ÔSÍ÷0ޝòyìŸÝí‰Ñ¿óíßï¾{Q¼ü³Ÿîûªâ´ ÀSOî}ü™øüÒ± ‡Û;jŸÉ-¬‡Š=RYS%‘q;°¯8y⸠²ß ¥VO<ñ¤Ø »þîÔ ÙôkÅŸ(w—DNH—j­ Š‘biO„îƒgy˜Ë^“t¥X9Ž%€mÊȲšPÄv»‹E3ØoNâ/q]XbÀ¸«‹°ö®T5-ôäXµõÑ*w 9zL:úô“Oú<‰÷éSÇÅžÅñö{¿’nH{iÐÏ7@/íè:ý"€;¡ °€ÀZ÷bŸ�~h¦{þü#âÞ{ïU–$„àЋ!T=„ƒ’H°ËÐá6Æ´g�Sä?à 0ý$'(�Z q|Œ«%@8õSÃqG•‡o<ÿý玆 JŠoAøsY¸ �x§l©UÖZ¼kÅ’@1W¿:µ ‚Ö7á@èóÏ?/½ô¢tÂÓºÿˆ^Çž|ìH.C"ð)â=¨› Æ)dºFÂLÙ÷ùOcÅ¿gÏbœfCËòý¤WK*ùQ!Nòÿä4H©ÿEßæ#‰o`��øöÜÊTa&öÆ�0,pÔ7¹Õ{÷QÝêÚÕ]ÊÉ™ŽÉs\}ŒðüóÏÃðÔš ºȩ́’ˆ²ð»õ* Ò ûV•ÌÍí’¡ŠYG§vèJøûß߇˜p „­�~¥ÇŽ‹Øxçâ{bŽ}TþÑ­¨NÛˆ€aK%Q? €³ÏD9|h ïÒá#G°Ïÿ”èŠ+HpUð‰úu·á_Ã3ÿ] yjt]“¡¢;™žž‘>2T¾}›ù�Èo²ÿ �Ü8&ÛÓSÓò¹Paæ1˱´;ÀNØÇñMMn`ÞÀ– 'µ�ÈüAîÿó#¥êx|L=ðð0�Έ¯À�]©¾øâ Ü«}µN±ðc΋˰xÿƒ�"í–y/ËðEïh,w?7} FWVB€¨—Êauˆÿ™¿ûü4ëË rŸa·$üûöï÷_àcüAò½â›éßewnà º‹6rükxá_ «=O�®kÔøìÓϺò;3�·“RÚ^’4D}2öQ)a:–ëQE¹×1– @4JÑÐ쨦ÈÙØ€ 4ù°ù?pMïR¼Üç#^ù­TüÜ~'@~£Úyyü?ÿü÷¥Å@¯ wV¬èà~ñ姃~‘ø% ùcô2ZÈm"¹ÊO þÛ* ø>A;»í- À“O\ÜßÀˆ?ýIpÅÿìs¿'‰¿ûùþ8(‰ìúÏ!ŸÅî¿Ö犟¬Î¿®ï“ïXT3ƒÇô!ìšGsfBìÝ꘵9(~¥i^üî›Ã¢)`>íÑ�RÅ4 ³j“Plp'? €Š¬ ø™ÎàÅ–!w"ŽH)¤yIÕ0 ÷æ8jãy�/òc�=Íî“Ò�Ðr¿„àžøá‡ׯßï¼û«PÏ�U0ÕZAšÑ#`ä5ž·`Ëfňï|'i3ÎwØM9¼Ë§O‡†¿ºÂ™[7藫ȳ÷Ý/½p~ûü­4�šA­ö“O*œº®ve¸uÃé{Õ7Óm/ê×qbä77@˜§¢ ¼NSÀTˆr·;2XsssâêÕ«�my/F�R€h�FÞJ;üxìà}, óTCiGJ$«\c ^c)¡ã�â!°ó¡ï>ûœ ¡×^{D¢¯�ŽÒ{÷. jŽ¿ÿÁÇâÒá~¨}G¹£+}Ú#TÆ;×½'�Qûªvà UO>õ´˜}[¶yÔýì’ÔP±#öŽGÇC€Œ×‰Çä>¿ŠŸwÕÖ<(žzâ)¡]n«"ÖK¹#±wï^¡Â↮�ÝouÜ^¹m+ƒá¹ñ¦n�¯O°0œœ««JfÄ'a �ø«¿ú«þ”‹¼ƒÚæã±b Ë8¥…íH`˜›p"¹…Üú•n=¾!/©J´ø_ИÅí@èÄñ£PV‚4à­wÅ­Û+1G£‹G!ÀUþ>à{ß™{ºµ÷q�ýÌbß÷4ûOÀjD§­C€Ûï½÷^d‡Ü&(äóRbY8 -ELi•P ‘�À36‹íBE “HT¡êgá­ŽÎÕ±bLÃ>N³í°Ô´�â¾pòî¬ìÂË„õå½F³%%1*i@/rƒ=v�FŒöè@h5Z$6º'~â±óÒJàâ{ˆZUÝiX»wû5îóßåËùyu-í(Ìxïzè!ñÀ*EŒ‹jO_�‡Õ´±±)2UËw–£EŽû­ï¼æ='“ISÀÏ/}îÍ<®Rð³À#va¬aGKh@)÷í—�ƒÜPŒ;-Ÿyhè¯ÑÞ}ûÄó¿÷=ñË7^¯¿þZ¬8�ƒãGäÈჰXo½óž¸zýFg}®ˆ€Üç?}B>t RIW±IÙΩS§Äã�?)]ÒªÖÓå‹@K€ øèGü*=¸*®ÝfÁ¨&Ã6©ðTËïôrcÅ`õ<ê¡I5\ƒÒqHØó!µUÑ`X™87WŶÛÞ¼{¨‡ˆ÷ÿ¨ù}ìèqHþI,/«ÅRÁ¥}äKâú�›â�·Þ“^ƒÊêüv{þÃâDid¬ŽA¥ý`ŸÿiíXkP€öÑN @¾OKÛÂ6}”8¸1SMhr¬L÷¦©"8Ìr؈jžb¡$⇧|={µjÇÑ$mÕ >*äÕ¢²-ÆqWèuñ»ß}V\¼øŽøùÏ_ŽŒ5F?ûú3óâ"B 3Ò ŠéST›ã|�zçΞ“PÖT¢"í… �Á;à=ƒjR·Ó'»á¨‹:X*[¡ë2,pï˯8ßï¹u +K€ñb„8öŒr•a"°³Š¹�mƒƒìýùëF¡¨Îh€.ø†žAs°sçTd¨áË—¿è«OF¢ûÒgÅÁûÄ/ß¼(6äD§v¸Ïîþ3b7¼-*Qzð¥/=„¿‡!I kf�v |ÇÈ\»v-rHôÐÏ÷—Û·6¾ß*ŸpJîÒ0S¬ °”B+ ÝNBaö;µÌ3ì{&l|®Y‰˜GПkt=n~<'@z ì>óC¼þöo[|ík_—z"ýöµÛ9_û꓈ZxL>lŽoÉ»û_nÑð?ó•'JüOŸ>-þàþHnëhâßï“;œúªÛ”T`÷;ë-O‡@*‰ Ãéie‡@GAkÒ*íŽB™±‘˜fýh”Ñž>°ÁBË7 2 0;((Æ “ÂøÙ:m/®¡—_þ)¼ ~Ð×`ø�¹žë( xý�w`y°ÖW{£Z™+ÀcG‹ûî=Qðà>= 4C¿ý{ch™�*†£>î8Š€ë°X”ßêÞfM+®dÒñûÖicܾu;¬˜{-a%-n5¿ïfŒòïàÞÂmFÁ¶�ÃQò®<¨±�Ã^#ÃJ 2©ZÌïš�Û Ç¥Û F€„¾öµ_—öâ/ýøEÁXåý$м «Þ?úúôeyÐÏ8¶£.íù8wŸ˜šœX÷tàÂ(�zŸ`�½!U ²�m€ÅE v{Kq¾áŒ¡šL[zÔ €*`[Qû=G¢úq½ëQ ‚þCq ò„p!ª»æu*�Ñ»•Jš‡BšN; é@hß~ñÊ/~�€@o÷¥ÔGÆó44Ý÷#�Íëo¼ Ë¥UÇÎ$Æh¦§§Ä÷ïÝÓû‡¼³;îóS_ãá‡Ï÷ì-®³M}¾5Ä‘Òl°Ÿo°=Õú»b˜‚4Œ�%ÀIÄ‘¨›-CÀQP�?Â,D¤+ÉÆ €ª&¸¶ØšQÜ^È<>õôÓ�(ƒÿ$ ì†+á/Ã�ð¥ËW¤'ÁJ¥Xv/¤!m;sïié¾7JÚg~ôÞ÷ÄOˆ©)å=Û8Íë²CF`O¬°ÀPŒ놌ױ)à¹Dï�ª zäšPk«Ê�øGÆït•€à‘Ù’ø_�{%Žnè´s ¡ç~ïûâ 8úå/_qÄŒ�³"a?ð×it`”Î xÏTù&r  Ÿ'F•ù>ª,Ôàfl,ÆF€%t €¦$”± 8JC8ŽèHåUÝÿg[»õ€ ¤ÛZ†Ï?FB/ˆ›7ûóü—…Ò“p'L‚¯ÿòmQ,ªmm+>�/-í=p¿˜™™ò¹Ú[Ö$%�œTœ9sŸü@÷ÖŠ®µ“ ÒægŸ}9$FäV,¿Ý½$Ö•õ�èúŒVÈ…¡¢¹öØ0ÑÈô‚üöÔ ešûÿðlüÇM‘¤°O EOÎy¿ÇªQ ›Þ £‘Eîw÷»reêJ—úù~ˆG¿ñëψcÇ Ìâj��ÂßöùŸzòQñÕ§ñ§åĹû?øÁˆûî;«‰?Ö«KGY*‰+xÇzª÷ïpË!Px¦ S@(é*¦ý0—«Øé0Š�…@Þ S„ÊÎÝtFÁ¬„�{™TÄ]n: ‹ÄhüruB…´£G�Š_ìß�PªG‚C"„ª}õ—oŠõ�þ,†‰b:�gî9%NÃÇA¯«4¿ñ9rLÕ—ÁL(�ýšÑy;�¥={•G¶±±.È`öšT·(þß—À7®+mÑ›8‰¿7z×N©7 ì2�˜V8¤TÄ=FXQX@‹#,1–tº']Mç j^åC¸KÝ¥«#�±mÐw}üñGâÇ?þ1¬>Š}�eqq^|ãë_ïýêcñÞûî(“A©»pø€xž³�*ÑK÷ù÷ÁâB§ñE –/€Íu±ÏèýyˆÐ-Ž) mÚTÔ ÀNxL >¤°4x4N@£áÝ-œ  1@€±­UÒ,V<®¤B¥¼.³ó –: ØË/ÿó€�†¡=⯾%Vî¬nû„÷pŸÿÁ³pœ2¸Õ9#ž?ÿˆ¸ÿþsZÔ¿íwxøØÃ`s}³¯o±ãÑ5ü{ïÎxn.Tˆì“¿Ø  =€±�À1Ãú}K®@6b €Z ªb£°þ¼×Tu´øß‹ÚèSËY::yJüq¨ÌÔOš›�¿ñµ§Å}&à z‹JelSS“âKçΈC{_�uöÃmƒ³÷Ý/½pA¸ïgg}>~ìÝ»W2z*÷Üè'Åù–Ïâ=SM 7ca 8 €0!3ìÇ]uÃgsP bHüˆ~˨>!�rårÑåÔ¼ j'@1ÁÝáÅ�>"ö}ÿ╟Æ�Ð=§Ž‰û—Ä/^{SܸqkKfO§;÷ž>!î;sJ$zÔÆöèÁƒ‡¤ûÞy½ååÏXç‘ÙcÞU‰Öúú‚á{Îm«¨433-uYT˜-ˆBs ¯ƒ“<Öo~ >¡©Q�Í |nëÀÅÿjûÿŽöà{SF:“ñ'è@èøÉ“â¥á@èÎ�¾æ3÷·¿öå'Äg—.ÃdðQ®Túj/¨2ß™£G"¢á}0�¢åÌ`=®=þäÓâÈaìÚét×"@= ]š:PdD{JüÖóO�`“�Ø)ø°ÇC0.f€GÂrœ|¸‚¡‡²°Ä=£A¦8>æ9N§ñD`ÄžÏ?ÿ}點&ný$çcG‰ßúÖ¯‰Ã‡'’wÇ´€ð¼¿ñµ/‹'.<<0âŸÉd¥‚ßóßû�&þ.Ðwñ¯ª· úu¤ºeFSÀYeÝc7¬ÏFþƒÝ#[µÃž\N€B$<®b]†+™­¶PT$¢ ó ; X=œA"À=ï‡zðL_7út Ä•ùÓO<*®¹.^yí-81éÏò€2>pFzò#“1ˆÄ9ß{ïñè£cpè¤ {à H5Q`×|ïÖQªV]|橇uåÊ¥¡!-_V*¼C �<ðÃþ!þD¨P7 0£>ktí:H €¢¢ ŸÍìзdÀÃ’„¾û¬xï½wÅÏ~öÏ‚±ÏûIXhiq ‚ïCQðS%w¦Þþ(‘`ˆÞûî=)ú•NxÛ•ûüO>¥�[yAÑÇ�x¾(òË ¬c |Ý{eWŒ¨€–£¨/€[}¼¾~ó„û¡2U7 ð£V6* qæ¸C ·â ;ÚeùÒÃÝ¡C‡ÅK°øâ‹K}MˆR®ó�“ZúT”ÊS-r ‡î“f}“°\Tb`• Ðì§I¤N?T·XWåYöëÃÍ“¾Ü“ˆßÙ]1,Æ *àÈKêõÔáÈ51Ê*ˆ ͨú`„BÆ7×éîB`zzZü‹ñÛs DIÀo~ãñî{Š‹ï}hÒº°kátωÅê�¤Ólq<$xàÁ�Jî®'âî˜íî…ÝR±Oe~}c­/Pâ|Óç¬H5��y_#ÏÀ#Ó‘(±¾ Œ»J L”$÷ŠP.JJ ú€°\~SÍ ÐüÊët÷"ÀÕòþýûå–À|ÐTv=wö8Ú'~þêbe¥ey�ƒìÜÙ{Å ÄÔsÎvN�:…0½ObŸp’„¾@Еw4Üj¢çÇë ®w×××ûzV%-p•ç�=96Å(ŸšØö§Ì¶ø% ozªaB2Mä°'~tX;î5>DåJÙ= ý]˜y…ÒÐùé‹Ñ�x:„NÃ�Ð };Ú57#¾ùõ/‹>üT¼i‰þÙûN7߇èE—Øà€xꉧłŽb –.цÀÒž=J @µR¥r &Üáßï¶Æ;N¸¸K¡;Ų•V§§¦Åêš’×ÍS /,hd6’iä%Pê?†>m; ÜT¦LÄÃ2Qü¯úlP1L'� �üþïÿ¾øùÏ&.^¼¨ü ù¡GøžÓÇÅi8âñ ãº?ñÄâB"ë¤è�½ðð–xS©êúÚ†È.õÎpq—€™_T"0çTŠ ÀÔ~ð* _�jw§^yÀ¶M˜³îþ?oÅŸaß@2a×ãÞDUÀlW»Ž‹îx—§RßÓOEœ8yz „Eü9®‡>¯÷ùÇûñÛ’Ùí…@5m (О=‹ªÅ»ÊÙ�¨~ÛçarøÙgŸvµá—Q¯gèX3~àlIža‡ºs÷ÿ©dgá~�¬:‰Á­’òù^õÀ–<.£Ö‰ë@èÍ7߯¾úŠêÞäP¦yúôiñøãOÁ[š¶ç ÀwY£KKKÊ3®ISÙÞ¿ÍŽƒ7µúqL âÀ‹ÊÙaÃ)âlçp°ªá=Ô™ï=ÏÀ “ŠW Ð[¿ŸãBQ�Ð:ý =Þu]BÏ=÷váqqúô=QEõu�@lLU™UÛ«ŒƒgÏ�{@îõ»ïOœúº¬F@�ZŒ¸+”‚õ³=Ëo» ôÖP_.7)cÆT`} �Fšé-ÛFàˆÔ ÑÌ‚ÉÞ “¡sàí¯RNT7oúx|˜˜œßúæoН}ý×]\*�>uZüþþP-ÒÄP¨êvü(#ÒŸjJ&ú_«ªnñ&á€V.Šé‚E›(6¶ÕÅúGu«Gìéû/G<§]‡ü€1ÂS®NÃDH–=X ÙgŒ=®õUx» ��œ†þG#І 6ƒ ýüç/Ë­�¶‹1N—Ä“O=-¨t¨“F`+(Ĉ“ÂoyØ÷[e¼ÜP +ŒŽ|èÖ­[*ͦ­”Å/÷'*…wZ™Ñf0ì¡ð®`r«¤ú€-x£³ˆ-­šn¯¬¨Õå4mÐÂå+_ùª8 ›ü—epsS!žy£J9ÿ¨8sæ>|`·ýÕ6@}¢ðAàÎ�–‡JŸËmY|ÆûÙ`c–…}ÅÄÀlâ?•”¨ šj@¬A–�[_XßÔ6[´tšŒp<äíïòåËÞS}¬ˆ�ÀáC‡Ä~ðûâµ×^o¼ñËPB2à™³âÂc� /£»S]A#Ð#+·Õ=ˆ“ÇÓëܬzøa˰©ðÿööV9¬Rš A€ 08¹6ºN¦!ìà'àOFŒ ¸T'*Ú¤lð=W>ùäcÏ™>Ôô†Eœ�=ö¸xî¹ß“¾ÖýZ9räˆdžzúiMüýÒy[‚Àå+ê‹Z¤D}o£®×-˜*~ãç,K5a!zšDIµüN*7[�àZ�„ÀÞtˆ7Djˆ†5ÖÃ]K§Òb ~¥76Ö#k_¹rE¬ÞY…Jõ/²Q]à®E`÷îEñ쳿'¼„|å)îóïÛ×â¢'¾3 ÕÕ_|¡4qã·´ßç?;«¬Èaq €‹½&5Âü‚EÓ,¼CÒŽf¼€vàeœ={NþE¨+)w +÷�:Zðœro(NÈHOÕÈC:øQaø¼ü╟‹o|㛑mê\BGŽ·–o"bßie‰”JûºŒF W.]º$T•à›‰ßX~ç“Âðx(‹mãR©^й*oA—vítF`Gn<&/ 8æyóïÔ©C\Æ„š_¤¡9Êe&%÷ÿ›-£Â�÷P_m½ôâKCcD$úŸ»�y|@ȩ́û•º+áГÞ¼õÖ[Ê£��ÒôMV]èˆ27«îðè·¿ýmÚ⺣lÎM²üâí( ,/¨î1üM„îÿSÉÉÄS.›Á� þ�EðÕþ®Õ#³Nߣî-r ƒþçÿ“Þ ˆDUÐhF J[ÿîoÿVyØK~å]Ì)W (X«©{|�0)` ú$H§Ú—)hŽÈ‡Þ5¯mõÁ¶2‚É3i¬á犟„ÿlÉxfqÑ\Ä44!Û7B·¼ •9ØMПܹTO5ÿÄñ“j¨@¼ýöÛâ/ÿò/Cí¸=˜éC�€F@#0üèGÿA|þùçÊcå·Sõ;«RŽú^*åXf.†€Qóða(§— É�n5°îB¶5m´/S+g{޶�àì;¦ìž7ˆ?<¿apÅO`gffæÊÊJ¢Tšæ¸C%n ö‘‰ð¨ªÒ1^åÓ4\Ÿ¹÷Œry|é¥ÅŸýÙŸ‰JELjœ.¬ÐìHÞzû-ñ7ó7Êc£×±£G•Ë«ä·aßTŠŠYõxt3xúæÍ› .P%#ºÅ­ê†4€ý5èšümöïC›×¶â`[ŸI&$NŠ¢~J`óù¼¹¹™Mr¹D>µš´ #T½Þ5L`ɵpºhÿ—âÿa3ìñÜÄe½òÊ/Ä¿ù7ÿ�øècí$¨ý®é3�€F`T Òß‹/¼(þ‡÷ïD=Fе'O .ž�T¿÷IX‘MB‡K-™'HŸ¸@uÒ/i›sé�lÚ‡ªu9€R[îÀg².gÄéH®‰«~þr¹lP³^Ÿ7ÊSE³fæÍÚfÕœª×gOi € €0† €1¦à뺒ƒ3‹ü’xõÕWbµxíÚ5ñoÿä�ÅC?,~ó[ßG�‹U_Öh4Û�¾ßâ(üýèG?}ôQ¬!ð›ü¥Ê·™ @B!º %ó ”ÏoFŽÃÝ7·/5{ûrn=“ÉYÕê i˜u0“±_X^¶ ðÑÝÙNSAº¸>¶œè@ �øSÜ' f­6gT§6ÌÊf)AŸË-š•d)°öò!ñKÔM&>rY¿"Í<é¸y6܃sçΉO¿ýöm¥(SÍÁ�ƒ~ÿÂßûù‡Ï‹ûï?+:Œ9ÔB³ }0|øaQt:¶SŒ*5¨1z>+ÕC®¶‘i1_º¬œo¶(äû;,òÊý†TuFÖ¯å€Û) ÐýáQ©€vÑP±PšÇIUl[n¬oˆ[øÎÅYñ{û Óªé™oÖÀŽ¥Õ—C"ÛÜ Ä«W¯*ÍcqnêäµÛ·ß´Ò5#µ9�¥å·pm讉e0[Lu‰W“û·ÿ— žÃ¾=ûÄ\¼&ÁRŽÄnäÆûMÚ�«ƒÀ•‹J¶B‡<(B9(Â:X}Q# PB€JÛ½ T¶—Bq¬¾>,ÞÿÕûðæa3™0O/$ç.¦eëf._¯grFfsSF™ƒ4@œ¼|Ùþèüy«! èbH·Z °e 'ç¹YíÇØów÷û´Á½®úk…й˜NCé/iVS…D½T3Sµdb"“TÚÿgr ÀÛ›g<$1jZG�ŸÎÏ/€ xFüã?þ¨o-ÿj¥*.}qIþ x˜º9�€F@#°-0˜U6BrÛÏÀÈð·Q£�Ʀ¦Tu¸•œ8U2 Éz)S_HÕ�åB¡ne³†(M£‡ §p–¸|Ùì`Ø;)”\‰’Nn%¼lä°”8)Ÿ¦˜'÷ü]âï*úU§Jf¥TJ€›J–“ÉD9QLVÊfrÂ0R&6ŒD2bÿ¿¥8’‹x�†Øg®mYG�. �ÃmëLŸh4�Aà^XK�:Ÿ:CLdTÓdŒ0Ä Ý'+¦™$½"ÝZL/$HÇŠÙ�µ”lƒ �‹]ÇB@þ.Ú@/U‡«Ü–0#r¿‡øì•å2Äþ¹|"ÐÓó îõÀd²b&Aý“U3—¬™¸€½m¶�zµþ)NbÈk+%IDAT K½îQ…µ©ríܹÀ<¦RT—Ñh4c�À¾ýûÅO<1ôy’ ^•JšÈM(b[†a›4 I¯H·Jf>)éèµ^&€ŠîpÔ¢…Ž@eH-3ô-€nÆ�0æ~^Úø/6öü777’0'KU3 âOQJµ\MLCË­fVF9—¬‚ȵ„‘0€tKÀ¨Nôøì—܇À_@áWc°y>ø âdÅO~ú%%“Áö®[Óh4;�ýûö‰o~ó[±¦õ3rê~™!zSnÛ DwîkkknVà/èȱj €Y1&ͤ±+6MµE1/*¥QÄnt¨$çA�€³g¡xÖâµmÓØJ €‡ ?ƒ}ÇÔÏQø+™Õ©))öoÿjRÿ*D*\ù×LdT3øÍ$AÐï.x•3”âp‚Amô›úž{Äoÿö·Eœ½¦~ûÔõ5�ÀNAà䉓â›ßúÍ-#þœw믙i5k¸™¹™Ì"éé¯P¾NR’Mºæld¡ÓV2I÷hêN7gA[~K†*èXýsrd°ï¿�}�2Dþeƒ`”Aük…‚Tø#XÎÊßJHâ_5’õD5‘3³ Ë®&êF¾}Le€�¬q‚8ûAú;»¡Åÿì³Ï :þyÿý÷•ÅSÃ�nW# Ð .Ô…¶ÿ™3Ž—Ô­ü×ÀJªmLMS‰O-MLL_ÞØX5Ø‚º²f¦€Bé=fÖ^Æ1è��(mÒ¿“øûôP´»�#ý¶BàPdžÕ?ö=°úç¾?�üÐΟ¦~Ôö‡â„YÅ~ 9'—øOäp\µ’õˆ¿i&²øb‹P V« zqJ&“‚aí¥�­|èÒЀÀÕ?Dô• w‰ÒÃÍýhç/Mý¬b‚f~Ü󯙹Wþ$þVÝ”Ä?S¯Ã"7¦±;lÎôŸO 7’a6ŸÔ  €« ÖæV][X˜¿ñß7n\—Ò€K—.mË ²UóÕýh4wYÄd9yò„8}N~bõíZ€ÑKmðÞŸóž†'’‰#X¬&E2+j`( 0Ò¦�*@~�Õ&=oϤkâV¢d#¦ ˆü†=´ÿA\â¢$ül¿Iü‡-àA©µú§�ÿ8£bÑÀ �î}éáO:ù��­œLˆI#aV&5£yÄþ�•Ƭ'¬ª™H¤¨–­Š�D`æi„Vqô³]aÈÄââ’ü£'7FÏúÎn"†öNkØ<ô5�€F`4 ˆ(ÖàY>�J DÇ“«ûØÑÏÎ͉½{÷‹]»æším÷w,a&”h'Ç#!ö§�PR- aH‚ ¨eÛ¨¦ìÔ¤a—lÓ6Ͳ�¨NصÚ*\ÝmÈ·mÒCtcKÓÀפB`,Ìû)¼ ÇÇ JÑ?CúR‚&ôíO÷¾ôðG'?Ó°ð¯ÖrX÷W©jÎ4±ç_áÏÔmIüíŒi¦’Éð P[v�£ö?ì)ňÂG #Eå?2™4̪]†þ?Výÿ'3¦‡ XýcåŸÁŸMóiî¦lC¯‘\ž|p'tÒh4�»e Êr;Y5aÝJsÔ[ƒÄÚ6�n—±Á�Àö†€C@èé´mäkЄkl”¡ (é#:‘ÛÏààü 9 ŸØÀþ?·(æ°jb,H¬bÖ°2ª nÄߦË$@$lþhÈšrï@¶O$b‚�)Cáƒ@)€N�€F@#pw %¿ŠVŽ”8�€s J­+\¼â¯’¨8R‘2À`Á ·v,taögåêÆ¤=i¥5Ü�NÊ;àÐÍ¡­üÝ»<|=5'…c¹ßÑØÿÏBbK üˆúÁ¤l#WKBü 0`öØû§O) Ó†ô€Œ@Œ¤Bص` @uQ�€F@#0ð»¯B8Õ^ié—# H9É@¹¿ÜÖÆ�±+“1 �†¯oÌ+{´�.nʽtô†Žô@€n €g0© 9 *ÖæLc.&ÀÂê ˜…À]ŸAÇ‘ø6ìŠ�@ÚňÀƒØßH‚hþ˜£¥ *Åæî¢ìñ:¥FÓ05apŒ¶IÀ(‰B¡ =ý©ÌïÛIh®ÎÁ g&% ?ßL~•tžF@# ØfêõšÈçóbåΊÀvñÀGÃï¾êwµA¥u:Ð3„³MB€m�]„JyâÐ9Ë6 XéZø4ONÖ�¦H=€J‰ŠònzÿÞ=á7í)µf�胘$5ºÁF N¢9‘S§NÁ�ýMy‰{ÿ o侌,æCg“àÍ“î¹me±÷_„òŸ%÷û™OΨ q?õýÛòq6¶Ú.ŸÀö"øbãÊv[0,&ƒ›€Ù +%ÜX][ ,ã^`ÙÝ »CÛsËê_�€F@#°Ó@€õ2AƒJRù[�>°¿^Å%½r·^iDrD& -ªF tOê½�¦Q*Î…±wn\8_ <Ü´e @Ð4’d óï$ EbïáŒÜ+qṴ-–�Ý7)«+Ó=«’¦áOŽ 1j ëç›\S@ß‹CÈÜ€–pþ×>‚ãrKíWz>«‡¬ªÝF{Õðtë÷ûKw‡QîŠÙCRr¬QÉLp+aèŒcÔ0ôu�€F@#00T¿ªÊï¾}`{ýЈÖB¶!@{4ÿ“:’P÷0¨:·8å¶”°{ Wý,¼œP›Èe¸ÿ×öß;yBØwçí¤ÏcƘN§£ ;Ë©p¡•JÍôy[tu�€F`!PUüþ©™ßRúÀöJ#\)€g Î‚Ø“±…‡Cf'@ÞùH'@ÞŒŽãN& ãr¬S‚I7ÀÛé 8_ع\6r^›ùM¥q®AO Ž9i³¿HHu�€F`DX]»£ôýS�NÝ/§l<%@¿q4·­tÚ°Jí{ÿ~õ†™7d`€CçHc.lÃëÜ‘ ”»s�ñ{çÎ17»Kp+ (qE¿²r[r¢AeÜ|8]7nÞ{–ö¸YúW# ÐŒ,ÔÀ¿zõŠÒ÷Ou’R ¸°Ý4BuN½”Û2€šŽÚî[™TöÖcÛxxp�$®\¹,8€U{7@âíÚUz‡Rîù‹/.I��ØÐê¤ÐhF:úøã�h7Ð)8Êßj+Ê^Ź…MmvÕäÝ"W­Óo¹-cü*IškAo^%@©ØÃ²ßÓ‘Š–§-ýDïÁ{šøáúúº #°{jñS7„Ï =.Ã!D±ÿáÿø£�Ä­ÙeiH Ó‡¹øDtƒ�€F O¸%[áßØØ€¹µÿw‡fữBXžeù×Oêÿw¶Å­ñbL_�môr¾­ @/ŽS§®  GKíú8ýöR6bŸÿü3¹ 5^ñ0¨è0„õµ‚íþé¤Ðh4-+µåy?V­wæÑX368ɨ´ÝJ€�ã“Üo­Ú™­Ï5�€F`@HúÀî€hZ2 ¡mi3cͨ8Ãg�-}’tg�€F`D�šý bNDZèo `§Â2Ö €Ê�fv꣩ǥÐh†ƒûTè{ïU p8#l«cͨIÔ£B zÝšF@# Ðlrá§(çEâX3*Þ8‹w¶ãÅÒ}j4��މº }à<Æ™è6<ßéwN�O# Ðh4�¾Ð @ßê4�€F@#0zh`ôî™±F@# ÐhúF@3}C¨Ðh4�Àè!0– € ³ÿÁƒ‡Ä.èn%{ܶ½³ºÚ¥4Î-dÕT­Ö»|Õ�Æ…ÕÇötèõ°ºý\‹'é£'pe� <ümV+ÕJ§SßRoàúú†˜™™†YàšH$}™ú‘Ëä–ÿtÒh4�ÑG T.‹��noó…úc M—‰D²nØõKl3™´Áyl)ˆ1†·¥ãêîÌGpg=ÿw ι ñQ™VVVÄF /�Á­ê+�€F@#0êpŸÿÖ­ei5æ� •È«Uä-Ô8¶-{õö��/|.íȬ!3Ó]«þ(QG”¨Ä»-pûÎúÇÕzý5Ud©Éyãú�æ ÖL€*rºœF@# _¨vãæux�í&ô+·Õ·Œk5ëÍ Ö™oV*¶‰-ðíDuÈ @ûÔ¤ˆ£‘ʼno‰N€(Bñ^÷;¾³ºù¿ ?²œ[·Œà@7o8:Tö nÀêšzÔ@·ý«Ðh4£�€CüotíûsfÜ6.Å(eóWBñÑ0¼=ÚÇÅ-eܾ}'ìŒ[¾û·ÅÜZYûR€Ÿv— Ρ}çòò­f�U(},//ÃzCÝÖ³YYh4�ÀH"@?1×o\o3ùs'‡s‚ÛÆ1RñÚòJ›uÂÊ5§­½}3›õ(½b4?ø¢ÛÂx§ÁU?49=+x…¼pÞÒþÇ7®¯ü®¨«i¢ð:Ì<\I[¥ÝçÕkW•@tÒh4�ñF€ Ákׯù®ü%cpõZ¬E!üÓ¼=‚éWr�ó¦„Ûã_ ­N`ÑÐ^·³À€Ï·Œ蜘a–$ä%þ(Õ 'Ì<ü5ìa½P¼™/ÿ׎ìÈÓ�� qåÊQoìûpÿç:ˆe(‚Ôµ4 ?]@# ÐŒTö£Ä÷Öí[¾ž>c®^¾ê«2Wke}ó¯�ë ©ªæ¥W©šÃFÍ®‚–‘îñº�_ZÈyé Û¸)AH_»4t€��¦œLfœh¢ØnH@ŒŒò+u×@ó ÛÑ é‹k·ÿ®^³Þ‰(Öu™Ñ¿øâr›ÛGº ¾zõ Ì×}�®Ft†F@# Ðìhèö|À\¹zYJ|;K�0nsQX«w+v–÷ž—«Õ¼sgís×Ïl´Ëi�t¬î{Ð8ÃÈ4 {´Pþ†ÇaÒG¶éÒKoûÃ:À‰4'úá‡6ÇïúàÄ+rA N ÀHŽÀ±0�›·Þ \`› u`ÿÞºº|ó�qÕU6mH%X\½zU”[Ì»×Á—¯\t ËdÇØô©F@# Ðlr/åvcQ·Ö´óކAã.c!xûÖmßëÞ²>ÇÅ[·Wþ77ßH¶¶HÇšù8ÆúVÒ6Ò=Qr®˜fÂÞð(¶èå¢KG›m¸m êw Ž€ÀA‘ˆcûÞM/`àçå92ØÉÕ”½šb&€’¶Q)ÙÿŒÈÛ5”‘„â’„‰½ÃB}pP"ºŽrRÐèP ··ßË«o&“©ÿnv*÷/‘ßuÝ[6èØ‰X`R$�Íeáî7 Qެ†<ƒKd$sCˆÉè-Š ¾t¾F@# Ðô†€šX‡¸¾Ž}}êpÑщ¹­´¬F™J±aJM§p½õìÔ‚dø•K×nü?\¸JåÒ)Ð-ô�y†Qµ‹Q²ž²XÎÎâ÷N©dg±=>3³!*u0 >ÅK/û›JÝá3ÓpT*œT"±b›Éœ�ßÜÄñ,}¾�h8Dà1ÁX°qVÍ,ƒ‹J#¯š4�:¼1µH­¿€“¾rmùÿKXÚ59‘ýÏT@*CN’š¢Þ°�Aeu¾F@# ÐÜ=€¡¸ôÙåk‚Ë•¹Ü¦†‹ùdݲˠY X’¶¥ë¦UNX üXÜRΨYfÅ’ ]úpùúšmNMIú˜ÉÌ;«|ÒÍ-HÃÒh ý…Ö¡k °A  dµš°plåk¦U7– °ÈÔÀ˜fÚ’T@ *"‘³Âu§U÷×9óþûù•›ÿG¾Pú äm �Þ¾õ±F@# ÐŒ/¶mݹrcõ_ÃôoSÒ#JªA—H§Œ:h�t¬ˆ_ÒµZ5c“Î1_Ò½d’´Ï–ô €.}”¨½°5Ø Ÿ/H“†Ë—/KBœX…%&MňѭÒÄ!`pnej NÉ$H° ^`Ò •´Û•)g6òÅÿ…´‡Ÿ­yžt/�€F`¬À.ýòõåÕµ±¹~]Nd¨µú7äêŸt‹‹Õ´Y—4 4Îʘ‹t®Šo‰žp%Ý+HzÈý¶E:阾ÀSæ u;LÀ¼œ·¤"`rUNXêPP©X‰jU‚T03ñWÇÄ?àfÕÀ�«’RL@›Àeü�/®.ÿÃ�;ÿ@v‡x"Ä:i4�€F@ø‡ùìêõåugmí ‹s5Ϫ]ò®þI·ê†i•¤$›L@Í&}£´;ø‰D´zp¥’åèÅ­HúèÙÿ*áw§:pÀWSñµiû"ztô°ú‡¸ãz±h¥*i ¶�–³ €}ˆGLDS�Û®€Ä{'ÜS¡ÀH:â•ö­2ÁŒÀµ[w^»|mù¿Ä-ù•;qý«Ðh4U`ëÿ?¹tõ_®onÞ`�6Ñ?èR‹ÔÆÞ¿e”¹¥ Â:Fz–€]¹¤oÕM‡Þ�î‘þQüo®9âÒGÒIzÉoò¥«Þ=œðÇ *óÒ!÷92›9Éõ‹Ë’('ªÀ«S<Ò& lࢠ*¡„Àª@+ÏaœmW o„츓hIÖ7‹7õÉÕÿj³Xús(Žönóá?A�«Ðh4cˆCünläÿøãÏ.ÿ[¸.‘Þð�ô‡«#)äâ´b9tJ^K㘄?AÕ¶d�t�ô�Ò;.|S©²¤ƒ™L΢t\n“K€/lŠÃfZÛÚû—.ÙÓÓÓaÅNf2Ö&t¤ P ˜ê ù…ÿ 0uÔ ¤Ë€h2T´àMàâß½)r.#À_*òÏ eHíÒå忺²¼ò_T«õÆÅ.NkË�×i4�ÀŽE£V*WþþÓË×þó/®ß|¡IcHøIw(öoÒ%\—[ÖuЮtÝÙÊ–´ ºm\ý“¾‘Îa\'Ý»šÏK:HzÈÕ¿ÿ;Áí[tsÈè ß °9�×0©³Ò.ŸR*&s) R#5;[/Õ‹æ8¤¤™ªÁT™X×'ìzÉ„�@ÊR@ËÍŒ)}ô×`ž—†•~ žm‘4¤M�–�`Ã_€;2NB  +øû¯vÝ;?3ýŸ¤’‰ó¸Ò“Ï·Mý«Ðh4c�@©R­üÇåÛwþwHŽq¿tòÂïN éÜó§Øß]ùËEj]Xi«n•X¨‚äs¹ZLÕë);UOX5«lÖêédÖJÙe ‚‹ÒðT¶&¥ãŽòéd{–øŸ½Èeq{wƒ97äTþ:ÇçÏ›ô ðÄÌLþöÍB.—(mn&Óó‰’YHVÊИ€#ŲHÕÌj2gfõZ5ɽ�Œ üÊ0ÈP½Â2Š ÿ€p«JXÂ[Tp{¤“ †ÃÀƱœQ«373sp~nú;™tê×Pvf0èV4�€F` ‘µ-ë=Pþ¯]»ý£J¥êz}k}HœáÄŒ„ŸR€º³ Wþù—A˜°õ_N@Ž Éµ•LÕ Pƒ°¿–JÛµ<$ iè´g­‰ÚíÚj-=1QŸ(ëóóóõ–—-n“‹×^£Åš+�ÐqhÃÂp+ŽÝe qö¬ùÌ⢉H|f©4mæS«É¥t:Q(Éjª’HV̤iV’U3—¬e�d¶ZMrE2uà f~‚Á �€HBcÃUPƒ Á¡w˜vÛbx•LÓH,-Î=0=‘{2•Hž3ÆQÔö6IÔ°ôu�€F@#   ©P)—«µúûåJõâ�ÍÍ×6× +m]�àS® š"-Ð Df�Z¤qïŸÖipöÑʆï)ö‡Ã)�+ü–@ð%ñ·ŠZ§k þµL=WËfkõ[p_˜+M׳٠kii©Îíqqñ¢—ø7‰þH2²C ³ÀgK™€••þ\¢’+$Á”“ÉD¥TJNš&üU�äD.+Á�“U@� ŸJRÀûÓ`( ÈðŽ!Á³#yà‰+»ÉðD²X­kNV׿©D"7?;}:•LÎv]Ô�€F@#02€V”``¾^-Wî¬Ë7!Ÿ—D„À!¶åÆT<ÛÈ$øÌ-CÑL®úyŽ?Ö¡ÈŸ¾j¨±F]5ú¯áÊŸÄÛܵB¢„HaÕŸÇ_”?ßÁ·Aü3ÅÉz&³a5Wÿþ·¹úg—²ßav24 €l¼µ OÙŸü;Þ8¹¶f.‚ X__O”§¦Lw+ ’,%‘/9 2O&€z�ܰê5°Võt0à´L›[1’¨`¥�Œ3@à5‰<B²sÎ�þW# Ðh4 =1z�0'*mƒèÓÜÜñPë~Ú÷Sé� ë\ùסñ_ŸAev—øo`¯ ~5Èê·°€¢ÿÌæ¦533S_†èÿ£ÙY ¢öéý“C6à,•ÝÙø—ƒ÷HغìkÂøèlÆ>ÍGÔ¡Ã}ae³õåÒŠXóÂBÌ„ÍjRL�ï)ì¹ÔR¦P† <¸Ø\B¹ÂLÛt©L}ûI‹ú²» ¾ : 2à0�Èñ0ž²nN# ÐhF Е†æ˜¨76}¹Êç4Ÿþާ>£œ´@Ž@×@n­¦dÒ�/4þ 5£å¿T-IÅ?¬ú7°1ƒ7зÊJR(þm‚Üáôï#ü5ìþÙ—û'Ñ6ñg'Ceä,Zÿpr�U8¸�ìyAîRY¢¶¹iˆ©)Q�MσðÄfÕÓä¼,ÄN9QO•í ä-uìú“êWàU˜Œ·��˜K&‚CÁ2£š0ìlcÅ/‰}÷4m0 ­¡é#�€F@# ¸à~÷¼! �L~J ÈñIô¨$&cˆd›´°Ù·qfcõ_v´ýAð“Vɺ³òwˆÅ!þõ‰ÿÔ”EsxÒ?©ø'^ðé¿{DÃÈê€;à) ¯óר 8xð A}€ryÚ,çò‰ÉjÕLÃ2€ÛÕJ%~*Q›D@^ì ÔjÕÄå+õ˜.~l}Z`.IÎÇÈÉc0Ü�)Õ¶àŽKÿj4�€FÀE€DÞ9vbó22-óЇ¿ùKs?æCìOûþ$$IC_¯Á.0™¯×«ÉZ=•N×¹ò'ñ/BÂ�ÚÌZSS¥ºKü¥Ùß6‰þ›óÁt�‡öëð¯6&€ú4 t™€˜šVÓ“L¬ÓXÚK�Š`À`™8NLÂÿB99A½ (‚ Ñw˜‡ø“)`gÌã¯N�€F@# BÀaªTb—ÄQþ#@©µü¸.ÏX6 üôð'�ØA3}£–ªÓú�ÄŸÎ~bIü]¥?zü Û÷ç¸@¯†O˜ÑÏ–H8¡`&à˜.›'ËeÃËT§J&,{²Y0ÉD=Ïp£”©§L8R2© ^‹!L;e`¸`T씑Ĺ ¯Ì.ñ'cÀ10iFÀÁAÿ«Ðh4-\‚/sšÄ¿s¿ ÄɈìgäe?~‹ç$ü�IƒøcÅ\õK¿UP)¸ù]®TêÉtÚrWþt÷ ôÇ.›«ˆ?;Ý2@v„ùÛH;WýÐÙh2¼®“F@# Ðܽ�¸{gOBÏsì÷ƒèWl!|�\šç›D¿4Ñe� >rÕq?ƒÛ1¾ 4ýëŒð—É8Úþ;•øs~ÛÍÈ1p<@2˜€³X±s;þ êÔëóF¹\lcr¹E)€= ÓJ(Z0ÌÈü%CÉ‹”8Í 8hNË>\_i϶€[Fÿj4�ÀÝ…@¥Aì>≼‹� œÎ–¸úgè^£$“ ü<ç>¿Kø“©”•ÜĪÑýàgrrRšúQìß¡ðÇæÙG³™�•*·2ýÿyÒÊéJûDIEND®B`‚ic09ëç‰PNG  IHDRôxÔúsRGB®ÎéDeXIfMM*‡i    øµ…@IDATxì½ ¸eIU&ûœ{îœwÊÌ2«2k¤d.@A&¡Àº>j ñu–| ýùäI¿÷a½þPÔ¯imÄŸÝ(*­¨Øèã!¢Ðv?ÑÖ×Т Ì•ãÍ›óÍ;ß³ßúcŸ}Î"v¬8gŸyE~'ïÞ+V¬øcWD¬Â0TA@A`¼¨ŒWu¥¶‚€ ‚€ D�ç@A@CDÃF—* ‚€ ¢È3 ‚€ Œ!¢Œa£K•A@QäA@ÆQưѥʂ€ ‚€(ò ‚€ cˆ€(cØèReA@A@yA@�1D@€1lt©² ‚€ €<‚€ ‚À" À6ºTYA@@žA@A` ` ]ª,‚€ ˆ Ï€ ‚€ 0†ˆ0†�.UA@D�g@A@CDÃF—* ‚€ ¢È3 ‚€ Œ!¢Œa£K•A@QäA@ÆQưѥʂ€ ‚€(ò ‚€ cˆ€(cØèReA@A@yA@�1D@€1lt©² ‚€ €<‚€ ‚À" À6ºTYA@@žA@A` ` ]ª,‚€ ˆ Ï€ ‚€ 0†LŒa�¥Ê‚ÀÀ ðè£�V&ç&¿-¨‡ß§‚à¶@…GH¸è750B–'Ȫ“*Pgƒ0ülNüáÛÞö¶o–Ç^8 ‚€ô†>ôB+% ðÎw¿óæ‰ýàm4 ÞGâ�%°N¤�âóë³Ó~ùÍo~óöpVB¤†Q†³ÝDê!Eà]ïz×re¢þ¤w¿™ª0=¤Õè†Øß ÎèßÜÜûOïxÇ;êÝ(@x ‚@QÒxÈ� Ð5~æg~æéaeÿ#�RÇ»VÈ�3¦)�?믡©�ËC^_xdàÀ7‘8 üìÏþôª²ÿ2ø·f¨‚—©`ÿ¯úÝ?ý„bJI�N @§J~AÀ� þ÷…Aø{DFã¿&'ökêÙoËÛO3é…L< €'`B.ø ðs?÷SwÓàÿÊ#ƒ¿pJÝTÝUD»$fý² µ p€‹”Ð ž<úžGh5ÛG(ÛœgV!�xÖÌlí A@è¢tWá*¨©ÍÚ� ÇŠö MÊÿì]ÿæ]ÏjŸƒäâȆŒÄ7xOLу2»¯ÔLµ±um�LÚÔ9/5‰ä"…Àî3Ÿ~(xõƒÿR,ÿ)XÚ¹ *ëë¿øï‚àMíd‡<ôN†ô%w)®+½—4ß´Mñë?†»q¼ü²ˆ�EdLîßÉ<};u7Òß›éïõTõ›¨ó¸ž~‡¨Y¤{ý£ëiüuˆÿbB? f‚#G „ª•ªšœªQVÿ¼fI'¶¾_W[Ûþ¾~ÂÙÙo�ûÿª�}sp*3@’Ðûªâ÷2)â~16è¶TÆ¿5¢?MO×IŠ;C×'éÝ?YSꫤ,€FÂ! Àˆ7öÏÁ-ÔÈwÓ‹þzéï¢êÞÙø­`ˆAçÿ¥Kp/¡3öžú$/33ÓêÆ#GÔââ‚uðŸ¨N¨‰ZMU+ЯBJB¿8­‡uÖëjŸ~{{»ô#û�% mmí¼:{ú,Ñ›†-sÆý§>Y‰`ÆÆ‹E”ø5½M&ßïøÝß!RV©9¿Dq_¦[üýÝÿÏ7…á)º—0‚È6ÀjÔ_‚ë¨ë}!½´Ï#­þô÷nªÞòUq(ªR¿ñFµùV¾ÅzyiIÝvÇmªBƒz6T‚ŠšššRµÚdßû¬l¶{¸ß%E`{{KÕI)0…­ÍMõ�_ü’ÚÝáY¨+çÏ«™Ÿy·‰•Äu�U*â³ôû õ)AjÛ_¼% ›SÝ/^Jè¢t Ùð…Ÿ^Èï¡¢^L¿Òï[è'¡ÏìÞó"µó½ßÍ’bffF=å)OV•jzðÇ—ýÔ䔚¤ß°Îг©vvv´"`:sdýêºúü¾™¡hÍþì»U@Ö }GZÝßÑï¿’•àSôûÄ�„ázߥ¼�)oÈú›�C=…†ŠWPçú}ôâ=—¤¡µ> �p…ot9vìfúº§ØD¨Ð:€ÙÙY2ñ§•‚ÉÐ\ÎLͨéÉiµ±¹¡ö÷ÓS‹‹‹êÐÊAµF_÷œP_YQUQ8Pu›æÓñ£~èÍôÛ¦éƒ?§¿¥åÿþá0üZ·þå @98v•Ë/ÁÍôr=L¿×Ó›÷Fƒ¿„E ~àK2|å¯Ð –4ýOLL¨™i𲡦• j~~^mmmªÝÝ´ÉÿÐáCl \àáÊ_ˆÊDGW7=²ßMÓ¿@ÊÀ_Q_õ[ùÛ´°p­Ì‚„W¹ˆP.ž¥q{4&):*V©¢—éEôwø?KCg°qª©išÛŸœlV&ügè~„Fÿfíh%ÚlC  õq˜›ãûH ™ŠUÌ[þö �o§'øÛiaá»išò�©ÿz/­ˆùešꛈR0`Àžƒ_ ‚EzqÞxP©·Ò‹s|ÀÄq8$õ"ò*­êÇŠ~Xfff‡f¡_Q½ŠÒf©Ž›[˜ˆvÔjü.(dâZT¾¤õ�õa¯¤_ù‹ØYïÝRêW, ¯õT )ÌŠÿí³²�„2 3ÿ<½,?J¿·¿…2x �ÁFßù˜ïG˜��‰9]™¢ÿ¨Ò³3szM>+�,a)‚k„Ò°ýøç§•ú?È*ðsd÷ù…7†!éú‰€(ýDŸÊ†—=úó&ê‚þÒ‡¿„±A€ÖÀ€-~Õq«jš¶6b‡@rýÃØ´ûxW;—ÞE[~„Ö ü$-ÿüõw„äHBB_ /°G…ÒŠ~ì×ÿUº{bÅÈ�Yi<’âîa¬ÆW+þÊã¡Èý÷UŠáø»†UZô75=ÝÄ5ÇlD#&§¦µã ìöÇ¢êÂ-åuEcœ†ý4°ê€¿Q‡‰üp�_kõÝô7£âÿ#­súçdýüç´�ðú+Îx–.}xÚ�æùgižÿ§h@}3Mw G•æFç�SŽWs䄦F «&hYü«$¶™Åw<ÀÇ!D<Øw Šd%NRW»Ý쎋 hŸÿd{_Ád>¯Ã ~ý ô@é튞ΠðÂÁÑæ<Õò‘—¾T=ó¥ßÅ#*#±r¥ VbE!V’ëd¥Ù£6Ú½vMí‘ó¦Ý«WÕµS§Ô•o|C­Ÿ8¡ÂÆZŽdŸk’çyôû YBÿõ¥~–¬CB� G@ÇÅÐü×qzà?B÷ØGÛvÀ@Ý·}›Z¾ë.5G.diÉñB v˜Ç?4¶4x¦ò#v¨{e Ì3'Éüï³ßnw±•.^DW¾ðíq¬ÒüøÔ¥2åÇcêƒÊ(�„ö€â…~ÁÖ@AˆX vIIÛ£Ý&}�žÃk'Oª ÿðjõ¯ÿZm] !¼½0Mr½“æ?ïù·Aðê·†aÛŒÚ+~|sÙžƒñE¤‹5§9¯çûÓ¯-Kæy�;ü¬g©ëiàŸ¿ù榤zw iâèJÑ=Æ?Sãâ—0`ÐÄNn¨Sïh×¹´xΠðqùtƒÏ æôaÑàÖ ƒ¶>²¹îô‹“]Ö¢Eð£{Í¥ÓZP ð Σ@?„ß±ïú.ué+_Q«ó7jíoÿVA9h#|'}¬üwšø'2%ÐzmdñxûÚà.YšÐCý2ºùÏôâ?¯€íúg?[{ÙË´y™ãŽƒ=^T0Å—¾„@€ñõ)1÷ÏÈ1Àî‘'½jÆeðÔ6%,üÓ ±’€k‡&gvYB˜B V [ °„¿x¨_¼í6…ßqê§ûØÇÔêg>ò„�g"ÜAÊŧiJà%oCœ? ¡‹ˆÐEpcÖdö½4¿K÷Þƒÿâw¨[_ùJ5{à šN]Ã@w18â+iø—¯{ Qÿÿç-#L�ù›«ìîÏŠy(°p‚Ïî(A²`œƒj÷i0pÌ7~°lÒ«9  ÔhÊàŽ‡R7<÷¹êëôGêêc�Q¬WX¢Wèã¿/üaøE¯œBì…€(^pùÓ—ÿi`þåÄûÂX¸wË+^¡Íý:uªð™†Ÿ4Æþ2óßü9óÀo¾¼?5o–Ð9=¨â³ý�#„›ÂÈEWÀGHÜ7A€—(X›ôäüAuⓟÔ?κʇÃ4¥ù1RžKJÀÙ8Rþ–‹³«*·Ðqáö»AP%íøT_þé0DŒUüw½á úªÐÀèÙ¯ýqÁp(ë 3=cô«NTX|ùV°bÐs¥}?±«hq“ö)³4±'Dsj&G ï‹�+ƒÊ@ÝÂ:¹D¿úÑ^u•~G_üb½;éË¿ó;¾kn%%à—ˆÅýô“ÐDè¨1KR[ßJ×ßßsþN.,¨oùþïW³ªÅÆàÏK8¬…¦g¸>ˆBßôz!•K¬€f]½¾”] {��u‡¹‚O½èìrÐ;!aðÀ‚dømÀÇ �€¦2ïzýëÕ—~ë·Ô>-õ÷Ñâéû߆¿ï‘GH™ˆÀÊ—ŒLÿX¦ÿ¨O>ìç¿ó5¯Q‡—–Ô2ut¼aħ¡í !½VŒÁV‚ >•! aþ®§õb)œb|È @ Æpü�ÌNßt“Ú¿ÿ~KÖ2y„÷PúqÚ]BB‰ˆP"˜IVôxÿ3º‡åžžpï½êöC‡Ô´|å°1fB“Ëò…�ÓŸýò êÅQh–Œ/-ÖÈ»ÁÇk€(ÑÊØûþ‰^UAÊÉ ÐΤ*mGž /¤ô%?A€8mœa£ý–¼à%/Qÿõ_W°êð hàšø �\¨8ˆÀAÉŸæÙÜ,pâóòî‡ù_ófHþ¹·ÉóÖ¹âÄ:ÓÑc.ò·]ܳú1gÌ“Ç×ö¿XÐNGkçØýÈË©–Br(åÙæ¢Õ:òîWüqµƒ¹þ­Ð‚åɧó™*`ú}ˆ¥š^YQ/ü–oQF}Óù@ÓªèWEˆA,á¯(%€˜eA:í-Ù8Ûý“ɤ6÷ä'û-n¢/þò²µCƒˆ#UmÌ%~ÀÀ—=GDб9ÌzBÉËÜùõ·á²ƒôêþråŠÚýô§Õ¹þ�zÞóTíIOò’eé©OUw’À=Ч_õdŒ‰EèNãßÂa ð�ÓÊØ*Mp¿þÃímµõÑ�ª}:‘K~ÊB3*ˆ0 -I–ÈÝO|B…ßø†šþÎï$-�§¦U®»Nݶ¸¨þáòenÅnå Qx8ùRÝÈÉ@KjÔäÑ£ì¯ÿp�Ž™ýƒ?PáÚš þ€ûDÃùîí“hC],p¥wp›°þå/«m꣦é,n˜Ã)ïÔþ¬~•[¶Ð‰WÙn=¬1D‡³€½¿üKÐàÏÓ¯»U5á[&<ÓþðMàÈά¯þâüåÙ/óé믯]íÿÝß© æt@�¶=³:ËHTÒ.ÔmYŠ ��Ч¹JnÆÖ?½��VAKØÇc-½(O2ý¾xæòÞ#~ö³ªr×]Ø@^Þ6@wýãúàù—w FcÀÿb[ò,Ãj‡Ž……�O€>7Q�ùU:ÎB™>WGŠº†¼ÇË;Ð5xKe �¦œsBÎÇO©’ ³$¢$Ñèñ5¾}êÌ`_€·NûÅñ¿iQ—šK×¾ÜåæäÊË¥‹� ÜVê7ôW8ÊÜ<r±“tODð¬lr® €²‘ ~¼•ò4øñÇÉ�¨W^.*%€�hZ–º_c(Üþ�U¨y# €7dåe@Ÿ¾�—„1YºO=¥[Ÿ.O6áÔ>C6V·_ÑçÔïK�Ë”âÚC NýçëžCÓž’‹ƒ€(”ºH£5`†:QºØ}bMݤ³dPp蜌zHÀ•×]û–Т´°ô+|Øp§|ž�A¯÷°É' @[ >w ¬ècCyíÕ¡qˆAá󔳫ä\y¹t$¬LtµÅJe®û5™(Ón0 ¨zðÔS z™`€4²$è”ß@A¬`åãÚ Žø`á¬ð—)€n Ïç) «Ò)u‡ÆÐ’Q°L”×z|Ô²?ì}xv­bŒ¹òréP´X< Ï¤ø°a)Ìþ¯ÏÕÙâEèsÓrWÁŠ Ï Õ­â¹# —®[rúòåÊË¥£òA*߆è=,§Üþ¯?µýREècë / #ÈHBÂÓ@É¡æÒ Z N½@È¥ À µ®K½€Ñ· ›‡KW½‡-]€>·{ ½LòõÓçÆêBñÜá�K×ÛbÉ•—K!@+ï@[ÍÑóLì52Ðó¶I( @�_ë�ùˆ Ç�ÓAq>ƒûØ‹i—••+/—Žä’5e5N÷ùÈ€îc\F ¢”�b<¸s`¢tò@gu�€ àªºëÉÊ¥ Àà´­[ôW¬5DÇÜå …¢øáU*5ü=±”Š©0]ð¾ÈÀp´/{ €±N`8j<œRŠÐçvÛßÙQªZuJQ§c3¥ósÂ4~_4 j�ø8Í*õoÔG¦¢aYB ¿âl¬£ÿ“Ð7Dèô‘Isow—Ž`œr>3£ê¤(p^ª>VIŠö@@�ë z.ƒUÏHøÃ:_$ð%˜�W¿(+ÓÓº¯"7§Nö©ÿ“Ð?DèöºäÝ� 2¸- ®^w�Ú>}ºÏKñ.º1ø¹Ê‡t± G+OÞpCtÈCÜÝÍM•�t Qº€, uÆ7½^ü²síššœ�eIQYYQÑï]¹Â¢¢GšG[€ùئ´¼Ü·€×N€J,<¬úE5yè�RsslýçhÔÇmRèWŇ´\QºÓp§ˆíÓ\¬÷ˆ`ûêUU¥��jGŽ(̯í^ºÄÍ"t=FÀ£Ccw~><{\]cq\y¹t(D,F¨#’¦11øWdþ|s}�ý9úU %" @‰`Ƭ*J=ÆéØÐ¡]$XmkKµZœÝù·zø°Ö²wÎ�S²ˆÆ —Œx§Ä0x�Y¥õIü1÷ÏÝÕ„ZìÓ×ÿÅ=|±Ã×Ù”BÈB@L~DÔQ}ƒ›ã"Î]¸ f`:ó SSjò¦›ÔiИØ'%BÂ` ÀQþš’²LûÄ‘E×äÚÿ  I\±ÄH À_úâ¯ÒÔeíÀýÍÈõjKõâEå3™IJ߈óÊßr S\èeø Îì'2¡S;MÓGñ"yXšÒK8A¿*­¦…PßÞÖV��4k9j³‰ÒÀ^pÇ?.Ý T”+/—õ­XúÐÂ4Ø4íÐbå }xTéW¡¯~Ü£Mö«ý³Rï�åó ý<Ú]å_fùÈ}gˆÐ~ÆÜoÃÿöÞ ø"=Üw 2‘»ôuwêüyu=™öÛnzA+¤à— ¢$ÑèÍu0áÑŠ¬/{z’Xt½©«®¼\:*´JJòÔÑ£¬â…¨<0øgîv·$£¿;KVOŒèá“?†�yÐ )�žŠÁMHšÐÃý²¼³á¸Ø¡¯÷sdÊ_¦´SZI/tg;dU†Ì+輂‡ÂàÅWˆ­´;ЛbÓß…Ë—Õ®ÿþÿ÷›øI\gˆÐ~ÖÜ´³ÿ?’ðãD°h%Ê$lÑ‹q�Ìm3db;@iy½;“An�qB€LÍeFã]¿ëŠ©›«ôÛ$¿'»´øÏ3u”ÿ�®ŸãÊã•îxIãBØB¬@•À5þ¶ÔŠôµ�ǸÆ_~à • áÐñKí>%Wf~½ È^u<Ûݯ×`–�D1¾ŽßsHŒkn‹ô¸†%¹~ˆ¬£'z\®GˆÐÇÇ€|[þwƒàùg”ú§4¸þ$‰r¼�⤊Nv©¹q"wÀNB0ˆA2lãäM*Œ6,Õof…Š]0F�¿§Zü+øÿp$j3¤•ÀÇ�„>"ð`îÓáA¿Fá]Ôa¾•D9×Gq¤èž# ‡w*uÔþHN�z¸Ø_¾NýÜÎ+õtüûÛ(]€þ·�–€”€m²ü´SŒ�{Ný{Û"R�kÔ´Eƒý£¿Ÿ¼�;ÃbfF>!0D°ñç�awÜoüÔ¿ ‚jÐgÓýóiŽàôr>ƒ®o£½«†Œ¤ø S€¼òØ S‹YdÅÀþEjÉÏÒßÏÐï/ÖhA-âƒï CŽ€(CÞ€Iñ߆8‰óÏ?�DS ¤ ÜM7O¥.ùNúÿn¡kx –ÐG8Ã:h8t}¬†±hŽÌ#s‰,,>þ*µÇ—©¿ø -Ñÿ2Ýÿ-­<þœÌã— õàð`pÚ¢+’ДÁbü_¿f�ÁÄuJ#5þ(½ðG)�,yê&ü¥Nàý]Ìüæè^BÙplà2P6êcÁ�¾Ú± ÿ2Uö2]_Æ5ýè^�Št�ëtæî ÙšGHŒY`Ì<®nÄ÷5ºÇ�°æ€L 3 &Åa‚ž¬ŒcFT?¸‚i™;ƬÚ]¯îþ³žùÞêÇ?ñî®4„ÐûeRäé�R«t8(½ßÙ#@†°V"r· Û�ÿÆš¬;ˆ$JÈ ð3?÷SØ å ôõÅ6íƒv˜B7äÝ_\¸ôÃaÈVX‡ /‘Uè¢ôu)SˆÕ)¨6ܺÅXÈ_A@è)d5’ ‚€ ㆀXÆ­Å¥¾ƒƒ€žpËCúš9_ÓƒS3ýñ°&8Üõ j‰(‚ÀH! €‘jN©Œ ‚€ ÀC@NB%‚€ Œ20RÍ)•68ðÈ ‡rpj�)‹0 ÝçŽ0\µrTF’�!C@,CÖ`"® ‚€ Pb(Eá!´‰ç 4º6EèJ6®ÌÃV¯®€%L�>! @Ÿ€—b�gu40\€qeæÔ¸j.Ò Cƒ€L MS‰ ‚€ ‚@yˆ <,…“ à�öø»‚öÀ sñémz$µ«LNý]<$]ÚC@€öp“\‚@9¸Çÿh‡®‰Êáy92shÊ‘H¸‚@™È"·‚€ ‚À8 €qhe©ã#Àùæ™Ó©’�˜ã ˜SûAª—È"Œ¢ŒRkJ]†ÎÝ Õž#3‡fÐê%ò#‚€(#Ò�R�áC€;®sé �a”y�ðY�^ @/P–2+œOàaN¹2sêoOA Dè<É*ô �a'!¯û(€áœÚèY£KA‚@w »ø wA θšaÛ/É\Xu�8lõr×H(�áA@¶O[‰¤‚€ ‚@iˆ 4(…‘ à‰ÿ3yøLåܺyB&ä‚€ P¢”‡¥p¼à˜ÀAáó.¼‹†Qæ.Â!¬��D@¦²YD(A@A »ˆ »ø wA ²“sŽÃå­[PRÏ“¸2sêßsá¥@A`<`<ÚYj9 `ªÜ†q:�+3§þ.|$]ÚC@¦ÚÃMr ‚€ C�€X†ºùDø¡G€c'š�C7H`@^†Ìö¸q� Y�N @§J~A@A` À6šˆ8ãß0î©�dv·‡L¸1 A [È@·�¾‚€ ‚À# €nmàØÆ‚ºÁC‹Q7ÉàÕK$FQF£¥CˆÆ>ÎøÇ¥$†QæAÂOdz�€Lôe)CA@0Ä0` "âŒÓ>sOý@!Ç•™Sÿ�ª˜#Œ¢ŒN[JM†�qŸàÔ›UD†™Šf!A@�r @¹x 7A€�ÓL7ÀÃæ XËË0ï‹€ÿ¸¥ P6b(Qá'‚€  b‚FÇ�aÜTÇý¶çÒ�û3 õÊG@€ò1Ž‚†•\; `ѱKí!�ë,™eüïAcH‚€™0ã"±‚€ ‚ÀH# €‘n^©Üà#Àù ‡n�jË•yØê5H‹,‚@gˆÐ~’[èÎðS:˜ޑ$åfÖ23X[½UA`h`hšJM8*jÎ¥$”82sh©N"‹ 0:ˆ0:m)5B8Ãh8tƒT}®ÌÃV¯AÂXd:E@€N”ü‚@'pG@.]'²”�we.á' 0¢ pãˆhã€g” ‡n�ðâʈàƒV—hzè¡Å°~w ÂçSÏðÕ÷?U©ú”.®^Qߥ’…m7ØÛÝU,ÀÇ[ΰyÌa×�ßWׯÞGïÂ}üBÙuÂ@Uö•ºxùüîC¯yàëtû?*uõ©0¬|êCúÐWº^¾Ð¢t_û™yä‘ÚÅ+k÷©0x­ª¨—JE~û,%§ ýB F?!ÕH x­ êJ+aøŸÂjð¿÷ÁßûÇ~ &åÚÀŽMWR^õªW-MNO<†á›• Žv¥a:4°l;D4l­8uãÐ McŠ Yn¥ÙÌ·“.ðö‡^{ÿ_õàçÉ2@†�‘Í@ @�Z_ü.¯ýPmºúüö¨X)fàà � áÐ Re¹2[½ ã!’% žM–�’eà_?ôÚ~vyáàûeÍ@ÿÛOŽîA<øÚŸGsdŸ Tð益Á¿˜Kñ09·¥­DÎR¸�úÀ÷Qø·¯~õßY GaÒ6bh:wÆ—¿üåSsf õcDÍ[îf+£„Ƕ¯µƒ!ûRÖ‹2sê?Jí-u‰x"Y>Aë>¬ê•¥i�3q‚üíbèÖ?üð¡ùÅ™�Óàÿ6*Bÿ.á,lA`¨¸OU꟣5ÿd¨k1¤Â‹Ð…†{àµxð�ZXXä’÷”ŽÖúŒdد×U½¾¯öööÕþþ¾Ú%/—´�¹Q×�â÷ÔÎÎŽÚÞÚÖKáU ÿü�‡x©8* Q Q,ÀøDÓài­¥FVVYWÕ‚…¹D(„§8‘Ê»i>ìÑbª|*æño^Ìmö7xt@m ÐT;n�Õ_ z^:Å¡VšžƒîU Ç»ÑÖàŒ¶õÿçU…^\S¤,-.Q¿6Ý,}Ý‘£GÕòòr3Îë‚|ª¾þàOyåb'¢8!2<ðê&Sò Ì©æX ôG�Q´¢vfVÍÏÏ©€Õ�� Ðfð2vX‡ì2åî´a<Þ�¶èœ;…Ú#@‹˜çæfi=À‚^Û„¬øfZ9¸¢­ŸXäÜFø±_óà½mä“,XÃ�%ïØF“éŸ6á…?é¶ñ9rToçÃKs.šJâö¹>B m@›r·í�Ž[7]ƒ&_E =àë9)¦8a € °~½éf½­9Žcþ%"ü?|ïõLz!s € SrÔ¢ø;Li¦8ìãÇœ•Æ m~5Zé/aðèt~9ÎïS³ü€Æ×õ9/0àÊ烗Ðö�øùöþK£Ìüü¼¶zÆÒOÒ.§#7ñï é4Õ½zí½1ùÛ¢´�YÙÜ'Ûáëë½ýpbR­Èàïƒ]’Ö»ã!+£OždY½»æ “�†C7¬4½C{”KòyÖÛ¡í;”73;­æçZ~Òjää J@Õÿƒè~:<è�N䑼2y> ä˜âéJUîæfƒæí��§Á¿$}+iomôãÊ1ütÜzÐYu=×ÌäÀ©V<®3YYd®‡´Ÿ| j7B`ÎÖËöÞóK4EŽœâ¹¾~U{„§Óo¼Q�:yB÷il`ÃàßÐTìGéáN=±‹EBQ<[u?¬¼žûBàÂ^Øó:þò߯G.7ww÷´÷­–;NÏ y×€ÂÇWöÜ@<ËÝuÁK, ª¯nÜbánvk{‹K.t% +“pE^›Tø«õ[wÓZK�yêc%‹£¯»þuæôikCÂ1rÀö/)þ�†4‰b"PÒ')³´ £1�mz‚ƒ¸÷…w?W€R‘ý!ül_¹zE]¾|EmnnißÛ2ø»Ð”tA@(ô58`skK]¥~è⥋êÚµkjŸÎè$@ ˜KL`Ç@[ßvßî»®9Æ=¯{dw„õàuÜFÓ-‰¨Â˃ä«ý1°û†]øÕÞ¸¦|ß¼B?<p\áãc‹C7Hµ†¼™94ƒT¯q—n}·HÀ+û±�Î~Ú S”?$'hP(°Ep“ÎØ"K3¨íVßB´ogÒ Y±d)º­ÔÃ{ŠÒ“i˜û_" €ïà�qcs“|h_–Á? èX_k€¶¿œFC�$ #°N^¦~jccƒÄo¯qîIÒaÐá믣>“ÿÅDŠ}Õ«^Õö!lÈ{™2‹à�f¨‚çpÉáæÇöFfx Ý? þWiqÌ&)A@t0E°¹µIS—ÔîÞn[âÂüO“NÒ:Ï©€…Éé‰GÚ*X2)Q<Z|‡&±•å%Ò‰ùÿ°ÂÚ4ÎÛ–0èïyê@щŽÜÏ>£^¬y‚ñx†º–˜ÀɘȮerÝWˆ gã/ÿ%ê;c…€ =g?¨Oeå M QRp8onwRÁ⹿d,ü‹y¡£Ä,¶‘ ‚À°"p�Ö-­Óœ>ui^»fà!ŠŽH÷·}þ‹ûbz!m óQxùË_>E¤7¹È±ov�Ÿ°¾¾®ÏÏöÉ#´c„€6P}‡í/§‰< K¡é/°¬_[Ï(î)ÐZ0 —éèXt/�©õÊuFùÏ Q˜p-,L'R'^8»JÎ-¸/‹˜ý¹h�oŠ#ÿ¨þkwñب= £VŸíímm @½híSw ´§Àx*`qÉkmß+yä~Ç;j€·YÙÈ®^Q·)rDæ 33¤Å’ÏNɛߠ,øÃ4Äîή^ÈS߯+¬I@œp:ß�?ÿqÍ'@—h±™„<xW±öÏ]µ:¡O�`öCynåÄlS{UéÔ¿™Ù96à Õafz†vBmh+Àùóç¹ýÐòÅ«ç^L}‚]˜ú�e?Îxû•[pÒnQÀÂø·ž˜œÐ aŠh‘¶NÛgú5È¢ÜÍ�Mý¢A ÙÙÞq‰+éLn¸þú¦)Ó™…£c�†Cç,¬‡\™=ê…çõüÚùVb¸‹‚B€½öÓ³3j†öëÃã^üuÝ«šaK3’É)Ì ò¬¨±ÇGžvåÊU^ÆzåûˆPZšJ,\°‚àVW/y9Qu[¢°àog‡íð‚+¥“nwoO]¹t™®Ó¢Ã='½t˜ö]!KÝt.>½M�&-\e[­\õ¤tô1€ñSê‚¶ÌÏÏi7¼Ód©ìUÀz€e:8ÐÛ¢¥Ò"@8Pƒ�9rÌVõw!I àœÓNÐŽùeH @q˜šŒ´ÜbÓÌ��·«âB™©{äYðÜê9õøcßÔfTü™À ™ 0"@!€Kñ“'OªÇ?¡Ö^X QÆUZèì"ªí ˜Ñ±ò†êî{ï½÷‹Vˆ4b`>ô¥r‹‹g\Ãö_�¦[ðiƒo#xÑêU¸|ù²ºpþ�¶ÅXÄÐ+¡¤œ�‚ç$†h/:긼2þjy9u+£0áá�À-Ò;·ºª.^¸H.x—õ>|o&°Ðy‹,34 sBTh-Yy®­Gî‚y«“³“Ï&š?sÐIr±°…b æÖ`âÂñ–®M¼ƒ1¾òO�<­Öέõ¤m³ëfÀ‰Z§NžR{²È¯›0 oA`ä¸L „OÐú€nMQâÃPÒ2X¿)Ú=€£ƒ=‚(`ɬ ŽÅ!VbK@µ~Š:H־ӧΈɿ %« 0Î`ð?IÖ€�Æ1½eb+œŸqªT+úÇÌó&��¢°ƒºSˆçþ±ç5´,@ûÈþíÖüÿ6�£}æôé®™ðâzÉ_A@mÐG�>}†v \.½¢°®’N”×êÚ¿ )ÌpËC=Ä&fòY2QMKËûnq‘M�Ó ô“µè/®M–¯n(°*œ9ƒ/ÆÒk“`WÒ¥A)ŒúŒc§@™ýß¶‡ã1X&É�3€ÐùÁÆä5òdlTG‰¢ †4@g…Øô_u<¨õp¿+_ègϬÒ�Bí;ö�c�ãÇŽé-AðF<)´Ž |y±cš}?)u´kâ¥u(ØÚ9ñV×!„Æì·ß~›z泞aLë4 ;¶žõ3àXÜNžÉ­­Mµ¾¾¡.\¸@[üÖÔêêY=ÕÞ©÷?ðÃûº¸´X<Û4Í€ù}N˜ 55¾€Ú±Ži€/rx�;�(œ' (Þñ  a¦Ã¿vÂõ7\¯žþ´§ê�+t9žéÚ)gœòìÓV§*Í[J(�Mš;^ª”7e¥ 9‡}d3•x¿�oŒNÇ@¹´¼¨·Ý~«þ 8}ê¬:Eóù§hj0þHiGd옠�›¹9¾_ÿ¢rvw·©¯9ÐøÐ(¢TúT@íc¥˜¬™J.Ûe!`�â QŠñ‰So‰/Lã#,'hþ¿ØN@ï7VÀ–°ÒN~|vTÿ¶6¶Ô—¾ôeõØ7‹&±8¤‰Îž9«n¾ù&U£ó: ˜ …Å’cÚ¯�¥Îk+ (ìæ‘ÏT?ü0öÿº—ôÙPöü?Ì;<_wH}ß+¿—ülÏêÁß�${"·ËÊG`PNÎ,¿fÝç8=;­žv÷SÕw½ì»ÔM7ÝÔV�XÁæ,¦\Ÿ9<ö{{±7TLÅÿp@7Ю-LH` €¤z}ûVIÓ´–Ü`ËS¦‡e\óô±}äÈ�êE/~Q×öùÚê=.ñ8J¹Ì6Ü8õÜ ¯X �!0==¥žñ¬§«|Çóé`Þ›N mÇâh*hoþP0ðçTŒäÖUŽ�•ˆS( @RcŒ(FXZ‘!:BµqÊU�LnÑb¢ÈÊfº†ö¬ÝcÁO‡¿ ž«s:¤žÿ‚çÑ \éHMÚvòyÏ6i» 1Ì…WB9,//©—¾äÅêØñ›½^ºtIÁ·§ýW’€ñßðËö�˜Î`; ÔÑ7¼á å,VðFg¸2ˆàj¯Š[ˆ×p4Ô²¾q6º�ïnÌù¿èžïhž³íª¶¤·‡À9Ú6%¡;` s‘MwŠI®!ílúöç>G=ù©Oñ®ßùvºøô…Úfí±ö ØÙY¿Ã»Rc˜AW£‡'@ÑÞÿÚdË@VƒÅ=æêËš?»tÑooîó_ð\1û»Úº„ô³«²°�,°ƒæò•+Æ4‰l¬«xÎs¾U=íi~JÀµ�k¯òé'&*Êg'@=d'ã‘�]�È`k Ì`ÐNà ûËm!$'= ï4À‰†Ïª}¬Ü=xè ǹU(_§rI~¥÷__ø @Ñðæ\8Q--vo+`Äx–ç×Ϋ¿äEäCàšúÚ׾Ζ÷2}„ÌÜx#›ÞDˆ53±Õ”ÇU«5Rx~�‡zeYƒWðW€pI·‘`õje‚ö´T|WëÛʽ|ù’-)˜÷‡o΋–c ^¬Qg*¡{\¼xYžã.À‹©«W¼òåêýïÿ u•ie�€> }L»>3jg Õ 9+ã²´Š€y›Ñ$¡�=xðÕ÷+ª�ÞHËU'&ªÎéIxÕ+#¬_½Æfó¤§x„fî ÕÎø«^²ðp¬zuÑåGÿí3�ùEYw?ý)úÅŽåtñ—ôö8{V¶�ž;§ž @že7VíP\¡Áüvò xÛ·©¯}åk,X˜¹,±hMD° ´úÄâþî€ÑöØ�à²Ð�}¸rˆÆ˜æVWÝãÎ(> °ˆÞgÕ«�ϺǾÿ[éež&>oØÊ•x7p—ufÅ™›“P€€¦XâwÎH#‘!€E�ÏÞ·³€]šZì$D}"ï}‰N2€à� ¸ò¡}ÈßMj'²¼¢5XuÿVEŽ¥‹BË  Û,YÆ€õ«W‹ÄI¥ÁÇ?VNã¤B ÝG`•¶FA(~bº/Ǩ–\¯Òô×>™«¹ÉŒ*ݪ,ŒGo:ª–Ȭ‰1�…C}: QŸ—?î·ë¬b·ëœ2ëÕ:Öüwí¸ÒˆPÔò '@ñ×HMOØ™aæ®Zæ.;­-e‡|öïRçÇ “ä¯ûÈ‘#´øgoKè«´�J·¯»/ë…8£W”+úÁÙÒ1ÚÙ"¡|ÐGa^þÐáC,8~ñ‡P;Õijƒ»+ ¯�œšt‘5Ó«u}(�(MDònÔóyÆ)æVWe£]tbþB}“5ìÑKÒiØaÍ}E¥Üù„ÛI©Å\Ú)èù± Ï£¨±$Åë|×h+àm·Þ2–ô¢Ò˜c_YY¡¢¾Ê*î�gfù¾ú³La¥¬T`(8*͸ANt#% @F8¸` ƒ?ÎðÆÈ�E€E¡ŒùÞâ—HŠ[o»Mw–²bº¨UÊKÛÚÚŽ¾˜Š´ÀòŠON�w `ÉsݽGÖÊ••evØb܉€Ã³¸¦}ŸC�¨w±+1¦„Å£Ö˜‚Ò¬v=¸½hj*2{ÑàOU�ã‰f6ÓE;ô–sCÜÒâ’|ýpéVÔ™³'4k±t áˆ/ðÅTK<õÖÝÒÆ—û²‡Ÿ Ð.Z!YGõ‡ƒ,x˜[Å€SQ,Ñ ÒÓ�Z’utÜ M$Î~·²zmX\Z�N²¨ñJN;{öœî˜"¶sA%—;NìøZ†³@IDATÐñk€¦Zâwoœêß˺Ⱐn€ “à㣠šá˜¹³éNz^RlÝr'b�D^Q,ÍXŸ¨ßV¡-ª–d�­x²°Ðõ„•£ðœ677›�Šj!ie pæìªà]�Z ­€øÛ‰:G1cŸ|àÀ½àóû®Ð©�¶lv¹äv�v�K`¦pà¾×ßwÉÚU‡qMÀÒòA=¸Ãuô¼ÿ!D ‹\(¡è´: {»<`aá€tŽ�ÝFÞ“'¥�i¶¶²`ÀX;A]Ýá¶òK&ËËËê왳Nâ�ÝúR¢¾­Íî }#õެi€Z­J[@'Õ:s7tu» �€òrZZ±xâÚ’i¢i–‰–Ñ‡Ø ™œ0åÀÞ©ðØÛãxÀRjaaÁ$†Äu�“'Ou‘»°Î"pæ´{`Êæ‘{?ÄNwÀ¢AŸÊ&Žu¦›ô mœ$�€ÜVäL€"¬D° ÃQªdŽBp­`­—à‡Ïc àÍÿKèðÎxåÊzï ”’Ôé3«‚B—ˆ¶ò ÙÞíÌßwŠ‹}vÐ'  šP¦,à�IЦìAo¤}©“´ qḭKû\;õÇ5ÿC€…¢¢K‘'Nˆ…±KÐZÙžf˜¦­™%�…ÀÊ !àîö® æÚœ i¸ ÷QhrS,ˆ`†¢ §¢-€t˜|L?øz•k1‰]ŠFÊÓüò…EQœ€–HpBæÿKD“Çê´Lð€ê€jY;â1؆ÇÑú8=EÊÌ�­€•jEÕéÊD(I¦ àÜsÏ=PŒ �Žçÿk“ Å“kÿu:ÿ}æØEd= ²°gP7   E×.z(;dè$Ä[±NÚõÃà?Y›âw;méŽVkssŒ�(†Æ>|øð-]3$5£àüƒ~m‚Ψvüƒ¦ê¢q¥ïíðv@@YØl¦ž\ˆ '0§ Y_¿¦|NÆLe–ð»?7?Ç¢Ý% €«+Jßß‹œÑÄi5šr�š*ìž“2CS(ü˜K�Ûµ(†‚z¡ùYªt0B�œR¸Â>ΰXi\ ¶80ïIÒKDàÔ©3%rV\NŸ–…€\¬Ú¥;x�çç”àPŸvû9Ú'¥öi7AŠ;Ë*,ä €d'€)QLØTÔ¦èd\s á0™_G[÷ãÛ¶ÿr×̓ƯOh»0ÉÈFà�L·±±Á¦6B½Gû´½äEƒ“‡v€³èˆW6È4@‘òï}Ötê(Ú-…Á¿8T*ä `š¯ÐŽ.Y`�4úŒµ$Žk4}°ßŽy¨¢)8mÞíÄÑü¿=½¨Œ8 ä9Ûà™ÿç T�Íü_§6sµ:Æ4´­+ÀM ‡ÎÅ'N/“WÌ3û—+s'²ÈN€,êåß{­ —ÀÓÓíŸ ¸GÓ5Ç‚jÔVΙYÞÔèé“­€ÂD0€BsM4`ï˜#7¤Q÷y40iDA«u)öÜQ ÈÔ›æ±bj™ÿ/ƧìT«ùŸzûI�ÁO›MBá°”¢…¤� ¢Ž¢Éf5Ïk…Ž£¶†F¶ƒºƒ,•øH€Ó¥KŸC�`褯 C~_9;ÃW4¨? €åùÀLNÄŽ`jŸ ×”E!^ÝZDãJÛÝᯰ]X\t±“ô0íÀ Æ”5M›šCk¸Ã`Z¤˜ó÷*¶%gªDšÃÖ £ ,�…‚à Yº3gdí·Nh¼œmwæ Ç›‚é ›œœÔS�,ÿ�L˜pE\ë µQŒY<}ÝãÛäÖ¢jcàU¬Ñî뽪`ÙþÏK s$ô“ó?:­äqÙEXÌ¿ ½V  ΋[í¿tÝô”!’ýGè$hÙëL³®®ž§í±üÝ1™ìrË@§r]ŠÖ´ßÏAÉÍ69îMS°SSìuÇ_þò—³‰Må�jœX2-{ßëî;ªöT¡})Ú`2aòƒŸeL슠<°ƒë”Á!�6Ù3œ”D÷×/|­÷Ä@ IÌϲä�9Ü�ùäDÝšå&è "d¹¿ÕsçÔÑ#7&2Êe™`ð_"«â…‹�l··h+ ¡Ýœûû{ú@ ›µ(ɱA`.À­.,LßFùÿ!ÉC®É‘�€�F ºS½]Ñ9ÀE!Zès©£/{;u·Ÿ@¼Ú[£Ü”sç.б¤y³gdÈWù²c‹@>%[Ò±ù;Sßk[ó9³1nÙ³9²÷!, £V‚LBšD0Ða+ (Yô˽ÇNŽs<~�ìB”i%p¦¡˜xXT=°P€Ì£! @UÝ¿C…¦®´EØÚ8I‘Å´­)€V~ß«�5² ÀÝöém'ôüƪ|A4ÈÁ$îDÕ`ÈâëfØu (7†°ûDÃY �MHÉVÀ®7¥Z!__ý*¯œ­í57;Ë#6Pi‡@´®ÊeI¨U5íµ°"  x‹�…N¥¾ÝÕ)ÇN€&É@ÑðmÖõ gŠ7Þîîñ¶baŒ�Vl,L"ÙضÒ�¦ÈfïæÇ)d&w=o°"q�K5Zô”šst‹q_¯U`M+fi àŒ Ä¯}²•ž3 ”°»³E‡µ¯Ôi'@¥âv®†²fg¦ñ‡H�–­€¤DÈ€BËPnwu—˜B˜ /€†>©É‘yXE“ÞvÁu°±(ÇÛ ìJü©S§�|ù¦ý�Æ÷臥QàÙ~è$/}y·‘SZr­±bj&EI¼:.!nØ& @'a_ïpõÀQ ð€5Û±uÌQ®X ‰�…¶.ß‘‰JÝjó?uˆÍAÅç¤7Úp¡� Al¹Ä×_ôRXÑ d `Ž®_ž8iÙ††pE£f£ÓZ]\ð£_™Á4˜–ÊŸê%À€ŸÓ´<¢Í;6*‰ï�••ƒììÑšÞnbm›æåŸ¤EØè�™nÒÅ`\€<(X-j ­ù2ÿ'ìû‰Ëf^˜my�r3Kî‚ùpë|‹K²0`—"°ØéìY³/z H…_ì�±³-ÿçvAaN�”É$�áÉêd$M²f\JOåÒ»Üã¤ØXäÎEç"”Ú¢•ç—._QËK¢ü¦ Ä¸Ÿ$k'g-’Þ ÐAÙÑÁi<X8ÍUŽÜ{ï½>ò‘�\åq*QíüðâۥDTî {œFåZ¬ªµYWO™+!±ËtŒ\ Ò ¦ÑëÞÝü÷öÌ{ÐõnfÃ`•“†h ¿Ä<´9�³¦ W@÷"\ö½ k0`¢×70ëf³ªœ>}F€î5µþÐY^^¶*¼É¢1]‰g:ùq”Lw]£Ï µIwÇY¡Y§¦'éTHW�Ôfk°î~–E=&D¢$º^ߦ¤øÁ«Õ"È`~ʇtÞÞo @¾Mºcr—„qÎj±O ‚ø²MþÒ�‘6qZâ‚ûñ7QŸdñ¨[Å B‹Ò¡µqEš·i`9{æœzÒïJÓÊ]©À# Íâ•,}¬–X�ÜnÀ”g¼ÐºˆGì  ˆ&™V­ë3DH€" @ ZJ ‹¿m&ªX¡DN€ÊBdÎÊôæ‰ò8—^S‹ð�,¯¸.œò…ÆŒ€uþŸÈõ{öë×À‹äÒ{£í2ͦWÔ3SCÞQ¶¯l_FzqŸE9HòŠœe*œ$H^™I8m™ŠIf•ëÎX9¸Âf€…€SÇõfC‰à L�š›áï8 Ëœ,Ì€ÍÃ9“idoÃúøÊ( ñ€ÚT±†‹½ÍÚ¼[Ì®¨(�¶³“w4cË´@ó iñÛ,œ:Út—œ¾³•?Nñ§h i0@�5�b€L ’fœÁ+ëßkýúd(�–­AQb(KZ$2 ›0?{Ƽ£„j‹>[···h>²}kd´ÊžþÙ9þ©€•@NÌ"* @ê\è@{@盦à (”¶�¹ž±ÌωÄO£/ýî¥ïØŒãYkXk]±y (!¶š#ˆ«ÇuÆ;Hðuï Ù¯ä~ è.³é‘Õ"›¿‡«€´$Oƒý’ƒ%ægΞ3g’ØÒðÙ ¸ÓáV@½xšÙåL“/<…›ÔG±džQÒ€*ú tEôÀéc€3Ãd’ÌXÔ]%£Úºæ¬¼ã]h2†—¶äbejT¿…B늕?&jT¢U—ÖULÒË¿˜–9·vÞ8AýõÛ´¡Ô™ªÑÌ ª¤]3Êõ‡¢² °¬ +¨{\FLbR.]º¬à�nÚa•‹yÉ_¼NÜ"g@ô}8�²±„ćÖ˜\rˆ�E€4 …{E±‘^­3�7uWÆ0äž �ÆCTJÎì·~31S¹Ìm“¬uá¦hÑz^5*ѪKëÊ“FÜL»ùË}êöÿÃ�-=ŠÇÖW­�?w`Ç× ×]nV¢ä}NÜd¢ãšó…•b�)0†i_ÓéÌÒš”»JV€cÇŽ¦D�›ò€‡Q˜Û7®]s2…2ÖÁø=ëææÏ•]¡“Y§¦§¸ ÀÊ}÷ÝwðÃþðù£1� Ñð=ôÐ")�‡‹žƒø�‹)ã€tÎ2¦0øs;Þh `ö­±>ii wV™[w†˜"#Bæ6¦JüuS$ˆý/©"麤ï8 O“ûYÛ@„üh3ŽãMÇøüÅ—4ëk:ÞR¥,²Œ¢cŽÞõ"@F®è,aVZ¢AÛëèꢘp,/î ¹æ(X· (÷ᬄȋç§bnìy•¶N“/€+ô�&'õ¡@¢4À D½Z¿³bès’ŽÿE˜$mØØ%ˆá(¦3Xí�©™–
:ä¥L%s™g¾™ÙÙ@˜!sk`e¡0nˆÊðsSd2°nO�*V4\œ¢¡(0Fa=˜2è´ðœrYµìŒH׋±¾�(Æ Y²Ø/YÐY{qrcÀã�Ÿà�ª]LÉxÖ“eŠu²¸ºÂD…šæŸ @\1 ðW.¾ã’îFxL�ÀQÇ!€äv2: F^±²[ S¬´–1À=e/Ä«n-ãgS¾øÂõ7Ùñ*›Jvñj¦·—Kg7ÔËÕ,)º0PD0Deø¤)àxÆ6!# iØà—á‘¿Õ»cn%Ãü¯ŒÎsÌ-³É´Ÿ†2:4ˆ­Ü˜ÎõÁDjx¤‚3 nØ¢c²§gg¸ä9:<ïœÍ€ôŠÏÌñË‘�i¨EhàAƒáü?Èb/€Óðh[bhÑïq¾ìbzÛ_/€‹‹ðX ”­Û×½ƒ•#9]Z³ÓÏçj&¥s8îÚË¥™æE` –Îtê4ÃPðõÛ”^/”kÞYëâÄ@ÎÉzV‰„tU í_šj 2v½¶Á¤(˜Ä±léê¹5S‰+�•ƒü3¶°°ƒ *7ÌÍòw?‰/€4ª¢4ð€ =P¦ñIÝA@¨MÂä”íI[÷ûu¸ˆmÝ#O;Ág; 6•S�l€–²YR|£ -(¢ )/@³s®fr>gAŒ_®­Í-…UçE®É:h]%8t.>:Ý]\)Ê+æq�^³Bz¸-ÛÁ ”î¢vÉ-÷~` 7àL€NúÀý=ûtOV†šjÀz±É™„5DYX“Ù »>ÇkÖ�ÎA»f5{ªL4KÍT¨qëHneJwÆli’VVËÕ)‡ 8[´ºß5©„¾ÿÜSØÞÔ™# cícqSù”©l¹›H¹ÉEG‰çI+ ‰{KŽFtz @VÖuRÐ6h›æ,Û�ç�.[8¥¢’9ì12·¸uS2ñ¢•hÕ¥u‡õ)8lë @Ôü… @´’ûÿq@ñ—]æˆÄyÁâ'wq™¼šÞ”Æûmý:?êXºÄ_“Œ‰dÎ¥®K<Øp2Xhüê!xæ4æ˜íh6‹"ù8&{ÈÀ±D·¥ Y~ mCÜV›ÿL4NÌÆÀbLà_Ô««PŽid­01 LŽäÛ”Üù\©äV.ÇU{¹4ÓŒ™[C¹ ƒqðÇ4ÀÙ3«~é¨È ¥Œ4©ñ.²¡Í�É©È â+“×_¿tœ|5Åd oÆ^xøá‡Q»º¸Š·²LÒyã®g>\4œçl—¹²ÍÎZÚ8qD¢6Þþb긋p׎OiáÅé,Yãh]ÆÓã«,@/îc˜i±²Ÿ3Oš¼—³âÖHÊ_wŒ{̈ñWOn0 „Àñ™€"ÁÎ¥œ[;¯ß¿äÎ †¸$•p4�#¹Ufªˆ|®Tr+—㪽\ši^„TŸ¶B;8 žYœ 8ENí†:ÎhìÀ*â1AVZŸC�ê}* (E ŽCíW½ÓñQßÜË @òM0½b½žX\X 5 &«„Iº¨Eí)–7tyJ‘îÅ‹)ý¤ó£ÎHí©HœqÜä¡ Û@«?¾„"ËR³äƒdÄ@]à pXu;ÄÒÓ©zØëDÛ·œGĞÙô€?c|ÀÊS$Pš¥naÉéj4‹Èçj&¥s8îò¹|¼- €uSíø¿` ¨Ÿ‹Ù9ˇ�¡ð Ô§þ±!i¬¢ÆÞVÂ;ó¯JúÐ(½Ðdó¯Cš¶Œ) »Ì£€É=§¹·kÔÊ °«¾éáÎÀÄ›Ï�Šqî$ˆØ5D²S›eNÉ’¸qQ_ºtIaU3k €ðbŒëzüÆ—r*dn‘) ½�HÉÔÆ ¾ê1®ª“â†5+2=`otuަ,/DªÜè�qIÇàäfÑ,·d+ á6ö uÎNM°V‹:mÊ*xug^�ÎxŽ5‰Ïí˜õŠ˜J�zVßSB½ô÷”¡0Ö&�êÙSLõ§8gáÁ™³Ž#€ìa¢ãF1æK¸ÁM+æ<®ÁÔYsñÍØ´ ÍèŽ.€äv­GE€inÖ&�wðü…KêºëÒgäX›¡Žä(øßœ-ǽ€ƒ%©ÉÂ\B2W“4_»³Ç”4drjÒe/z ÜéN€=Z�³’ejÜ£®MÔÔîÞ®…"íì÷SÔ#z3ö µkდoµRU799U+| "ßçí½TIÆX8à KK¶)€k§ÉÚ¹Ä(‹ùš–RXƒ‰¡^~ì °eS°è‰gþ�ðU ’Ù’$þ€¦ˆÎÄ¡ˆ>Áºk—‘ÕÂÍ®^šÓ%¦Š4YDë Œm�it8ºî:¬í-™Àü2ÄøD¶ÓÄ´E··y :Ã…%Zè*ÒüfEȳÊÇ3"óÅ6bÚg™.%_@"½Î€p\º¤ñ»k€ækït½°±€©©é§Y£?y&Ìì ·9²½™šení\Ó)6îíP¥D°2NQ¥‹1Ýy�ç‹ÌǘŠXÕ.€‘â¦Ç‰T,8ió·~nò¥&scà°-Ì[-’5jÕƒzº×A �å( ][»ÀSœMê$@qM ’�Ú\ÿ(sþ?ê|~ׇ�!G.J×Å¢H¤óRv9Nœà €óîähD` €›Û±J 3ÔåXà±Vè%+ÔÑÙÀÇ4ôù£´¿Üå8»ÃüúíRá)€iiM¤àá™®(sëÎߢHKÔŠO_¹©R"XÉSTé"LwägW‹¶¦€ÀüQŸ.wú4À¸ƒM³II˜LSÉý¹‰å¶”®=ü™�HåÀ×¾EJÑáEr€s´àÖ[Žçx¤#Òm’L³§$©×íQ9 "²FÁÅÔ|éø”‰º$/-‰*õ‡K‹KêâÅ‹IjãuçS ÑñÌ¡p}* ÍRuãë_ÿú…ßüÍß¼b| "ÇZ ö-ܨ¢7qš4JrP8«¹ 4Ynƒv±` [Vq‡’¥NÞ§s¶¯H€g¦;ÊÜ&K-ºNKd£tS¥Š·�£]ñ5Éx´õµd |�:²¸§h ) ¼¯dœøœ 呈 #·ØÅŒ4 [  õšËF‰kt(P¡Æž”ÝPq;gS} döŠcÌ"Ò¯[1¥ŸtYjœ ÀQàÖ–ÍøP5KÍ­ÑPCz‚tãéá ~ .Só‘{{›wPìgò)ã3¶ ÀýÿôþéÛ¾pÅHüÀNM'}Q›_©Òb.Äã¹´L‡Z4tãã›}ƒ³²1S:Ò\û4�ý®•[‹Òºµg1¦d*’¹5f1DrŠÇžr˜ß9 @Üz`gÈùËw¶ë ˜ñÁd@Å7Ê,?0³Íå'KÐÇsØj'L¤4€$«ÆuD} 7´ àæÈÐ5 ðcm`Ð(Éž’%yë,ÜIqkž¥>xè�úÊW¾š,Ñz�­€jŒ5{¤ONºÚžÖà�5çq(m0ÄNQ,¸�lôÄ��}¢3µÅ|Tr}  cJy[sW 9—Éç¨BºF âè�ND¤)‹ïr½Q.¢8?¥¶Yrƒo:·«íÂdäÎÜ"Ÿ×-¾Vì%&S0@³IâÓÅ“|ñrr,!ÑT�øD¥âTüå(bȺF¾ÊÙ uŽÒ�%3S’¬ÁÀP ?k*Ê›”Ápm)ÜçTÀÍ Ræ ¿· ·¢ÌëÌ‚ÍÍ{œ 8æ[ÇÖ@ŽÚît½  çXh~ÔZ¨6QµnÛ¾âú@Ú °U²âWÝUŸÂ $2G¥$" 3f�"#3Ó·m–NLÒ9MŠÄê¹¢ùÿ¬‘ì‘€Q$£Ž-Í'{W§Erp†ÒIH×´ˆŸÒÄ%¶:DS&Št×_€ÎE�ê5™vKsŒî._¾¢}7Ôȃ§36U9jD<ô ÚY4¸Caþ¬ L%øìØÞêt+ M0Ú¢±v4ê@ï|á:°ÙÈþ_ Ü›lm­yÄ€ÉìX‘ËRÚó€IrL43ï„¶`yÕíï³…QqK)^™èDæV)‰È yám‹Aƒ,Q˜‰¦’WÏ®±¿:É€.#ZÙî,®±Ð-gô5í¦+*‘Ÿ;OÙŽÕ!ú²w+->u‹¿Å¹V€ .ªn gŸ�`¨Y”bjø8“ïß|! Q!å#á®”ÂL õ:tˆï `ssÃü‚9ª'k˜YŸY:§Ïç¹¥ó¥DˆA§¿ô|8> F×hQ‰áùoÂ¥¿b4ó mæÌ_pψÎÈçwÆXD´×Ï;ƒE{ È`)ÅÂË�`•”ˆ0�[£2bïÈp‰óâ3˜3 @4õý8S†Wâ» œtÞÕu—›Áû’;°kºÂ·«Ut<ìq,ÞL*yˆò1­ÒìWyä1í±Ì–`™Oä~ çs¦cbDÓ±Æ;C½è(rXXvw܇ïôr+à-œ¢>»a¬½Ž­€ ·@[]*t`�(Ãóß|¶ðõ_”Þ$t\`Eôï(Kµ¼D ;.µÕËxËoÉÐ☭¬w†,ƒÄ½½YJJä/¸LdÞ¡ÕË—.]æYbÛ7±Öþí Šˆ“hw?ë+J¨Ëa”P{HTÊNTj ÄÑÛ·\áä…³[l¢/:¾/€ó8;Á—©CÍ|L&‹ñ6_d"¦=–ùr,ó‰�)PÄ.¯Ð:™³yÖ™˜Í޽š­Ú½[qöSX~øá‡}ðƒ\ˈ<·c©Ðƒ<øêûo+jamþ§—pŠÌÿ®©í¤¥„v{g‹9 `þŸNÌ”Yø®Ç•MeÄ4Þ[L½9Z2´8š„1d*Î`bBqÅ™ÖέQ{`@œ½yG4ÿ¤$¹qÌ�Ø•ÎÕd§/bñoû΀’’¥ËèÆä�Ðg"dÍ y9pðg±`œc<\²Ÿ?‘”A{›¥¤±’¥¨\Eº©Tþ|‘ù˜T†‚›¼”�˜öY¦KË ÓÒ4GÀ³‹cÎ'iKu»Êbµê®Pä  Øq[R†ÝpVQ’ Œòõ}¯»ï¨ÚS…KE±Ÿÿ4mtu8ÚUeAGÃÅÒo`Þ�~5,olšÈ(š%gž¶IÈ`šÏmˆi2ôGÓ"B‹£¡8[)�L««4ÿ ïTM&Æv$ Í‚³ƒýÎ.y}¤N�f蔳«WYÇœšÙYë`&çÄÆ J–v޾¾€[±U"Ê…ýá1nY>¦{`¨«ÒÒÊLd:‡8$nªÔ3f%OQ‰ÕJóÈ’/6Óbl¿ÊÙˆ±°ƒÀ äpÒáQµˆ”ôËîÐB@þŽZ»àÓn¾£GÁAsäj=±ËØXÃü?,Ø`yúÈÀYL1EƒÐñ‡{Ø,ê)€"†íK”Ιï𥦠›ÑÉ‹‚Ü-²&ƒa+—ã*bÚÇF¦Ü 3D'ë‰6+e•s—¾ˆ²J��x™,>W¯^IXˆ,ÂØ¸ÅÑ9mƒº�­)~™ÌÂIf÷�<“‘ë×6 ¿dQØõqÝáà ‹£AÚÍe•1 'ev]>tX[͸fýõkël›MAU7ûJ"F‡s‘uáê•«j‘«™CšÞ´ Ôœ/›i‹Ìm–ÚvŸ–¦3ª”VÆ)*[�­xù!ŸS·èL«,­blWÑNŸSƒÈ�­Ø‘ŽK€³÷3ö7À®§¶¬)€­-¾ ¥ehÒÅ/ƒ5µ™`x£ �»•Êš``â�Õ”#×YGZ N%³7¯-9›éIöçl>å4/“6*„ã h‹:Äjó î¦�•fM,J]%%`{gÇ”¥½¸f%ÚËŽ\5²˜AAÁÕI(Š8bñì-c[@N1³´FV»�ÎÔ> éœýV$P«´Déz¶îÜT©gÐ@~ˆÚœ°�+™‰§Ù�‰R©š†ªÒ9.,ÿA(3Œ#ëlpþƒ€“¥ Ï}t^8ú´ˆ¦Iì¸àžP¡ƒ Ø�v“HæIm¥j&¤º‰ÿü¥•Òš�çÑI'ÒY³µJDh‹+W×ó€X¢‰yeçé¯n†€�òÊúUµÒœÎ‰9Æ…äÿÎLϨãÇŽkù #wðÌs*'†NÔ$O™“ôÕ-ºŠçý“[IWèë|�Vwî`håŽ[+Z˜éÆ 9Ï_¸ n¿ýÖ×U\H“.ÑL±]ð$3åNçl_‘ïŒÜ™[S馸´DyŠ™™i5;;«6ȼï �9ÚÇzfÀV@œ €c¡á �>úhåïxoAƒá°�Œ¥€Ç©L�`½°±ÀÔ èèËrþúÜlSA™¸……U¥­.Å!ÿƧbR7Ŝܩ®n¢ÅÁJ©øBY)­ -ZWViè+;Z\ô¥©sëÿÒý†žà|ÓÀ—µhO ¤>¡69A–ƒ¾¾\}ûóTwLŸá'T[À|€‹dðB6AU9Á2¦Éá•‹ˆ)­Û,¹Á¯•[—ܺµ–gNÈÈ�¹E8úæ7Ý Ö8¡½ñÓN€R©Ûž‘¿Z«ªiZ8ËTf>÷åÏ!™N´#×0çéa20AÓ#I ·Ât„އ÷ã´~T½´ýv5A�2±Ã9ÚrDæÿfVËE^¦TLêÆÂBGçßøTLꦈ'�-”½So²à f£Z]=gÿú§ª4‹¡ëø«7®!î9sïx¶àD뮬ÄÙ{ö·øÙNˆ‘¬l"º“ËK—.©Mš‰¬%ÿùÏßDE>tõ|&jˆùÿô㟯w�V¦ºhò¹ò1p:“5%ç©¢˜NVÒÚxÚãóµKŤnì\r&I"Mue©›">Ü4ž`6ªstš\Ñ “Ì—S¨f-Ó|’2-;ö´ƒ.¶@ñì$p”»””£ä¶Ás~îÜ9=u¦•%Ë@¶Ø ¶Z6o'À…‹´ÐZ9CB²ˆfY®‹âL†R\ [é‰ÌQ)‰ˆ•ûÊ(¢1²�—©tŸ3¶È%0ævC´ãªæÎNpó>‡íëS?éfLѹµõkÚÍÔ$ÓM¾X]D1>me2ˆÞV)>†¨„ˆ­R‘†,Ö¨Mrˆv~pÃ)Û,eøô·>§r†së8Ltc§Tê¡û`=ï46ÏW•µ`˜!:�I\@V†öýV$ CªOIÝ Õ2=‘Å1çΙ-iÖÑ�#ÙoÚpÌ([tS1€cSú×p„�êôé3êÆnÈæè{ÝöiP å]¥/ÿ+äÏ °˜¡ÊÇbÐ�0ž˜à|þü%uäÆvq�äà•K×økÉ”¯YœÏ;Cœ1ó×^-¥dxÜ& ¤Ã‡ýœu"Á>)Ë…ËeUŸ?`Ûþi¬›s]˜1×�GŽ�ÀÑô"´`Ò} pYS^Z5;'J<ï™GÑž’!ìøv8 T;Ñ“5P@Ì&­VƪfS½²9L‹ß°]‰¥Ñ Ú¸-±£áõ×_g”­!bþx>;Lòs4!‹_2 CÏN¡$Ó“×û‰Å‚¡Ç:€‹4 pôH¤°DMe[<)‘Ïu‹©7GK†ǬÞ² ÷öR@„’0€…}œ)Ì-¼W î¾—ú}3-D00‚Ö\ímÒ�ž‰Â€± c§P ;5½‰ÆÊ¸. èä“_%E´®4`é ¹n¼Eö—ÉžÒ”%óngndæØ&�/Ln;ìYƒoã³–ýÿ&D±4rSY‚Äý¹†ïò8àì¹éèQUe¬vŽóýí] š¥ÀÀpúÌuùrëË?¦Ü¥­]ÜöJOhÚ€ÖÞ¤$u³×/\¸D£ô4•™4QŠW|cÉ'GSD ¦éÜ–»SoŽ– -ަ" ™2°cjaqQ]ºˆ—Å[; Ø×ÐnÈPpÅììŒÂvSF¸í‘G©½ï}ïsmÈ`6,$¢ZªF2¶ÙÅŠ€�DGí×i_jº²‘:ã·ÈO67”¶0óngnâ˜cs²&:…Äe‚Ì› (í²SEb�eþ�Ä5}õ`Zˆ+Ã9ô R4Ïü|åk_W×]wX-ØÌ˜ÉѰ4äÊg´¾¾® Pe\ÍBM‰Î¸tnKÁi"²RñOÄV@ãî”,"ˆú]ñò²É ¤^:ׇqy ÝX)a=¸3óÜæÚ ZÐùjb߯˜\a�˜{¹a™j–w3ù%a%áèÒ˜’|,xY¢“9× J‹Ð–h>e63N�Û¦czc HöØ �å�/óí�ÍÆ"¾"Ùœñ=9Å×EiAcaL•Ý’Çì_F=±URíg* q;¹êwñâeµ’ò¶h.8›ºq•àJç=Vªf_(+¥5ÁT‡fÁ¦ÄfœÏ±À/Äù¡Ù?Íš»Eü›Ø7–º¨·¶ò–{•/<“º)*9�a*&uS㓯ÊÞe6YÀ-3Ö¬¨3§[ST6)6i�(ªÒl;¡%ëHô6À¤¦n¡ÅB@œVÈR”ºù¡‡šùЇ>ÔÙ^E‹,ƒ=V €bœû ¹E‡•Ó¤8%‹"7À jÏN¡EÞ|{ÌÚ½lpe²d‘µÍÕÊm%Éä0ßšsÃ/ÿÿßÞ›Ir�çb'³öÞ§{ºgß`0€Àƒ•$DJ$¥+R" �”d9üd†q#|Ÿïƒ}!ÝGûáZ![–íP„­k),Éa…}©+$E�Xˆm;³÷LOoµWeúûNVVeUår²–s€žÊ×e "Zw Ý9°ße‚ wä{`öê¸Òvêyt=‡ž"þ¹ž;ŒÄ,FOî}[\d+µ$ƒm©õ-ÅmµàûÑ^e:Ž7@-holÎlãdȆ°œ&}ÿSrH1!¶ŽÚSǻ鈠TÁö¦¼g$þî äÍ÷;¦€_�Có«ê“çÓ’O–OEŸ¬—Ƚ H¶]µ‡AúVéîéÖín€>“•Y\yª&zî;rh¯XƒRàÄ)WàüwC±Ùž+*¶¿5Å:Ÿýl&-þ¦áúX%Õ`v¦BÌø^¯®®áý��W¥aYF½¤R“žçÐsè©êŸë)àz†å9ôóÏõè¡‹?ÝUó/بT„€üØF ¸îHÔðJáýËÂ¥v .ˆ#Ó]æ ஑@° <¹®U3Ù>Ð!¢Qùù€oÝÿj1†é¸co“­b8©+£ãzðio5;°ê8 î­óÊö2·o·{ì�÷œÚýa‰EjS‰vÈÞ­2�Ò¶�ânÆ“@Y7!qâ±ûA k¯¹ÄÞ1ã¢r¤I‘ØPï‚Û&8o>Ïv®×Ö×|½-ºóWÙ²qÇUXbþ¯˜%^Š€ì€¶ã•hÄÉöLÀsè©âŸë)àz†ä:Zì¥@IDAT9ôóÏõè:Ü �€ùü¥®üÎŒ"‚Ÿñ=èÕ“%ëòyRyWPÀ�Ä»¨Ä{Æga ¸Ö9æq<¿kܼH€iá¼E~¸øð à‰(lj(·|:íHW†O%Ÿ,¼ïí5ãÜV{«ÙÞ{Ç`ܦ~;î�â`Vîа³ù® Y ÁìÞ½ &D�üè¥ê·s´;ûÜÁÒ ~Œ—]C&ï�J§ÄlzV®(oܸ¸åÂ-‚0BâÞÁUOXà(i`Û ÜÚ2;ߕދ]§h$à9 ÈîjÁɈW: 'Û3/Ï¡§Š®§€sèM?ÿ,šà=+•Š Ì“]Í©fðÞ;ŽÛÂk$ñ=g?·aâ«’¬„E�€¯ª”õ2w �œ$‰VF ðáeT U@¶´ÐÙ—ç}ì¼~Þ5¥®Œðúž«ƒ’Jô6—Žqwœz†Ù<$SÇ=b•…Œb‡XPš�› $þAut¾?dÊ©\víÚuÿÈ%3–èpäöÞz*¢ù.• ½ä;øNº­„ýƨ팧ɀlO ïa£t@·ÙÞÇê%}*·gy&°Ã €­‚É uW½í�ÒL”$Oç�Üs™Ž¡h:zšpñõß‹/�`¨j?‰?WÒL(â™’âÿˆ2ª˜•Ê ûR�Ƥ/óî%ªsÕó>« ÍS!`°Ñ%*2»k]!•[—¶Š‘ £U‘bXà&2³1�´fª�‚Hƒ¸s;…[~© } ?ß�OïïÚb,Ì·5ÓY®í¢{ø2´jqÛˆü�ÑB«Û¶V²ÛÊ8'�’]dû´Ó^’[ª‰!£ÕÜÕ,Mk»¿3]ÅdFœ @pÝq×ĸ+$"Y;)¬N­¾ö¥i¨b@Íãöê=Ÿ•·HXÚâtö{@<ïs@‰Îžqî©äs•YÑ%*v ¢+# b{v#±BÀ¼« ͇)’Xw¥x¿ïAŸ‘1bjˆ¤é'¹ñ»•wx~aAqxž§Ö¯±ŽVŠD¼N ž^;zP=�ÑBÀ ²}Ð(Ùèriq�Oÿ¬~-qSuœ³1œA%E3þ·lDs-3rÿße2r |ž’ó /¢|U•˜šœ‚=t¨#OŸí€ö3O1¿CÕ7ª­®O¥ŽN}J´µàœtTê©„O%fu  =ã6‚Å´‘öËm�VaΔjQJ‚ªëüÂöø+ÕŠ”Þy«JfΛÑ8^ƒ¯‡�[+KµžÂ¨’>f5ZUhR¡ˆ#Á¡´æ8°� 1j7½{ÑqÚDiKT*äa ÐGRñÅá6O}>Cζ�›ëÿ ¢»ÆÐ]!ÀÇàô­ýïv#×õ¶àJßkÔ^vOB[�¾XRŒ°‹b˨/CsŠíÛÏ¢ÇÔ*ÑlPfµŸµJùõÔi@%OÇ%:†à©ÐqÅ=í,±âgÐY'(U«á¡Jƒêéüpj4� H50dmÌÊuß:'‡¦€Î»Ûù´ï®Û¸\¥UÙsÔ^<°UO •ÃF«ŠÍ)k¨g­Ú�E<¥y˜Â"…‘U\/ú4¤5‰ß�î’ŒVC�ù¸À›Ùù'ïžS©s® Ió�³õž³=¤ úàÛã÷GÁ0 Aì¹ë r» …1?¼™ÇÀ2ÞûD“Bï¹sì<­ 0Îs ð0á4Êòþwýµ.»ÅÜß®¢nõà*nÕ¶ßöG ‡q·µæÖ÷´êf…üÎuš×óZTT,—+R�3¤[ß‘»åù@pEý¦HÑ·¦1ûìú¬ºW#”£vxW00,ŽÞ‡˜L¤ !œÂsg!õzËUi¿7[UüÏ~d@üv®^Î�ZÈèÃÞ¤8×ÚºhUìeÌ­:ÎèÝÖÚºˆ:q+uüÖàUlccÇh4zÅÎ%(UWœ+ÿ?D+47âå€2:ß» \xoªÕr(žd¼Ü{öË-ŽŽG ðœw1<©¶ä)פ˜Èk;FOžbîq[oÿânµ®ßöyøtÔUC¥Œ§ÕFñ¥¥81ü;=­†ªºd§‰é)õ @ÉRò®�Œý€Š Ÿ0šÉý¾m!Iî!E” ©Þv©¨(þg%Ç P[õîItº³;sœ†Ï³³Žß¹Ó]H§ýwátÛÖE{£íg~£ Êk5ºº_ý Ü$¡ —é †«Î°T€bçÍ›7}„í]‡µ¡¯Cl¬¯‹[Øž‰JdÌ2TÀŒ(H Ž<ÂMøhh¯Þ›?‰ö6Zg� F�¾Õ–< ,îßSÌâ�µŸ¶zènUUÀé5pvv¶½ñgRàÑ» Ó=ˆÈ6mJ�c(#Yt옄 \û'*Rl˜ÍE@\‘SpkêWÔ´í¯TÊ`¸¸÷ý\�1S‰âH@Wa ÐÉ䲂R¨û¶³ÚÖ%L— )%S…"Ÿ£vSáh4bN °xpOaU–bÄ KàÀ¶§_ïãáýFvVŸ›�éÌ <¿[,ÆžP1tãJgé8"I @DÕËt‡©švíŠc»ï•ò}Íe¾W”†œIgÄÔÄ4œ+¥”ÊoU!ZpäñQÍS©ñåX�@•@;±‚GËvL0éFßÓ:$%ªõs“9o¢ÿ é®`Æ_ ÐŽŽ@¿ãL×ò} û”ûPÝ`0™©©ÉƸŠi8óþ×5_&¥¿ˆ‰ârÜÄ9¥ÁLíTâï�%®Ò•†®Ãþ,|l¨l&¤ï Ó%þÞžôñ p¶“ ´ìÀ�çå’#»î5©ˆAQÜÏö($>«d.ý¾M½�¿£%¥o†ß·¥ãsƒÁp+f÷n5�€ROÇh”Ϲ Z—ÁÞH1�lè�)Íbã¿À0ÀÝnàÚî–+¦–>Ú®tŸ * [.ª„§D9ǰ{,Þ¾ñRp ~_å[¥Ô ,Ý,™Ë)¿`Í:Ûu@IÅH ›Ø‡ä0"Ñl)HͪÔ4¦¸Z§á!@Œk> sµÉ?'8Lø½ä™çl&zËox3‰ß2£”Ö Ý"óîÏ[oaoµ:°nœ.aàúõàø îì)Q¥®‡Š6·Nç/#k2àOTr¢NŠõõ�¨¢¼ž“úcB\R)<ªeÆŸP0LCÀp¬Üs KýJç Ä"¬^£.÷“UÒ®y:êxÑÂ*ú| Êw^‹Ñª¶—N%Ã1ììk;Ϲ¢Ìƒøs $Q‰«Ì°�¤éÑFŽjK_ï bt¨ ÉwÙ?µî/4?ÀT þmmOn EIkj��ue( x�ŒÄbS@XÈìJ£ì.$õ²°º�Jdò§aqpM�1a[†aq`¬€±ÞÀ ç›pœ73,Q@@~XäÇ…µ|þê¸C³7l| ^â3œàZoˆbÿ‘ïwd9<ÅÞšSá*L=hQ³Ú¶”AnÝºÕø¦FÏ”ÑËÂtÈü˜F�z[¦8v�ã {@Ïšª¨µõ5ØÙWabW EÀ|qÓãâã¶F?ºxPÉóY(ÑÑ–§‚ç uT“ôÏïÇØ(6 -‚æÓÑF’ nŒukLîɨ»×ò b““¥b@ls¿æ`V£ö2zjwTè~M; xª¶vWl»Ü¹’(WJ"SÍîx&€û†ù‚–„ùhaä*™uL¾í”e’03 Ü«m+­Oâ"À{FŒƒ•jU€õ†"àÆæºTÞìG -h<ƒÊ§€ÍMlU)Z/mëHq4­G¾¯ Sp»;�û ÎÞÆé °Ÿg €ŠÞtü&­y¢:5ÆßÀX3`ŸˆºÇn@úˆzö]À¨6U®aO®šæç)N‹�·µî×>NmoK�ݪ´¼‘_“аߩZÖTÛćŸ.aiׯB4Š…bàÊÓ‹W R…$̵ïÞ+ú¸W¸åÏ{¶úgÛ|§(PaÀÖ‰åi_O³Pê%pïvF²%³S,¥–¼SØKÜöb¶ ¶Žz™U[£ ›ž>Ð1¥Ÿ}ö™'Óÿ°Ç\¸Jï¡_ ü.3©ÌUZLMÁ$}*D¦c?üáSú§ÿ;²‰�]`§<íÃAIÁ°å€&Aá�P�a€#ʨNDU€Äi×®Y¼NËí¯dPo(¬V0¨�F~w#AQüú“þ‰øèÛmRÜvìèq¬BÕ0šå{:ë¼D®ÿò•Ë0OºÖìJ…ø³p¡èD£kVì8 . ™Hi†S5jO0pTç:ªéÓø&†HÛiQƒ'@>ga«·¶Ö&¢¾ÝY]ñÿsDï;ï2Cí>zá‚Ø¿êMÞ4¨­í•oo´ýÌÛ¹ÿ1ƒ©0–eCñàë¤×$M@ÝÂÚ s>5©Ì$×Ö–�¡½ÂÚåkãÍ2 Cèý¡é·$#ò„ó½ #˜A}ÑUÝ3P†ë§€QVË ™ Ûi{Ï™á›�(Î"Öö¯ý7ÄéÓ÷ˆŸüø%]�QÜÞzûMß–ãeFL†�ué8mëN•è{+9÷ËŠ:gÁì¸Lš·ž>,Ó î\鯮®ùJd(rVa;ªá·–ΤŹûgï;+M–{í±õ·ŽÚÚ {qÚ FŸ,-í‰.Ô(‘‡ƒÉéÞi ¨8öYĸ|Ymhu‘ €fÔàÚq¥"M¹DÑ_T >`ŠÏW$;vøªÄ @íi0£Pf$ØyH—�Ÿ‘Ç‹ï}ÿâµ×_o½ñ†ïGº}>½œ È“í9쥃®:y¹Е-rØSœs5‹;Áè.®s€@.}�9CÐ…sgRu®ÕYo§žs;ãÔ©S⡇–žJCÇ©ðüµÞ‹ÖQh›!�î‚;�`ssSìÙ£Î0t+ŽIgE@SaÙ9–Q:[ ÀsôÜ>„c µòáËÅ¿‰ ìÿG,ßÿÑý¿4|8*]ªª¸2¿À€nƒ_½Æ#Z  Ñ˜O¯#A—›�_xL;zT¼ôâKRÑ.f³=oÌ?�£Ú®R�ÁƒÚ¯ó™ájB§­G —É`ß>-Êx—¼‰¡˜Ç%탘ÿ1ˆû;�q~¾¯¶|ð}¯ÄƒD± ç= ~ÛöIJ "`p[Q 7@¿ú~ßΙᇱVkSÀ±eåÄ a†kísõÏ$ñuÔ3?Hñ¿êêŸ}˜`ðëÒ˜Mp�®÷)tþ¡ƒ;Ù³´W<ûìsâ HÞ|ó—ÊLO×à¶9ƒG¾3ÑÜQºÿí¼ Ï·†ïf o*ÀËߨ'n']}ðСÀ©¿uÁWüó}µ›Mø^õkÆ7�J€d’ƒÌ6½•dL€f¿Þ+jÇü>Šž8§§§•Ç%l㵌f©±eD¢~7/ô®8>àô>ÁÃKÒu¯t"´AÅ‹E(”©¦yºŽ�¢fÓÝ �ÆÀÿbw#Èéüd$’ qþ‘óâø‰câÇ/½$nܸá[o'gÒL©s^¯‰¹é´}Pc¿ó¾ä! Á‰’èmߨƒ{æ÷èK_ú’8s&hŸ¿s¶>m)ñÖ µÃ¯zÛñë6…ùÌíšw|¶j¼uyL—Üê½uÖÆ÷™Þ÷îK]9”e A¢PdÒ[‘íȈÊ~4,l+M S[~â#y±ñ5U£Êxˇ«*² †n}Ìü^³VO¾W}3[uÂ�T^§öükØÒ›áïüÎïŠ÷Þ{O¼üòÏB5¹Û[Üþ3騗Îdƒ)äGG§íA€ž9;ñ¯ÃO·Øø�•Ä÷ûø‰â£ S Èÿíâ<›Oap‘8š5:ò§—;+uKE@€¡ÑkÓëúeél?ꜯ¡ÃøE{·§RõÔä¤ ÄÁï|ç;÷w§¾j‹ìº>¶< ‘ €®v½T* z‚7Ëñ8˜;GQ‰qZ)|�¾W}3ÙbøÞuµ+£5ªè£Ö Μ¹O9rTüä'/‰Ïl„£Û~‰ü&}t÷S*A—ùŠ’ÇîtN_Ðç‚ß}¡Âæ¨0ûöî�=þ¶úvõ…Eë SmÆ¿Fó1÷¿ìÓx³†Ï5!öÀðWï¿ï{­3“ŒöìÜlg¶ò¹tCÐ��qÛn Û,·nßViÛHO¥O¢à[*…G­ÌØ2¸‘ €Ô€Ðˆ‹°ç��¸ô7= »K§2ª©Û @µfT¹°ûÈ:‹‡º®"ƒfsßøÆ·Ä'Ÿ|,~úÓŸ(‡EŽšÑ°®o@ÐOšD¦�­©>ì—‡5æqo—{gk¦ë “Þçæ!zÞɉ+ЇyDœ:ÙÒ1ëžI3ðãˆb4øš¶á_Ã�K,S@XÌõÁØ”)zâž›Qg4u©¨€Àg`'^°Áø?›ÍÑ2th–Á†%' `Dca t\£:•ı‘XÆKî«çYã·²â5¥T:—®«žŒ'NŠƒ‰_üüeñî{ï*õ¶… tU€!c˧  °“]ÉnfÃ쓌×Êí;–ÛrÝ=Ñ©ÌNMqŸ;÷€xðÁáà¨]‡Äójô>ü†Ú{Àã¯Ý>˜ w.q€M©ÀéÖŒ7T–f (Ÿ¥‹oC31 „ŸÛ˜c)øÞ÷¾Ã~±×÷Î72¹ïÆ�¾¡£…AŠÿÙ½ªàÂîNa3r¯µ^ æQóÀ-£úë|Fš“æ�jýèr”¾|ù+_G�?þñ‹Ò�Pt-µ·o¯Àò€…Þ“ë‚6¨î;.£Ÿ‰‰œdÖè �~u<®'À� Æi¸õëe�~nÞ¼åwI)� ƒ~V¢~�ð{söü�<ú("�0õ‘ïWd�f×y1fõ›]·ìÙ«nÛ߯%@œï4--T¶øZbÕJ#Rn,+i‡`è;@�[2ËtäÂß°4Èô'@%%•´°«@¥eÕ20MxšªõÝr­ÏˆqâØ‘�Îiq÷¢xüÉ'±¾ÞnÇûÕýštj­»bGÉÈÍòŠ=6Ëwì†3¥aAœÜ:NÀ#÷,þ/@£õqœÅ1„m�–Ä¿ÛW{6Ç#, ÊÀ0Ê\p�1â(.ìö*nžý÷ÜúŒÈ£Öi[ÓÔÉ BÔ ì‹¾ðÂ?!TïrÛõ¸'ÜByèKç°\ïýêáXdá•Îl~uÔ?¬qç¢Ë·#�Lš�à9~(…é7ÍÏω{NŸ“±·Ü‚{žD ®øO�R¡ÝsxM‚;t¯tTì¹åîŠnžßðB”|,.î×®]÷Ôñ?Ü„) o%ëô’h©%½µ7ßÇàV’‰””Ää}|ùÔR¹�>Õv~ÖXJ@וy{(— 6€êþ?Ç6ß嘹ãŸvïÞ-½ IÀ«¯¾Ò·!%Ùµk—øðãOÄ•+­@@ý"I{béB:üØo7º~ �KgL�΀ŠûfO@JtÏÉb÷™m®xï¿ÿ~Øô?$j�Gƒè©ö�‹“ÙÝ@oä­ùTì¥õ%¸øUa(%(Wˈ"J³ìÞ�5Œ¬Lÿ$ >¤È,þáþᮿüË¿ìö=ÙÓÎ.0– ?{+ p˜Ý­Ó µK}Þ…¨.|¯—b9Úî-ß)lI&ű=ô°8~ü„xñ¥ÄÕ+Wúê—{ó÷Ý{Z,A¯‚ÛJN@Bzä*%™€òh÷1¤–¾4hPàå»W Àçáø±cØzÚßóŠÓo.Œ…ñä“O Š˜Ýk'=äC£ö¨5P+ìÑç׿��áù´ádí‰ÛhÒ,;°µð t±Þ©\éWÃÀ÷envÒÁ›~—»ò�’z¿èº0âãÊDK èÃÄ@@¾¬®¼êüG»ÔSÍ÷0ޝòyìŸÝí‰Ñ¿óíßï¾{Q¼ü³Ÿîûªâ´ ÀSOî}ü™øüÒ± ‡Û;jŸÉ-¬‡Š=RYS%‘q;°¯8y⸠²ß ¥VO<ñ¤Ø »þîÔ ÙôkÅŸ(w—DNH—j­ Š‘biO„îƒgy˜Ë^“t¥X9Ž%€mÊȲšPÄv»‹E3ØoNâ/q]XbÀ¸«‹°ö®T5-ôäXµõÑ*w 9zL:úô“Oú<‰÷éSÇÅžÅñö{¿’nH{iÐÏ7@/íè:ý"€;¡ °€ÀZ÷bŸ�~h¦{þü#âÞ{ïU–$„àЋ!T=„ƒ’H°ËÐá6Æ´g�Sä?à 0ý$'(�Z q|Œ«%@8õSÃqG•‡o<ÿý玆 JŠoAøsY¸ �x§l©UÖZ¼kÅ’@1W¿:µ ‚Ö7á@èóÏ?/½ô¢tÂÓºÿˆ^Çž|ìH.C"ð)â=¨› Æ)dºFÂLÙ÷ùOcÅ¿gÏbœfCËòý¤WK*ùQ!Nòÿä4H©ÿEßæ#‰o`��øöÜÊTa&öÆ�0,pÔ7¹Õ{÷QÝêÚÕ]ÊÉ™ŽÉs\}ŒðüóÏÃðÔš ºȩ́’ˆ²ð»õ* Ò ûV•ÌÍí’¡ŠYG§vèJøûß߇˜p „­�~¥ÇŽ‹Øxçâ{bŽ}TþÑ­¨NÛˆ€aK%Q? €³ÏD9|h ïÒá#G°Ïÿ”èŠ+HpUð‰úu·á_Ã3ÿ] yjt]“¡¢;™žž‘>2T¾}›ù�Èo²ÿ �Ü8&ÛÓSÓò¹Paæ1˱´;ÀNØÇñMMn`ÞÀ– 'µ�ÈüAîÿó#¥êx|L=ðð0�Έ¯À�]©¾øâ Ü«}µN±ðc΋˰xÿƒ�"í–y/ËðEïh,w?7} FWVB€¨—Êauˆÿ™¿ûü4ëË rŸa·$üûöï÷_àcüAò½â›éßewnà º‹6rükxá_ «=O�®kÔøìÓϺò;3�·“RÚ^’4D}2öQ)a:–ëQE¹×1– @4JÑÐ쨦ÈÙØ€ 4ù°ù?pMïR¼Üç#^ù­TüÜ~'@~£Úyyü?ÿü÷¥Å@¯ wV¬èà~ñ姃~‘ø% ùcô2ZÈm"¹ÊO þÛ* ø>A;»í- À“O\ÜßÀˆ?ýIpÅÿìs¿'‰¿ûùþ8(‰ìúÏ!ŸÅî¿Ö犟¬Î¿®ï“ïXT3ƒÇô!ìšGsfBìÝ꘵9(~¥i^üî›Ã¢)`>íÑ�RÅ4 ³j“Plp'? €Š¬ ø™ÎàÅ–!w"ŽH)¤yIÕ0 ÷æ8jãy�/òc�=Íî“Ò�Ðr¿„àžøá‡ׯßï¼û«PÏ�U0ÕZAšÑ#`ä5ž·`Ëfňï|'i3ÎwØM9¼Ë§O‡†¿ºÂ™[7藫ȳ÷Ý/½p~ûü­4�šA­ö“O*œº®ve¸uÃé{Õ7Óm/ê×qbä77@˜§¢ ¼NSÀTˆr·;2XsssâêÕ«�my/F�R€h�FÞJ;üxìà}, óTCiGJ$«\c ^c)¡ã�â!°ó¡ï>ûœ ¡×^{D¢¯�ŽÒ{÷. jŽ¿ÿÁÇâÒá~¨}G¹£+}Ú#TÆ;×½'�Qûªvà UO>õ´˜}[¶yÔýì’ÔP±#öŽGÇC€Œ×‰Çä>¿ŠŸwÕÖ<(žzâ)¡]n«"ÖK¹#±wï^¡Â↮�ÝouÜ^¹m+ƒá¹ñ¦n�¯O°0œœ««JfÄ'a �ø«¿ú«þ”‹¼ƒÚæã±b Ë8¥…íH`˜›p"¹…Üú•n=¾!/©J´ø_ИÅí@èÄñ£PV‚4à­wÅ­Û+1G£‹G!ÀUþ>à{ß™{ºµ÷q�ýÌbß÷4ûOÀjD§­C€Ûï½÷^d‡Ü&(äóRbY8 -ELi•P ‘�À36‹íBE “HT¡êgá­ŽÎÕ±bLÃ>N³í°Ô´�â¾pòî¬ìÂË„õå½F³%%1*i@/rƒ=v�FŒöè@h5Z$6º'~â±óÒJàâ{ˆZUÝiX»wû5îóßåËùyu-í(Ìxïzè!ñÀ*EŒ‹jO_�‡Õ´±±)2UËw–£EŽû­ï¼æ='“ISÀÏ/}îÍ<®Rð³À#va¬aGKh@)÷í—�ƒÜPŒ;-Ÿyhè¯ÑÞ}ûÄó¿÷=ñË7^¯¿þZ¬8�ƒãGäÈჰXo½óž¸zýFg}®ˆ€Üç?}B>t RIW±IÙΩS§Äã�?)]ÒªÖÓå‹@K€ øèGü*=¸*®ÝfÁ¨&Ã6©ðTËïôrcÅ`õ<ê¡I5\ƒÒqHØó!µUÑ`X™87WŶÛÞ¼{¨‡ˆ÷ÿ¨ù}ìèqHþI,/«ÅRÁ¥}äKâú�›â�·Þ“^ƒÊêüv{þÃâDid¬ŽA¥ý`ŸÿiíXkP€öÑN @¾OKÛÂ6}”8¸1SMhr¬L÷¦©"8Ìr؈jžb¡$⇧|={µjÇÑ$mÕ >*äÕ¢²-ÆqWèuñ»ß}V\¼øŽøùÏ_ŽŒ5F?ûú3óâ"B 3Ò ŠéST›ã|�zçΞ“PÖT¢"í… �Á;à=ƒjR·Ó'»á¨‹:X*[¡ë2,pï˯8ßï¹u +K€ñb„8öŒr•a"°³Š¹�mƒƒìýùëF¡¨Îh€.ø†žAs°sçTd¨áË—¿è«OF¢ûÒgÅÁûÄ/ß¼(6äD§v¸Ïîþ3b7¼-*Qzð¥/=„¿‡!I kf�v |ÇÈ\»v-rHôÐÏ÷—Û·6¾ß*ŸpJîÒ0S¬ °”B+ ÝNBaö;µÌ3ì{&l|®Y‰˜GПkt=n~<'@z ì>óC¼þöo[|ík_—z"ýöµÛ9_û꓈ZxL>lŽoÉ»û_nÑð?ó•'JüOŸ>-þàþHnëhâßï“;œúªÛ”T`÷;ë-O‡@*‰ Ãéie‡@GAkÒ*íŽB™±‘˜fýh”Ñž>°ÁBË7 2 0;((Æ “ÂøÙ:m/®¡—_þ)¼ ~Ð×`ø�¹žë( xý�w`y°ÖW{£Z™+ÀcG‹ûî=Qðà>= 4C¿ý{ch™�*†£>î8Š€ë°X”ßêÞfM+®dÒñûÖicܾu;¬˜{-a%-n5¿ïfŒòïàÞÂmFÁ¶�ÃQò®<¨±�Ã^#ÃJ 2©ZÌïš�Û Ç¥Û F€„¾öµ_—öâ/ýøEÁXåý$м «Þ?úúôeyÐÏ8¶£.íù8wŸ˜šœX÷tàÂ(�zŸ`�½!U ²�m€ÅE v{Kq¾áŒ¡šL[zÔ €*`[Qû=G¢úq½ëQ ‚þCq ò„p!ª»æu*�Ñ»•Jš‡BšN; é@hß~ñÊ/~�€@o÷¥ÔGÆó44Ý÷#�Íëo¼ Ë¥UÇÎ$Æh¦§§Ä÷ïÝÓû‡¼³;îóS_ãá‡Ï÷ì-®³M}¾5Ä‘Òl°Ÿo°=Õú»b˜‚4Œ�%ÀIÄ‘¨›-CÀQP�?Â,D¤+ÉÆ €ª&¸¶ØšQÜ^È<>õôÓ�(ƒÿ$ ì†+á/Ã�ð¥ËW¤'ÁJ¥Xv/¤!m;sïié¾7JÚg~ôÞ÷ÄOˆ©)å=Û8Íë²CF`O¬°ÀPŒ놌ױ)à¹Dï�ª zäšPk«Ê�øGÆït•€à‘Ù’ø_�{%Žnè´s ¡ç~ïûâ 8úå/_qÄŒ�³"a?ð×it`”Î xÏTù&r  Ÿ'F•ù>ª,Ôàfl,ÆF€%t €¦$”± 8JC8ŽèHåUÝÿg[»õ€ ¤ÛZ†Ï?FB/ˆ›7ûóü—…Ò“p'L‚¯ÿòmQ,ªmm+>�/-í=p¿˜™™ò¹Ú[Ö$%�œTœ9sŸü@÷ÖŠ®µ“ ÒægŸ}9$FäV,¿Ý½$Ö•õ�èúŒVÈ…¡¢¹öØ0ÑÈô‚üöÔ ešûÿðlüÇM‘¤°O EOÎy¿ÇªQ ›Þ £‘Eîw÷»reêJ—úù~ˆG¿ñëψcÇ Ìâj��ÂßöùŸzòQñÕ§ñ§åĹû?øÁˆûî;«‰?Ö«KGY*‰+xÇzª÷ïpË!Px¦ S@(é*¦ý0—«Øé0Š�…@Þ S„ÊÎÝtFÁ¬„�{™TÄ]n: ‹ÄhüruB…´£G�Š_ìß�PªG‚C"„ª}õ—oŠõ�þ,†‰b:�gî9%NÃÇA¯«4¿ñ9rLÕ—ÁL(�ýšÑy;�¥={•G¶±±.È`öšT·(þß—À7®+mÑ›8‰¿7z×N©7 ì2�˜V8¤TÄ=FXQX@‹#,1–tº']Mç j^åC¸KÝ¥«#�±mÐw}üñGâÇ?þ1¬>Š}�eqq^|ãë_ïýêcñÞûî(“A©»pø€xž³�*ÑK÷ù÷ÁâB§ñE –/€Íu±ÏèýyˆÐ-Ž) mÚTÔ ÀNxL >¤°4x4N@£áÝ-œ  1@€±­UÒ,V<®¤B¥¼.³ó –: ØË/ÿó€�†¡=⯾%Vî¬nû„÷pŸÿÁ³pœ2¸Õ9#ž?ÿˆ¸ÿþsZÔ¿íwxøØÃ`s}³¯o±ãÑ5ü{ïÎxn.Tˆì“¿Ø  =€±�À1Ãú}K®@6b €Z ªb£°þ¼×Tu´øß‹ÚèSËY::yJüq¨ÌÔOš›�¿ñµ§Å}&à z‹JelSS“âKçΈC{_�uöÃmƒ³÷Ý/½pA¸ïgg}>~ìÝ»W2z*÷Üè'Åù–Ïâ=SM 7ca 8 €0!3ìÇ]uÃgsP bHüˆ~˨>!�rårÑåÔ¼ j'@1ÁÝáÅ�>"ö}ÿ╟Æ�Ð=§Ž‰û—Ä/^{SܸqkKfO§;÷ž>!î;sJ$zÔÆöèÁƒ‡¤ûÞy½ååÏXç‘ÙcÞU‰Öúú‚á{Îm«¨433-uYT˜-ˆBs ¯ƒ“<Öo~ >¡©Q�Í |nëÀÅÿjûÿŽöà{SF:“ñ'è@èøÉ“â¥á@èÎ�¾æ3÷·¿öå'Äg—.ÃdðQ®Túj/¨2ß™£G"¢á}0�¢åÌ`=®=þäÓâÈaìÚét×"@= ]š:PdD{JüÖóO�`“�Ø)ø°ÇC0.f€GÂrœ|¸‚¡‡²°Ä=£A¦8>æ9N§ñD`ÄžÏ?ÿ}點&ný$çcG‰ßúÖ¯‰Ã‡'’wÇ´€ð¼¿ñµ/‹'.<<0âŸÉd¥‚ßóßû�&þ.Ðwñ¯ª· úu¤ºeFSÀYeÝc7¬ÏFþƒÝ#[µÃž\N€B$<®b]†+™­¶PT$¢ ó ; X=œA"À=ï‡zðL_7út Ä•ùÓO<*®¹.^yí-81éÏò€2>pFzò#“1ˆÄ9ß{ïñè£cpè¤ {à H5Q`×|ïÖQªV]|橇uåÊ¥¡!-_V*¼C �<ðÃþ!þD¨P7 0£>ktí:H €¢¢ ŸÍìзdÀÃ’„¾û¬xï½wÅÏ~öÏ‚±ÏûIXhiq ‚ïCQðS%w¦Þþ(‘`ˆÞûî=)ú•NxÛ•ûüO>¥�[yAÑÇ�x¾(òË ¬c |Ý{eWŒ¨€–£¨/€[}¼¾~ó„û¡2U7 ð£V6* qæ¸C ·â ;ÚeùÒÃÝ¡C‡ÅK°øâ‹K}MˆR®ó�“ZúT”ÊS-r ‡î“f}“°\Tb`• Ðì§I¤N?T·XWåYöëÃÍ“¾Ü“ˆßÙ]1,Æ *àÈKêõÔáÈ51Ê*ˆ ͨú`„BÆ7×éîB`zzZü‹ñÛs DIÀo~ãñî{Š‹ï}hÒº°kátωÅê�¤Ólq<$xàÁ�Jî®'âî˜íî…ÝR±Oe~}c­/Pâ|Óç¬H5��y_#ÏÀ#Ó‘(±¾ Œ»J L”$÷ŠP.JJ ú€°\~SÍ ÐüÊët÷"ÀÕòþýûå–À|ÐTv=wö8Ú'~þêbe¥ey�ƒìÜÙ{Å ÄÔsÎvN�:…0½ObŸp’„¾@Еw4Üj¢çÇë ®w×××ûzV%-p•ç�=96Å(ŸšØö§Ì¶ø% ozªaB2Mä°'~tX;î5>DåJÙ= ý]˜y…ÒÐùé‹Ñ�x:„NÃ�Ð };Ú57#¾ùõ/‹>üT¼i‰þÙûN7߇èE—Øà€xꉧłŽb –.цÀÒž=J @µR¥r &Üáßï¶Æ;N¸¸K¡;Ų•V§§¦Åêš’×ÍS /,hd6’iä%Pê?†>m; ÜT¦LÄÃ2Qü¯úlP1L'� �üþïÿ¾øùÏ&.^¼¨ü ù¡GøžÓÇÅi8âñ ãº?ñÄâB"ë¤è�½ðð–xS©êúÚ†È.õÎpq—€™_T"0çTŠ ÀÔ~ð* _�jw§^yÀ¶M˜³îþ?oÅŸaß@2a×ãÞDUÀlW»Ž‹îx—§RßÓOEœ8yz „Eü9®‡>¯÷ùÇûñÛ’Ùí…@5m (О=‹ªÅ»ÊÙ�¨~ÛçarøÙgŸvµá—Q¯gèX3~àlIža‡ºs÷ÿ©dgá~�¬:‰Á­’òù^õÀ–<.£Ö‰ë@èÍ7߯¾úŠêÞäP¦yúôiñøãOÁ[š¶ç ÀwY£KKKÊ3®ISÙÞ¿ÍŽƒ7µúqL âÀ‹ÊÙaÃ)âlçp°ªá=Ô™ï=ÏÀ “ŠW Ð[¿ŸãBQ�Ð:ý =Þu]BÏ=÷váqqúô=QEõu�@lLU™UÛ«ŒƒgÏ�{@îõ»ïOœúº¬F@�ZŒ¸+”‚õ³=Ëo» ôÖP_.7)cÆT`} �Fšé-ÛFàˆÔ ÑÌ‚ÉÞ “¡sàí¯RNT7oúx|˜˜œßúæoН}ý×]\*�>uZüþþP-ÒÄP¨êvü(#ÒŸjJ&ú_«ªnñ&á€V.Šé‚E›(6¶ÕÅúGu«Gìéû/G<§]‡ü€1ÂS®NÃDH–=X ÙgŒ=®õUx» ��œ†þG#І 6ƒ ýüç/Ë­�¶‹1N—Ä“O=-¨t¨“F`+(Ĉ“ÂoyØ÷[e¼ÜP +ŒŽ|èÖ­[*ͦ­”Å/÷'*…wZ™Ñf0ì¡ð®`r«¤ú€-x£³ˆ-­šn¯¬¨Õå4mÐÂå+_ùª8 ›ü—epsS!žy£J9ÿ¨8sæ>|`·ýÕ6@}¢ðAàÎ�–‡JŸËmY|ÆûÙ`c–…}ÅÄÀlâ?•”¨ šj@¬A–�[_XßÔ6[´tšŒp<äíïòåËÞS}¬ˆ�ÀáC‡Ä~ðûâµ×^o¼ñËPB2à™³âÂc� /£»S]A#Ð#+·Õ=ˆ“ÇÓëܬzøa˰©ðÿööV9¬Rš A€ 08¹6ºN¦!ìà'àOFŒ ¸T'*Ú¤lð=W>ùäcÏ™>Ôô†Eœ�=ö¸xî¹ß“¾ÖýZ9räˆdžzúiMüýÒy[‚Àå+ê‹Z¤D}o£®×-˜*~ãç,K5a!zšDIµüN*7[�àZ�„ÀÞtˆ7Djˆ†5ÖÃ]K§Òb ~¥76Ö#k_¹rE¬ÞY…Jõ/²Q]à®E`÷îEñ쳿'¼„|å)îóïÛ×â¢'¾3 ÕÕ_|¡4qã·´ßç?;«¬Èaq €‹½&5Âü‚EÓ,¼CÒŽf¼€vàeœ={NþE¨+)w +÷�:Zðœro(NÈHOÕÈC:øQaø¼ü╟‹o|㛑mê\BGŽ·–o"bßie‰”JûºŒF W.]º$T•à›‰ßX~ç“Âðx(‹mãR©^й*oA—vítF`Gn<&/ 8æyóïÔ©C\Æ„š_¤¡9Êe&%÷ÿ›-£Â�÷P_m½ôâKCcD$úŸ»�y|@ȩ́û•º+áГÞ¼õÖ[Ê£��ÒôMV]èˆ27«îðè·¿ýmÚ⺣lÎM²üâí( ,/¨î1üM„îÿSÉÉÄS.›Á� þ�EðÕþ®Õ#³Nߣî-r ƒþçÿ“Þ ˆDUÐhF J[ÿîoÿVyØK~å]Ì)W (X«©{|�0)` ú$H§Ú—)hŽÈ‡Þ5¯mõÁ¶2‚É3i¬á犟„ÿlÉxfqÑ\Ä44!Û7B·¼ •9ØMПܹTO5ÿÄñ“j¨@¼ýöÛâ/ÿò/Cí¸=˜éC�€F@#0üèGÿA|þùçÊcå·Sõ;«RŽú^*åXf.†€Qóða(§— É�n5°îB¶5m´/S+g{޶�àì;¦ìž7ˆ?<¿apÅO`gffæÊÊJ¢Tšæ¸C%n ö‘‰ð¨ªÒ1^åÓ4\Ÿ¹÷Œry|é¥ÅŸýÙŸ‰JELjœ.¬ÐìHÞzû-ñ7ó7Êc£×±£G•Ë«ä·aßTŠŠYõxt3xúæÍ› .P%#ºÅ­ê†4€ý5èšümöïC›×¶â`[ŸI&$NŠ¢~J`óù¼¹¹™Mr¹D>µš´ #T½Þ5L`ɵpºhÿ—âÿa3ìñÜÄe½òÊ/Ä¿ù7ÿ�øècí$¨ý®é3�€F`T Òß‹/¼(þ‡÷ïD=Fе'O .ž�T¿÷IX‘MB‡K-™'HŸ¸@uÒ/i›sé�lÚ‡ªu9€R[îÀg².gÄéH®‰«~þr¹lP³^Ÿ7ÊSE³fæÍÚfÕœª×gOi € €0† €1¦à뺒ƒ3‹ü’xõÕWbµxíÚ5ñoÿä�ÅC?,~ó[ßG�‹U_Öh4Û�¾ßâ(üýèG?}ôQ¬!ð›ü¥Ê·™ @B!º %ó ”ÏoFŽÃÝ7·/5{ûrn=“ÉYÕê i˜u0“±_X^¶ ðÑÝÙNSAº¸>¶œè@ �øSÜ' f­6gT§6ÌÊf)AŸË-š•d)°öò!ñKÔM&>rY¿"Í<é¸y6܃sçΉO¿ýöm¥(SÍÁ�ƒ~ÿÂßûù‡Ï‹ûï?+:Œ9ÔB³ }0|øaQt:¶SŒ*5¨1z>+ÕC®¶‘i1_º¬œo¶(äû;,òÊý†TuFÖ¯å€Û) ÐýáQ©€vÑP±PšÇIUl[n¬oˆ[øÎÅYñ{û Óªé™oÖÀŽ¥Õ—C"ÛÜ Ä«W¯*ÍcqnêäµÛ·ß´Ò5#µ9�¥å·pm讉e0[Lu‰W“û·ÿ— žÃ¾=ûÄ\¼&ÁRŽÄnäÆûMÚ�«ƒÀ•‹J¶B‡<(B9(Â:X}Q# PB€JÛ½ T¶—Bq¬¾>,ÞÿÕûðæa3™0O/$ç.¦eëf._¯grFfsSF™ƒ4@œ¼|Ùþèüy«! èbH·Z °e 'ç¹YíÇØów÷û´Á½®úk…й˜NCé/iVS…D½T3Sµdb"“TÚÿgr ÀÛ›g<$1jZG�ŸÎÏ/€ xFüã?þ¨o-ÿj¥*.}qIþ x˜º9�€F@#°-0˜U6BrÛÏÀÈð·Q£�Ʀ¦Tu¸•œ8U2 Éz)S_HÕ�åB¡ne³†(M£‡ §p–¸|Ùì`Ø;)”\‰’Nn%¼lä°”8)Ÿ¦˜'÷ü]âï*úU§Jf¥TJ€›J–“ÉD9QLVÊfrÂ0R&6ŒD2bÿ¿¥8’‹x�†Øg®mYG�. �ÃmëLŸh4�Aà^XK�:Ÿ:CLdTÓdŒ0Ä Ý'+¦™$½"ÝZL/$HÇŠÙ�µ”lƒ �‹]ÇB@þ.Ú@/U‡«Ü–0#r¿‡øì•å2Äþ¹|"ÐÓó îõÀd²b&Aý“U3—¬™¸€½m¶�zµþ)NbÈk+%IDAT K½îQ…µ©ríܹÀ<¦RT—Ñh4c�À¾ýûÅO<1ôy’ ^•JšÈM(b[†a›4 I¯H·Jf>)éèµ^&€ŠîpÔ¢…Ž@eH-3ô-€nÆ�0æ~^Úø/6öü777’0'KU3 âOQJµ\MLCË­fVF9—¬‚ȵ„‘0€tKÀ¨Nôøì—܇À_@áWc°y>ø âdÅO~ú%%“Áö®[Óh4;�ýûö‰o~ó[±¦õ3rê~™!zSnÛ DwîkkknVà/èȱj €Y1&ͤ±+6MµE1/*¥QÄnt¨$çA�€³g¡xÖâµmÓØJ €‡ ?ƒ}ÇÔÏQø+™Õ©))öoÿjRÿ*D*\ù×LdT3øÍ$AÐï.x•3”âp‚Amô›úž{Äoÿö·Eœ½¦~ûÔõ5�ÀNAà䉓â›ßúÍ-#þœw믙i5k¸™¹™Ì"éé¯P¾NR’Mºæld¡ÓV2I÷hêN7gA[~K†*èXýsrd°ï¿�}�2Dþeƒ`”Aük…‚Tø#XÎÊßJHâ_5’õD5‘3³ Ë®&êF¾}Le€�¬q‚8ûAú;»¡Åÿì³Ï :þyÿý÷•ÅSÃ�nW# Ð .Ô…¶ÿ™3Ž—Ô­ü×ÀJªmLMS‰O-MLL_ÞØX5Ø‚º²f¦€Bé=fÖ^Æ1è��(mÒ¿“øûôP´»�#ý¶BàPdžÕ?ö=°úç¾?�üÐΟ¦~Ôö‡â„YÅ~ 9'—øOäp\µ’õˆ¿i&²øb‹P V« zqJ&“‚aí¥�­|èÒЀÀÕ?Dô• w‰ÒÃÍýhç/Mý¬b‚f~Ü󯙹Wþ$þVÝ”Ä?S¯Ã"7¦±;lÎôŸO 7’a6ŸÔ  €« ÖæV][X˜¿ñß7n\—Ò€K—.mË ²UóÕýh4wYÄd9yò„8}N~bõíZ€ÑKmðÞŸóž†'’‰#X¬&E2+j`( 0Ò¦�*@~�Õ&=oϤkâV¢d#¦ ˆü†=´ÿA\â¢$ül¿Iü‡-àA©µú§�ÿ8£bÑÀ �î}éáO:ù��­œLˆI#aV&5£yÄþ�•Ƭ'¬ª™H¤¨–­Š�D`æi„Vqô³]aÈÄââ’ü£'7FÏúÎn"†öNkØ<ô5�€F`4 ˆ(ÖàY>�J DÇ“«ûØÑÏÎ͉½{÷‹]»æším÷w,a&”h'Ç#!ö§�PR- aH‚ ¨eÛ¨¦ìÔ¤a—lÓ6Ͳ�¨NصÚ*\ÝmÈ·mÒCtcKÓÀפB`,Ìû)¼ ÇÇ JÑ?CúR‚&ôíO÷¾ôðG'?Ó°ð¯ÖrX÷W©jÎ4±ç_áÏÔmIüíŒi¦’Éð P[v�£ö?ì)ňÂG #Eå?2™4̪]†þ?Výÿ'3¦‡ XýcåŸÁŸMóiî¦lC¯‘\ž|p'tÒh4�»e Êr;Y5aÝJsÔ[ƒÄÚ6�n—±Á�Àö†€C@èé´mäkЄkl”¡ (é#:‘ÛÏààü 9 ŸØÀþ?·(æ°jb,H¬bÖ°2ª nÄߦË$@$lþhÈšrï@¶O$b‚�)Cáƒ@)€N�€F@#pw %¿ŠVŽ”8�€s J­+\¼â¯’¨8R‘2À`Á ·v,taögåêÆ¤=i¥5Ü�NÊ;àÐÍ¡­üÝ»<|=5'…c¹ßÑØÿÏBbK üˆúÁ¤l#WKBü 0`öØû§O) Ó†ô€Œ@Œ¤Bص` @uQ�€F@#0ð»¯B8Õ^ié—# H9É@¹¿ÜÖÆ�±+“1 �†¯oÌ+{´�.nʽtô†Žô@€n €g0© 9 *ÖæLc.&ÀÂê ˜…À]ŸAÇ‘ø6ìŠ�@ÚňÀƒØßH‚hþ˜£¥ *Åæî¢ìñ:¥FÓ05apŒ¶IÀ(‰B¡ =ý©ÌïÛIh®ÎÁ g&% ?ßL~•tžF@# ØfêõšÈçóbåΊÀvñÀGÃï¾êwµA¥u:Ð3„³MB€m�]„JyâÐ9Ë6 XéZø4ONÖ�¦H=€J‰ŠònzÿÞ=á7í)µf�胘$5ºÁF N¢9‘S§NÁ�ýMy‰{ÿ o侌,æCg“àÍ“î¹me±÷_„òŸ%÷û™OΨ q?õýÛòq6¶Ú.ŸÀö"øbãÊv[0,&ƒ›€Ù +%ÜX][ ,ã^`ÙÝ »CÛsËê_�€F@#°Ó@€õ2AƒJRù[�>°¿^Å%½r·^iDrD& -ªF tOê½�¦Q*Î…±wn\8_ <Ü´e @Ð4’d óï$ EbïáŒÜ+qṴ-–�Ý7)«+Ó=«’¦áOŽ 1j ëç›\S@ß‹CÈÜ€–pþ×>‚ãrKíWz>«‡¬ªÝF{Õðtë÷ûKw‡QîŠÙCRr¬QÉLp+aèŒcÔ0ôu�€F@#00T¿ªÊï¾}`{ýЈÖB¶!@{4ÿ“:’P÷0¨:·8å¶”°{ Wý,¼œP›Èe¸ÿ×öß;yBØwçí¤ÏcƘN§£ ;Ë©p¡•JÍôy[tu�€F`!PUüþ©™ßRúÀöJ#\)€g Î‚Ø“±…‡Cf'@ÞùH'@ÞŒŽãN& ãr¬S‚I7ÀÛé 8_ع\6r^›ùM¥q®AO Ž9i³¿HHu�€F`DX]»£ôýS�NÝ/§l<%@¿q4·­tÚ°Jí{ÿ~õ†™7d`€CçHc.lÃëÜ‘ ”»s�ñ{çÎ17»Kp+ (qE¿²r[r¢AeÜ|8]7nÞ{–ö¸YúW# ÐŒ,ÔÀ¿zõŠÒ÷Ou’R ¸°Ý4BuN½”Û2€šŽÚî[™TöÖcÛxxp�$®\¹,8€U{7@âíÚUz‡Rîù‹/.I��ØÐê¤ÐhF:úøã�h7Ð)8Êßj+Ê^Ź…MmvÕäÝ"W­Óo¹-cü*IškAo^%@©ØÃ²ßÓ‘Š–§-ýDïÁ{šøáúúº #°{jñS7„Ï =.Ã!D±ÿáÿø£�Ä­ÙeiH Ó‡¹øDtƒ�€F O¸%[áßØØ€¹µÿw‡fữBXžeù×Oêÿw¶Å­ñbL_�môr¾­ @/ŽS§®  GKíú8ýöR6bŸÿü3¹ 5^ñ0¨è0„õµ‚íþé¤Ðh4-+µåy?V­wæÑX368ɨ´ÝJ€�ã“Üo­Ú™­Ï5�€F`@HúÀî€hZ2 ¡mi3cͨ8Ãg�-}’tg�€F`D�šý bNDZèo `§Â2Ö €Ê�fv꣩ǥÐh†ƒûTè{ïU p8#l«cͨIÔ£B zÝšF@# Ðlrá§(çEâX3*Þ8‹w¶ãÅÒ}j4��މº }à<Æ™è6<ßéwN�O# Ðh4�¾Ð @ßê4�€F@#0zh`ôî™±F@# ÐhúF@3}C¨Ðh4�Àè!0– € ³ÿÁƒ‡Ä.èn%{ܶ½³ºÚ¥4Î-dÕT­Ö»|Õ�Æ…ÕÇötèõ°ºý\‹'é£'pe� <ümV+ÕJ§SßRoàúú†˜™™†YàšH$}™ú‘Ëä–ÿtÒh4�ÑG T.‹��noó…úc M—‰D²nØõKl3™´Áyl)ˆ1†·¥ãêîÌGpg=ÿw ι ñQ™VVVÄF /�Á­ê+�€F@#0êpŸÿÖ­ei5æ� •È«Uä-Ô8¶-{õö��/|.íȬ!3Ó]«þ(QG”¨Ä»-pûÎúÇÕzý5Ud©Éyãú�æ ÖL€*rºœF@# _¨vãæux�í&ô+·Õ·Œk5ëÍ Ö™oV*¶‰-ðíDuÈ @ûÔ¤ˆ£‘ʼno‰N€(Bñ^÷;¾³ºù¿ ?²œ[·Œà@7o8:Tö nÀêšzÔ@·ý«Ðh4£�€CüotíûsfÜ6.Å(eóWBñÑ0¼=ÚÇÅ-eܾ}'ìŒ[¾û·ÅÜZYûR€Ÿv— Ρ}çòò­f�U(},//ÃzCÝÖ³YYh4�ÀH"@?1×o\o3ùs'‡s‚ÛÆ1RñÚòJ›uÂÊ5§­½}3›õ(½b4?ø¢ÛÂx§ÁU?49=+x…¼pÞÒþÇ7®¯ü®¨«i¢ð:Ì<\I[¥ÝçÕkW•@tÒh4�ñF€ Ákׯù®ü%cpõZ¬E!üÓ¼=‚éWr�ó¦„Ûã_ ­N`ÑÐ^·³À€Ï·Œ蜘a–$ä%þ(Õ 'Ì<ü5ìa½P¼™/ÿ׎ìÈÓ�� qåÊQoìûpÿç:ˆe(‚Ôµ4 ?]@# ÐŒTö£Ä÷Öí[¾ž>c®^¾ê«2Wke}ó¯�ë ©ªæ¥W©šÃFÍ®‚–‘îñº�_ZÈyé Û¸)AH_»4t€��¦œLfœh¢ØnH@ŒŒò+u×@ó ÛÑ é‹k·ÿ®^³Þ‰(Öu™Ñ¿øâr›ÛGº ¾zõ Ì×}�®Ft†F@# Ðìhèö|À\¹zYJ|;K�0nsQX«w+v–÷ž—«Õ¼sgís×Ïl´Ëi�t¬î{Ð8ÃÈ4 {´Pþ†ÇaÒG¶éÒKoûÃ:À‰4'úá‡6ÇïúàÄ+rA N ÀHŽÀ±0�›·Þ \`› u`ÿÞºº|ó�qÕU6mH%X\½zU”[Ì»×Á—¯\t ËdÇØô©F@# Ðlr/åvcQ·Ö´óކAã.c!xûÖmßëÞ²>ÇÅ[·Wþ77ßH¶¶HÇšù8ÆúVÒ6Ò=Qr®˜fÂÞð(¶èå¢KG›m¸m êw Ž€ÀA‘ˆcûÞM/`àçå92ØÉÕ”½šb&€’¶Q)ÙÿŒÈÛ5”‘„â’„‰½ÃB}pP"ºŽrRÐèP ··ßË«o&“©ÿnv*÷/‘ßuÝ[6èØ‰X`R$�Íeáî7 Qެ†<ƒKd$sCˆÉè-Š ¾t¾F@# Ðô†€šX‡¸¾Ž}}êpÑщ¹­´¬F™J±aJM§p½õìÔ‚dø•K×nü?\¸JåÒ)Ð-ô�y†Qµ‹Q²ž²XÎÎâ÷N©dg±=>3³!*u0 >ÅK/û›JÝá3ÓpT*œT"±b›Éœ�ßÜÄñ,}¾�h8Dà1ÁX°qVÍ,ƒ‹J#¯š4�:¼1µH­¿€“¾rmùÿKXÚ59‘ýÏT@*CN’š¢Þ°�Aeu¾F@# ÐÜ=€¡¸ôÙåk‚Ë•¹Ü¦†‹ùdݲˠY X’¶¥ë¦UNX üXÜRΨYfÅ’ ]úpùúšmNMIú˜ÉÌ;«|ÒÍ-HÃÒh ý…Ö¡k °A  dµš°plåk¦U7– °ÈÔÀ˜fÚ’T@ *"‘³Âu§U÷×9óþûù•›ÿG¾Pú äm �Þ¾õ±F@# ÐŒ/¶mݹrcõ_ÃôoSÒ#JªA—H§Œ:h�t¬ˆ_ÒµZ5c“Î1_Ò½d’´Ï–ô €.}”¨½°5Ø Ÿ/H“†Ë—/KBœX…%&MňѭÒÄ!`pnej NÉ$H° ^`Ò •´Û•)g6òÅÿ…´‡Ÿ­yžt/�€F`¬À.ýòõåÕµ±¹~]Nd¨µú7äêŸt‹‹Õ´Y—4 4Îʘ‹t®Šo‰žp%Ý+HzÈý¶E:阾ÀSæ u;LÀ¼œ·¤"`rUNXêPP©X‰jU‚T03ñWÇÄ?àfÕÀ�«’RL@›Àeü�/®.ÿÃ�;ÿ@v‡x"Ä:i4�€F@ø‡ùìêõåugmí ‹s5Ϫ]ò®þI·ê†i•¤$›L@Í&}£´;ø‰D´zp¥’åèÅ­HúèÙÿ*áw§:pÀWSñµiû"ztô°ú‡¸ãz±h¥*i ¶�–³ €}ˆGLDS�Û®€Ä{'ÜS¡ÀH:â•ö­2ÁŒÀµ[w^»|mù¿Ä-ù•;qý«Ðh4U`ëÿ?¹tõ_®onÞ`�6Ñ?èR‹ÔÆÞ¿e”¹¥ Â:Fz–€]¹¤oÕM‡Þ�î‘þQüo®9âÒGÒIzÉoò¥«Þ=œðÇ *óÒ!÷92›9Éõ‹Ë’('ªÀ«S<Ò& lࢠ*¡„Àª@+ÏaœmW o„츓hIÖ7‹7õÉÕÿj³Xús(Žönóá?A�«Ðh4cˆCünläÿøãÏ.ÿ[¸.‘Þð�ô‡«#)äâ´b9tJ^K㘄?AÕ¶d�t�ô�Ò;.|S©²¤ƒ™L΢t\n“K€/lŠÃfZÛÚû—.ÙÓÓÓaÅNf2Ö&t¤ P ˜ê ù…ÿ 0uÔ ¤Ë€h2T´àMàâß½)r.#À_*òÏ eHíÒå忺²¼ò_T«õÆÅ.NkË�×i4�ÀŽE£V*WþþÓË×þó/®ß|¡IcHøIw(öoÒ%\—[ÖuЮtÝÙÊ–´ ºm\ý“¾‘Îa\'Ý»šÏK:HzÈÕ¿ÿ;Áí[tsÈè ß °9�×0©³Ò.ŸR*&s) R#5;[/Õ‹æ8¤¤™ªÁT™X×'ìzÉ„�@ÊR@ËÍŒ)}ô×`ž—†•~ žm‘4¤M�–�`Ã_€;2NB  +øû¯vÝ;?3ýŸ¤’‰ó¸Ò“Ï·Mý«Ðh4c�@©R­üÇåÛwþwHŽq¿tòÂïN éÜó§Øß]ùËEj]Xi«n•X¨‚äs¹ZLÕë);UOX5«lÖêédÖJÙe ‚‹ÒðT¶&¥ãŽòéd{–øŸ½Èeq{wƒ97äTþ:ÇçÏ›ô ðÄÌLþöÍB.—(mn&Óó‰’YHVÊИ€#ŲHÕÌj2gfõZ5ɽ�Œ üÊ0ÈP½Â2Š ÿ€p«JXÂ[Tp{¤“ †ÃÀƱœQ«373sp~nú;™tê×Pvf0èV4�€F` ‘µ-ë=Pþ¯]»ý£J¥êz}k}HœáÄŒ„ŸR€º³ Wþù—A˜°õ_N@Ž Éµ•LÕ Pƒ°¿–JÛµ<$ iè´g­‰ÚíÚj-=1QŸ(ëóóóõ–—-n“‹×^£Åš+�ÐqhÃÂp+ŽÝe qö¬ùÌ⢉H|f©4mæS«É¥t:Q(Éjª’HV̤iV’U3—¬e�d¶ZMrE2uà f~‚Á �€HBcÃUPƒ Á¡w˜vÛbx•LÓH,-Î=0=‘{2•Hž3ÆQÔö6IÔ°ôu�€F@#   ©P)—«µúûåJõâ�ÍÍ×6× +m]�àS® š"-Ð Df�Z¤qïŸÖipöÑʆï)ö‡Ã)�+ü–@ð%ñ·ŠZ§k þµL=WËfkõ[p_˜+M׳٠kii©Îíqqñ¢—ø7‰þH2²C ³ÀgK™€••þ\¢’+$Á”“ÉD¥TJNš&üU�äD.+Á�“U@� ŸJRÀûÓ`( ÈðŽ!Á³#yà‰+»ÉðD²X­kNV׿©D"7?;}:•LÎv]Ô�€F@#02€V”``¾^-Wî¬Ë7!Ÿ—D„À!¶åÆT<ÛÈ$øÌ-CÑL®úyŽ?Ö¡ÈŸ¾j¨±F]5ú¯áÊŸÄÛܵB¢„HaÕŸÇ_”?ßÁ·Aü3ÅÉz&³a5Wÿþ·¹úg—²ßav24 €l¼µ OÙŸü;Þ8¹¶f.‚ X__O”§¦Lw+ ’,%‘/9 2O&€z�ܰê5°Võt0à´L›[1’¨`¥�Œ3@à5‰<B²sÎ�þW# Ðh4 =1z�0'*mƒèÓÜÜñPë~Ú÷Sé� ë\ùסñ_ŸAev—øo`¯ ~5Èê·°€¢ÿÌæ¦533S_†èÿ£ÙY ¢öéý“C6à,•ÝÙø—ƒ÷HغìkÂøèlÆ>ÍGÔ¡Ã}ae³õåÒŠXóÂBÌ„ÍjRL�ï)ì¹ÔR¦P† <¸Ø\B¹ÂLÛt©L}ûI‹ú²» ¾ : 2à0�Èñ0ž²nN# ÐhF Е†æ˜¨76}¹Êç4Ÿþާ>£œ´@Ž@×@n­¦dÒ�/4þ 5£å¿T-IÅ?¬ú7°1ƒ7зÊJR(þm‚Üáôï#ü5ìþÙ—û'Ñ6ñg'Ceä,Zÿpr�U8¸�ìyAîRY¢¶¹iˆ©)Q�MσðÄfÕÓä¼,ÄN9QO•í ä-uìú“êWàU˜Œ·��˜K&‚CÁ2£š0ìlcÅ/‰}÷4m0 ­¡é#�€F@# ¸à~÷¼! �L~J ÈñIô¨$&cˆd›´°Ù·qfcõ_v´ýAð“Vɺ³òwˆÅ!þõ‰ÿÔ”EsxÒ?©ø'^ðé¿{DÃÈê€;à) ¯óר 8xð A}€ryÚ,çò‰ÉjÕLÃ2€ÛÕJ%~*Q›D@^ì ÔjÕÄå+õ˜.~l}Z`.IÎÇÈÉc0Ü�)Õ¶àŽKÿj4�€FÀE€DÞ9vbó22-óЇ¿ùKs?æCìOûþ$$IC_¯Á.0™¯×«ÉZ=•N×¹ò'ñ/BÂ�ÚÌZSS¥ºKü¥Ùß6‰þ›óÁt�‡öëð¯6&€ú4 t™€˜šVÓ“L¬ÓXÚK�Š`À`™8NLÂÿB99A½ (‚ Ñw˜‡ø“)`gÌã¯N�€F@# BÀaªTb—ÄQþ#@©µü¸.ÏX6 üôð'�ØA3}£–ªÓú�ÄŸÎ~bIü]¥?zü Û÷ç¸@¯†O˜ÑÏ–H8¡`&à˜.›'ËeÃËT§J&,{²Y0ÉD=Ïp£”©§L8R2© ^‹!L;e`¸`T씑Ĺ ¯Ì.ñ'cÀ10iFÀÁAÿ«Ðh4-\‚/sšÄ¿s¿ ÄɈìgäe?~‹ç$ü�IƒøcÅ\õK¿UP)¸ù]®TêÉtÚrWþt÷ ôÇ.›«ˆ?;Ý2@v„ùÛH;WýÐÙh2¼®“F@# Ðܽ�¸{gOBÏsì÷ƒèWl!|�\šç›D¿4Ñe� >rÕq?ƒÛ1¾ 4ýëŒð—É8Úþ;•øs~ÛÍÈ1p<@2˜€³X±s;þ êÔëóF¹\lcr¹E)€= ÓJ(Z0ÌÈü%CÉ‹”8Í 8hNË>\_i϶€[Fÿj4�ÀÝ…@¥Aì>≼‹� œÎ–¸úgè^£$“ ü<ç>¿Kø“©”•ÜĪÑýàgrrRšúQìß¡ðÇæÙG³™�•*·2ýÿyÒÊéJûDIEND®B`‚ic05 ARGBŠè�ÿè�„Êö‹ÿöÊ„‡¢ö�ÿö¢‡þ‘ÿþ‡“ÿ‡¤÷�ÿ÷¤‡ŠÎö‹ÿöΊ�ƒÿ—ƒÿŽ ™ÛŽÿûÉo†±“ÿe„”ÿÈ„H”ÿùƒ|•ÿ<ƒ±•ÿvƒæ•ÿ±‚–ÿê‚P—ÿ$�†—ÿ`�»—ÿ›�ì—ÿÓ€!˜ÿýY™ÿH�™ÿ„Á™ÿ½ò™ÿðþ™ÿù üÿ÷÷øù‚úùùø÷ööôôóòñððîîíòÿú ú™ÿúÇ™ÿÈ QËï•ÿïËS #•'#Љ�ˆ‰�™•—”€“‰ˆÙÛÛÙˆx€“”—•™‡–§×á€äÔˆôîîôˆº€äá×§–‡”í‚óâˆýûûýˆÆ‚ó픇“ÑËǀƸˆûõõûˆ¢€ÆÇËÑ“‡”ž¸¿€ÃµˆñÞÝòˆŸ€Ã¿¸ž”‡™”•”€“‰ˆáÇÆâˆx€“”•”™�ˆÌ°¯Íˆ—ˆ²š™³ˆŽ]…Uˆ˜‰ˆ™ˆ…U†Uj„†„�}ywˆŠˆˆŠˆ€ƒ€†…~^V„YY¢}J4€10ƒˆ�1:T{�U„ Ui¯ec¦¨§§‰ƒˆ†€§›Kf•V`ƒ T…³c¾áßÜÙ�ˆ�–ˆŽ�Ö˜a�bUƒ Uª«`çîëéæpˆ�¨ˆp�Û¾f—wTƒ TÕ–zøõóðíHˆ�¹ˆK�ÛØn‰“U‚ R[å� ÿýú÷ô#|µ€´{*Ý�Û‹y®U‚ SsÙ‘ÃûýþÿÞ!^�ˆ \ÙáßÜÛ«~¬\U� R�¿‘Õøùúü´+?>€= <$¼éæãá‹¢mR� S­¥Œèôþÿÿ±.‚XW*À€þèÜ‹ …T� TÄ��ïð©9ƒa 2"§ï푟¡U€M\Ĉ €îäa (H�JA ‰öù÷ôª ½W[Pl¹�·‚î»"-32 aæüýþþüÉžÀdQQ�µ”˃îéx  <Ìõ÷øùúüýå—Æ|QS›¶™·íîîíí€ìÉM©ëìîîïðñòòΞϟTT¸»®”ˆ‚‰ŠŠ‰‰€Š‹‹Œ��Ž�™ÄØÉTU½€º)µ®¨¡›”Žˆ�•›¢¨®´ºÁÇÍÓÙàâ¾UU}·ÃÇËÍÍÌÊž¹´°€¯P¬­­««¨¦¡��uUTnŠoqrstvwxz{|~€‚ƒ„†‡†}umh{sT?`�’••–˜™š›��ž ¡¡£¤¤¥¦¥ž˜‘�ˆc> >M•UM> žŠŒ�ŠŒ—ŠÜÞÞÝŠ� dn€phŠôîîôŠ[€pnb ‰.[Z€WQŠýûûýŠG€WZZ.‰8�ŠûööûŠ�9‰7B€JEŠòàßòŠ<€JB7�ŠâÉÈ㊗ŠÎ³²ÏŠ—Š´œ›µŠŽ]…WŠš‹ŠšŠƒWXW†Vl„‡„�}{xŠŒŠŠŒŠ�…€ˆ‡`V„YZ¢}J4�1ƒŠ�1:U}ƒW„Yj¯ec¦¨§§„Šˆ€§›Kg—X`ƒ V†³c¾áßÜÙ‘Š�˜Š‘�Ö˜bŸdUƒ V««bçîëéæsŠ�©Šs�Û¾g™yVƒ VÕ—{øõóðíLŠ�ºŠN�ÛØoŠ”V‚ \]æ�¢ÿýú÷ô'·€¶~,Ý�ÛŒ{¯V‚ VuÙ’ÄûýþÿÞ$a€Œ ‹`ÙáßÜÛ¬€®^U� T’À“Öøùúüµ,E�C B%½éæãáÃ�£oU� U®§�èôþÿÿ²/X�YX+Á€þèÜ�¡†V� VÅ��ïð©;ƒd 3"§ïí“¡£V€U^Å‹¡€îäa +K�MD! ‰öù÷ô¬ ¾Y[Sn»’¸‚î»#298! aæüýþþüÊ ÁfUSƒ·–̃îéx <Ìõ÷øùúüýæ™Ç}SUž¹š¸íîîíí€ìÉM©ëìîîïðñòòÏŸÏ UVº¾±–Š‚‹ŒŒ‹€Œ€�ŽŽ€�›ÆØÉVW¿€½-·±ª¤�—�Š‘—�£©¯µ»ÁÇÍÔÚàâ¿WW~·ÅÉÌÏÎÍËÆÀºµ±±¯±­€®:¬©¦¢ž�uWVoŒqstuvxyz|}~€�‚„…†ˆ‰ˆwoj}tV@b‘”–—˜™›œ�žŸ ¡¢£€¥§§¦ ™“�‹e@ ?O•WO? žŠ†�…†—…ÖØØÖ…� dn€ph…óííó…[€pnb ‰.[Z€WQ…ýûûý…G€WZZ.‰8�…ûõõû…�9‰7B€JE…ðÜÛñ…<€JB7�…àÄÃá…—…ʬ«Ê…—…¯–•°…Ž]…S…•†…–…ƒSRS†Ti‚…‚€|yu…ˆ……ˆ…~�€…ƒ|\S„YW¢zI3�0ƒ…1€09RyS„ Qg®db¦¨§§‹ƒ…ˆ€§šJd“T`ƒ R„²a½áßÜÙ’…�“…’�Ö—_›`Uƒ T¨ª^çîëéæu…�¥…u�Û½d”uRƒ SÔ”xøõóðíL…�·…O�ÛØk†‘R‚ RZ匞ÿýú÷ô(z´€µz.Ý�Ûˆw­S‚ PqØŽÁûüþÿÞ%_�‹ ^ÙáßÜÛ©{«ZU� P�¾ŽÔ÷øúûµ,EC€D C%½éæãáÁˆ kR� R¬¤‰æóþÿÿ²-‚WV*Á€þèÛˆž„Q� RÄ‹Šíï¨:ƒa 2"§ï펜¡R€MZÂ…œ€ìãa +J�MC! ‰öù÷ô¨ž¼U[Mj·Œµ‚ìº#3:9" `æûýþþüÇœ¾bNP~±‘Ƀìçw <Ëôö÷øúûýå•ÅzOR™²•´ëììëë€êÇL§éëìíîîðñòÍšÍ�QRµ·«‘…‚†€‡†€‡ˆˆ‰ŠŠ‹‹–ÂÖÈRSº€¶ ±«¤ž—‘‹…Œ’™Ÿ¥¬²¸¿ÅËÒØßá¼SS|µÁÄÈ€ËYÈý·²­®¬­¬«¬ª©§¤ �‹sSRm‡kmnoqrsuvxyz|}€�ƒ„ƒzrjexrR>^Ž�‘’”•–—™šœ��ŸŸ¡¡¢£¤£œ•�‹†a= ¤ÑŒ„N’9KÆËÚ€ƒ4£‘âðÚtpøc¼bXÀÈ`�Ì!{ýp. nccü˜E<æX d0§Œ8$�4šûè»*¿÷Íîê©é®ê®êz#+3ãŸRNUWeEFü"«2ãÍÈÈ(ŽcÇ„ € € €Å(»x”@@@T€Û € € € ’)" € € €Ø@@@@€@•L@@@ À6€ € € €@¨dŠˆ € € €¶@@@ @%SD@@@° € € € €€*™""€ € € @€m@@@�PÉ@@@l € € € @ €J¦ˆ € € €`@@@@ T2ED@@@€Û € € € ’)" € € €Ø@@@@€@•L@@@ À6€ € € €@¨dŠˆ € € €¶@@@ @%SD@@@° € € € €€*™""€ € € @€m@@@�PÉ@@@l € € € @ €J¦ˆ € € €`@@@@ T2ED@@@€Û € € € ’)" € € €Ø@@@@€@•L@@@ À6€ € € €@¨dŠˆ € € €¶@@@ @%SD@@@° € € € €€*™""€ € € @€m@@@�PÉ@@@l € € € @ €J¦ˆ € € €`@@@@ T2ED@@@€Û € € € ’)" € € €Ø@@@@€@•L@@@ À6€ € € €@¨dŠˆ € € €¶@@@ @%SD@@@° € € € €€*™""€ € € @€m@@@�PÉ@@@l € € € @ €J¦ˆ € € €`@@@@ T2ED@@@ @H¢·¼å §;W>«ÇgE‘{�‹ÝygØEnHGpʶ@äÜxì⣑+Œ£ê©¿»cç~äJåŸL™øáõ×_?ší�;@ü Dq,»F&@xã[߸µ§Rzº‹âß‘ÆâB  ~¦b LJ]Žzn�KÑ?ÿéµúu)&AŬkJ… ÐD€@^F(¦À®]»Vôõn—³ÅÏ“j£ŸËáŠYÕK•ê.iÿ°äâ÷¿ò•¯ýéR ó> €E P„Z¤  €K ìºy×HÿXïK¤kÿ+eáuK~€B¨ÊeŸ“Ë^ÿêÿïÕß ¥Ð”@ LaÖ;¥F‚¸ùæ›ûGÇ�H£?¾N ½2˜‚SÐvôr€Ï”£êŸ\{íkÖî‡Y@�<ÈC-‘G@�e Üð–~7rÕ[äÃg,+>¢À˜sÑ›†V¼åšk®™€2#€W€@që–’!€Á èYÿ£cGÞEñKƒE à� ÄñwJQ¼SƸ³³„ø4 €Ù ��º ' €7ÜpÃéQ©r›‹¢s ’#‰°Ë- _ôªk_ó‘°(= €@Qù¸(5I9@Ü yÃc¢rõk4þÙŒVÊ�?üæ›ÞtƒQz$ƒ €@WèÐU~VŽ `%ðæ7¿é¿Å‘û”¤Ç@V¨¤3' ÛÖ;'ŽN]sýõ×Wç^ä  €9 @Î*Œì"€,¸ñÆ7ýž4Ð>'ïÐø_ÈÃ+Qì®ìy·AR$� €@×t�ž#€Xh·Wv—´ú,Ò# š DÑßðæ7½¾éû¼� €@Ƹ ãDö@š ÜtÓT‰Kß�%Ö7_Šw°ˆ�ûãW¿ò5k›*©!€ à_€þ�Y €€�[o½µ·—>$IÓø÷àK’Í"çn¹á­7p—‰æD¼ƒ �Q­²… °¸Àþ{ß,gb¿øR¼‹€�þ¨Z½m×Í»F¼¤N¢ €x à –d@ü Üø—o<ßEñËü­�”XB v§÷OôþÅKñ6 €™` €LU™AXJ`×®]=ýC½ß’nØ�XjYÞGÀ³@U¤Ýu¦ãP0!€ �yzd¾ŠÈ  €@½ÀÀPÏËiü׋𼋥8Šn–õË&É„ €@öè�ý:"‡ €³7ÝtÓp%žü¹üÉÀl™ˆâèÙ×]÷§ŸÎL†È €Mzš¼ÎË €@Ë»¢¨o­s«äeeŹáêìýØå”X¯$²¢.¡ÕòZr¦Lþ’×ûëÞã)K \~éﺇÿ�ÿ¥¥X"M�#‡ßñÎryS\­Ê¸”L´. ûË£²?œœýÄ”l@GjŸ–×ÈûÉ6%û×Qù{BÞÛ¿Û¹ƒ×DZ¼Å„´/@ }3>�@¡nŽ¢ r�±Y y’ulÒGù{•<ê¼zv^¥¯ÉûÉë뜔×�4þ“©•k‹’#šÙåy@ %�žW=y[K‹²i Ä+Wl�:ý´w—rgš«e]�}éqÓü¿kûÓúÖ¾ìsÝ_GÑaÙ�”§å3kÏåï³Ï�×ï—ÏÝ+û÷ITážëâøðq+ãR€@�ÕN¡CxW­™vî 9(8] ¶Éã r °¥ö(¯m–çsgååyÓI>Ç„@ª•Ÿåâ‘tïº648èúeî)—\¹\Nµ¼¬¬}�jµê*2ONLºÑÑQW©ÔÂ’í§Õî'¦Ï}Œ#ЮË/W@öÁ+å³:k°þ¸©~ß­ h—»[¢H{Ü#Ëß/�wËëú¨ÿBïœrî¿^ÇcÇ%Æ P8…«R ºÀ{£hà¨s§Éý¡²C?UO“¹Y^››ä½dª=νÁ2(0õ¨³SÉÕàР۸a£[½fUÇ�~ ”JeI§” Ec´Ÿ¼c³‚å Ë·ê,ùðYúíªýÕõš= ì—¿ï’àÀ$xð}YNŸß%×(üèÚ8–à &È»ƒæ½É°rÝ}IúãŸ!ç$Ï–ô9²ÃÖÖÑ#eÖîûLJ r£þj'-ioå*÷”ÝÖ­[ݺu2¢Å2§R©äz{{¥Ç€ÌrÉÂÜö2Ó öcò£6]™vÓÓ:Ou|&ltÌýâ—¿tG�øm¿ô}âÓ®÷ß¾lµQðB hG‚ŸËüùzÞ!Á�;ä×ø;/Žã{ ]j ‡@°R)Rñäºü~ià?¼ÖÐׯ¾<×{ ×°W¼‚S"f¦Ï~„›xÎNoƒræöŒ3Ow}}}m¯CÏäk£¿¯·Ï•µÑÏd.P•®ü“SSnrrbÙ=´GÁ/y·{`÷æù«%XþÁÝÀ{?Xû“GBÐ/Ô2'� ܱÁ¹Ÿìˆãô®¿ A™2"`(À‘Š!&I!`%ðÎ(Z!{ÎÇICÿ‰’æfä1™¤ñÏ„@PÕÓô*?“6þü�Ïœ±ocÚð×€Aß@ðÝùÛ`[Ö¢z Å ôªtSŸO.h'1½ââ”SN‘K1Jîþûîoç£-/[yЃœ¬@.ºÖ“¥L! í}÷ßuÖcÝòåÛuD.%øº<ýª¯|E*øÊ âx\þfB� È@%�ÞEeð�sE¢ÖجìHõr<&�Êé~z�þYgž)gïÛûºõ÷÷Ï6ü©žt"qïK/“““nBíŽpò¶“Ýøø¸;x@P7ž\eˉ®|÷=Æ “¹ÐÞ‰OÖYƒr‚1 |[ƒòçWe¤á}Q{øJêL °¤€%‰X{�[£¨WüçËÎð÷%õ§ÈüÝI2!€@”¯ºNo|e?m“Û H£­ÕI¯ë�³Ðz™©{ú{©Aí�¡�ù©©ÚmԗΓö8íÔSÝüÇw;[ ÑÚâ­'ÉøêÙðZ°z«`=Áñ�oë´ÜÆðr ô9ù%ýÇ+ãX/!`B�”¤Íj�FÿzÙé=IvxÏ��à3DDÆðcB�¥*´‡©ýÔ?0à6oÞÔrÂýýröyî.™-Žý èeC2@äôt¯k¹7@ÿ@¿Û|Âfwï=öã—U7¬÷W`RF =r,tžå<¹dà Ò;`·üýÏrlôÙQç>].F1)Ù �Íz!W�Áû);´gJqž.ó£e.ÉßL І@ì©Au¢4µ¹Ô¤gûõÚór™]æRVÝz¿W`”úmù¬þ 'èV}±^æ l”_âËåµË‡f.ø’<ÿùÅý w˜'ÅŸp4c€HÔ È~[%¢}™¼ö\Ù¡=¬þ=ž#€@ûյ˿-ßbk[·~½ïʯ�ÿ¡¡a‰Ü-(Xl]¼ç_@ÇsZ™ôö�KM:xãȪwèà¡¥më}_—«´• F ¿z¹ÀSu–oñÍÒ;àKòüƒÒCà×ıí—UfB D!Ö:e6�îý«&œûi"ì�ÄuÇåïfåæ¹'A²-KW}ëI¯\"]mPµÔKÀ:¤·<�Ó KÀf\.л,5­±È K­–÷@ 5�ÐþŽÎòÕ~§¾ Ïß¿Wz\Ç­üÑÚºX �`SÕÔZàcQT¾Ï¹§ÉNI»­éuýõY#“*Ðßgî0$ .ÖýŸÆ¿9yª jàFzËØ‹eüë)ö°½Zç‘ôÈ¡€céå”O—>a{äË�Ê™–÷É‚ßÌaYÈ2] ÐU~VžGÙéŒHcÿ…’÷—ÉãƒòXòŒ@®äl½õ¤£ù7 $Ýþ“3ÿ‹_`�'Ò³tcc2Üx¥ùå½mÞþ±¥ʶ%Ƚ++--ÎB О€{éH›WÉå–WÉݾ%�ï�½Ä‡åÖ‚‹GüÚ[ K#PX…­Z f- ×öŸ.‡s$;ž+$mFð·&=š x¸å^)*K`afTy®ùoVùz=’K8†ÝèØQW­J¡ÁäívŽhóö26À£ä¸ì}r=À�r‰À{¤¿Ø-Øc¿&RD 8ŠS—”Ä“€4üŸ(‡Ž/•ä/”� ×ö{r&YR�/s£2Ú¿·Faªde* cèåcc£ o9Y€ Š °Y q½^-=5o“oö[®Žãï¡`”kÖ¢¤Wiø?Eþo’�S˜BQH´aXšwSÎ>¹•\Ÿ�.á˜wU $gãd`¾‰‰ñùhÐ dÁ2¼€¹è—Ÿw›é¹Ò#àÓÒCàµr÷€äªdÏ<“|þäz²s%×!;�'å/÷äZ�s¿˜�ô¬ŸŒ5ÀášH±5¸S•kòç�@}«ž) uúÿlùç$ð é¾ùê—Äñ]uïó�`[õ|¾€œñˆœñß%¯o—ùXË`þ‚ü�¹Ð/xýýýzW€…cä¾ `N _.ï¨Î»€:Ÿãá EÐö2†“Þ+\/w�›81!®€pëž’Ï ¼#жIcà 2_&/Ñ`Ë@ 9û_ë wЙ©ØZßÚ`rªîöá„z‹]é”�cz?ÙIïÎK%ðV¹ è-ׯñÑcoó �p8â §®)é<�[£¨w¹—k4XÞž÷6zÐãnFTôŒ\�äëšk¦%ªúúèúo ›áÄzûú’ËâXš2Õ¶ë,ëo±kÕb¦§7‹œÙ‹Y¾¬•J¬WJž®pî2Xà52>Àg³–Gòƒ€o¾…I?“r�ÿãe'ð9@{x&3˜¡Lé�¬†Í{eÖçú£QÿXÿ¼ö^­…מËÓä³úÈ„@;ß•…w·ó�–Õk¿KrF¸\îqe·l! ,Ò­wí05Û  ä©™þ()›þ62!ÐŽ€4 P›æ?׿§eÖÇÚóùëë4�’Y.kdj.p²£ü½(Ç.W¿8Žïm¾(ï P,ŪOJ³„À®(ê[çÜëd±ëdÎôñYYF­Þ¼Ùõ­^íúeî[µjæq¥¯¥áÒ34””¶wö1ù£Í Ö€×†º>¯=ÖÞ«oÀ·™<‹#`"ð“>Ý�'9òÓë¿“³ÿ\ûoŒ›íä4P‘“^ò[êc:áÏþŒ€§XÒlK@ƒ¨ =.7HW«nz|ÜÅò]ªLL¸êô´›:tÈM8à&t“ò8¾oŸ½ÿþd™¶2žîÂÏ’ ÊÒà*é ðátWÍÚ莀Ö.ÈYÿ‡Iãÿƒ²ê³»°úEW©�ùU§ŸîVnÛæ†O<1™ÖIn &mÀë]çZ£¾öÜÏ¡¯A¦I�:�hrLŽ`õ|–ݤg‚Ër{8�S›¤ÛyUš«qUŸºÖäŸÔVŸ½é8 3�½ ƒ¯îø Ê­TóaZ,¾ÖY^»6Ûæ(¼PD�V~ÝêÚ› ¾GÁrƒõ– Û½Û¹÷^wôW¿rúSwä—¿LõËuùùù)ú?ÒàdOså+âx_—óÃêð* í& / ?êÏ•B¾Gæ™Óæ].q$�ŽÕgœáVŸuVò¸â¤“’³úõÙÒèz;“6æµ›¾~©ëi䷣Ȳ™˜½^Û4oòÅèííM ¦é6HLüÓSÓ3g�ƒoô7š}Iƒ1==½©\’Ñ#u_©V¼$ÒÓ¼ ¼ƒ@†ô$�ÎzÜ0Òµ €†`uÖ×Ú ]JÀmpÓ¦dÞð›¿)Ÿ–^ SSîÐ]w¹wÞéöÿð‡îÈ=÷$¯gàŸ�âðX9a´ýª8þvòCð"@À +‰fE@ºtõK^Þ&Çú/évž"9õò”S܆³Ïvºì]±âø,µÑÈÑF½^—_ߨ×çú:´& g�}�ÖèA²^k®gš“I¾ Ú󀩱€öŽ˜œœHzeh7}Ÿu£Áíuà­:Úø=o¬Á«t_ ÖkP¤j“†¶æÚíŸU’ßÞÕgž™Ì§üþﻉýûÝÞï}Ï=pÇI` ¶®.=>H~»¿*A€k$ð·]Ê«EÀ«¯¼$ÞM�wE‘véú”äá‚næc@º‚žð„'¸��}¬ë«oô·q€¨M�ÎëN¸6 ͈nÖ*ëNU �ïJ«ùê•3Í¥R+d[Mñøåb9ë?91)]ý«2Ø ž_cjU ®Æ˜LÆgHé­~°�åô÷³§,ÛÀqÔ¶‘ÀR‹zØf—Z%ï#�†€þšéqˆÎµ©ÐÁåîJÉ „µ÷ZyÔqŽNü­ßJæ£÷Ýç~ý•¯¸ÝÿþïÉØ­|ÞÃ2ø <|�s¯¸^È™(�€U&E9& ?Ú'Ë�÷ç䕇{5ÝgÚÅ_whkúй3Y­vë×l­±_;Ó??÷R>&X¦@ŸÜÎ׿¤ñ/ XíàkË,v®>¦½úûû½jzz$ä)’ª¿õüFçjs#³ÔNRè‰ ½§²nûÚ+@ks«ß‡¡�Ýi^è´gÀýßü¦ûÕ—¿ìÆ÷H3¼ “äù¥2Ó–·EÑå2.€ FÄ@1£)E�À;£è!ò£ýyéĺ—S{:rÊ)nÛSŸêV�vÚÜ:k÷œž{¡Ámèk£_îMÛðZ¼á%Âðp65 xÐK|åúV�hüw<%–ýýe/ít½ À[yØf;×$ÒЀ@}/ý]Ô@Àøì¬ã,5•$H«½'O8ï<·ç»ßu¿øÜçÜøÞ½K}ÌÇûIciÓ›£èi×Åña+ MÒ �¶8ëó*ðö(:KÎét¥ñ?¼e‹;Yþz]›NK�í×d­Á¯�~�‘57L P/ ƒÍIë¯þ%“ç¹Þ_~Þ–&¹ÌW"ÓÓS2`£6'l'�ÁÛe h[Y¤–k�ÚñŽó¬’YǨ40 ‚Ŧu¸[+½)ïÿ·swá nêÈ‘Å7OòÿDéÙðy <… €9/ vA€@ÐY¥�ÙÆÿí’ú ~ÖÐ8Õ’Œ&½õÉOv[.¸`¦A±È™ŸÚNPoE ;ÂúæÇR;ÀÆkçUŠ/×Q Š›Ür.¹íœñµùòÝ×ûËG\óoPKÇ’¨Ê˜ÚBÐ�T­§Oã@hˆßtëÚ"½¢è ½T v¹€ö­•YƒÍ&ýþo~üã݆sÎq?—Þ÷ãò»�Þ·LÖtÞ \Zz“®�ã£ÍòÉëäA€@j‰<.)pk­—Ê?È‚©6þ×Èmüô¬g¹�5kfòØdg¤×Åi£_v¾†œšY?ÿ"PHmýÙLÔä¬Û“ÓÓ2à_É0£vEÎ}JÕ¸âz­+L·ÀÇÔd_àcU¤‰@žôS��tÖK´g€¶®µ—@£©,゜öìg'wTúé§>åÆvïn´˜—×$¯O”›·},Šž¹#–%&r*@ §G¶� ¼7ŠäŠÛÏÈ+§{Õï3½…ÍÉ2@ÍæsÏMVÔ¨»¿~¹j�~¾h~ëƒÔ .`ÜF+Ïþf}K>ü�®ÿ~¶Åä² Ë(Ðl6Ë‚ IÒ\àgC ÕB Ô÷ ÐÞÚ3@ƒ�Îó¯<ùd÷ˆ«¯v?ûÌgÜîo}+M—§ÝïÜ[d…’æJY–´K,5I«+2"Ë­•=/­•ëíjθôR·â¤“’k}ç¯W»ö¯�YÏö3!€€�€ýžX×FzÒø7€kÿ êz‘$’ÍÀ¸Î¼lè°HMòK Ô\)‹êå˜^õØg#ÈóT¹cÀÈ©§º»>ýiW�€õØÞž½â–(úþÕqü¿½­�„ð(@À#.Iû�Ûý].í‚Ëý¯if zk¿Ó/¾Ø•ŽäOÛ&Úà׆¿˜@ÀP l{íwI¯Ñ—/­åµú3�Û| &)Ë:S”ÈÓŽ…Ùæ(HwôWU�­t¼€ ™uø¿ù÷ã[÷ˆG¸Á Ü�?ô!7yð ,‘Êô |U‚?Jem¬C†˜$•®À»¢èT9#tKZk]ó�‡¸3¤ñ¯çÚÀ3Úð×è´Îú§U¬'8�älª~Ûl¦H®Ó×ÔLO&Ë�‘·³É6Å.@*Rov›AâámÈz`{£YЯ¾Þ1Igí  Íüú@ÀÐæÍîaôGî‡ï}oZ· Ô«ÈM2úŠv½iã#Ë^tý#éN•Áþ’Q¨gì4­·³1¾ Bšä]ÀGCÊÇÙdo”ç½mò¯�ë€$h“¹ù©è €>¶Ûùëáo˜ÐK3u@f vgÊ›tL€Êxí"�¹EMŸH¸oû;£è1WÆñ7M&1< ðK²~vEQ�\oõR?©KµoåJwúÎ�n°§Çm”ƒ8ºû³á…0nü™7N mT­/ã:3Oo¶¨\`Tç$ƒ@›:.Ó™õά[—ŒßtçG?ê= '!¿?‘U^*3™ �ù*"ƒõÒø×kÿ·Õ¿fþ\0·=õ©n½\G¶NÿžÎ™g›@ =ë»uz핦øK«¯µ±¯ßwÅß)a¶t¼¦“dî;õTwðÑ�v»¿éýäü¹-àkä¶€?˶ ¹CÀ9lyÐîÿ^§MçœãÎ’Æ îãìÕ™ÄhYÀCWjmHZÚ']@%=nÚr�.cA­/Ë:Ó,XæŠÅ%söõ%·GŽ]I®¹/Éå“%¹_$ƒðeeÒ¯ø†RÉ�'wsº]nXõxrGÇ�ÕÈJå“�¦šÒðFž%yòvŠMG÷?ÿIOrCrÝ¿ì!ºZüxlÌMÝ}·›¾çWÙ½ÛU$j{Ħ«få,"P=ó4=,´kýiCÒºqºI�oÍøÚÕ™"úª³Éo|ÃU&õ.åL„' A€ò† ®¼i“ëݶÍõœ(çoÊz>¾{Ó†�ÝãùH÷µï|Çi�-OÓ™r›ê‡]Çß÷”>É"`"@À„‘DRx¦¯õèá<ÙA­”®ÿݹ¹º¿›üñ�ÝÔ�w&�þùù°=ìõ%IºØ øØö­ìÚiÈWcÒ^4Ÿ)ª¯½±�­k&´ä'å|Ö¹K –ÞŠÓ:ÿô§nâk_s‘œXéÙºÕõžy¦ë•Û+G]9éq�sÿÏÿtß�šò¶~ÙèÉ*Þ„IØB€€…"iø˜9ê{œ�éAÚÙ2oxœܔêT©¸©ŸüÄMHDºòë_Ï­Ú[7‡¹5ð4`7ùhœÚå®)©±mI$EÛgSÓTù÷BK¢y�Ëa*?ûY2�á ®÷ôÓ]¿Œ·TÞ²%ÕÒD2¸óigŸíŽÈ€€wyZ³|÷Ïõ”4É"`&@ÀŒ’„| ¼Ë¹Iú«|¬CÞ¢‘é“d¼Ø”ºþÇÒðŸüÞ÷Ü„tÕëùuÒF&X(Àwc¡ ¯d[@·Y¶Ûl׹뒀ÿLKoG�Ë›7»9ùÒ#— ¤5 ÈrÇn¿ôØïa¥réÉ’$¦L9IÌ—@ÅÓê dø,™ûö°ÔºþOßu—ÿÒ—’ýÔ‹ƒDU`BŠ#@ 8uIIü Tï»Ï�~âÉå¿ó;®$1ûžt€Â>é�ðˆþÐý«¬ÌC¿Ï“nŽ¢ ×ı×[øv"ýb (vý©t2 ˜ýt†$Ù'£Øöʵÿ‘ç³ÿ:Zÿø¿è¦å�èš0ð]°¼â'–Ätfò) Æ¶!SIÑK†õ÷�ßx/´$Z@�ª z<ú¡¹þÇ>ÖõÉzßSÿƒìVI@o-u¯Ÿ•é1+?¶¤j @À‘$RXm½=8Û*sïi§¹HG§õxð^•‘üÇÿ韒³þ¶‡¯Ö*¤‡@öøÎd¯NÈÑâôX܇wX ãLÊ€�Õ{ïuý¿û»^ Ô±J+V¸mr[eùþûïʰ�h]€@ëV,ÙE9G³Úº  ôê,£Òúü¯òË_&�ÿX®7³.C«„U#€4 І—XB ò‹_¸q¹,`ð™Ït‘4Ò}Mz7‚õ2³~W=ô"à«âH×D€€ #‰ø�hóõúÿR©”ܧÖW÷ÿÊ=÷¸‰üGÉ 74þ}o%¤_T¾;E­Ùâ–‹KŠ[·”,�}ûÜø'?é/ºÈEƒz´f?õžp‚«H@OM'ïã˜Õ8‹$¸€À7€_úèÛNÚ¨(¯]ë­û¼¿›üüç“Æ¿mÎI @ ˺!p•å"o™8t(9�2ð¬gÉÁšù! +mܘ|G}ŒÕ!= h_e~ ;ƒl a×ð¥/oÚägð?O`Bîuë&'9 ~+ ‹ÚåÓv8MÍCGÒ,âu)O3¾ÖÆÖéÍàèÒFÂj %ËøIÓr»äÞsÏ5/—Þ  42✘M€@h5Nyçô­gÃ/×ÿOÿàÎíÙÃ(ÐsÚ­óÚºK"P<�HzX÷ØŒä�ïiñ¶J´´€¥�X¢ É»6þ�UIϸOAAk€b!К€¯FZkkoq©ä¥ÅeYlyjl}´îiãÒ}û�åU3ŸB ‘€[EÆ=6cIÓÓO@£"ð™ �™ª #݈ugÙQÒèFM²N@ ;ì²Sä¤IcÝø„�¦É„@ˆB¬uÊœèϾF”­'Ý¡pæÇZ•ôB°ÿ–†¬IÙÓ @ʬ#$Çl>ŽCªÊš_ù­;rn!ࣀh°XTi à[Àò›ªiY¦ç»ìyL??ÆùÉi·ò¢@ÒcÓøëK@C¬ÊœOù¬7rm  h>¢¿œù1¨’@ NÀW³Ú2]|uæé©cËm ¾Øìê5xŽ@çú�²ÐÇ1`ç%%ü ðoÌ2,�\ÿe|€�ƒÔ ’5ò+`Ùúã‹ï;ðal¹ Ô øÈj]òcÏEÜR(S+ZQb™Â Р°UKÁhAÀ2´�„[X'‹,_ ?ÆùÉéòkƒO"�¦€�>ŽÓ4a],W€Àråø\îô-‰þZ_S&é1@î7 Ppëšuzç_Vñ|kš>&zøP%Í�’ñ•èò&@Ù b’Tþ’ûÊF‘yÆ}Tšg”È�€—ï“ukÒ:½ÔKêYôaìeãrÉuÅž’N��"�zd¡ÈCQ¥&)GÛɱ¤ñÙÍD¥n;7|@ (º¡'XQj“rdAÀÇ1Aº,Ô,y膀n¨³ÎÌøè�ì¤2SB2‚@þ8HˆVö¡Õ8åõ-�ô0¾ 9ô�qÒG ƒ2X)d)=@ó1,=Ò©?Ö‚@g> >Ò쬔|z)?u¦gÿí/.[ª,¼�@q’c+ã^›š¦Ÿ_€âÖ%+†€bÔ#¥X¦=– ÇÇHQÀךeºš–ez)òæfUy2ÎS^s³�Ñ ¼ô0(]A>WrU]dÖR 9@óðãÏèÏ–µDZ €@þè�¿:#ÇÙHÎÖ³išL„(@ ÄZ§Ìs>î›D©çÖÀèTÀÛ!šeÂID±Ó’òùErdœ£¬.JΛdE ùN7Ø}fÅ‹| °˜€Åtx¯Ðº3©ê€2Æ·L®S+´…CXL€‹éðí èñZrÌÖþG›~‚@SÞ(¸€‚W0Å[\ÀG÷/.XÜœwhW@ƒu~&Ë”“óS~²Iª³ù1&ÀF‹€­€~û­�Ù¬Ó³-1©!àO€€?[Rθ@r(é¡—d¼âÉ àY ?¡ Ï$�€‘@rle}@ãK ŒŠJ2x à�˜dY@£¿Ö·j¢@–kœ¼åQ@S>&Ëtiðù¨¡ãÓôal¹ Ôç–õ!Èu¶älÊ ˜N:ô‹ž¥™³dI@ ›>‚Ù,)¹BÀP@{NÊå“zRÆÇ µœÆÒõêðaçë´Œ|ÿ÷×ÖÅ#Y �ÅZ!O |ý˜–5�È™ù^¹LGšõ9õ¬_ï¢�7±{·‹¹õŒOjÒFº*@€®ò³ò ”eðdmüG}}®âñÌ¿ÒLKÏOWà�''_Ǭž²K² °ÒóXdéNõ#=£b=”‡e'0¥—H—3ßSixØ ž|²›”žS¤‡˜§}—ƒôHSÀÇw?É¿å÷O3i™^šÀyY—úšûÙº’Í!/®ä�. D2SŸÜ�©gd$É…Ï3ÿµbê1ŸÞÐS€¸ß¹×ÖÅ#Y �ÅZ!O ÊÎ}G»ë[Oz–F;寓ËÊz€t×÷>I7½õ ®ojÿ~7-ÝÐ|ÜÞÆ{9X €@Cz4dáEæJÒëR/¿ìÕ†¿{ùéneuOtPæ éùéñ‚Ì»^Çz~‰ �Ì ÈlÕ�±z�Çñ½·DÑnymcýëÏõ.°}Ò%¿G‚½>ǘŸYÙáõjÔ{Íš$0-Òèmi˜@àxçh­ÏÐZ§w¼©€cÛ–¯¼² �{9Rr½+W&�µïŠóÜå¿ÞmbïÞd„>�ÃôßQ¿>ž#�EY¬òÔLàkòƳš½ÙÉë:\kYºä¯–ûÍFiô¨Ï¬î%®sU¹5�Þ1@ƒô ¨‡â9 � ,Ð uE.ý hÿ²Œ”Ìrdí+­3þõ¥«ÈÙÿýr|¥'~”52.³^ëZ�šrÕÉÉdŽå¹Ž\«Aûë`çg„¿ÈŽ€¯³´Ù)!9)šÛlÑj”ò,%  û¨\vÚà/É@~ÚÅ_ëXÖïE7Olì—ñ—tðgÏÓ§=§Oòt,@ cBHK@îÕú÷²ÁNÊúú|¬SwL÷K`“ŒD;èùŽ­æ_wœe�%j^?%;ÐÙ±’sÌƪ_Ï讀ž9²Ÿ’CQÃd­Ó3ÌZa’òa¬iÚO}2šyI‚¶LV@z/F2Ë?3gõõyƒ)�Aý¬vÁK‡ä2Ëþ/³üöÕqü³+ç2&@ cBvš ¼<ŽÜE_”]ÌSš/ÕÙ;º£Ú½g�Û°q£l²3ël †Ÿ®íx “$)2)àë»hÙöóÑ6Ídet1S>Œ-·�:= ˜­ái‘²|"B¿â‡¤çä~t9…éc)¬ƒU б€Ž I Miü¿CÖç- eÑn÷ûå1SrM¾tÄw�cÚº$ € f{h5x‡—@ %é9êÊwñ¨týOáÒÉQ9^ü›”ŠÆjèH€@G||8m�«�ûü-Îý@ÖûPŸëž”®b£ríÚt¿[!+òrÍ�Ï�6EÈÁ™T'§‹T…eñt²Þ"k Ó °Ð„WHQ`\ÖuDæ ¹´³"c)ùžä÷é}Òý¯ïõ�>,I#=ígEo•þ­×•ÊjÆ5b¼v­; ×àÊÊô*|zxU'q@ :NK7;+"…@`zÖ_þÚ䟖ÿ§äÎJ)L¹ëÇÛSX«@ÀD€€ #‰¤)°É¹÷ÞçÜK¥1þ¯ë• À„\3Ö/A€1ÙvBV¦½ú½®”Ä@`�€žMeB OºÍ²Ýæ©ÆÈkôö~µæ~Eü›”Ë9Ó˜äÛ~ëËâøÇi¬‹u `!@ÀB‘4RØÇ•wFÑUmýWY±×“òzgòÀ×'·Œå7e…ú¥ÑÞ\ L¤ à¯ùo™²¦e™^ °¹\…µ±uz3¨ôÈåÆE¦s* 'h´á¯gÿuÒ±œ´ëJA¸ýrbèά™ȇ€|Ô¹œ'peEîð1iý_<ï-ó?«2z¬öÐ €ÞãVoìt@f h�¾D‚À„€O�<œI¥ýïs ˜IÛO[ÝO¾éàÇ•T¨І¿v÷¯5üõ­ª\ï?yøpZ�]åk_Ç{ô y í’—š"Ÿ $âz•\ãõDyãÄo¿�ô�hrïÊ•®$·wÒI¯/“Q’K†ä‘/“ 0!�#ëö¤uz9¢L%«>b,¾êL‡«In�–Š +A ,=þÒ3þÓóŠ­Ýþ“kþÓ ÿóÕνk^6ø�Ì ÐfÉ|‘Áfqýë(z�À}^–)5[Îêõ$ דõ»’ X›t¤Y�õ•�ÙÙëu ²&‚Hï`.(V ëQ@.s:3!€€‰€ëôXK¯ó¯?ã_K\ü«ŒëéL’Ÿ=’�éKÇ›µØ °õ$µ”®Šã¹%Šn”Õþi*«–†È”Ü"°<0àzd®ŸôÒ�uç¤ïè� ˜=«pŠëM@Ãhz\¥MûF=vô»6-#ýëåš)N9ëôükâø×)®“U!`&@ÀŒ’„º% 7]}í:çÎ’õ_˜V4ÊËÎF‘ X?é.H¯IÓîir™B `À@�`B`¹¹è ‡¦�O—[h>·P G¾ôXX}¼‚@Úà×küun6Å•Jr«?í¡™æ$'w®•PŸKs�¬ K–š¤Õ�ëã¸ú¶(z®tÁ¿]2pnZ™ÐhsU¢Îå¾¾dž¿^=TÕ˜Î"¨øÒ mxköY'l�^FA,ª¾ÖÆÖéÍV=‚Ø")¤±€ž@Ñc¦±%ÒÕï—öW‘9íI~2þöê8þ«´×Ëú° -b©IZ]xE�½;Šž);�Û%M-#²ÒAgô–3åþþ½jùÐëÕ´ ›Îú¥«�p|ßyƒ  ä¢ÀÂlóJÀºÍ¦|V2`mŠžcmôkƒ_Ïô·r¿*gýõ¸+¥[üÍ—ý¨ þü’ù/ò7y �·#¿M^Ç»%ð$Ù™|QzXÓ=¼¡ÝÏt½C@2@`ÔüêÿÚXr“š$ —hïý26ÿ”¼É„fzâWg«É:=«|)Æ–ÛÀqÖг”L °P@�ƒjÝû è·ðò{-ÇYzÖ?íîþuyù„\rú\éuª1 &r-@ ×ÕGæç hà�Qôd‰"ÿ‹¼÷óß÷ýwrY€\ �€HçEšÝ‹è¬=´7€j‚‚Á„äQ@ÿòXsäÙƒ€žÙ׿vØ×¹�ÐXÒð—^–]løKŽÝGiü+SQ¥&)ÇœÀ•q|ß­QôDÙÉ|R^ü�¹7R|’ŒF+� 0*—›^PŸ%�‚k78�µñ¯_N èíu& La xkHµsº½¦e™ÞRë ñýü'�.q#¥Ì" Ç3zr£Öàoõ,=ž~‡t�¿.7üõØë/÷8w�œùoå …ú"ð�Ì ÈlÕ�±N^ÇwEÑS×Ê`-òã}y'iuòÙd¦�Ò $�}\ªW€®Osk— ÔÖ/ŸNµ^ŒP“áȘ=2V!dǧ@­±¯�z¦¹¡P½lfî¸É[à·e‰Š”ãå2àß--‚ȉ€œTÙl_@¢µ“ÒÚ~¾ürG>}£Ìz"½;“ìÈæîQ;˜ûÀ¥2¦;V�k£ãj@@¿Àõ³¾Æ„@Ñô Ð|Ò$-“µNϼÀHЇ±å6PGœœµ¤@�O‹Ôm•@IDAT‹" Ç!z‚¢v–_Ÿwú5Jýú;ïã·~yð»åc—^Ç_\ÞÇùÙ �íú!w� HËájçÞ&ã|SÎÃß&É�Ði’^vpµ(·öÐ)é0û¼Õôk�úåµW€~©5ÒQ hP€Ë� �y�´Ö'§iY¦WŸ6Ï� X[§7—Ól5fæ²ÅZÐþîõ�üÚ1‡Éwfö8(ÉKvý5šÿ'e¼Xÿ÷Ô^à�¢ (Z�Rž†2.ÀWägK¤úVi ÿAÃ…ºñâìŽï¸3œmê³­;k�µ ^ýT Ôz ÔÿMp ^Šç™ÈÞb&™ÈTvè��º 'Íêùú¼ÖÈ×㓆þüUgû·\‹£\f¹K.#ÕŽ LV€@a«–‚ÍÐ;ÈkϺ9ŠþP½o“ç#ó—ÉÄßv�ºc×ÁxM ÐÀ€>Ö?¯M_gB x–‡¸š–ezÅÓî¼D>Œ=Õ™þŽ{ø-ïÜ�B¨}S´!_käëãüçú7S"p§˜=OÎúB B-SÆãäþïþ:Šþ¯üØëÀ.O?îÍÿ¨,Vtí% A}¬Íõk°@§ú×ôo]¶6Õ>Wû› BM‚G(š€6À˜hU ¾!^k¼×>[Û–j¯ë²‹=oeŸ^K›G7%Ç&%—M¾NÎúë™™B€@ÕL!ç \Ç¿�מ!½ž!?þØ6þ>& z&� �nÌ¿¤¥y`�´#ð@; ³,¤. Ç~ß’c›Ëñà¿§¾rVˆ@—8 ×å `õÝ�ÞŸ•/ÁÃ$¯“�Ááîæ†µ#€@ªµÓh<;¥˜e Ý8|ä/Õ�Ž•!€@7äXïçò3òü=Î=VnñGã¿›•Áº»&@€®Ñ³â¬È�G$/»ä²€[d§ðJÙ9¼LþÈJþÈØ h;ÒrªµK-Ó$­ã¬ëìøÔù Š, ¿{ä„ÏMÃνýq<^ä²R6– ÀRB¼Œ€tÛ+=^-_Š3¥Ð·Ê¬#Â2!€ €äSàˆ4þo”¬Ÿ&Çy7ÒøÏg%’k[zØz’Z¤GÀÝRŒ+d|€·Ko€7Ès½m Á²Ô-E@à˜€�söœ£>æëã™�:ó‘OÒD� ŒÊ/Æ»û�û àOzü3!€@M€@M‚Gæ Ho€ÈKJ à4yüc \!�«ç-ÆŸ € €Ù¸O²ñž>à™†6*„\dO€³šÙ«r”1 üT/ �HòÉ’µ—ë2Ë"ÙA@‚�c3ÕÿùÒðß&ƒûémý8ëìÖ@Á— ÀRB¼�À¬€Éӷ›×:÷û²³y©üýd€@ ‡r¤(ÿ›M±¦g™ YΊ“Pbl\ªÌ”äHW@ïPüO2ß ×÷5ÝU³6ò+@ ¿uGλ$p}WeÕŸÕù–(z¸.—ƒÈçÈß[º”%V‹ €„"ðrÜõ9þú°œíÿU(…¦œX °’$� dÇó=)øuÒ+àÕœ;OvH;d¾L^[$…F Wòm5›4-ËôÌ2V „|SgÚ@(J±î‘ÿ'¥ˆï“³ýß.vQ)~øõ%õ@f{|EŠû•·I0 ×¹gÊŽê29´|м&2!€ €´!p@Ž£>.ËpŸs_ž=Öjãã,Š�4Rá5:xE�ÉÇ?ªó_GÑ:¹^à) xºüý{2¯‘™ @@`¡ÀÏ䥔ù¤ñÿ%ibá"¼‚�èD�Ï"°„€tSÛ+‹|Hç�EQù×Î�-·Þx†dÇö¨%>ÎÛ àQÀrÐ>ôXQ³I{1–b&èª@Eމî�¯â?È “ÏÊèÊß–UùfvµJXyѽ†)_fvıŽVû­Ùùu2€àƒd§wxª¼öD™GdfB@Š,p¯û|Iüÿ8íÜ?KÏIéáÏ„i HKšõ 0O@ü™¼t‹ÎÚ;`·s–çO�âeÇx�<ß&3äB@OXéÌäOÀ‡¯�4ý �29¸K¾i_•�_‘üUzG~?§å ÛB€@!ª‘Bä]`¶w€îu¾UËóö(:«<Ó3à|ùS{œª¯3!€ €ÐÞŽz‡¤/ë,�þ/_Ç÷e4¯d � Yí:/‹ãK>uþßšß[£h•t•{¸ôx”DÒ%;Õ‡ÊãoÈ[ýú> € �¢À”¬ëN9ù–ô\ü–—|kÔ¹ï\ÇGSÌ«B�6´ ÆâtKàEq|PÖ­ÝçtN&¹åà |‰.œ#;Þsd'üHy~¦Ìk“ø@è\à^9Æø‘kÜ!'"¾£�{ä$…ÜšOÎM0!€@žä©¶È+ófo9ø yYç¹é]Q´FÂò§ÊúT‰ÊŸ*;íSåM�µÇÀæ¹y‚@ ò���¦ /i™¦g˜µ¢$Å]ŠR“”#Ãûå˜A¯×¿Kòø™¿/�ý»Æ�ûÉuq|8Ãù&k І€6°X�¼¼$Ž÷K^kw8.Ûr)Áz¹©î鲓?YædG¿EРÀIu�+Žû P8mýël5Y§g•¯¢¥cYgjc�^Ѽ)OQdKß#ûüûäñ99 ×äß#sò(¯ÿBžÿ—\«¤(å¥ Ð\€@sÞA �r)�ôÚs:½YoŠ¢áç¶Êû›å`a‹¬ÒYÎèãy}•¼ž¼¦Ïe^=û8,�L € àG@/¬Íô¹ì—Ê>9yMŸËkûåï½òxŸôÏ¿Wþõ5q,±&@À9l °@`vŸÉ:·5é`…2°œ`pN<†åG¦OŸËÁH¿ü=¤ÏåEŽG܈>gB`)�ÊI[^'Ë>Œ �_Ëó¿´Nýoû7f  €EHê*z!) € € €¡ }  ü € € €A¢š)$ € € €@èŒú@ù@:àêôŽ —LÀrĆÚÊ|¤YK›G@²(@ ‹µBž@ü ĆͿ$`˜žÿÒço Z_–u–PgùÛÈ1 €@'\ЉŸE@@@ 'rRQd@@@�Nt¢Çg@@@ȉc䤢È& €€­€åÕßš–ez¶%-NjÖÆÖéGš’ €U€@Qk–r!€,"`ÝôÓô¬Ó\$ûA¾…o�ÕN¡@L˜r’ €@�´ÿýW»�ö¿�4ýK°@–-À˦ム€ € €äG€@~ꊜ"€ € € °l˦ム€ € €äG€1òSWä@ÀP 6´OÓ²Lϰ˜…I ãÂT%A袀.â³j@�. XÚg�^—X2¿ZëAû¬ÓË< D]€KBß(? € € €@‚¨f ‰ € € º€Ð·Ê� € € „cQÍ@`¾€õåßÖéÍÏo諯µ±uz¡×åGȾ€ì×9Dð"`ÙüóÑ<õRè'êÃØrÈ1-YGF€@0UMA@ްlûùh›—YþHNÿ[Ö¤ €(ÀV:EF@@O€@xuN‰@@@ `¥Sd@@@�ð ¼:§Ä €@ð3—’Û]PËêú“?�_[cêÌ_}‘2 €@6è�Íz!W € € €˜ ÐÀ”“Ä@r#`{2Ùþ&õ¹�L)£Z_ÔYJج@ ¨ô(jÍR.@@@êÔað@@@�¢ p @Qk–r!€,*`9œ¦e™Þ¢ôMŒ­xŠ� `*@SNC@@@ ›²Y/ä @@@S.0å$1@� |ŒP$ä"…¶¾€®ÊGš‹�·@è¶=º]¬@@@�è�2«@Èž€åÉß™–)fÏ«Û9š1¶Îuf-Jz €Ù @¶ë‡Ü!€ € € `"@À„‘D@@@ȶ—d»~È €€/ËÞß~ú§û*y>ÓÅ8ŸõF®@2%@€LU™A@@@À�?®¤Š € € €@¦¸ SÕAf@ÒˆåðúŸÕd�žU¾Š”ŽcËm HÖ”@ ¸ô(nÝR2@@@æÌQð@@@�â p @që–’!€,&`w€K®&°Lo±|‡úž�»Pg¡nM”V€ÁV=G@@I€!Õ6eE˜°ÎÇusåI"àǘ.l^ €a Ð ¬ú¦´ € € €� ´â)6 € € €@XªoJ‹ € € ¨cZñZÀzDyëô‚®œ&…Ǹ /#€ к€Ö­X@ P–ÀÑ:õ¿i`ìߘ5 €]€@Ñk˜ò!€¤"`NH%ìÄQgl €¡ 0@h5Ny@@@‚ dµSh@@@�ЄVã”@@@ HƲÚ)4 º€\ýmyx¬éY&zý4(bÜàu^B@�–´LÅ‚ €EЦºesÝ:½¢8[–DZå6`YVÒB@À——ø’%]@@@2$@ C•AV@@@ð%@À—,é"€ € € �!ª ²‚ € € €€/ô%Kº €0Ž»x®oÃRgžëŒä@²&@ k5B~@R°lþ{hš¦b�§•ø0¶ÜòdI^@Âà€pëž’#€ € €$@  Ê¦¨ € € €á ·î)9 € € €@@ŒPeST@�YüÍr8ëô¨¨…>Œ`¡3¯ €Z€@¡«—Â!€4°lûiZ–é5ËsȯcríSv@+.°’$@@@2,@ ÕCÖ@@@° `%I: € € €dX€12\9d @À§€åUû\¡î³¦Ž¥mYgšªuzÇrÊ3@²(@ ‹µBž@ü X¶ýhÿ§S_–u¦9¶NÏ¿k@@ #ñña@fÚ‘´%ýn >|}¤éW�Ô@èL€1:óãÓ € € €äB€@.ª‰L"€ € € Й—tæÇ§@íLN‡r¿›‚_iúU u@: @'z|@@@�œÐ 'E6@lbË“¿’–iz¶E-Djê‹q!ª’B €tQ€]ÄgÕ € € €¤%@ -iÖƒ € € €@tŸU#€ € € �–c¤%Íz@2&`<wð\¿Z_–u¦ÙµNÏ3É#€ С€ù8 €@þ¬›’ÖéåOÔŽ1öoÌ@Š/@ øuL @æ XŸø¥u:_Øþoë:ÓúHӾ䤈 €€™c˜Q’ € € €Ù �ݺ!g € € €˜ p €% !€äKÀºÿ·uzùÒôŸ[õÅØ¿3k@(²=Š\»” @@@�Yz°) €)žLNÎM¦d…,Uhñµ¬3]�uzK�÷@è¶=º]¬@@@�¤€Ì*@@@è¶—t»X? €@—,ûì't©¡¬Ö‡±å6J=PN@�< Ð ϵGÞ@@@hQ€-B± €Í|œ›n¶®P_÷q®ÞGš¡ÖåFȇ€|Ô¹D°°lý°®�…éa¼Ð„W@hS€KÚcq@@@ò(@€<ÖyFèPÀút²uz¯�Ǹ�ÕJ¡@R @ªÜ¬ @@@�îèŽ;kE@@@ U.H•›•!€dAÀº3¹uzY0ÊZ|kšL €„$@ ¤Ú¦¬ \|ñÅ}’À¶(ªžR�¢S\\ÝæJц(võ=iVE;Z'FçÎ<óÌÇ®X¹ÂžÂ²õç£uj_âü§hYg5¾ýío~`ç%Û§=®‚¤ȹ@\‘Ã¥{cÿßu�—ìx^¹}O2ò¤®g† € €dUàì(ŽÿUz|VOe5“ä ŸŒàS—´½ \tùE'”'KEñS¼®ˆÄ@ x±œïÖÙj²NÏ*_EJLJ±å6P$kÊ‚@ñž.'�Η“H×|ì#{ñ‹K 8&@€c<Ë‘ÀöK·ŸSžŠ¾îhüç¨ÖÈ* € ��‘ÈÅï»xçŽ�ÉØk3“+2‚€gž�IÞ^@¢µ—”b÷UIy›}꤈ € Œ@o—±îعó".% ¦ÒÃ.(—„]ÿ¹*}2rk©ú‘s¯ÌUÆÉ,dN 6à�UƬӳÊW‘ÒñalxH‘¨) ! l�£è ;/ÙqS\�þ\n8"eC€aÔsîK)�ÿAéîÿ)�ÿÜ×&@@2' Lãë¤7À¿<ç9ÏYŸ¹Ü‘!ŒA’Œ?�ç=ïyÃòcü÷ø}k!eN 6¨œÅ£âY¤Cé:·ÑS`hAàüéêä¿mÎö‡µ°,‹ �;¹«²°2,gþWŒOŽþƒ”úÉa•œÒ"€ €tIàÔRÕý¿�;·?£Këgµx à�–„;xö³Ÿ½ZÎüAÒùíNÓâó € €´!°2ŽÜ§v^ºãUm|†Eȼ€ÌWQ˜”3ÿ«zûË·KéÏ S€R#€ €tY Çñ ;vn{$S—óÂê0à.&Œ$b)pÅWôÊ™ÿ�Kšg[¦k�–îz{{]¹§ì"ù¯T:~¿ ï—Ëey½4÷X*•å¹.wlYýlÝŸÖÙ$=r/088˜ù2ø¡>ó…N9ƒy±ÿ¤“N’!!ò”ã”+“ÕåZ Ž«®R©$óô´>N»jµÚò6¯_�éé)755%ŸËÇ÷Dé^ºý’‹úäØîJùnç#Ó¹ÞÊȼO>uI{Yîy�´ˆ3uÍ©\rƒn@æ~iŒôiÃ_÷õ“þÝÓÓãzÊ2÷öÈût°©÷á9ˈå1ûÇ[z<È1ár븵Ïùðõ‘¦s›6mj­H,…@J¾Ï]k“¸ª��Ù€À¤4î§§§—,ÝÔÔ´›œœpãnttÔMLL,ù™n-ÅîÅ;v^Ô»k×®]ýõÕnåƒõ"Щ@”ýƒªN‹Èçó$ ×Yý¹l“¯ÏBžËÒ�_±bÈ ¯Xéd©Ö ¯·/é ÑÞ_`Ä XXFFV¹­[·ºm2[M•jÅ=zÄ*9Òi 00 Á×¾ï,ÿ¥½ûöºo|ãËO É'µ·íøn ·“«e5( gù50%gü[ hO‚±Ñ1766æŽ9ꦥwA§¸jé…·Ýv[%ƒy#K,)@€%‰X -�—ì¸,rñ®´Ö×l=ƒƒCnõšUnhhhÁ"Úè׳üý}ý®·¯×q‡½ Ð¼€ €@àìïÛßÔ¬OŽ•tvnPzÄnrbÒMNM&��F)êe“Ã+†“yý†õI¯€#‡»# ÈÐIËË£R¥÷IOzÒå·ß~{&#�ly �š€š�]�Aÿ•â[%Ç.ŽO1GÚ°_¹r…[µjµëë_x†IwHýýýr @¿4ú»’Å5Xi ´ú]òÓM;­R²ð/À®Ù¿q§k(I%éq”Îz¦B»þK—ÿJ¥yoz=£óú UwäðwàÀ�¦ÁƒNó×Îçc]²aÓºCò™+ÚùË"�Y¨…Àóð´§=­ÅÈÐÿ†…§ÜS°të×­oØð׳ýýœíO¡XEÖZmœ·™ïå´å—ó™²e™¬¦e™^ ÙrŒó[í4Ðó[wíå¼µ}‡^&380”Ìzy€vù×ËšMz"fdÕH2�=êöí? ãŒ7[<­×_$cÜý±�~â i­�õ `!@ÀB‘4:^9ô—’À#;Jdîë듈ò:¹¾aÜAG÷×ëþ5À„@6Z;Èj;ï´°Ú&ãA€zj±•2xÚwÔ­Z†m­ûké§z¬µråʤ'Àøø˜ 8¹hwÿ¡áa§óèèQ·wϾdù¥×âg éúú�;·ßýÑ�~ü}~Ö@ªØ к±7%Å6äGóirÐqe1YtÕªUnÝúu ºóëÈýCÐëû™èLÀÓAV{ÇU�¡ÐŸVHKLëô �¿ÌÂù0¶Ü–Y¬?F½E¨Ü/æißQçÒn½î£^Ÿê1ذ6îå8l\Îî�OŒ/vƒ[‡Üa#`ïÞ½®ºÈ¥3Å‘û9ž½O‚ÿìq=$�€™3JjW@®ûßìÊîýr îo7›9=³¿QnϤןÕOÚµ,¹ÅŸ\çÏAV½L‘Ÿ{ØìòÓ–(rŶV6붤uz­•"¬¥|{úβ)ê¦éa¿Ñ€*« ôYõòRTŠœ^ž90Ø/gùǽ5 ŽË422’öíÛçÔËòSŸz%pÛ%—\xþG>òÉÿH}í¬�6¸qY›`,n(Pª¾Yÿë S\4©ADfËI'-hü륫ä¶`3ü-šo¦* ZƳì¡ÝÜ,É×V�©ú°2@�ãŒ÷ öA3kÓºÏÿŽ/S¸Ereí 4ë œÅ¦r¹ì6lØ Çy[dÙ®œß©ºÒg.¼ðÂÔŽkóà=èÊ7d± ñ^;.Ýqžì¦Ÿ›ViµË¿ÞN¦~Ò³þÚͬ¯ñ�Jýgx^/ ZÆ“§³qƹ$¹‚XnnµRAh2Y Æ–Û@&Ñ2—)û�e ý z’\¿¤—è““SÉuÿz�f“öæ–]ݨ™1Èîº0göÇí‹fÖè³{;�ÿ…µÊ+mÈW@ÇÐëþµ¡¿Ø´RNþlÙ²Eîè”nOOéðÆ�;/:±¼ñÝXü›ÓÍœ±îB ÈÙÿµR°×û.œ ³yóf·BºŒÕ&ÝQŒÈß:¨ÌÒ“§ƒ,èKÓ³ �{OûqñÝ8§�žû�/ˆè­õòÎþþ�EËÛ×ß'—œ˜ ô¼è‚¶oöÄQéÃÏyÎl²M–Ô°àGRiQ *Ç/•«¢Ö´¸ø²Û(gþ‡W Ï}^w:�ŒÓtâ wSÞ@�%j –XŒ·;ÈÑo4�èê™�"Т€žì–±œd€À££G›võ/—{܉[Nt»w?àŽÈ-ә⧫½#ëzf:ëc-´.@€Ö­X²C�ç=ïyÃq\½ªÃd–üøºõë’.þµuäX=ó_.Iã¿v�Þè±ö@ [z2• @�½26À*¹$@Oö4›4X —…®^³ºÙ">^ÆŽKv\æ#aÒD �èñÙ¶Æ'G¯”ëÞ�Š¿­–^X¯ [½úØ�»ÞâO¯÷Oi¼Á¥3ÈP@Ž«œÏÙY£8àr_Ó<.÷³|®{v¾¶-ÒE�Ö¬ö%¹SÀÈʹ$`ñË<×­[çÖ¬ñÞu®ðQ¿].Ý<÷OÈ€@óPY2GŠ#ð¾p@ŽŽ_ásÜÿÁÁ·aã†9´¾¾~iüÉ‘%§çPx¤€`15Ц·ÕTkÆ[¥G: 0^hÂ+øÈÛ¾#’ÓšµË=GGG›Â¬]§ÃQ9·ÿþ¦Ë˜½»u®TÕ;_]d–& !С=:äã­ ŒŽ~�4þOhméö—ÒȯÞò¥6é}`Wè4þk$g!¹ÞÞ§cCém ›M6lpCr—(ïSä6NU&^ë}=¬� $éL ª¸Ë}]û_*Eníú™.\:ú«vùÊ꙽-TµZq•Š<‹« jµ:óØ™p¶?­RL!ø«è¹…“~׳<ÕN6g9�yÏ›[O>ÒÔ<Ž��[g•ôò,àkCë‚I©Éïq�ë‘Û*ë5÷ËŸÒùM×cÑ‘•îСÃMo¸yó&÷«{ï•ïíÄò‹ÓÚ'¯Þ~ÙöwüC¿³µÅY ?ü¸’꬀Œ|Z–Ä¥¾@Ö®]';!m”¤«—ŽöŸ�iºRqÓSSnº2í¦§§›îx²“cr‚@6†‡†ä;Þkœ9MöåkÛ%—ã±±1 ôæ#¯vuCJ¡ è±—Î}½}®Gζ÷êmø¬¢þ†_Ÿ²,V�¬r‡’“0•Õ¤— l>áwÏÝ÷$Çk °{¡¯4Ý(É]h—$)!о@G±»öWÇ'Bˆ¢ê“¤Ì[|”[oñ§×néwr�—ëþæ¬�~=ð;xè D›ºÑ±Q799IãßÇ@š € Ð5íŨ'8ôXçСCnŸŒªðàÁ¤L¬=µ¿ÜÙ¸T¨ÐÛêc£©,=6mNán}Qül99vA£<ði 4þ¤µvÖSx�ª×³ÿk’Æÿðð�+7ùAO8Š“F¾F–µÑ?6>Ö0œJ^X  € Ðíõ¢�£G�&Á=.š˜˜ÈLomü/60àÀ@¿[·~�¹Rõ­»ví¢ æ_š54à€&0¼l# mãÿf“Òñ©ôè`+V¸þþ~ézÖü›óþ²ê�6/Ù$¨=11.güÇ9Ã?‡¿Ⱥ@í¬”U>­Ó³ÊW‘ÒÁ¸HµIY˜’Ë uŽF#§·g”9êlà€ŽÕtì‚Ú˜�.ÓY½zµ›�1<Ž9ÚñºIà7ÿó‡ÿy‘¼ÿ±E–á-¼ }òFKÂ2ÐÉ©¢p²‰µkÖ8í®5<4œ\n¦�üf³�õOLN%ÝÜŽ¥ñï˜4@@ ÚÐÖË#÷8àŽHï€n€ÜSîu+†›�µaã&¹µ´õ¸3ÇWeäâ×È%¬éŒ„xüªù � \4M%V <í~ÛGáJeéÂ%#º®�iÿtV«± "sX"Çéæï£rI�”j'“y\þ%ºE±Ki“c5/ �í9yàà7::ª÷FꚉŽ#¥'‘Mz×™�›Ö7zËòµ³/¾ø¢§Z&HZ´*@€V¥X®m¹þ_4ŸVÈ­þ“[И'¾H‚:˜Ÿî§ÝÙ˜@�ãŠÒÎz9ŽWïü/-/¤)�ô�ñ’�q“ôŽÉE•iæ`f]zYBÿ@Ã%— 4|ÓèÅ8rn”É Ð–€¶¸X¸¹þÿüv–ouÙ5«W¹�ÁÆ?Ø­¦ÑÎrU‰X>r$™»Ým­�|³, € �U=¦Ò;è�*Õé®dS’îÑÛ6˜Ö­ßÐô½‹/ç¥ÇíÜy‘—cååd†Ï„#@ œºNµ¤r‹“m²B�M§^üoÍšµ®Ôv߽̪ý9éò/·¶Ñ³ÿL € €¶¹…òÁƒ‡’ÞÉ¡šmò‹¦É W®\Ùðö€z)Àú ý|§oÆQôšNÓàó´+@ ]1–oM gú¬Ölo©ÕkV;½n«ýÆüÌz´›Y«ÿMMË@Òå_wLL P@ËÞîÊc™i¥ãYÀÍš"!�G½, Ö §{ÜU’;è¸R�&í!044Ôè-«×~WÍ>Ã*1ÒA ­(±LûÕÒéíhñOè`©ëÖ­o¹ߨ¡¿ø޽;55íË`tù?fÂ3Š&@;�6vÖ�‹¶]Sò,0==�ôГ0ó'íüékCrÝ£iýúu²^íEêeŠ¢i÷?¼¤L¢4 І—;�.U§u–ÂÂO6±uáÒËeJnñ§£ük4š Š,�õ¦)ùó¢(òöMÙȧ€ž|9$—_Ž��×à÷]šÁ!`ºÁx½ÒótåȈ·ÕKpáW\q…ßûzË= çQ€@k-y–³ïæ=VËॲ·l¢ª‘ç#G�ÐøÏÁ6F@@ ¸GG�ÊÌG“««Ò(¥aÎÜbzá±æÚµkŽ`“¯xÓ�{Ÿi“© °´€¥�Xby¦=ûßìV-ËËÞÂOU*Õ¤Û?gþÚð  € �¶ÀÄÄxÒ@ïÈ”ÆÔS.Ë¥ƒ VU–×W­Zµàu«bý‘UZ¤ƒÀR–âý¶víÚ¥Û•i`xh¸a·¬¶3÷ÿ³÷¦¿–$×�XÜýí÷-½°7vUWu·$R”FCi$K²‡–4’h‰­&ÅMô‰Ñ�?ëËÈ0Ùh"¤‚aÀ#Ã6lx<ð=C )’&»YUoUõêmw÷9™/oFæÍ̈È8q3óÞ�ê—KÄY~‘73âĉsRL&8ù?ç•ÿ|ø2#À0Œ#À0E 0 ü¸LäF\éŸý·²²*0ëT¼tÁÕY,€Úä_ÝfЊ³åsF€6�CÊ¿ûÝï¾(t¨�XYYñ&ÿIû²¨x<~Áÿ¨Àd:ŒÀ2"ÀÛõÝo×w±è‚æ2>ÿ¬3#à�Àà{iÎNÚ“&òêkÉBã$c}cf²ïØ  ^kŒ¿’,_ehhÒ’cjŒFhÝW&2(W¬fsÖKÁäæúFàÞ.Œ#°\`¦ªd¡¢Çtfpƒ1Ý30+1_aJÐpš677¦îË*¶¿ì:¸üãÔõõu„Mw{²a �ýëqÀ�I.>ãZý•–õz]´;¾3A³Iÿ¸‡#/ï,•¼L‡`F€`FÀ h¸¸ðƒ5ã$?íw\€Š»ü7› /P ý¿Û^L¸Î—RègT¤â1±J"0ߥ’ƒÿ‹t bHU(® Â,F€`F€`�j Ðï÷½ŒMµ`€èJlð2ÀTñ²Õu–°Q«�ÿ0Î�Ïjx 5¢Lܲě¶®WŒè~…¥WØ’EZ0¿,»þÏBŠ˜ ú1ôáß0ˆää�ÁüÓ&�-a A9[Â,v|Å oݽ+0øF *üäÇ?á¸2U鬂å¬Ã ©&¢ëqµF]4MˆÔðÆ?þq<"ÛN" …û^¯'õ†X][3jgZÇ¡7�‰@/âµv»-ÐA]&5oÀ¿¦¦Ëô6Èhð1 °ÿÿ!ŒÀÚº]õoÀ‡P.¶ÆœÌ^]_É$—ò'ñ77=q{Ü®¯®à#×ã‰ýR> åV÷~saª„ÀØ3šŽ«$2ËZð¨@E¹CX¢¾èE¯ÝžÕÁ8Ði¯@jä¶g@OI—A’…(ÉEÇ5[MˆØßv&~}Ö××½}ÿ2“­­-qrr"_¢:þ�/}éKkû·˃T*D™Î ÑYÕÌm¾ÀäBàN®V±FØûLô[Íæô8V-×éõÕõÒNtq`zY../`â³´8äzp¸Ñ àíYAïJzd‚-!sŸ-Ðó±$ªŒÁ ï&¾ø/(m˜¯®­úÿV×z,KÁxÝntn8SƒPÇWü1áéé©‹÷þÊÊFóWA™ãL!&¼ô°`éZ>øàôc{ƒ‚*¾lƒÒlѽØ'°ú�î\ËVÐèñ¢ç^^^²kê²u>ëË0Œ#°°ôaË^ÿYºJ�Æœ âÊ5S^ä‚‹8¶énÁ6±`ÕÈ�Âèi!»ü£'Æà|yb¨ØÖ&�_Zl ”éÌ À€Hø‚ ýà£Wa·¾¶ß‚F]Å-A¡ x {ÿ—¥à‡ñüü\<{úö¯Å\ —Ö“`F€X"Ðà�ÿpò¿¾¾&66·¼Éê¢B€ûó¯`ã<\ôÀq)f! òsaÏ$4paœ!°ØfAg°1á4ýÆ�´{&×ñ%[—,¹-ÈÇJRÀãÇ,zA·ÖgOŸ‰O>ù©8=9åÉÿ¢w8ëÇ0Œ#ÀÄðWÇ/ÄþãÇâ“�?ñÆ‹ºíåúæÆ:ôAùdHWa{…\6Ö7äSÊã_ƒt€D_J±˜Ö¢ À‹Ò“%ÑÒ—Ü¡ØU+»ÿ£j˜À/vûÚú`¹Åâ"—gÏž‰³³3�û¹0Œ#À0Œ#€+״Ǹg~k{ËË4°HÈ\\\‚n3ÊÍú&.NaÀÅ #nX…Œ D€ ”h2-xáÖîPÀ€Ö`\Ö„“g .äd€noòÞ­œdJ×Ìû˜Ÿ=ñ‚ÿ,ªk_é@g��˜ªÿãâ7ørŸ¹ë1¦\fp¼€1‚0Šþöö¶ènc$}7+çóÄ·tV:ÎR#v @õ%àx“®€€‹=�\'Tÿ—î&j�À‹¶Ó¦ é#Ô Ê0€ 1‹6AÀªÿ£‡=Kþ¢é6}ø€`F€`œ €YÜðÉ'Ÿ€1à¹ó&ŠÙŽÀ¾á¦ÀЦ©ŠŸ²:ôX ®Ûþ�ˆÚ;¶4¸=#�†Ò�áëy¸“·aÐ÷XÁ‹/8…=ja6€éÅÃa¹5GóÒ5ÁõÇ� -ÍbéU: Y F€`F`ÁA¦ Ãƒ#ñVÝ[åï; ø¼º²:}0kUÇ…ÀD¼=eÂŒ1l t™É}ðÁø<½a‹A|/ZsÐŽrÕ?h�ö¸Òr|ì}¨´àÿeF€`F /˜>ð§�Aè l-¬r¹º/€±�.D%ÿ«ÃÖT9Xµ“m5ñÊ7¾ñ�­*÷Ë^^ØPÞ¾©œdßûÞ¿ „nÛ Žûÿå7È÷LŽ18LÕ Nþö`¿€¦ê:±üŒ#À0Œ#Pp¬�ÛÐ˰ª‹'#È„ä§}NžÄ§MîÃëÙýÑé„{ÿÛmOÕ8Ç~¿Ï^qPøœ6�ÀÈD�¡hÝ¡@¢ÙˆZÑó<<ÆÈOàlCX‰ÌÛàjÿãG�ÅÕÕ•ycnÁ0Œ#À0Œ€½›ñð§!ê}5c\]ÃxÉfì‡mSþa0@tÿÇÒ†­«.ʤ>a€ `™¦`?dÔÄø±øŠ+fÈãê®ò˜s-ñ7ð1æÂ0Œ#À0ŒÀ<ÀñÓÑá‘÷¯jc)”÷Ó?§Lâ•×ÛZ-kç×DN51á@€‰ÈðE[Ø`‹ ·Ÿ"Põ;Ó‹ƒøžÿ¸A é1¸‚UµŒ`òÿø¸áAÄ.Œ#À0Œ#ÀÌôÀ�CX¥r}}í,Ak§íg¨7ê‚b¬ÇuRã@€qLøœ6ÐàÈTp²¿CD­Þ˜’iB •ÀÅjz1ÇÁp4ÌѪø&h½~ {þƒjÊ_<‚,#À0Œ#ÀP лé‰G°%·T¥`ü'ØKïD\ÌZŒQ›¶°€“nc¢€ø1 C 6®Ý±%†/S¹4öÿã­ª¹­!³`¬í.SÙÈXó1#À0Œ#À0Yà‚ÊãÇûâââ"«Z©îÝÜ\;‘'ÿÁ¸µM0^�rRã-3 ð Ø@�"Óð¨Ù{  N؃-Hçý‹ÂUÍàMþaåÿ¬í\F€`F€( 8¦:<8ççÕˆ™ Æc7[Ú·ûÿÅØ~ðàÁ‹eéw–cq`Àâôe¡šÀ ýöß°"¾ÿ?ž0/} ¢W¥rvz&®9Ú•ºŒeeF€`– �ã£#HKü¬:__»Ù¶ÐºMè*à¤Á™*ñ€ULH6T¬ÃJ,îk [Ô?‡°�XÄÿxJÀ$oƒ¼B*—Š”Ë‹KñôéÓŠHËb2Œ#À0ŒÀ²"pr|R‰1K¯ïÆ£²q«p+€‹øLA†-Þ°¬?.‡zÛ'Xw(“®µÚèÎ^S¶%EµÑ æ¥=„0U)éÿ,êe)葱³»#vwwÅÆÆÆívŒº¨×ëb.€¶«`Œ)väYt)ƒ �%Ä£NWmOÑÏ*ó/�ßú­/x“‚¹JŸà2¼£æªó‚0C/Å º>¬»€ˆûàxþô™8;{Rú÷ßéÉ©¨Ã$x«Û-m�à7ãP,,Å•D÷Wߨɘ3Äñæs{Ø`�!SƵúˆVj�EÜå¿ÙÈãT5DŒ+bÀ�Ç>ì©sõÑé´b¿ùæ§Å�»wÄË/½$6·6ºÍa�Áp C‚ë0äœ�>7ŽÜ7É…e‚Œ ÐYíÌßPäk°7ä!Vцü–bj8Š‚Æö§`x†ÜwÿþáAĹ X:ƒ'@£ÙëëëÁ¥Òýí÷ú¢¹F?õi�Þý>xÀ¢I`È¡Rž$ö “éL ÿLIóÁ2!“ÿ;¶úzL/`8��òð(rBm"ïÙÙ™@€"ÊöζøÅ_üqÿí{žâ 6®n®¼EÈÃ<F€`ª‡ÀJ‘v·:ŽZ�¼²º"^yíïß/B‹ógçâáÃGâã {ðÏîC—SÒiuPéèðH¼úê«¢³Òq@Ýž$nX[[›; Iñâ\Á>Xð‰3Ô&l é%&"#À >Î�l· %�AiÂy¼äñ1(Å`$®HìüVØqÒ=ï²»·+~õ×þ…xã�×<<<œ·Ì�P"0çùå+ÉrF€`*‰ÀVwKüþûÌÏz‘øü£‹üÓ�`;ÞHt:�¶‚xžŒûûâõ×_s¶'Þ¦ÃFà:‚lIãKºÍfË¿¢=&‰Ë}× ;‰�]frlXæÞ'Ô}RoÙîð]ÙÂ÷ºR…gù…-»Êwtxœ_Á-ñ#õë¿ñkâÝŸyGœžœy9}s�á&ŒÀ\è�Û&F€`�Y¶¶6Å/ü³Ï‰ŸÿÜgÅO~ò±øÞú¾xéùV×V#‹*³-Ý\ÁIöþÁ^/„¿J+tѧ6àXÜvdPÉ`xík_{ïuhóSÃv\�HE€ ©Ðð #&µ»Fõ*ÇSâ^2Š‚£2\yŸçþú7>ý†øÝßûm2tÉÿ2?,Û�^¯«Zíé90Œ#ÀDÀ˜oÝ»ëý{ôÓGâþá#o5=æ]p¯ýÉɉxñÅò¥° úbÅÁ÷±pqÅEŠn`€ p—”&Í kIÁcµ}úè?@:dؤ)m±œ‡x§`ueUllñ�6�ÁÀ6 q¸UUfŠéÛŽ õñämàõ¿Èüø˜°A€ 6èq[�qküf}$f7ìâ�fKpTîIîÉñ©÷a6„ʸ:bûøE/•ßa‰Ò + –ܰ¹¹”ª³ÒEàú ‚�­¯TTz{!€yê›w?-^{ãUñ�ÿña{À's7{]­Œçé'ô Ű2˜¶�²à˜µ 4´Poׇm¶hàÂ�!@“£„L&TECØÿOPâþVÃÞB¥1Ž@IDAT>åå©'�͉ËËKpσ°±ŽK¢ù+•qþ|>ÞŽUbòK†nàÂT �SÈêÂ…(8¶úÜ/~NüËù›3þ̳x1ŽŽæãHG¿ ¤.õÛ@ÖM‚±k‚lœ ¾”ûV~ÞÜrAðè÷hµš`Iµ#<7÷y»½éJ|vrª[5w=´ºå+_öb \ÍÁØ�[PnȤ ps݃ç—~°–ÂŽ/3$œAÎö7`å• #P¶¶»â7~ã??ùñOÄw¿ûŸÈW©ÓôÄ,GÏ/Ä&+,KÁÅ!ŒÜ�i+© Zp¼Ùj·]ÄubªŽb:ìÀwm‰àä_žìãK´Yl˨¤[pßßzZ6:‘ö½÷þ@ ÇC�¹o¹0UDà°„«GUÄ‘ež/ggOçË�¹1Ôê�²éÞ[â÷¾ø»à8¿à€°Lã1Ü@9ùGè뀭g€Å+å­o}ë[n" :–I–6”¿�Ê/!A€øê?M@œa)ñ{«C®ËïÿW¿+ZéöææÆ5+¦Ï8Cãdpaª†À ¤W唜ø¶`µúËü¾ØÝÝ�‹ˆ8á>=-ϻܕ1DzmðpPZ§§wÐe’KŠ–´ãIÕ®M¬cÄ÷ÿ£kE)c€sÈÏk…6�þù_{{{âââ"»"ßeJŽÀññIÉ%dñ�Y0½+F ¬ àêòJüÉýUñé7?=1ÏŸ=˜Ñ¥ ÅÕ⦳nAÜ%âmñ6À.)M6,iÇ«moˆMi¥¸ÿã6ݨcÙ ö‰ãàP¯CúŸ_þ•Ï‹'OÜ{?GLŽ˜Aàˆ 3˜ð…ò#ð&;CŽ]QþŽZb GïèðH|å�ÿH¼õ–õNN-$]�´„€J®Æ†˜ °Õrâ Ƶ Ôí`®§D€ Jˆ¸Bï¿ÿ>†”µ+ëO '÷AÀød?K–ø½1ìÿ§NÅçaz~yyátõ]Ͼøû¿'MEãúŒ@)8â-¥ì*�1ä{ú”ãd£Äw‹F ?è‹/|å«ï‹·ßu¿Àü‚öK“ .Ƈ XÌòǯ–¬ ˜°¹ï ¾|i1`ÀböëÜ´‚(ó÷(˜Åcàþ4§bõ¾àí(з~öô\©4~ûw~K<#ð¤±RçŒ@——âêê:á_bÊ�À)o('±„ÞVÁgÏÎÅýÑŠ;wÞtŽÈS�I?º2§8Ò�Ñ€’`&ê25ö u‰é±`‰;ŸDõƈÄo,nhDQE÷6x—æZ½¯¯Ýä{ëþ[âÎÝ7yß?ɃÍDÊ€Àáay‚F•–¡Zœœð6¬jõØòJ‹^ƒÃáPüÑ—ß/¼°çˆóçi8(|l6šÐ§—m4`ZãNLè °€P—•¤“\Ë æ2ê=‹·ÐMߦ`ººz#J¤AÐÕ¯¼ºâžPWÏüÎoý—âøèHÔkl×s…3Ó�/Ç�:ÊÛ4_¶Ì�°FŸÛSÈöÂïck(™Àœ8Ø?o‚ÀW¿öÇâ¿ý×ÿ�¸vè}…©�1Pq‘ÅH› ÑKa cZÜŽyI¯ÜiLÞ‚Ö%¾çŸ&àØZ.*ÏŸ_P‘ŠÐÁ=f¿øÏ~ÁsÏãÕÿ4|²àþ.Œ@Õ8†=Õü~®z/.Ÿü——^0@\ ù�ßü5ñýïÿ““€€}ˆP¤ÀK-hSö!fhük·Zä1©s*Àåû9:ј N`]¢a½¬�/Éx�$ÀhXŠNÀ¼ºÔà@±Ïþüg¼òx4žÁ0¨Ã�ª"p(yD×Wªª˽Là· ÿ�œp Àeê÷EÒ�[�à…_ï¼û¶øþ÷þ‰\½~¿ONÓ„ ‹XQÞ¸<21 }™¼üþûïoç;ßyJO›).lX¦Þ¦Öu2¾ëù9YÐ�¯þ#½&´-®"î›Ê…P?0¿ôÏI\]^qš)3ÍÂ@F êŸð;ºê�¸¤ò_]]yp/ûç?ÿKN ƒ‚ ¸Hã¢àØÖU&€ÆJã>Èüï\ÈÍ4—6,O_“k çß²Ýà§ EýSÅ…U7�\——nÜÿ_ãuoÞÑá¬þóúhž¾á6åEß+8qBC—Wø/og±d‰ೋÿžA ³L2åOäÍ*n UßÚúšÀ1Çöζ—!€Š6Òé÷‹ ôêj¬X¯C @'Boy™Ø@ù .!-6,a§S© ÑHß²Í×I‚Q¸ÿ£~eð˜@`¾KX¡wQ>û™Ÿøaæ½¥.ÐešE#€]­Ì­ó_.&eüäôL¼úê+Ë¥8k»\C#4 1ë�×_s`(v €—1 ²`®#í/Ì,Ðqd€�³œ �´·–“–³ßi´žÔîÚŠ»û£ XôËK\8ùž®æ%dÙîêúÊIÐôšx÷gßøa¦N_c©27gH8‚Õÿè‹€vpF"$a²ÀÙíÇìâ¼ñÆkYµù#PJ`4%¨=XvvwÉeÄ øâŽAÉeDš ÚéP Ʋ ÿQ{Àø–fô'ßÒC€ö‰×ãɵ�/}éKk+kí—mUÁ—£\âùžî1¦,Cé÷ÜX¶ïÝ{ËûXN@ÏxÅ2èÍ20¶¯ÇØJÄí3Ðd˜­Ž�Nù]m×.¨ »{;N¤ÂT€ÍæšÚ:D‡CHHd@ƒ –FÝŸ^a @\¬!.ì@ è2’cÀ2ö:�Îí�ö]1žŽorSÄ@)òŠ?~däó<„©­­ydÀ6h5wQÞy÷�6»ÿ»@—i–�ƒÃãéêiäacd€ãcÞ®e 7( 8–A¯Ê]¨câ¬7ÿ÷V胉;æ5’…^¨Ž ì@ÕQKL‡ KÜù6ª7F0XÞÈI_Ž ˆ.+—lËx<²%AÒÞÅfÄì­·îzû£ãØ‘ÍD� ppp±.Z¾jJ ‹°lÈGàÀïëe{KßÁ`(v  ŽAÐ@YúàPdq5fô3¸H(º_ÿú{/ÿÕ_ýÍa‘¸1ïj#`?Ûª¶þ,}^êâ®mÀøê?Š‚û¦lKY<†¢Û¾öú«¢Ýi‹áÕV”xZdû¬pûò!0ŒÄL ~ÔË×Q,Q60Q ÜÙ�޽Ïe�ßÙÙ˜ñÝÒ"0 ÅêêªØÜÚçÏÎIå8+™ˆº¹(˜>±Õ¶Ok�$Û`ÒÆmlH‡¯i!`?ÛÒbÕ�Ií¾°´Ä÷ÿ£K{"§Ú–²ÐbN]^zé%/`~X¸0‹ˆÀÑñ!dñ ]aZDœX§ê 0€4€ÏÎÏÅÞ®›=ÔÕA‚%­*Apå]x†© c È‚A]ãâ¥>öþï.h3Íå@€ ËÑÏäZÂ~©{¶DÑ@.èþŸžŠE ?TétdŽîŽÑEnèÀª¼ÖwtWc€»¾cÊÅ"àÄÕÓH‰ŸGnò #P:ÐUÿS[¾øÂ^pÊ�Ê!€ÑòÑð“L*»/ l¾òï…”�‚˜gÜ€‰‡ŽôÓN¥Ž�;íNê}›°—ãØÈmEtÆ€0úؼ¨«á©ÙBKix®/JX'Þã ¼ÉíÈ„s¹XýGQ6·¶¼�dQÊœpp3F@�}ØÿÏ…X4ÁðÙÏþ좩Åú,¾€> ŽÛÐK¦íÈ]^§‹pa¥A” à×ÀE-ØöÓ„àÖCê Ð“Ú»þËäA€ yP[ò6n)ÀíšíNK\]éÉaPëþ|Pÿö·¿í&€�� \µš° šýV¨Ôãqã¾­‹=®ŽÌx�d(Ç»p8¤÷ÀÉÆÆ:¯Žúô3s—ôz=qöäéÌ3®³ßÒ¥\L›0E îp°4ó\›ÒäúŒ@Ñt»]Ñh6Ä'Ì„¥_p&€1I ÀÙ½§�º³@€«ýÓGo@|LØ Lj‰`Àu6•ªãZíd‚µ"çMþc£úfÓ>] ºq•¡¸ðØ‚ýÿq£Ite*Ž Èe9ŒxT:1FÀ,777bmm�a*�ÀÎö¶899%Õ·YFÀEð(4–¸ÊÐÖ0 Š|p*Ì›}‰+ÜyE‰“ë€3Y0œâ–ŒÞoóßÞá6í©Ú¤Ä \�EFàá£ýEV�u[rŽ—VØÙ£Øï »�‡càO?~la&€ñÞ‚Û‡h\›¼³ÏëP l(÷js�Ô¬ƒ�̦lyÑRoßÀ¾!Ö{VÜílùÆÛ;ñØîVû¹aé�Ø @ˆoW�ýýƒ*‹Ï²3{� �º Fƒ0ƒSc¸Im"FA)Bþuôè@È,¤I\ê¢Î©‰1]&rlX¦Þ¦Òµfï0c Øÿ/ •ŒïÆE¥hN:°;bà fàÂ,2ì°È½ËºíÃ.Œ@ÕØq`À]¥EÇ Žk€ý쥄É?f .ÁÔ˜.=6,SoÓéz×–T¬¢ri¼˲wx8ŠÉØ.F‚ŒMpŒÁw¸0‹ŒÀ£G�Y=ÖmÉ؇@€\�ª#°·»çD…�-²¸X@Âí®¸µÔQö(ò�©8o6T¼ç-þƒ><­—¢=«¨$| öÿۖш6*m^yöÿ£,[]kØóªÄíç\^^‹§OÏ�óaŒ@Qò€¢°g¾tììÑo@ézƒ>��9(¹ð@Ï? ½�¾ cr'„sÀÇM*†*Öa%×zÿ?ê0»À>ÀØ‹âZ-€O�9!à"@ßKXÜøm䥠UóÇ‘¦É?ôxmwè &ØÕ“ú„·Ìé™_46ö~׋†듉ÀRâ~&›â퉊‘h·1 € UùÃGžñÀŽŠ}ëá``O$FammUF’Ø->e�G�9àBt$+‘‰ÆøÔË/eÖᛌ@ÙÀ8û�i�¶½~¿Ð1œçE ÙT%6|UU¸ÅµãÈi¹ßQ À�,§\ ùÒb#P§OˆÛl'ÿº+÷-Óu‘€šöׯœ°j=ÆòæA€SæA�Û” �]qÆ£±À ÊE–‘þõFÝ[À©ÕMMj$&5Áj˜¸FlH…/¥#ÖFë-Íft/ÅÊ6d€qºàs¼3èÓ{l±ûÿ{�Y�ÀCÞPìÌsÎp*À9#Îì\ °»»ë‚¬èßÐ�ŸL éÇ‘MØ€¥Ý¢ßPרÀ¤ƒ¹î6L¡à-&ö¸@.­Vž�(hI ÿ¡år¢Êd ;vá°Å) ëOfì�gÏž‰‹‹ ÷Œ˜#P0‡KÀì{\Ä@©úƒ{á,(ؤL‹_UoÀÆY¸ÙéD¿,Ä ›Ö8@™ À´–¼îûï¿¿ X›}ý 8yoµ0‹Ix®w팲¬þ£.Rɰ@´¿ùl±xø�v/éb¡ÃÚ,�÷Ù°Hý¹¬ºìîîx“Zjý{%È�6‘W]ϽÚmzàùú7¿ùÍõ,Þ|�HB€ I¨ðµD+�û‰7 /Îzä1D ºÑ[õŒ QÚ&m†7ûרÀð!ãê•Bàá£Ç•’—…eò"pyy%ÎÏÙÛ%/~Ü®àÖÍÍ� raúÞÊüc0“ñZRÝx“&]·½VÏWG€ÚÕðÊzk.yG2ÁÒ#À€ÒwQy¬ìÿ¯×ë¢^�nh‡Äöu?†ôJ¶4(Únòز@y~, =�‰£IÓKÈ:x–L©8v÷¬Bg„ï÷z…ŽåFáþž@»èK}™èQ]|ŠlXü>¦Ó�`ÿ?Fü�Š €ãÉÈÖ@KÒ~e«»å‚,ÓdJ�{”¢Xˆ9!°Ïqæ„4³q‰€‹8}HèEs¢X‘ÉAcI�Æ6ñ¤’™6šuˆàÆPõw\ö3Ó^Lذ˜ýêD+š Q@Ò£4R£øn[NÔ6"ê"MÖVW�äàÊŒ@U˜@ �Ç�y_tUú‹å´G`Ÿ3؃È GãPü †n<)ue�aA)·@2—ÄÀ±\| lrm³«€§4ƒŒklàÇ@�‰¨Yï3 Ò¡L›^Àà,ÿßl(Cé;Ø€«ÿA– #°ˆœž=77ÅF~^D\Y§ò"°Ï�ËÛ9,™6{ŽRönŠ5 ‡ôãI\ìÂÒn;ñ`í§–+D—cƒ«ü—HDSÚ¸F Ï*“ΗP¦ À] R‚Õ¸ eà àÖ»ÿ—¡oY7C0]ÐTl9ÀR?Æ £üŽ#@¿ßŒè êï{èÖÖ«W«aü«ºg¸¾¾Ök¤_‹=ô±âš·°? Z|éK_Zƒ¡Ò+Z•3*5š'ëþ?ÜÿžVÿü€Éû®ò»qå£çb @·»™�(ßbª�À£GûZ 5Û¿ZÌH*i¼CòJ…oÇà-éþ/‰ºLdŠ>£YåääL¸0gñä{Œ5Û;]XÔ‰u¦àÑ÷Rj¼?s»égÓÝdh4ZÀI*À<<¯¿nmS§‚)1†àsÞÝÞ§'§†-³«÷ `‘cº ÄpÁ¿�ÛŽ¢à¡+þm6²|—`@ˆe 0�{¸NoSðc�.Pria Ë7í¶XÒ�eÊ{<` ™·uz»­MöHG‡ïT*]ƒINÚ©J@“ŠÓ±D@³kѨ@mБ|ÿ€ :@q�R#°�© Þ€ÇtÃdsrÀ¿�y@œ¨1Æ`@©-åŽ åê�òJ3߇7Œ•|ñÕ$ÖjÛ?‚e èÂý1 —ED'^:)Ѩ'×ÞÄ­uš“Dì=r„ô<ž¯Ü"&µ:8ÙÔñ'ë6ÚÛ0nÅ/YtGœ”à83�ð… "à"Àp0cHÅW'È•RŒ-Õˆ-X奴Ã@€í¶“-bR šÿj!`?ûÒbÕªŽ¬ÞÛg€(r©CPL�¢[ÒìeIè"bÃÝ'„ëU �H‡6€ÁžNQ¹UÇi('À8ÿ§œ°1%ϸ�ªsJùT¼ ¹o¨ + n!Ó} ¥ÍD”SfÂÃ7+‚À®ƒ@€ø3G/€•••ÂP ‡¢A±=`ÀlXž—¡và¡Ë™ñ\Rú³¯e@ƒuÌB€Ä OâÛØÿO°¤R͉LÈñ{8éÙä-qXø|AxôXoÿ?µÂçOæ(§tHËŒ¹Á`Až‹T5YŠèÏ—Tø#Pvw·�HÒƒ@€…À0hö6WÃà{ÔD{¥-†—°Í€°@0m6â¹ ¤Ø° ½L£ã}[2ñ-­&ìÿ·,p›…Ê+’‹-ëëë`1¦�²›WGnÇP"ðèÑc-rÔ� ïÐ¥œªàB2º­ÎµP•òÎY¥,y<€¬ 9îáv�"TÄ-øšz¶äP‘›0ÎØÛs|¾×»™‹K�<Â8Ôt0Ƹº¼¢¦þ6¼KjðN)âuF­ Ó›l˜ÈUgñ…/|¡ùâË{oØê�nOri¶[òi®cßý£`µx.Ò:u·‹ûª5挀º8I¢Õ -}zz5ý‰¤^];Ô¤øTóag+.a{T8¦´íx׳*ÄhJœFêæ¦/ž<}&vwܬ ¦ñåëŒ%k°H�+õ778a§+È ½éèkR!¸´ƒ‚ aŽâl~ù_þˆ¬—W×�nL²ZDgdÕ’�¥�/¾øâXÛ²ž­ÇW²;˜Ðrî>Æt-–4¨`t`k“ TýÃtʇ€‰€î>m--ƒI©÷L"m'£ZÂÏL�çÅVS:wÕ¼ €Ä^Á£àNêtÊûû‡lH‡‡ïT���±¿O;ïÄ-EŽíгÔÅвÙl8ËÐì{�i;¢"Ï ‹iŽÌ1[¾Íá}1ަï3Wðš-te_©-/ÀJxnJë{)ó4tÐÆ·XÓînwi 25F $ ÇÌÁÁ±¶4ÙskyWýNñhá|�0¥<Ò¢¤§ LVE5Y­IïQ8Úc/Ë=M! »-jIÑð™Ÿ{—B ¦Á†ÀîÞ¶@¯0}�±[ ߟ꟱‘œ�:ì·¿&1×êàKºÇ×�8lˆ#Âç3Ô&õ{¶ï@ÿt4ÚÛR–€(Ç7—ÍÍ bŠLŽ(Mû7/ ìwPôÝ¢Òiy?³fÙdk@þ¥•B¶f¦‹“&f©¯#†¦8*»0 &Ù¦°h`xx”ƒ07aÊ…€‹L8¦B$þxì¨yjŽ2à„�²4À Mœ] �–é8`ÿU"À%D\æµ÷l]±â/q/`,-`¤Ëbp±ú�xt»ì�ç¹à6åGàÑ#}OEœ¬“ÎÐpr–c2™…ª�”Ø==‹ù=ò0¹„°Íü9PÍÃñÙ25*Ìj6kf€ð~³ÕbWЀ #Puv¤DLú½^±ˆÐhÓ0lA ,êÀÑð&C.Œ€lЂi¹+Áäÿ¾-ñýÿh°5* L¥18Hˆúmmmâ.ŒÀÂ!`dPÍâr ãMÐbt=CCZØ$ÏêtNV4ÍâóÓø9 R*Ø]±.³¦�v{š 4ðd€u÷1� à.@_`�Á¢ÊhŒ™ò»ë'�qk0ö­‰N»CnaÙ ¨‡¥‚|ÙPÁN+@ä{¶<›Í¨»ÛË 1BÊ`ì§Ä vt2XhßêÃ~fe«ËA]àÊ4‹GÀÄ€ÒÚLÎãÚ"­à¿ø½¼ç8L˜æ%—ÚÎ~µú–ô<„MÕ"ß Fï9 ô~pÕ ‰Y0zz‘ùÎóõ·bBvww¼I-Ù»é–t¯àL£}šN4 4À S^\\„ ÒÝðàAãÃ?t“€FF¦RØP’Ž(«`©¬}õO¾r×V¾ø€f«m=mÇ€ÅOý}d†<Ú(††¶O·/+Æ€`BL°lWro2Iè²ïÓËŽBñ¾¢ a�ª-N¨�/Ò"1ýÄmö(Ã:Ÿ|föŽÄÝ;ŸVÖå Œ@YÀxNëâùsÚ mÿ¦§ñ+r‡Êb¨Åæü› ˆà&`¤Á—É�ͥ∖­Ç õýòŸ~ù51«†Ífª7›Ñ,¸ÿɶx\¼�sÖ÷,Õ9f4ÙÚâýÿðð­ #Ѓ½�§gOô5À9ñ¼"öéó7¬Ø*âͨUˆÓ'=Ï Oî†JñqÛ†ÎÞz5¡°'’­ð¾’VZMØ�† _¯€ÚÐëC&Íß‘ ¨ÆhptÀvVp®N_&õ nø1=e¦¸h°`Ñz”XŸú¨~ŸbäÝhD'ü~ @=aÓ†bcð(Kéúä¢luyÿ?9¨L°<~| Ÿ$Æèú”+Ë>­1Að·NÏ mæV+͑ϴo�`nnüGZ°ÏèªK=¸0UGã|òñ'¤jà ¾WqÏ|³ÔŒ�]›¿žœ·¸(`"Å@€ÿ³ ÚLs±`Àbõ'¹651†ýÿz/µ4æ^À?¼)�ˆÚP:Mkšy}4"fr˾I͹u·xÿ6ê|·ª<4È€:â Ðú…!ƒ…䀦r,·Q#=,r±G¿8^ŸÇHyðæÆ8ù;†är“ŒÉ'Ÿ&s“køÇ�“ #Puv uÁ ø¶WºJ›§#/š®ÇbXé´ËªÓ�Ø6nƒÃBÒ{.«­ê¼ËЀ # D€ Jˆ–»BMÔïÙ’ãûÿá­'pϘmÑÎ!nËHÑ_àø‘¢.]H )Ó+ ¦ûÿq?5å$Í›¦ãÊù:Ò"¥GIŒ¸ãug·9ÙºÑ<�*\“<%ó™Ì¼rÛß?Oøˆ¨({{ô„·‹j€T€-b@½Öð&ÿ¨êGYà[É©)]`ZlXàÎ%Qm2�-v%nðÎkјy8øŽG¢‚¹˜ü#[N¨>W©$ÆÞZ}¾IZ@þÚ?þŸÎ‹È§IGÏÒñ*Imòkšs\2¾èaAíe�=F½ g¢ðáá±Ç¿(7gY¹.#�†ÆpQz½¾ØÜ,nœ7/[/Ø8.õzCà?Ü@mc&{ÄçóDØ� _ €!·} @ˆx*´zÚ¾Î1Ô\³léÈrå=8ˆ²t»0oŸp»r#ðèÑc3½¹?�—ê‘Å6 áMN ³ ˜´$µìo*�Ãt²T:z_§¤�=>>/½ôBÐ�ÿ2•C`Æ+ ë¡Ë㪥C ^¯‹îvWœ�ž‘ÊÖ/8Àp44kê¼s½Ln6Ö¶×îB'|�´#˜ØÂ!À€…ëR:…¾þõ¯ã’„õ2t| Åþ/ �ªV”\dÀ�éúƆ•\ܘ(#Æ«ÿ „l@Ì¥S|¦ï�CDyÏ ›¢Î`) -¤Èå(âý“âÜqɳx4M„MÎâòþþ¡øÜç>cA�›2Å#°Û¨ èPd bMQ¿!Рí(`}âe`@‘Nx³ �T”ˆãqï¾±é3AØ&ä<•K› µ›™,Ÿé±‹-›0ùG#F`Ñ0 ˆúãmÊ}Ú8˜ó朚O­>@Zô(õÓ’·´•Ò—äý>£’Œ¡õFY4ªDid4˜jx{°È©£àñYp‘ Xp^ÔxßË€š +�¶“n†àÝÐ ²‹E” ‹ÕŸ¤Ú@ò“{¸×Þ¦àK3žCµ1l˸D)û}ú [àJÇ…XD=Þ7VËrn=Ã/ çM(gîæ»€ÛÿíBL§…ùpÜJe 0’>³rú7½,ð?Ò2ñÂ*IfŠœÐº^‡$ºc½VûlH€�/U ôpQzgiueÅi-š£áPÔ Æ­2³F£)š°=ÇÈ�—�|ßæÞÕÐÀ%iË€%éè\jNÆ÷m}o}÷ÿè ¨ÕD@ôš©|."³šÊÔw±€3èòßEC �¾-l�‘2ŽH˧g0™TUõ¬ 8™\Ìb÷ÆŽa¢Â2V=<…†mót0x£Ê¸ò·?JÂñbpaªŽ€«L}H•·º²Z2Ï{î­ûæ™Ý¥ �¤¼u_Âù:ÎK EL‘<û2ùêx6;ƒ»IÓ^ƒæ·Uí½,’xúæ ¤;V×@eÝÔ~ç—âþ­o¬[±äÆŒ@‘ìíí8aïe(Ð)r�k¢C«¼°ú�™Ø€¶·’²[É”²6Ì$q£‚=YÄ©fò"6×0j÷-”ek«›=x”VÁ¤Ã˜éwbù”˜ Ïž=æ¼ ¤º·W[£¥Îj®7Q‡Ÿå„ùRç¨×P·"U¤w‘Ž•$Å^à÷-¡ÕÐóCAÒÒô:ER9褵¼ÛûÇâþ}6Xñßê!€,¹í{àPdq5ÞÄñq>#×îÕ÷Þ{oóoþæož“Sf‚ ƒ¦+(2°ÀŽn< ÉþÈb+—�VakîÿH½»½2I:’úE:ŒÕL¿©( T¥C©JòU©2ZC–þsgN¡[”Fô,ÎOˆÃÃãÙ‹|…¨€ÚpS€´‰|Þ.Â@€è1á¢ÔjcŒÀà.M6,HG:Pã>ÍÙ-ö�œ�B:{HQã¢ln)<\0µ¥)�p¥C‰jòU©‚(Íà¤C©ZòU©–�Ç�ï\o{V ¶è¨>Mý–ò(iÍcŒ0˜¼P>v½š§1üK¢ö‹÷�Pô-ÁÿÈ ¸øÏº–�¼ ˜àüØÝÝ&g:ŽþÃó¢ z4!uei=WØÚÄ�);kiÙÏÆV ƒ�ïÙ‰¼€±\öí&Z;õDiý€[i´M¯÷{ô€ÕµUø(Ø{J˜êRšúÒã!Jâ%_•*ø‡Ò,ƪ¥ß‰UäSü šý'ñ£ž¤ô4� $IÒšé5hïPLZÍ{‚VSj¸ „Bï_°(ÔòX|"DÒNjÚ_»ƒN˜†"_¯»ŽR¢Àz³¸£á ´S&4(`*@4PÇ€,¤°:?›B$¥}š Q�™º@†Y÷ô‡.ÉÄWÿ±V«Ý�Ü2ûêx‚€rÝÍ-4úIÊòkÉ[yä�>ÑI¿Á^‚S:”ª$_•*,ý!ºZïk{D᪼ñIšâ ‹?‘Ô‹+•&í $š£ €æ“›&lÂuzŠÔój�žUÇϪíynÓD.¸ú¯çíâËt|r&`Ün¸Ê9‹_aÌØÛÛ5k Y㬯gBàétgý|ïN\$ÃL!+° àòêJ ½j`Öd=¨–¶–¶ë³‡—’õË#n¨7ë©.T&ã/ôÈ÷ºÍÖ9Ïݾƒ€[Û��:¥¶N­¢Cß«cOA›UY+†p¦ô‹T!K JéPj‘|UªPÙÃÓ³'ÂßË©‰•¤)¢B=™ô¼¾akUAù&1z”1¨äL§SþgÏ߯¯�Ó K}3P` JG&Ï|LŒèn{r|"^yåå<ì¸ #P 0Nj©ß}èmiðÓ&Çb[L~ÏzÔ¼1q{e…Üü­Çðz:p­ª"À€ªöœ{¹­Ý‡ÐµIªµðœà îEd% Ca¿O¿`k ºE�*0sÉbèSȬ’Õ|zϞ”TU¤Î�%m’¯JüC JéPª–|Uª0÷ÃÇ�ögpÚêÒNÒjcÍÌ:`!ÍzšúéÐwV§|�Jªª~¿)VÜŽž…øõø¹iWÖ�ÀȰÑááâÀóy¥hƒ—'®ÔçJ÷š¡éM2þž3Ô™Þ¸èà ì~ùË_Þûë¿þëS´™ä À€èD7*Ôî+f…J¶q€vÛþ%7Æ€%)( õ¾-T­Û50Pa¡ñaÓ¨¢xdB †cô˜–v­cĪyB9;©ö4’*di(A)ÆZ¤ß‰UÌ<}üRC™øDgR£¹‰)àÈVª0 eP5Ÿ^îˆà$‹ïeAóQ6T¿É5è÷/`õbˆZ–€VÛ’"¸TB –²”þÀ�Eº»…[øF¤eNW 4ªd¬î…­íѲ§  Kië…p&>½� `^D—ÒØ†´t]¿uñöh:˜ êò/²eßdéácì2òã~aé'‘¥ÖôÞ!g˜bÁÕE`<¨ NÀ1è2…'i^ÙF£¡¨ÃVVÊ‚[0ë“‹¸ œ €²§�¯O­5×joë¸Df1—Y,›ri‘lÀ©€é°J–‚î¸×Ò“(ᕆê馊‚ÄPy¨æ¦$Qd (4ªdP9Ÿ‚=RöŠ„:�7N°pr£…sB%ÏUßö¥$ çMþj ;®�ç�À|Z))¾Ì‡i?-Ïàx�ñÁ5(¼À,®ZZve¸¹A€›Õr01`«‰šÌ~×Y´1µ`þá‚Ù`0Ȫj|²yq @cÔ–§–§¯µ5…1ìÛ¶^NñÕdÞ�- c{m¹°â<li1̨<èÓ{ nëkÁ|2•{Ëo‘ºy„[ªLêjNjÖЀA£Š–!µ´CË®55ÊÜl0„Á�@)•(U h F©kQN dI#áb¥;�—‹ëض�ÿH ’“I¦<~¦<ñjú<ô`Ÿó³ósÑ5 k*×g#€1\”ÞÍ�¨øÛð· Â6JåàEÑ€t€Hmû£u0oJU™V¹`@¹ú£Ò Û�<Ê#TÜ€/¸F+ê�‡®·ÂFúöÍ#…ߦOl­Eª[[›æ{Ü,ñˆ4·èxuÓ§üÀGFëdŠjª ƒVµTÐÃÖ©U´ô·k°x¼�«ÿ¡LÁõÌ¿ruù8³QöÍ`åÆó€çˆrÅ'×”ô²5YÌ»Aÿ¤i‡Y`U+ív®ë>E‰¦t˜‹àm#�LŽçöðð˜ 6ÀsÛÂØÞÞк Úìe]Êñ›¢„ZŸ@./¤œôßoÜ|›ˆÞj�Äüw`À"ô"±°ˆe½ÝšäÒnûûÿm= †C7n÷²¬ºÇ.< É +lñq�4µøÜ¨›F8ÉÒ«9œu (4ªDW@#Z„­mÐz)Mçÿ²8~!à2ðÏF'Y><Œ ñëzç!ΤBé1/Q- ‡D©à¾f§©(äMWéu·�`@ý€¡´‡Çâ�·ïâñ_F ràä�ggg¤²ß€@‘e1\/€›T€ë_ýêW_™¹�›iV�è,­Úº°ôTLêà6¤9ÊJáÙj�»8¦í r’RYó2n¯%�hòM«ÖïÑî×B>~K <�íú/Mgíë*DšZ¨¡n᤭ZrE5·äv%¹ª…F•Ôׯ4ö�¡Ax)1? ›Ø“†— € &½$uÙ°$­«æ{ï½·ÙYm½¬[?­^ö²Ë¥³`P·>D¤¥.]²€º£T•ºÃh�œ÷-Ôˆ4µTCÝ<Â-§²ØLÍÉ‚¸û¦)0<Ú·ó@ïÒíõ=Ä:ÞI­Ð5�Ú=Ý}‡åà�ÒÇ9(7Ág@ë9Ð4 ^¯Ô×~d€f¨0^F êìîî‚ ?"U£q—Ð = Š(h(!ÿœÆ½4™] d!ü_ÓxóõåE :K[^Xó[VW›oç^Ä’PŒÇð3ä…DýýWv4BjvG˜ŽÆhÕH“ݸ¡Ð0i‚¢)B¬…4I:©ŠÚ-Õˆ4·PCÝ4ÂI[½ÙŠjN³mÜ\Ám;ÇG§V¨ eð· ˜éo'‘‘‰$U_š÷ ©^æìݵ@â €ù¦é*Öp’bþ;<::Ñ ÎU�r#à*`¿×+««…)�^yRfÙ 0 @²d¹ð.À Þ…�ÅŒK�JÝ=ónL°ÿ_bhÑô‹?öòœZNmÇ£±%:<ÜÿQºîöVþ½ÒÒX“jê!‘$¯œR+f¡F¤©¸ê¦NÆ*† ԜºÉG�þÏ:x§Ö²o”d.½åÍÓQ³1ádÒK¡bGÕ15–ïtÆÈBô™ÁDêhoÀ>Ú‚>}úTôûHwÖÒnÃ�²!à* fX-Ð0‚¶¸R–¤ÌÆ@Ù-ð”E/Ê[ï8 %  D‹ Ô™ªP¥D#€\Ú­Ž|šëx4åjç¢Q@û’d´ …< iþW´R‘D!$²æ2EZPIE'QD<Ý 5"M-ÔP7�pÒÕ,Rï±íþ aŒýIš‰8Yu½üì°ö›U'¢…úi™Ð#›tªE[˜v�Ía@~FEªîš<·ŒÐ†tt|,^ cwqaª‰€+€ëÞ�Ø.ãPȼåmkX�LÔøx²@�ÏK™Y³ Ì½S€l)ÃÕ_/`Ó~Ϻ^•Å ×§ßÿ�F“­-ü´QÌJ¤‘¨ésDÁyÆDpDÖT;©~ù$’„Ó?´P#Ò4Ö_úø5Óš?Þ?°~¤'0a÷&ciL …ÅÉ7þÃU{ª‚AJ)= ¨ä²¡C��Ó¶1§mTþ³�V+ò I«”xÝz™ïùÂT€lH„•/V��� ðbÁmÚ±Rï_òÿ,­ÑãB”þ¸mveeEœŸŸ[Ë#pï _øBóïÿþïݤ0ˆ1ãÓê À€êôÕ\$O[û€±€m¢ô&.,¯yAÅ}hÔe}}M4›ÁÖ [êT_¨|XOz*�˜$Y‰¤%ØTŵ”ʸ¹¥‘æ¸� ÆK*Qrlˆ�ÏnùHrÊwóF…±‚)åB±—‡3~@™rqÇ.2luÕç?¬£üÐYHO%†$‚’d9¥2VLR·ä�MùêNÒ$¾Ó¶‰ð xÏ‚ô@$Ö3¹ˆ´(ééñFÃÃ"•,mƘg]-‹ç[âm !$Ф`ò¯ý8ÆX£@f‘K‡R“ä«RÿP[:”ª%_•*ð!#�Šf 6Ü @�Ç¡øÎÈÿëN¾Q‡@€7©ë“1Æ`@2ôK{• KÛõ³Š?xð  ùM^˜½cv¥ Må‚ÑMm#-�G轤9¨‘™;:Æ MÔe[# Å ëJ ½¨HâóD×/TRÙI„îŸOžz‡"ξÿb?ømˆlª9Œ˜‘)¸€/\[-Š¿âH­(SwöÿÛ*ž©¦-›€¸&» :ÑßHïHGd-p·“èñ>fö9]¡Á'àþO¹Ð�"ùÏ.åR äþ.Ú*} ×¼ÿ†J¾Al }”w•ðèðÄ‹OA�oœ·-é5!JU’¯JüCéç(ƪ¥ß‰UäÓ‚ØÝu®ã´6Š›¾`&€Zl¡ËêF¶ÀbY²P{»Â·“Úvж/î´€`V]%Š€ˆAÜ2ÛZi[Cã^±¦BCƒ¢šºàk ÀtœE0HÒ 1eg †+KIÍK =•¨ž"Y‰d€Êkû�DÁ�ä¿&˜HRNúÁWÖ#A0HÒ$A ±‡«&;â>(�ô„"ì?>¤§mP÷œ? ¥�L"Mê�nüár1Q�ó(ó9êïª~“Ÿ<¤‰“Ÿ™’pi¦\8::?û3ãv™Ù ŸIf•™6Iì)$Q­Ô5©¿¤CI…ä«RÿP‚R:”ª%_•*Tî°Ýi‹õµuqqyA*»ƒ ²‘|#Ð¤ÞÆÚŽ2¼ùgög+ñAîZ`W.l(Uw+ ÄØÊmèÉwÿïÀþŠ‚�_ÊR\¤Dݺ[]«ÉRÒ¥,˜‘˜ë§ã,KM5›OÙY‚¨ÉÎ’K¼y ¤ Êf'�<¹¦Às “3å7FÍBL èýï Òs ùH刓ŢšLÇXÜžbÔFÂÈ�@Í—7ö$3ϸê# ãP¯ƒŸI’ ƒŠ|ëèHPf&7”Ž5ªRƒ™Ã�Bfµ™vñ v­ãÔ*yB™ò6”*d)(A)ÆZ¤ß‰Ut~ºûÂ6¹·YÐ#ÕÂh@¥~yyùÐýG´™dE`@E;ΑØÖ€F,0J«c¿ !k)@IDATS ?‹c?’˜#µÍÈödÀÔ‰këkVË[šÃ-e]|Ü´kU²ÔtÚœ@K SvZº¥WÒ`•Þ8÷�üÒ_]]‰ógÏa°›ŸÆTli"E@Í—(‰� ÈØþ¹X¡žâPÒƒ(lIÀÒ Jì—lå é…”ðšBÖŒÛGGG!)—G2Èlµª%Áà [§V‘™¥ÛµN%[¥!”O—T)K7 NéPj‘|Uª <Ä8Ÿ|üPYϤÂMSjêhBX³.ŽGñ½acàKb…©W¥¬Õ¼T€lH~I¯±`I;>®öŸþéŸîÀµ¼§äZ­è#ÕiG·¥ñ|p)õïhH»â“ÊHó† €íÀýߘ,y-g!TŸVûáC–’¶÷´ôHh©A‚@Ú)`ì¦uóìïŽÑÒ5ÄÇ™reÙ›ôï'G¯ßP™±y¶›Ç@¯ Die÷ž-b𾿶§ôº¦˜þžrÖ¨’ÑÅakû§ÀžÂT©ª„p¦L§¥ Y:JPJ‡^‹½Ý½¬–¹îá–Ðdb¨ùE ÷xµ•ctVVmÉ$¶‹ LDfy/FgkË‹ÃÒkÞ÷Þ© €/DyÛð„´*˜À’„ÿxc1º]âýÿr'Ä09gC‚ZO§G">tÒ`¯¢I‚@b�s;4�¬þ#'xñ?²rKÏȨ  '§þ•LÊ¥&”ôêñŒ,I7,�òû-éÙRt¸’§Aû„ªçÏŸ‹ë뱺šgB�¤�RàòTHÀ#.œF• CRó)Ø#eO!®[åÎ¥Î�=5v÷pm‰¾ôaÀJ�/ Œw)K2´a­)Ç�úš²À8ÜÚ×R¦U<´Ooñú°9hŒÅÛg˺4c[:�dFcܬK › Ϭºý~?ëv®{[Ô€\R$4bCB(®.<äS–ƒRÍæSv �øn丄ï&‘²ä‰°õ*âÿ´[¨ð„4 (á„x¢›À¡Ô—’�„85ÿÑ0Såøø©�G:.ÂIZ†ÚC€7ß|#¼p{¤† ›î ÁÔ jN©MËpC�*Šß~HÁ-»Öe€;I†ü™,’¨…×pÀV}xcNG#Ì@\j0K2\]^‘R‡·{�"Z}bl¨~’hãkë æ0­7¢oä6�…¶LqÐ7 H0—‰lw·R§" 1, 2$ h£èÓ)÷€ùq¹ûÅRÓióüZîÁ3=%d°Ô£âÅ‘.&êJ‹6EŒ8ñå¹Ï%)½<2̹�ÅO1—¤øþE/Ê‚4Õ“ù<ñ©ÍñüKMŽŽ�Å›wf RÅä4[n5’NÙÄ2ïª9e6/ú¦& ZÕR¡[§VÑÂÁ®µ ƒJ;;Û^~{êLN7àSdÁ-ºÅdøÓ/ Hm ºõ_W_®W ØP�~r.%lWV¢û¡Ð’ ®Lð2³-Ôy°úïb°¸ÝÝN+¤VѺQ®a�–ÈÉ•L¾¦Éü«–‰åè—üZî˜ÆHï,¿9äfùÀ˜*üU¹pOOGd9ï Ú2æjÂþI�Œ£@Mȸ¾¦l_UÇÇŠL(UþŸh´©ê¦BF�WsŠT/ã‰U2 ?ak{´ì)àÂÐ6x8ž=yBÚ…§€íï; g™jâ•o|ã[ù—yžÄ—¯-lX¾>OÔ˜" F²—G#�6¦ ?F‰Œ5..z„`k{+-Ús�â,ûžð;k–²½rÿ§úz[Î"±_ž�?÷V0¨<ð™£|î�–NŽ�*µ�D?¨´é-ÕsG$¦ï·–·Ï2ÚYþ†“„÷Ÿ} ”¡é¡*`ã¼×ìD ¹fÀVJ>R7µr†¥šÛL“2]ЀB£ŠâÅêS°Ejwoב@KC'½6‚=ú˜•„jét” `8¼¾|þ߀ÿ]nذÜý/kO>Ésßÿß&Hˆ_'W«ÆL¦?œ‚{ÖTÜ)`1M-ÕP7·T’™èC¡8× 4ªd°€þðG¤jõ þn©Sñé ‰¼1>U£f>;Šn³Ñr– p$ê€ º�¼àõذଣރ^„¶u¨Vß äØiµ�‡.akÿÈ[ýÇ—|üFAç.2`à—­ÍMz�H@»%¢M)äO&@""pN¦®©l·mg ²>Š>9À SBÓYB£cì_ÊIZ°*nÓŒüH3SÆ,(™‘l‘.xO‚¤P¨RµŒÃ <¨ø/³äîÝL¦Bœœœypt Ö/v<£| |³Zªin¡†ºi„S>£35'#r󮜃«@€=È�/K 8cLH°ÍU–¦VWWäKdÇð¦ã@€dhVŸªß‡ÖLëýÿ(îù—K{Å>Gëò½–©¸ðØÜÜò‚ä”IÏY2>î3u/Ü ã̱ïVæYʉʔÿ¢jB£«AΉÏîÿO3ÛtVPû…²o�–¿@½z™2fÞ 4Õã3ïZ™†�9 ƒ}�™ ª–døÆdé—»g ¡íÏSà>9{*^|Q?—º="2l¹µ—‰À1­T1âêS 5"M-ÔP7�pRë”ZCÍ)µ©£¸ÀE¹¹¹.Ô0B @ûDWhÐØ‡ñ´ðulŠ`ßaù¤ÒDgl•V……Ï‹@}>U B¢ø,ñrõd ^E_Î9ƒ9À Æ`¦7H¿“ X �çhöH£˜5IÄ6ž+yÉ –iºTö:öWjŸé÷¥¬¿ç µ*È·ó¿jôRR bì�:x`&€‹‹‹tPsÜ¡ˆõ•ƒ-7))Ñ[I…d±Ü"@aôRâ^¨à{£ÿlNójP6€^>8Ä%Ì`þ%Ioa޼y b LÈ¥+n@ˆ�(=K„D¼[Mf©¨X@Uœ(g 2ªÈ·�&N°©J°ï›4m_æäTCrYa�êIUèJ¢>¯ké@dë—ÞN)¹EÓ4Ú¸7ÙÜ6KíèèX|æ3ïÎÞ�¯PÉØYYrÃãòId¨€_ÝB�HÓX™Ê¢náfJ~Zcc]t:mÑëõ§×(®{ÕIh¢/®þ¯€-µdà-&±àuٰଣ} @!0 Z1m‹Ÿ�æ#d+ ¶80t½ Ò©¿Î �‚Kæ˜%·0¼E s“•6ä–ˆµâÉHD�’©ªªQýÉ“§0Ðë‘L~¦ì R‡§„üÄ›¯Ç®Ûœzîé6æÕA62Ú¶Õê t TîúyÄóŒKZr™QO×B¦£~¨�NLêq–¥˜«Å™VUHbP‘•H*ÙgW ’¹ÐI•-sÂ]K5"Í-ÔP5­ÁâÐÎÎŽð·†%è‘óÒÍ5"Z䤔¯fpÁ¿ãçÎÊj>¡²[í`̯?üð8»ß]ذ ½¬Ð‘Â--–ri¯` @ûR¦ ÃÁ2á Ÿ¶t·»É©¾kª¯s2÷Û«æB$·0¼E¦"îo&+nÀ÷–‰â³D¬Å»Õd–rTÅ}h<(Š ;õfë*ü¨ª3Ôþ©”Q‘‘ï{…l‚Ùweb³Ç3˜ÌVá+y˜±ÙôR(öE„òãT€”OŠTbH"8 vXî£rJe¬Ž…‘¦RÉ2`&rÄ(²àø·ù)?é$Á8®bÌ/�” tÝUYJÑY[eÕ`Á-¸oÙ–D#¡àþÿÈG!'Œ²JA''ûH³þ€ÞýlwS î'T¦|Øõ$3"¹…¹æ-ô4rV+YqCv@„DñY"*ñp�GáúQXcr©¯:¹�ôQp HI/®xjÍ¥¸Ñ�Fx”mŒ}GX�öy!ú` DÏ�„*Óô›•ôD "‰Òõ •Tti< ³URÔpÀ[”½€y³‚Ì犗 Vì) fÀ.JMŒqÀÿé‚6Ó¬´Omµtgipú¤´ÎAØ€mñÇüÌ$%ú7´û×ÒÛÛÛ·äP×ð(¨U‚¿)vcɬ”3"½…¹ æ-ŒÑ¡k�®¸ b¨ô¾îþÿ)’æM¸Æb(F uÿ2Ò þ%VÌ3É„6n&Ï·R�¨´Ë‹DÂ{�F;]‡TÞNú ~×××ââòRl¬¯“tQOÊBò2žÒ;ÁYC|åêå“¥ÛÝU²”U08¾�T€-h«¢ny�›·fÔZ…-øþ þ¦Àû=¸0bö©eP– �û Ç_€Lhù*[WÝ©@Xx”·wü�’Åp)/cu;{åBV š ’ÜÂ\ó¡Ês?JV:UŒC(é@èv²NIût^ýJ)wjG–ð®þÛ ¬“UBóRy Êv ^HMY4@*]5X)Å5¯Pé©D@å%©ÈJ$Íá�´ ’ÈWtoo'B�êä › U'õ;©ñ³MHØjAÜ)Ú…'È1€\6,û30©OÞ¦xÍã‹J.È *I+}r? |¥Øc궨ÍÊê DÇc yñ{Õ¾oýáÝ ‚XQ{}�¬4"¹…¹æ-ˆñ�‘Ãèúµ<,Éš†÷£G‘ÚÒ ®ÿK§^#«I°·Âèáò/Q)4 `"�ÒÉh0tñp"Mbºø(Ð=!bø}£ð@ŠÇàîÝ;p¤(^·¤ABã PÞÖ`V&9*‰ôTbH: I€xMìî¹ñè-`&ü¶yq`µ@Ô¼}Ê$ªŽ{T½-å¯×ÄÛÞXØ‚rõz¸ïÎÛà šà«XaoW™JßE€îv¶§„ôa/ 8a?¨ �ŠÑ(‘«½‚>Y+åÌ…Hna.„y‹DÅñɉ!n‡í�?F~ôf&i<�ž_‚¿1†¹N‘V”õÊPªXT˜Ê ~cî =üõ:Þøíƒ¨¤)&¤(¤ ž¦c/@@Yõ7h¥ª—qß#A �& ‰=e4Ùe(žçV ¤w IDû:ÖaË%lg¡,7°E¦Èâ"04êÓ¸�p~~N­Þ;|ðAýÛßþöÌ'•šÓ+7l(wÿ8—ÎÛdù¦Ç Óñ4HÜ‚€€u0Ø–±—÷ÛR8[!¤ö˜ÚŒºlc À¬B¥~1##I³P‘ðHº­}*i7v_ÑN¹P>+åÌ…Hoa&ºÿç)Ê(Ê ®°Soµf”åÆð2Å•*¶Öß9+;|)Ÿ߇€îÙ A#•!ìøSλ¤¿´%™’°ÄW³ù”�¶€É5Ù%7Î}µÒ;! »÷¹àúÇe‡Üàçnègª¢ç_‡L+«NR®~ôƒ�^…æVš.lXˆn´P‚À¨sÿowh2L`oýk5?V.¶l£ÀTK‡ÃJ Š©î�P‘ðHÝ*¹†¯H¡ê$ >i÷M®[)h†òþÁ1Ù¤'ª"*a¥H”Rr”“ItÛŒÓ3CoFDº Z‚hU¢“I“’ìEAÿ ´ÏU –­mEî�cØV#Ÿ<ä¿n´�9Ø«¤WО6'ÐRƒÄ”�B,Õm V*9î—@ú0À'’C§ô&7� 0…]z#Â;“1%áÇ^—WÁ$ú)—¥ɇ¸�æ*@c<Á8lH†~i®²`iºzVQX�¨}õO¾roöŽÙ?`ø ö÷´‡çfÔÂÚ®\«BúG�ÀÅ–„nwKúxéc–=°È¾«¯uJM}1SÜ^v,f6s¼ë+b¯Ž¯Háê¤)l¯ OYCÁ/@š É×uÄ£ZI�‘@‡ùL£” H+FO²báe«X-A´*‹ùëYmhå´'™,�÷8Äž‡(h™7#UÏž>ƒÁPijíÈ•ô©É­f�“µ™­WÌ-=Zj� �v ³»i]šzéwwwiD“¨àØ ·g¶Û.b)IŒ2ǰX…‘û) \u• päü7”ò2­ê!@ûÄVOÿ¥–øk_ûÒ+£‰°ŸÚlE£Á‹]==wZÊo�Eo÷}Ú(ä·²lï €yɆ%û®ÌM=¨P×�éë‹™MÖ¡ˆÙŒƒ»¾"öꄊ„G�üÕP Ô“uŒ/â­®Û.©JNéÒ„ÖŒ€Är¡IŒ[74ªjW™> Ú-L*jühbä[Àszzv&>õ©—cµN-ŸéDþ lT—Jù› M ¥G‚@KM{Úk²›"EsàKï* fh; ¦¬§=nÈ2ÎéQ‰Ö €$¿§Ôï× ÄþŠrã³eD :s[F–XçѨó¶¨ÛïYÅri¼ˆË´ú�º¹Øÿ�t»[ù Ø–¢¨ê(‡zP¡®‘[=ÕäЍfŽ5BEÂ#½–ÑZ¡"áQ´†‹3ô�9�te&EGÏTÂ9�å¼&"ââý#$ŠñÊ@aC³ZDùͽ®È''¯ô®vó+A×ß¼î¿2´�Â@Ë@ÃÔú—³›¢ªÃ™›Þ™a“ó�–S–šj6Ÿ²Ë©qÐL“]PÝû»·G¯!`˜N9Âr.'#ðÈ.æ¨×!B7pAí†>Ó§Ìî°¥¸�¹-…ʬd€U ÀV̰ÒV§ dHûëVI»;ÿëým.Ö@ƒí]¨ómÈóµ ˜Ìá¯Zu S­¦ºFnuõDÔ#ïPLµ¡"ᑺUr _u0ý®TP{ÔÀRë7’¬@ÆUywyF5£[öˆ±3©œ>Öùá™pÊUWçËÔq}Xt¸çQËìyȪ}lh\Ë#m¤ •!‰f={¦³'Y˜ÌÖξ⪗³¹êÞµÔtÚœ@K SvºêA½Ý�/@4uŠç›úÍj g�Áû ½ØÀ¤ƒ´.´cuÔ‚aõÛïùLR8Øo4¢�‘çó²*]€>½­»˜ À*£7‚*!DùŽ2Xä#HÛJ­¦ºF Q¶ªÙw¹ÿꋙͱ˜ÙÌñ®¯ˆŽ:‡‡ÇjrR šX=𠘥óÏ¥NâAû$ñŒ=÷tØ�Ê%?*“ zXàö0Êâm Ì0!ËF9‡Æ›•-T@?àœ@è¾sTä -g H ¥G‚@Ë ólowÅÙÙ“L Loú© ô6e|[8Äw=ÿFÃY À·¾õ­oµþüÏÿ|�Sen¶Dgn  « �|Kß¶}gù)Ã_ Vÿ)VÕ.UúzRÔìÓ»`�ûÿV$r,¬�Rˆ 4b_]™êXÈìŠ<ÎV5û®,wš·ÕÔ5dzFÇúbf“u(bÀøÐÛÿœá_ákA ÊINþ=Lè€ÁÉ«j+£“z¬‰×TþI7æÆRÅï«ê$)�~ ß»ƒ ªþ~\3YUZ�ÀU�4-•�TZò]7è§LÑÙ�� �3ïÓ&‘öHDŸ¾]Ø@mÀE è�»0“i6�¥l=yòäM`õC‰.lX²�¨K�-ºré @¾ yýD1† *yèh²3®æ"`7�J_:CÂï™[èè„5î‹y7P«ª®�2§ )©£®!U6;ÔQM3CăƒCh¯ÇH¯Pƒ?l…„ ¼ÔÒi×@ZYÓu]ùffà3S7÷…¹0‰J'±ÔŸGIPœáê?µ€·§À`B¨ûl`=ªù*bw|rê­Ôv©³nAוù¶zæŸYê™ÕËy“ªc ž›$ ¨ú¥Ü}ÕrooOüð?J‚#÷µ~¶€�¸†‹" <èµÚ æß¬7Üe¨�1Šx^J“ %éˆy‹ƒ’€­V+2±ÁT,#ê=b¶øºØW8…òHgLHy*h…u'Mµªê(L ’’œêRe³Ã g=tI§ t5`Q�ÙQ*�#”2€ÏB0íNŒ1 wx]¦CÞ ¼-.úÍ£™þlËjèÔôâùóçb RÇf—[j$ÓK& #k2½èÕdêÑ:¥?£|)Y<ƒT}‚x»î�ñ7|^«k«…=2£Ñ öñ¯dà°ºâF§qm‚€¿“ùññr!À€åêï©¶_ûÚ{¯C @ë7K«ÙšÒăΠ¼ ¾FÞ:áržàÇ¥yf©Ëö–¥œT]àÜ�€JÓ K !9-µšê�PÙƒ½ì» üÛ‡gOžÁQ6ïì»2Eÿ÷ÿcAIðwHUüI_êü?”/·Œ¦Èd‹H‰U6'¸K+z6;À77Ä”éž,‰ L#ÐDN¤z‡G° @m¸%hÍï–€58ɬŻU3™º¨e©JeL°ü�¸îg©{×buÝzH›ûiðÒÎÿ:«ϳ�2^ *‰1Àr+Ë 6,D7š+1ªÕߦ0õ6b(r±¢û™Êp0$ èæmˆ|ôCÅí€nÿÒ *è(¥â4©¼ç|#[Õ컲¨~@ÚUz¤/»;›?þY-ü{”+ö¢ ?½,Ye¤Ëuì=!9EÏÓÌ7Üäi™�N˜,'M©ÔSBá�ð(•Êô¼ÿîô|.&& tK€¤Ûf‰X‹w+ó,åDeÊ12¦°×ò7‘Ö//8Lh¡­uSq«Ð„XÇL++âúêÚZF™|­Ñ€Ë#À€%íüɸñ6„>²Ö¾ÕŠ>B˜³Ô¶ŒFn"ªæ•ëÆA”egf @Ú'óVrÅíP?E¿*n‡tÊw¤ �Bt:�d”(�° mŠ¿}ppBd+œ}7Y‡z0˜õ£)8'7;ê‘"¤—Í-ù®å;™è|¯ÎAêçàò½%’5±í¢˜€ß9y~·Ýz ˜,õ|Ÿmnz‡<€‰Ò³DHÄ»t–z¨AeŽ‚w¯­À±Ñææ¦hC¬(ô£,7×è¥IÙ‹fÒ�Á¥–’àÅÕ/[Ü@maÙÀ¬‹®vtö¶pê±BiP¤DÚ¥4(¸ZGá0*Y*-/ÀL $áßíî¶ÑçJP¡ø*n‡*jpÔ¨Ò+Ï‘6"ÓRÀ¤VC#ó*G3Ìi$µb*ùcÈ|’*®MW’ SÊyê¦ï.ÊNS(ãð6 â„PøÁiFSõ´�½Ù¬¼éh­“Ó³ÑèÝ—Tm‚ 3Í[Ì�˜ß…d¥ ùß±V<™‰ˆ Q2uCU‹®3$à8qwwGøAcé„»¹¡]!7•l8Oј±Ã”FRý\u²µá�¬~øá‡Å—¤4_› l˜ Ìåc;)ïÛJågÀQŽ_Ú0°×Òþƾ ‘jc¦Bõ�Îyâ" вÕ5‹@…‰þ Bƒ£FvWÅíœ]7—fÚ(¤qnH@þtÂF´9@ñ‚-êbH4UŒ(Îj¾œ°šCƒá–€9T1:ɬŻå’L=&ÂOw_Ø#7\__›þœH5ÆT€Þ¸–ìf·81Àî‚á=á?’ÁÄ*ƒ*ÓUä‚Z»ÿ`y"ßY�`%_¬òe uUÞÜX_7¸hEòN!HÐEe³ïœ‚«âv¨Š‚«âvH§|GÚ(D/Â�€1(~û²jµv¦�Š7ÉSô­~Þ¬NWìeÎ9�ÑX``0ÈÉe¦™íx†`‰/ø^ŠÁP~Ì0á¢xÏcÊC™rY)ÆÅùs�[ÒV:šÑƬT3—2¹…¹æ-”й­�¬¸Ï[$ŠÏ±ïV“YÊ*JU÷vw¥3šÃÁ` p¾‹KEC]� n_mÀž}ÊÒ¨7�¥‰Æ`e‡Uˆ*ÔYT¢~ðÁø†zË–^üEÛéØïÿG™Ê”åq“p(Ç?ËTŸW”Ú}‰KŸ—£™Ö ®ŠÛ¡Œ ®ŠÛ!�òiC ]×�€+/çÏÏcÔB)£XÅ©lPð㕤Pº�À™tY0WG;UAš”ôôåJÁEŸ€ûšY“lñ±»»ÌÓÛŶ‚P¥@X18Êü‹ºŸžžŠ×^}%³Þô¦¶Ó6IV › ‘ÞÂ\óIÌéZºâ¥g‰�ˆš¼. ¦ÜØÜpAZ‹¦— °¡iœÓ¢(¼L+ë+šµÍªÁw•šA¶PµÙ°PÝ©§ÌGÿôÑ Q³~£Ìì_|8NôÒ©è©2—Z. Ûpæk:s!Y¿Ùïr¬ž²B¬~±§šZk ©¯¹‚«âv(ŒG�*!½òéBpˆûÿã³nã uåíDÞü1�¦l)È 'ßš¤Ñ’+cG'wö2­Òç‚M³‘ï ’Œ±&‰„j˜Z�š&²š ÏYÂ¥™Ò/�À6m@:³;¶BËܬ 6$¹…¹æ-d¥ç|œ¬´¡·D¬Ÿ%°·ëÆ€ÆèB ·e9>I©}ƒY0¸6n¹¥ÃwÐÚ8Up¾QzØPú.¢°9¬½M1nAtRy„Óî MÁÎýi<FhÒkoN‘:Z-J°½íœý4jȧü¸++øL”Ì•4„�oMÍ•Bék®ÁQ£ŠV‡è ¥Ô�²’&:ÈC­zz 9K‰??£éÁ´�tâ7†åüWö’†8âQúG.§÷0ÐëŽ8ÏÆtNë Û¶§ÇOt}]è5Ò^£ŽB? ~+Í…Hna.„y mDÜTLVÜ€×-Iñ^xÁ ½~ÕkðÐùêèS4«éǰ,Â� �FS¬@ ÀË‹‹È=Ûö°E°ÚíÙPíþË%=•Õ/žp¥míT üT*¹ÔrÒ-®¸·ŒºtoSR}*¤o«¾¨JæÊ >/-æZ•ôew\SSs¥fZ+¸*n‡Â(¸*n‡tô޼ýÿiS}m™gyÉ«(h ð�³õò]� q‚ñsSˆ«- Sž9êw šÄž\©§^­÷hˆ¤[·Ðà3,ãSG—T¤Þ±2 ÏÅžW(yx¥Ø{}ù­”3"¹…¹æ-Ší.ùÑYíˆuˆƒtyyI*Ô xY†Ã‘öṵ²Bn“:{8é±je@5ú‰TJC½-®óox~k? ÌKì¶Ý&ܶ4,Eˆ4ï÷è"ô ,”˜,´˜kUŠŽ–2–.‘ʼ.jj­ÇLkWÅíP×ÛÛ‡‡‰‹.ÚlB†Ó#yõ/"+…4Ó¶:HËÛûM™�¤RÎjRû¬Ð6Wb�ŽúScca#mÌ8áSžýÌ^IdšRíäSΣ„„Gyø†‡Gyè8jc§\(”•ræB¤·0ļE¨vÞ£½v�ÒqÉ+©~;üJ/A Òm»I8yùý÷ßßþÎw¾óT_K®¹(°`QzÒ@˜ü[[ýp?îM J§ÕÆõŽà4÷_/`îÖô {}t)£/¾ ŽWŸáYÝâRÍÖл’K%se_8%se=%çTKSk-iô5Wp½½}|x¢÷ÛW�‹ �•qŠîoÒ—¨Vƒ•dJ�}œü�©g§UùëXÿ ß(áÀ€Ii�×[�"m‚Ú­µ=rÏB�³³3ï9«ËA2TôYTõœÜ5�ò2ò)T�4Ñí• )[)h.Hr s!LZìí½ >ùø§¡ÎGWà€ï/…,=S˜Á ¿uŠ6‰9z¬¬Ú{ØJ$§‡�•Æ}8ùwÓ |°4°`iº:¢¨µ °½b%¤rÑ:ÇIïfžÉŸas±M>ÃæÔu[ª�’¹²‚¯¦Je]¸æVOSs¥<¨ùsHKvy}ñ  ï§ E@š8IÓ£­®Uƒ}P˜eÐË4²±: è9ž[Éè¾±{+¼ŸõïV]C¡ ˆé{²ä•Wj'ÁKîÙÓ§bgwG!€t[j/]5?´Åœe´…¯ˆ½:¾"…«U.<³WЧe¥ ¹É-’…xa�> NÀûƒ¾4/s¾GãñPÔ‹®4�y`Ô./身2”ØP™®¢ôÁƒ°t/îÚRkµ¢/¸6D)Õ�g²._ @zÄnm}ͯäoê-¶ÉŸáLàof2Iláâb¡Ú(™++ø�hA©UÉĹh¢æ‰TÔ4 ³Û ÆÑ[­9)]öqå§:jŒTÝXÄ}¿¿BÏ Ð`CùÈ2¡!k«��¼ÜLy| ÛvRs©;ì[*EЍÏ«à+b¯N¨Hx¤'Á\jÙ+è‹i¥\²{{n2ÜÜ\ ª”Ôyúã4¥í±yhÄÛÔÁãvuÆŒJMÔ9 \«@’ Uè%BÇ­ñ›õ‘°^®oBTR¹¬¬€€`˜ãGÑNþ`ÈüæuÜëÓ{`@]±Ôï.D© á¹0™WW’iƒg–¤‘”•´kUJ’ÒøÚáñqä1ÑÐ@‹GÜ€¿)µò·MÊ(#!=- ´NÊ“Šgd¡Ü·LÀlã¹ 'ñÓ¿6ûäû:èz­hrºesrz*Þ÷SÍÊ’T1b©ªº†TÙìPOD5M‡"ª™c�P‘ðH¯e´V¨Hx­Qè™�r¡è’r¸ÀE¹¾ê‰n—J`s G^*@Zþ ØÐn·E³ÕÃfÊ¢+ð-´ö¦“†)Í�è,nžœ™W!4†‚$`3æ cqU á½½˜i#¿BТ×ë‘sÞ1Hõ‘¾»Q}¨ Õb™Dõr|F[*diòk1Öª”Ñ_sµtGÇÒ8h‡5eH«c�¯…Ø%™Y¾c¤I‘õ–»'#)½Pã4˜ò)^áVI‹Z-àò­ÐGňž¡`k¸\3Ù«¯ÖÈ«qzlP �º 3«y\ u�x ís=õÈ9S-@¨Hx¤n•\ÃW¤Pu’‹|v_Ø�8R5òԩ׸%­À‚Û\Œ¹µ¶²&ÎçÔäـъÐc@E:ŠJLÚ€á'·¨&ø*0‡jÙJßË+K+Ur@äâIË1òݵ"�)¡ýÈÅ—­x&V™4ž di)™++ˆÃã#x¸üzêÚi‚h\Gâ¤ÆA|[áƒF9X£¥GGJdó*¤]¢Å>è7­Êz¯TðÚp³uÑ¡ÄùL é:ªS¦·¥¾£þÝ«k2e¾ú~=þúbâ&ÿÍV"¹ éU_{u‚ç—T¸)1 $Ýín‹'OžL¯Q\À"ËpH»Bè‚^·+««âü9¹€=�—ì/–¬ÃaÀF’PÞ€ƒtO²ýàŒFck”݉nÂ.¶¤§´E0ÐÞÝ„JB”4UJ*&© �û\˜ £¹”"´ÁßÇñfÈ[¤–òa¢=L£g›Þ* <Ö$¥g åó·0åŃ۩ðà–0W×WWÆ>›}¶¤RÅ$å~ðlÕQb/Ì`üÃSC‘¢Éü.g£ž}W–R­ªº†LÏèX_Ìl²EÌfÜõ±W'T$<òy¼ð½2ØË``þwtEª7€íN'X´B³l°ÿv`h¥¢×¸k° 7ªÏ_üs�Oš´VÎ¥tÎ5› “4ɯ›hóìé3ß@Fälö¹ÃÉT0¡¢PÚŸôÑÒÄg�RF =3Ìl4äçLŸO\QÏ*ò3,§µÁ¸ NÒ†Ý2÷=Ò¸ç»~yy%®®oĬΖ|tÀ˜%6{%…ülÅ⮨UU×@éÕªªkäFAOD5y‡"ª™c�P‘ðÈo‰�ðƒê‘Ѭջéyã6'¿i ð€f9M¶F3eô˜XYq“ p8ìༀ Ê^X¬ lX¬þÔÑÆÚO¨³ÿ_G°ñ¶Ä¿: Õ¹q°ÿEÝÞÙ¦ÓÓéÇ�ª3œ IöȤJIv~±LP²rptD†}T¨Y�ðJ^—ø$#n7ÂàoIã¬ÕÄÏé¬ìM—ºJRÿ¤ÒŒUõM{`8„ €Ä�QþÐP�yÉe�"'ò £ã3ȰþÆk3mè¦ I#¾/w ‹¥ º VU]EW«©®‘=õÈ‹¹Ôß×½4ã5·dz~*@�XÛã,º­ºñ€÷ÓãüÉÚðÕEE€ ‹Ú³ z}á _h¾øòÞ› ·Œ.%ô¾2Ñ�P”-n ›Giç9ëõé¢;�@-µÔd1Lj?ìQÉ©´Eªî¥’2SÂÅa)1õîfèáqPG9IóÞ5ÀG5™Œ>ÇŠ3è[LñÙÏ•9$Ai»e»�+z�v Ô΋Uò�ïÿgïM›$9®AÏ£*ë¾�¾ïH �8Hj¥¥fwg—ÔŠ$öhµ4šé—ÐøKfMôæãÚÚÚØ’ 9<†�R‰ƒ”Ð t£îêªÌ:òØ÷"22N÷ðãyFd–{[uFøñ.�ðãÅó÷ðk].ÄÜ ½Ñœýg,wF+é¬íív=C@…ɺ"9¥#VZnETÌg3¿F@·øQ—0´åɣ蓹f)`«Ùë;aóÄŒù¥¨TôôÁ¹•åZ«M@xÃ)˜rÑÒŒ¶Ÿ©|ƒå²ë*”JNPªî°KÌê•ÕÛ¬Ãpe”p‘ÝÈcÀè½.ð²8S2ꄃ7:¾I¯|ôtùéCÌÉh(�2ŽÅ1(¥¢Ià>TH¸»ÉÖ–Éù„ÏIœÅ '›Dœ}Þ¥D 1ÎÌÌBä�СbY‹¹±_J½àÔ¡xvô0Pé÷YöÃ~v~ž½ ŽvIôZ‘x i©>?æ#Ü�P€6S>rØ�"ANN²µÄý".�âÈ~ÔjDk+]÷É´a€t´À@‘©cÉ¡5ZÛNNLÂÑ;òµ©‹PäSn§(HðE ¥ 8Q�ë 0Mè…¹ ‹Ì(gŽÌ/Ì3<Ë•Nòwºm2'jO¶ ÂN¦H@ÕÙH²%¸'ãX€Ã¼È:•ãõ��X(p;«ÛÑœºWÉ*ƒã•žžÃ9Q¯P¦ÌÆs~~Žíìú3ÑòȾÐù‹0vŠ“|_Fáœy‹éŒ¶YÑvù×>o^ÃKâwe{Ç<`>æ5¨Þ:EòÄé\:bÍgB>«ù5’ŒPÏ‘5ÔXX\ð¬¦ÎA G™N Hý1+:ªÕ›¿ÆGƒ)ûÏÁŽœ`8r.–*»Ç›÷TÄs˜Ñ„ZI¹©"Ú*Þ=§Æs¢u‹¹¶á€€’G*Iò'nµ¤˜I�ñ(6Í "ÉÞ­à‰@³é8ƒL�p£Ÿ™p÷-³ÏlœÎDŸ~$¾t)6À:j”Ož<É€`7‹PVRT™Àº�uàÏDÝ÷°°Ÿ‰× óÞ÷¾÷½êw¿û]ʸ¸並Ã�@|'7 ŒGa€"‰–obx­VªžIRp¯Ë:å*[:µ`°„æÿÁÌÄY;”G¡¦©3J…)'[Û«³¡ 8RŽðªU ¨ÜÛÝcíö9ç£fPK 6ÖÎŒè�˜òOoù4 ·h^•WWÖØÑQ“Qâs2:%:V^¸IX_[ç„X”x(âA%~}Ô¡k0Ž à‡EH§þ{ ¯ööX6õ˜E™‰*>–Ùæ‡”–ïJZ9¤[W$ ~:bs¸)¦xmm…\pRôx/éßxÖ£uÏÀBOMÿê½_]¸-Àv K*§(iÇX"‹D5aŸlL’˜w`!F6Ñ ïôä„Rfye)d“3±+¯«”„ô çŠÃ¨2r=Fɰ“Rf\±�}B?ý†O²"}‚êœÏظASÙÜ 0 ŠŽ›ÇlaiapOuQƒxÍ—.m²�>þXocIEHád<šY1Ê7Ö×`Ñk'üÕÉI úçžplá<‚1š¼›°Iº,Y€¦>ÖsZÉf#Ý{û c©Ë¦\’c€rz&§8•ƒ5§8„S¾+iä�~‘ ««k9ÒQ/FG¬m°*M:¬V‡¤ß×´õH¸l>$ù§­oñ€�ToWpðÐl³œp €rö‹-ªŒ¾ÀpÀB¯¤©l_ËÚí¶•/xKKË ®P~¡ìÂUPViX/ãŠÓ „˜Ñ†—¥ÕˆŒ:ŸÃ¨uF©°“Y$ ßêl(H‹ÏñãÇxþ_TfÕ € *¤wf™ÈR™ÍfägG€Ÿýòæ&ûL[©éN1Bœ1lzÓ=>ëkklaa‘óõßœñ£ã¦÷Xz öPÁ�9îªh]`$@(Y-tƒFé>e\ä`Í)æ`Í)á”ïJZ9¤Ó)G tÄzÜØrˆVsóy~CrjPÜ�5d½fî+J*l)º•:ü¯Q|îz¼%àãÝ¿îkNqÈ…F‰*!¼ò\I‹@‚d:eG˜}bUXdª €¹yz ¯‚œ‹Ž7¦Pö̸p83ãÍYÔŠÚ*« ̉+.™œ db‘œÛÛ¸¿ë‰SžÀœjÏ!—92x>ØFZZA Û‰jÒñ'n-hœFœ¥_ Ê ø ì”pUF¦Î(f$UÕ*akk‹|C#Ú„áb'Un(´:::>bósö¾¡��›·n²ýý}¶»·GfUD½øS~\-5¨V«&u‘­®¬xJžBˆ=úx99=ñž}Õç_”v¯¿ð'Öìlï!Ð9êãH¤q!—QêM�ç\£DŸÖ¬9Å&üÚn+-‚Bò kàßÃF*< A N¨¬Æc z» Êã• H)Ѐe¼!‰ �^µwŸb@¯ÃùÖhB€)\q_H¢€ ¾¶áYâ[Ìp&zª WAÊ9 Bˆ™„§3•¤AØÍá0ªŒT�Qìèxlg'¹ñP&4Õ ×8S›4C»mwö<ïò1*rÚäý"Ìå¥%ïÝo6[°pkB§3Öi££(½þÊÃYŠòk|É¢O™I°,›�/[³³øu«ê“�VóÓáDj@øgø,e’™z`Ñ >f¶SÈôBNÆD»áCŠõ ¯šT%^ã¡çKržK—×9XsŠCbr°æ‡pÊw•'ôï�ïý1Ç¡L'`ᕇ›_V¯cgC§ŸÓÓ3ä  ßY$;qÌï�`Ì;8`¯ZéݧXãL$,&AišÊvþù9±`€Ç%¦g2¸Œðä.×÷TÓp((eˆœ!D9N¼ZZ�àUå0ª3›ÑíímØ/Ù0iÎÆ7�9Å–�C¼õwÛ]ïks¢Èø6½¹¯x›\ÜèºÄ—@Znüºº%¨ü9|rȺðãŸ÷…^˜d;ÄSƒrIî=Þ†PœXSðæd£“/QI ±T¥l: È•àZŠ*5®s°æ‡å`Í)á ÷j|}HŠ´Yp$tBh#Õª56 €]øGœnãQá×_ýŒ®WR 8@I;†š,Xï�h÷êa@3j;¥ Hk^…ZZ^†ÅZÆLîeÉÏÌܚܳþ)Oë Ùiç K bF#-Ñk5ÒfX£a£åñãø?»LÉ ‰ï(mp»ð¬„Tä!ÌXìì¡#5zOÔ1Êz£"Ó²ò A×Áá°Â°sÌ‹Ga©Ô%L€Œü3àáèèˆÍùJP’¤Ø‘ª$¡ÅP¦.CbÃË’äZŠ yÎs°æ‡ÄH`”¨Â_­­®²+ÀÇx[>^Ä�aú±€£ÌLÙ‰ŠR¯VÛ·€¯÷òxsåã!§�~”áÂXœ? �y# v: ¿ŒXØOè�2hsbÁ šóSD�üJ^ ·¦W ßܚ܂ÂFª˜+EE.|aiAã4R¿rEÕ·¶("$€Â­Ž-�­—#!üÒŒ›²ÝÝ=0Ñ_f58î’¬8¯ls¥z�©�AŸÃ׳>XoÞ¢G1€�Fú|{@ý>ØÞÙ%SPõª–8s‘çVðE�‹<·BÐe¥ù•ä<—^yÎ%0JT‘ê ÊF$Ü€Ÿ�Ÿ³„ª.*áÇ-êP„¨È´$%`²ÃjèÀ)d9 œ`z‰†FcdޘߟEPßóKÏ1ŽÊ‰³ápiY¤ˆ‰ÄàF¾¸5rËn-n�{¥k:–!e¡°”!r„%I“lðé§€—8ÄðªGòsˆ’ôE@Š/ûðp¡¸úð+T5Û4<–“*ÞáˆWÄ X1:v ) ´p±ñ5r·nÞì³.IŒŒ  êè�q¤ZÜä"Ï­à!…\ªRœ©ï$9Ï¥P�ë¬PlË †ô#Uå²d¥úyI:Î6ET…# ÀFª8G€6ÄZZ˜NPÚ®¡#ìë_ÿzcnaæš)ĤÀFcʤ׾l>ð« šVR'±56 x9w·Ö @n¹ ¬%,¤àµha J"§AÑ'mÛ‹À©¬I}7ç*r*¸�|„×ík0·vvØ<ÄŒž˜,îk‘ ù¥«×—޼€ ìóOáù�>Kà vÔ5ÿ�¿9¶"Ýìz9j©ÞË8íôaÿRqƒt+s$…\¢’b©JÃ?Ÿ×ܶт€ëµu;ÊWtºº(´¼ŒRCÝ¥uBúÀˆ)Ôc ø-5þPHͯƒgON`O¶¥� çï‰$YïC\º“šÌ8¤˜Ð[*.áŠ'6ÿHš_Ì$×»ÂZ^a°\È—"·&· æhÔJQ‘_XQˆggçžç|E@Õ£PaqîÅLΨdAu£¯´œàXó¸ëo\qCøøñcvíšžž””¶€Æ¡ÿÆûdèè-" 6ü¾#0D>ýÇ šm|�)MôP£=j²ƒÇ2‘I°à¿âuf€Rßzg1Σâˆ+2…Rˆ¥*õ-"ÌW¦NÌzYÀ5†ý´±¡Åˆ+ëÌd è‚#@øýHÓà8ñ8«~V¥[q¡õÅ7r-�`äºL�`ª€É²¦U¢ÐǸîGo©6L#Õ%å·8�š‡êÉh·´²Äå3X f4sY ÈO¥Üš^�ü"‰[“[0 v .ÒRÜ‚¸ÆŒ9³LWSæ½—kÀߤi£‡Á'úÞ=gjhŽ–c™rŸÙÜ #+|F0´ëø{¨$Ù´±ûçHÊ�@¬€#¢w ÉnŒ4QÃXÅœ›â‘äHW<‘ñÈÍEž[Á‡,ì/¬’[�G¡V>/]\\ôŽbià4j À–u«o@¯€ÁÑYpž¥qÌv €qìÕOU0ë¡XãÔ'À@d~™ž €È}­Ô-š0•ió�DÛ8ÿ�pEG¨dÝÐ N—²$ ÿÐrk äJÜZÜ‚,º‹ÏûôÓ-ý¯�ò»9qgÀ�abÄw%ù¾|òè‘wftbr"Óˆg‰„XÖ¤;S�`œg~ÿñ¼¨É–h½�ÿèS¦>3º]'è“'Ož°óó6›€ù9+ €MsÔ/¢BÂE€x†‚dÀ«í‹B¹ÉEž[Á�°¿ JUò¢/ÊÔ„HE&[ <Ž;5�RÚœÙkßùÎwfÿþïÿžÖ´Àœ.Á‚²g ˆÈâ$‹\­^ÔÍ=漣ó�û.„á"›×yHóON#¢Ûòª£yÛü¯˜,ŸJ‘€%7GdDŽ ¹€[kP ·HÖÒ}ƾþGÁx‰fÊ]÷p$’MZ‚>8ï˜Úø¡5Ò>þˆÝ¼qƒa˜Óa§!tá°Y*¾�= €s %ñ@ާ’¼d5�笱Øóu‘&ÁHF8Nc$‹ÍÍ äÆV^c*Ò¹TZG€Iž(ÉÊ©¸A‚¸ý£V ¹D%@¼¡Xûþoy˜´òÑÙ'ÎYï¢@ÅFˆ­~pý'“d?¨a$€™+ €J³Ý„#ÃìW2ôº:£-§íþ“¥žD€AB€áYbÐ`eŒ`#àââ’ô$ȸè_ªIÓ)dzRî}Öò å—pÜšÜæ9P#[?bÉø¿)ÔÛ¨¿èwá&S«yÂ>þø»zåJ²h¤ï…ÏŽ$g¸ µ„ÇVöùd£õ‰äÃ¥&ô¬P� #–»áÓ)ÁH¾@½mLä2M{¤ÐøÒ:•T�S®(†‚ÄXÖ*¨8⊌G ¶ ×­Ö©­Í2�›X~|\Õˆý~Ôjuˆ0ÃCuSíõЀSP ´ÄpÂ]‰‰t¤Kàž)„Z­û"6åE0Ÿ.{$€-@IDATl™H™ðkãÀÒò¢ I#ÝÖ)†Ù}òï$·¦W�½„ÛÚXDÙä�Vò¯ó"`-ôÖ_‘ܤ¥1dçøŠ©l>Xm›››©Æ^;þR ÊÈæ´ b,¢�>&ûûû¾ÒJ€¿ÐQ+6PÖ°�Naív Oö3_ªuv>Š»è�4Q>ài9P‘JE¥�ÂñAB%ö\8:"[__Ë…«S¡Ç,}-—"§Ýé° Ößj 4)p^¥†œ¢‰Â•ÄZaUç0)”1½w €1íØ€­L³*»ÜëþNLÄÏÄN >¥Tœ¶tÅÄm·¼´e¢i‘hfàR0úN‘0Ì>L?«-0§DGyÂÇXƒD™1 )É7iø„Ì"{Ûó®^aëëëéâB_×tߤÌØ|¦êŒyÆ8t|¦ÿü/¸¾:h BüPó”Dè­ „c™D÷ú”‡ñ€6 ÖŸå�N³GΡT"\*©�p ö¡ ADäiŽØH ì l€–‚Ùn·á¡ Ý¬£qzfÆ;†ëÁ—¢D®Œ]$ÃrØ\­"%àEJ¸«Õö]<�dŠÊ;ï‚(¦š:ŒÀ-ún§§ñ0Q4-/c¤~!grçdg”¯™ÑøBd ߊðúŠmFµú8>ÂY¥âž(¼MQŽP~~~Î._¾¬‚�Ž(ôßq™î1Ö.8{aeRèÉ0”ü'B"Ô��~¥– ÖÀë9E 0`(ÀàZ.ú KVM*B­©ÝÉ>àRI%Dh A’]¶QðCŽ·”©Õl‘õ‰]YçÌ´ÅÕ`Y? ŽÑ (qrÄ-+8šY¨¬Ü9ºX·R¹Ïä唲˜lÈ7æÔÄÍ“Ì×?Ns+Ùçís+4-�@*q&wNvÈ~MμËÉ΀#_3£ñ…É¢R&Œ‚"Íÿs´XZýîû7í‚%#Ý©øøðëIrlKR²¿à� W._a¸è’Jy/¬í°‘ÅUÊ�.e8j=†�?*dpxæÿ¸YϨlòNv&šËdR ¯ š2B&ÀG*‹^› _MÐ$ó×›1H�e¢!È "¥ž_agrë¢�G€séÊê {ŒV;„©Ù*Ö¡=õú@4uð03=cCà,!�ù¯Ô<4æ2kö`r_gQ�J½ž8!Mg(\|™ÂHÒiz�±¢m$´jâÌ»œì Òú59«NvÌR«Í2ÖÙ£ HØÚʳÈé¢ÁÃ7¸ðôÀ?/µ¡Ž ¯ümPø¡Gå8EijÁœcJ_½z]Þ—Çà‘5ðËá>úÈë§tf真µáHv™Éä䇵Ì~ªüHÙeÇó¹~åç%n$^Ì—pw!�@¡ˆÞA™Þ˜M†� Ѐ!Kå'”ŠB ï©° àôìÌ‹6…ëÖ¢R�ýÀ{ÊTEG€``)ݸÿÝl¶$ }"K”##”hÒiŽD,&§ÌÍÿ‘BÑ")ä`¸W§§'V.^t'€‚Õƒ (ÑýšœÕ';oåkf4¾YYŠ?û‘ïôò¿þ#FTà1€Š¬ ¾™øåù•‰ÕŒt ‰ù>˜˜_ºt‰ÍÎj„bŠðŸGf^UåIŽQ.?ƒÅý£G�ØÑ±š™/Žç Ð©X P±ÅK]øZoª<Êz.vÀàíÛ·xh ÊÏ¢T—õq› ;™E²®Î†‚ÀÈ8VÀ©^•Gåº%G€Mˆ0??¯N(Q ´Ð¡VøG¬XÀðÔE?N@Ôÿeãeí*ºzU8Ïc6ãx!# o? 9�*_ḵÉA8±`€›„©FÚb"Þ+¼)QŽî°Vj˜?FWQq²3ï×䈊“�G¾fFã‘Ê:ïÿ­(Lj¿zã—}Ùä��ÎpØgÚ Øþìì”MNNJ+N þ>ô•k««ÇO£ÁùUxdk¹ž %~ÕÛÞÙaøå_þ9´·¥€Fzð@ß8!%]4 Öî5AÃ=tØ/ÏnRd™.2Rg ˜WB¥.R̤À”W¨\ ‘kãª(:%VµajgtÐ1(Ù7Þ‡Ãì"—;Fp €1êÌ$+ßùÎwàóTïr2_õ>€* ÷EO•ËõO!ÔuZZ^Î4áäN±êë“ˆŠ‡ U‘- "1^�#*Nv¹ýšQq²3à`–ZmkÙÆçÿ³(ñùç#³ y ϱZÏŽ'JMüÑ! Œ%@@Ýáᆨä[YYfóss°¹Ãç"ÙŸÉû‚ûU‘Àñq“í�õžw×Ùøã¦-><ÿ3QļÝz´Žä5mÃÀ‚¢„Ï}¶Irþèëá…ì#åÕÑÄÓJˆ9É€¨Ü †Õ,-édP¤Î(f$†Ì*A�� Yð²Ô8Î ¹Ê­�þ[Ô(Q.QµÛi[À_�K 6 ÇqÏÎi#W�e”‹ ѯ£^Å)F½ôŸ�Ýcà:KPEª(ð’Ì øRÆý¼!ѯdë Œ ©ª'Ê5z,³IÐa).ùa“Lˆü†Â’ª°Ú¨rDÅÉæp µb%àÉ×L4Þ~ºµCþõâÂH*�xÐtº áÓÔä*ݯ›À3pêù8Q ÓvRüöx{þÐY Ö‘k òeaç©áã“-ÁM>.ÞŸ€Ê>|í…Ë*œÑ#7á^?“�.kñh©ð¥ÚÃã,¦g‚ ïî¡Ï®Df&…2™!íÊ9 Bˆ2ø¡ŽrI¸dÕ8Œ*Ã×c” ; eÆS ¦g¦á«ö k‚¢�2À"“-kWÏ8<Û§Uô0z˜Kc/§ã.¦ ˆ¢STcjŠDj¶EâN,øXZ,È ÆÄÎm}$¯Î…*)Z%5Z2×1 Ь÷krDÅÉNÀÀÛxÍ�mÃ0 ÏFʤj_�‰*àüˆ>…üv@)�cšX¢5nâ}©æ÷¶Ãt^:°ÇÆ8ͳ33l¾Ô â´G�pÑ&L!)Âj”…ùœQbˆ…çæÑ´Ïõão í¸pÅy_ü³Äæ›ãw='`yí³©RÍÅжþ±QÔ ´˜Pz¼zü©<>j2ŒbÓ€gÍ<¥áëÁ {E"§AQ�"­F ð�ªrÕ‚©Î(v*‹ôððøC-îy�p,)2c5 upŒ‹¡ T-iê±§À/NÆLõŠ”̦œÀ¦t †M0Yò|‡ 5yÄqÅY áh¢Ïl† MôN�| €¼i¶äãlù ¡q«+²¯Î…šÀžw‡šW{$Ë9¢âdg°Ø¯ÙÕÖ6|]6fÀǯÿr›l .ú¼ñ¢Ýî‚™~Î:[^fš~ÄTz|£Y¿?T¥ë‰ Ÿžž±ýŒÅYàW%ËÙ"^žY9¯]ÙóQi‚c/éZçïÙî»î)µûkfÑQ’<[þQ‚Þ‰<’èðÊåK@©@Dˆ3é÷¥ÄŒFZO†V#cÆd0ªÐ:¬ªÎ(æUT<$V€r¤2Þ†²0¿Â#G8~SãǶӠ`¶�æ_ûök8�|b¶Y 8@I:ÂT!Ñ Hu/ ø±‘ÙÓ·ÿnÛ¿-0ÿ']„ö ^Æ�a¹‰júTŸ¸sI£¬ È&·º›a.TENCˆŠ G§:ˆ ߉í­mº KŸ{@½^e ÜsàB 7‹Ô )a‡@7So¾½�¢© �êñ Ð/ºïG8È9~Ù¾„Nà3ËI�.(šúQ¶*:,—€#­lY)ä÷ŸQ-hœFÊO½rƒ<ž¨Ë9Œ*£QgÔF$<úƒçä“Ö1ÊBðà˜Ÿô§¥)l…ÖdxÔÌFªŸA1§°!ÚÒÀïäJC¦#DGž'Oõñ7… -‚Mý„ ®S2T¼+€5ªjà ´¼´¬²Öð ×uÅM܇q¡Áff !‡M2!jrBÕ`­Ùáá¡ïdˆ„ÝH[öü?p61Y‡�•/#Ü<Ñ-¤Bz(Øÿø«õ¾Kã/ï£,ÐÊSZ& ©•9j<ÂÐïÿ�&~$|¸AÍ‚ù¯ô$÷âäÃ�Tr…¥ ‘Ó „()Må’pɪqÀ·  U¬Ž»ÑÍ[¾½P€3¡hòŠº•*:üapï~ÇON0~}rD¿´E½cõ!=$)¸ò@Á]9~O-„DÎVVÀ€†Ð>¤”¦·FÐ 8“*=ì™ ldj°Ém¢Èj¼:ª"×q¨Š�3«oYøú�ˆð<¿lššœ`ÁûÐíÀ® |Rh"=ÃPY*äëÑ÷‚<îÜš¥&.—úÒVÀ/|�hÑeÅ<³¼äy�óg¡22ìíìÊü܃‚€~¾°–°P w4JÂ2$7”2DNƒ¢iZ�àKV][_—¬©Ví¤yÌ*KùcR{`½†ÝE2m¬×*lnÎŽì—œ#À@ÎcúëcÚ±ßüæ7çÓ›¦ì8�)47âÌ:A%‰_éð_°¨ªœœ´¨@ à ÆwBƒi%s1{hƒ�“ ‘Fzë"&<:ô(ˆ°`÷R‘UnuE6ãÕ¹Py¡¢€à•OáoK:Ćè@•‘]ï{�á"üú¡›l}Ö¥‡´]Þ#�WNJŒ&°ðñÓ0Üf¸¡�†œlLAü± ;í( ÄuT¹À‰4IŽ.a-¯P¾#¹5¹4œE(EEò|aiAÌh¤%z­F!›kk«žóU|§(S³I¿¾S¡„9C‚ ¸A]\£ã‡9êV0GºP€)�ç…þ j<å16\MO×ïS Ÿ¾ œ0î(E²åÕ„6G–Aã\©ðÏ‚†ôΚ! ôUØ}é2…*E¢Ôã–ˆMì ¢2«ªÈ&·º†�Ã&!ÔííØÿ‡÷fÌ…­½sÐá­ð 7ÿÓÓSžWx¬Øë�|fÍö´.C«L!IV �a'Z¥‘x¹{AÀaLÆ=ß¡epøšÍÁ³š—Úp”e¢Â·ÀöªòÙÛß÷|dˆæÑE[.Ï·¦W¸�DnMn�܈r¥¨È‡/,-hœF²â¯Ã‘ÓÅÅEF§ÌòYo @¬ðæ-ÑkÁ³h6¾ëè�Z8œ@TÐcxícØ©ÈR·/¯ìˆ+�ÁDÄ Vk Ã5¦÷µ„Ž€t墡D�––—%AÊMùÀ:�‡„�DBP0$ØòŠì¼“GeYÊåeí9(�{긵¸e‘a …¥’+Z °À5§ç ¢Ù¥ !Ö`�> ãÍê!N÷ˆ°á.ÇHN0F�e#PlŽ¢1·½€ÏÆQü¢k�Å¿UÆì¦Nx šô¦Ã(„ðÚîÚ�ŠR»TRmB(Þì=n©d­OAøTůöÁ¤ØóÖOI" À°Lª nK‹³l{÷pд__ðˆ·L¡àdˆ[íí‚Âð ›Âºcll/äd,¬åÊ¿”ܚ܂Ѿ�HøL·Z-6æòE%TúÑ+êlfÖOí^ý|P”¼^»p »ò- :lhHÎïÔë“ÐÑEBïßÂI‘‰"ŒS0ÿ§v:ƒ$,¯¬˜})LìT2 P”bVu**¶EJ‰È¤R$èsKÄH_ÖÛ[xþ_&ÉÕ y_?ƒ…ßÅùY¶x „Ð*7a5ð†œõ1Ö|RI�z)�®’´$;IžýŠH±Ê6ñ€¡é?:¬”I¨G8É/’¦Ï"ú0MIštá9E‚Œää{œ[Ó+��¸5¹2|ÐÕA ©~f Tà;Ÿt¬mÊ'~˜³¥Ô¨øŽÿ_S]ûrJÀ)ÊÙ/T+p0Q¯ ¶] ÇF2„ÛØhËàÕ±à@ÅCçûàNÛ}’³n^SSѲbQD)1¸‚0 ¨óš’HJƒ+ûÖ6žÿ§¢$$CÇ[㘴¾¼È>ùt'Wx, G’¤ÒSCKr“ØC’ÀÔ2 ÓfNµa ŒôàÙIœûGÈpJ¹ºº „¤ VÉcvJ2*ïîÒÎ@#•å Rb"ª<¯ùà¸5ég> *·· J:oÝR(À¢Ç×Òëçà;67;ëÍ�Ô 7˜›Œ÷úœ¹–¶%à¶%\|pܱ¾FŒƒ©b;815à` BQ¤è×; x0l)V¤œ†26ç…3óŽ æ¢@ †EJ©HžM¬¶A@GŠ )& ¡Y8c=77Íž…QK‰›24¯L*Ý^Ö :[á°T +ò€f¿ß¨ÀÎZh£bjBñˆ¯©G,(…½=s žDŠÊ§R$ ýY}W_åÅ+÷$rk ²ß¡$ßÉZKËKÞ—òóóódU£ûâ¡åš#‰Æ5‰; º©ù»: 0!ëqºu €qêÍ>/ÝZ÷~59¢jð™4UòŽ v €ý&žÓ$8ú¤[ÚPà¢e)á �™:‡J°‚È2 *ð(Y‚§¢Ô.•&;øímøšH¼£ö"Jcu™�žž³³óЗ€¯XèÀqô@Õ7>¡Faù y-[sZÉqgù ¨ämþççfØâÂlPMú·GY¨å‰s:<›–ˆD MèU¤R&8E‚ÌC!÷t'ka­¬®²Ç�É ‘®Óô"HW'¯hkí[K]<@­€¯Î€ü)(@§(O_�QRñ"˜¯ˆêÉ€ž€äP­N6†*¢^ «SoaC07?§üEȼ×â|éß™÷³�[ÀÑx Ðñ %•  @Þ|rx^q—|ƒB¡¨‚¹õ•K«ìm�“ÂøW<›�æØ”‘ ({HNú굆²!AÐ=aê<šµ0ëE”¯Ç{B!6_ð7×õÂîyGaúð̨‹K­’ç‚ÕúM­vûŸsŠ»ý¼¾F¯8>†P€ä3š¼<§¡øjQ¾ì®^›€÷�pd�6Ý‹âIHï!›–NMCN !´²7�µï} ³Í‰‰øã15Õ0g+¥pH�V–Á b¢šʳ|Gã�BñIáU§ÆÎÎ.¼«ˆ§“´îùÿ$Çhn}åò*ûè“Ïü?(Çg¿JFÜWQ-ÒØ¥þMlLËFëPB¦õGEoóŸ1©6Þ3¨ûœéG2Êv!(øÕkWb•Ô^_¨-— (†S$Qó¢Þê>;Iyÿ~%)2»_ߨo›I´>ƒu¾sx¼µ¨„ŠuübO™ª ÐŽÅO­[ïÞZß¡¤×Á*‡Š{ ÊÁÿxRÑëÞ,Ú 8Œ@ež32M8ø•1�@xê´ çØä̧%–SU¢ô«-ö¢-Ó׊¨ÓHr†ÀÑ¡°ß'¶½ (eÒÚmºs�èqýÚ•5O pž-ˆ b\\S-°Ic¤ÔÈÚDeå!Ó ¿ V(&a½¼wÞ1ÿ5£{ÙHüÈ%ú·_“3`q²0ðV¾fFã ‘E9Îñžùa rmÕR$80?¯æ¬“’ï.DÁªÁ?Ê„kó¤Åüj¯‡~œ€J %‚c¾£+3Ž_°ì½K!‹¨ æÿò“>;~pøôJNOOô Z-K[HHD¢ŠOJÎB)§8‹iÔY�#y¨#­)/‡À‘eTàQª¼~ÙÞAOûLœl™^¢²pMNL°W×Ù£­]8™°ä%@ ê»_‹0x.ô¨¢En¢ð¹�w#ã«?Ò¹¸0Ç6V�•K¿Û†0–Ä_÷ööôÄi«§›8ÙTôkr+Nvùš�/LVø˜±Ì{d ®oØQ4Á?ÆB� ü&(TFJ~�:XÌC$©ÂªÎ  Á–¦S” ÈIÀÐò3+}=òÅŸ@õ ¡;™‹±˜tÄtq&FEŲT*lœœŽÏ) ð·…ýë(iÔ!™W¨3á˜g�£QìZ°À¡ + ÄB¾²¹Æö�ÙÎÞaê˜âõµ Ï<ß&‹VógjÄ! ýå¤G(êÿ:lÔ7Ö–ÙÜ MDìívçBÊ~”J§ z¬9ìs²3°ökrNvÌR«Í2ÖÙ&Š„�u<@ŸšÇÇô@ Ú˜ÿý48Äy�>Œ‡Î BÿŽRU§¥Þ’ õÛßþö¬p�GNüú�Ž´‚„çÿU7ûAÛè¯ç5šQ‚ëÖ ½ù?²… €@‚£·T(çtPNqØ*‡óœâNx%�:l’y¥�:Žyæ8Ò@±µ#áPȃ+kQ‡uŠÊ›Ë‹sð%d†mï°Ã'ñ…ž·±ƒN<ú›,N£x‹¸mR‹ g\pò䊑%–çÙÊÒ¼ñWÿ¤¬|‹¹w(Ù–wxø6`Y½TžÑQ�þœVqs²9À ¶@L‚¢<ùš‰†c};ãø,üùŽûèXm5Ñ`qÉÖG0|×g à“£#j朵DKÏ)JÒTd´Û­{\³]$¾“”pb2wèO­«›D“ ÀK^Õ æÿˆsy%ô ­¶°àSö¿N¹Jr8Ï)y‘à\¢J�â- ¡)¢’^I 3ëùù{›êMr›8®s#õz•]¯ì«°YÛ%*Ú`å“LÁfÏWlÒÉ.‰§ ÷Ãæ.�mŒwi"¤+ÆÀSÜ$Ÿ÷Æd�-ÌÍz!þLÎú‹hC?Êõ$Žýý}¶¡ÔdS| £êƒ8TYZF¦ž@L‚¢{ýšQq²0ðV¾fFãRf­­¯�àCRÚŽQ`ã…“¤² Ç`iW>âD횆HÎ@²oG­šSŒZ�åÐÛÀò3Ú„0455 uÃ{nÜ‚2FhµèÏÿ£VV N4¹órn…�TÍ{À%�q€ºà Î%ªHq¯!iÔ9RÔ@�Q¯xg{—{þ™ Q ¿¨ÂÎ â†ļá«ÇÄä„ïÙ™³°C³H ؆¯¢ø&sƒÊeÌŒ‹Dï>£¨À¯ýuøš†æþ±M?D–@G“è/7§'§$ÏQûÌΆ�¬A(5ÙÄ}}EB˯΅šÑR”‡*ª9²eQq²3Øì×䈊“�G¾fFcÒ¬µµuöðZ@³h ø†VhyGÑT‰cZLXHW à„$7-°@«© §PÖHT­‚€ã¸H…þ:ˆ¦†§ˆæ¨_ã G}>I�Št \ÈQ§F£zeÍ�Ás+øäIÍËR•T9 $Q ×^޼sŠCô9²Î)á„WÒ¨Ã&™W¨cpÐ`òkh¬‚æM›ÈׯÂÂ[Z'l �Ih‰ƒ@¦è| ÇIøË’nü¼?Ø ‚$æ�†‚Í!G¿ C‚«3D ø] }‹ý nï?½í=çðâU½Î¼�ð ¢ ÿææçÙùÙ9ÛÙÝE€™•b6ž+2?‘du)·zÖ‹– ’6áB�Ô–¹ !ÊÔÉ:Qq²3Xì×䈊“�³Ôj'�¬¯¯'³ŒïñÈÍÙé›lP»â“' ýNQ‡"ôCZQà–Åì-y]ÍQ�€SŒB/©ÐH0'*Ô.š&üò&? ™b“obÁ€Pž¹šR“¨$5'KU"ëO9lrb^­Yç‡tæpŸS ¯¤Q‡MbW;^€X–üMyôÖônÊ76ÖF%1J°ãò7||(¸yÄ…céãüV®¤¬À2eBEÀåÍM¶¿À´­:�¶§³§V¸‘„¤Xt �„›ÙDc< ›dB”¤&Y-„š,‹{ލ8Ù–¡¶@L‚"Þ:°‘ZÍcÖ(P€ É,9æÉC¤(D§Ýhg#U*]ôà6„[ L§(Pø6PƒajêŒÓÄ×/@P.û‹!˘N ¿àdñ$0«µå¼¬™'…Rª’pr÷AæMi~-Il)*³2ä0fµ,*/‡ûœâ�j Î%ª ¼/€4â�Á.zLœ€âæss¾Ü ¾ã °¹"'ûèÁ+²¸Œ–(fòŽ}†_$ûÏ5Ñk‡>ôŽíIöÅ›�AC>Ü&Š¬Æ«s¡fÓÍÍ�CåVÕ�˜E·6,0:\*$J“߉¨ôc•p�íçê½­AÛ*ŒAsssÁ-é/ØN¡À¥1“€SŒY‡‚¶•È@øh˜;ô…Œ^‰Ë˜N,øX€y“[ ‹‘žþs™Ì­à‹!W¹qJË}Ѐs!�‘`èÙ²–¨‚dKE�à/úÅ¢ ŽMÒ*84l’L¹¶NÄXX\`g°‰?Ö 1v‚ÉŠ¡UK‚§ý½}Ï*Aݲ@r�Hà˾-ù(ªÈ*·º"›ñê\¨Ù"忯¡r«�PÁÚÚšw�ÚgK³Ul$µ£°òÏúÃA‹]?²]GƒnÁ9¤gi …»¼Ò�äÑ•Àw¾ó�YØý\Òm´Ã³²ñ€à0º’**þâ@ 8Šh…Õqb9=£÷à[È Ü‘�œ�úxáHOÿ¹¢Ê­à CJR•.¬"áôô”5á ‰úÆ!þ<&ïÎ "ÌÌL3 å’“@Y%€Q_pcÑg�ª ý Tf­$‡¼D«Á-úÀ÷x¢P$u®«!Âõ°šÛ¾Pd“[]ƒÍ° ª"÷!DņäÕ1õÒÒ’‘uMQ^hA‚5ml™<[Ѱj°ùGG€‡2dH×K'§�–ÖèTt €Ñé«\JÏÎŽî¡+£ÜŠ9pÐ�¦Æ�Ém§ fO%KèÀ‰Z»Œ,.£yY´'„sj´¢¾€¨ H��\}í·”¥D%)HUŠ=&�Ãf‚Áo»µ½m Ĥ 7îL7--.é6u휆"TœÏƒsÀC�Å·#öÂd‘œ[!Õho(`ðQÇž"ÇËÐNJŞ͉…\ 63›h9l’ Q“Ùª&¶‘LŽ×dám‚€"“š€<¥Õj å³ä Ö«<-O…«9* õŠ3*\�)�½^æib"®šj`@óÔi—€ø5(–pN¥ú‹ŽÞP!ðÔP@‹R7R×RÌC%üŠ�÷—Ûñr’‘" @ÉÔaD€y,…åè?û/¨:?ÓSNLL²‰6‰dåÊœ¢˜›Õ;ƒÛF Ñ?ïËjÂ÷Ò¯KæPfP‘¬#Y-w‹Ê]þºXìòtÖÔ`3ùì î�pqÊUZf×]³à°ÕjYùðç�‡�<‹X~­tÖ=3mÅRnåµ×^“�)ªE½k4l ÄwzÃÆîðÑJ€(àD=îœdjzÊ›L‰íõ²=ŸšÂ5irÚ2iÎm›Rpkjà7pX«Ð~%±)ZÛIÏáê$´�A Ê´>xï$â±?†‰°«pj‘RB A©§_·XìK5ÙXÛ�ª§Z d2;º)R¼P€ŒÆ¿V€�mØ è"RŸßp§7><]\Nzà©“`ˆ.B šÐDž×S âˆ{·N©ÓÊjâü.q§‰K£ÀÅ‹$qiNA×òŒæÈe– •2�Kj>—ä50ŒÙÉIË;¾O üLÓüièÂ×Q—œFEº–nçí6Ì� ã“DÕf³É0úÆ$çS£)±ÛCG€R*AâTV zýBÈ�Š �SÚmw Y�Ôó¯SÉ‹M)kŸ \³7ÀgÎé©YØÝ$O`aŒ.�‘œ`L:óÁƒӬʮ˜²ƒæëø¤)ˆ+ª²& Ú%Ûç˜N®é½�#+Ë+|^õfdI6Å“‰¸4‰‚O(¿$ £À{5fù„ ™¥AR&EÂŽÁù‘4L,¼±ƒßC®ÄI TÐ�vq~ vÑK¤Á%*#öÙú:Ñ9jÎxHI6…÷YM¨(µK%ÕsP¬"åo.oô‹±¶ºÊ=~œÕ¡Úyž€9yÚømYÅb(Àð@­€g }¸4Fp €1éÌjµ}· [wSv’g§Àš™&[ƒ�)]øµ“:­¬€€—dé�¶¼þMÜj,ˆ —ò„8Ä|5fù„ ¥AB¡HØÙÝ&yß“‚8?S;mªxž:ÚÖ]; Sèë¦ gùuZÉ(YHr†�½½P¨˜Q «\I¹¿Å¢È%/¿• “@ù„ˆk‘I¥HÐåv ž_z@±¡m)¶1Àìì yèDè»§@S¥¢ÅVü8»RZ 8­< ƒæ…$ìQÝðOOMèpaН«ä‘Ê¿JڈǺ¼¬z@ƒ-JaZÎÅ„ŠK£r).�Â)èZžQ1�BFÍ‘ìììõñ«ÁÊ«}aÎtúé@bÔ2I–kç$`Sͦ~x±s|Gò^$ âÑ€Z¢"B0X� 5±rkA\Ü’„$ê€Ú° à”Òž'~%M’ò’¬†ëbêyÍ h'ÀìßüÍŸ_Ö>–dÏU+¹œ ä$MUÀÄYÁÆT–ù!<ÐÚØhKˆScÁÚH+ Èû’_ÅïlÐ$„)ß�B06?dä­ŒåYËZ\*f(¥òŒæ““Á잀 c¨h3µ:<8`KË#Y#_ê®Æ¸J ËÀÜþP;ÚM|ˆ7$ïeF•ýýƒ‚$žAŒ6%ƒÂ!´%’nH% ‹”j’¸¾Nï­?dc$€…ù…´(‡”ƒŽ«ÕxØmSÔUh+@›M¢§0í¤’´w €’t„)T!'êà…¸ç�Ò8�`D€^ÿ^—F\ü—ÑjÈÆù”:­ÉKñy0~—×6YN×áU²ÎPîÍØI´Ê†˜HqiH¢Å'”_’„Qà}‚Y|GwvU¾ú`R ™|ý€>a³ss6ÉMY�LÜo¹$pxô”]fŽ·Ðaßt$ ‡{ûûÚûäòŒcy£Œ¬¤YFA9p!+A=*J騴a€h‚ÓÛ"m¡! ÉÜ유õ‹à˜!*~ Áµ,“ÜjªL½a@ œÑºgÐ|дY`O7Р±[�þ×?ªIe@ªñ…�HÔ2:b %^é §ëðJŽq36BôÖÙàÊ/ É ¯Ä„ŠKC(¦WOžøtºD™LÎÿt qëÓmvùò& I´ô8ܯ“€®ÐáÖž’ò,:œAǻکÿnD^´žéöÀÔXc.�€Ñ¦k Ë'r�Š|QæÖ DëSòa‘Kؠ١5#€–ºð-céä �Ÿ[€£¼°]LpƒÃ_Fœ#@C–©¹S”©7Ìh!QDÃ5¦ÁüŸ`lêvÁ)3ñ¤[Ûp8=3ͦPny©<+¢¥a…WƒB…‹8kñ;0æUÍØˆã·Ê†˜Pqi”L1‘âR_ÿw¢À¤®ehÃÍÅ:äôô”=zô)ÛÜ\'?7)Ŭ«ä$�!|¾ƒ‡òn7ï ËhœÈòþä µ œ†r{×ÀI€J¸Ð><:b{;»d_ÚÎÎOi÷#ý—�È+rFŠœâP¾ƒ�D•í^Mu” âki�æàåpD±[FÁïñ½¾¶Æ~øaŽ ÔŠ›à Èä[ôäÑN†u|­‘fɬÒsE>0ĸ�€X E€[X˜º[lع›%/`伃h�íY˜‘f¥õÉ)½À••U 5:Ë�.šörÚ«¢â�3 �Ò4?ÎZüNvÈ^x¥ ƒ¤¾! ØP;ÿ/ÿ”cx3Ê„ÞÒ=zä™JÏ/Ì�ÅÍŒòGI�ƒu±$Ð�ã(Í£&;8<ð\RrÖ±0„¤z5dFÈCÊz°Pů kXx0ÂÆ®…hÒÜ¥°ÿïóï/”*^Ÿœž³“­=X<íÁùfضx¾¨d�»ªBÞ«›Tê¯Z œæÂÐÏÃ/y‘JC*§à€�o @ðfð(ÄwkvfˆÍ³Œ«.åK#7‚7ù£üÊ95xý’ÓÌBq𤘂p¤�bÍB$ì?ôQƒ ¯¢FþÀ5 eBÀXXH“«WVQ“øo`;�C–íS7d⺾ªàP0ÖÊÊÉ‹©<Õ�8˩ϻôÍœx¥Å䣿ÕÔcsåË�"iÌ�€6ÝJï±Ðkåñ�&A_2p¡:=5ãýQOœúÄ–³%nHNÏNX³uÌ2¿Ä÷û ߇}0–I*]ì™ÿsúQW´nJj°°á}-�$"dQÄîÚI` 0¯ÍØc¾k8×yçzõÕ/0j•õ]€ý)ëýÅqtei 6"¨ÐpIV(·¥Å°Fše»ûÛaßk CYý"KG´žêhsÂk*Ž�¤[ éè¸ÉV T ò�z#æ…œ±¢`Õ^ý8€�‡qÈ0©Ÿ»!“ïС`ü¸KaA4ŽC‚ä…œœ nµqQä;G¢œ ´É4Ħ_kÀ"+!#Õ­_êI=ÝJQ¡ß2CŸá™¥ÎÏ-f.­ sàfy ¢yày9|â{O²rñË;ð5Äïyø?ý$›HÝ£™¬i±­^›„¯þDD™äÚ; dHß5ÜV»5°¼Ñw|‰J3jÿžr/X$$†d<ú·ºìkft©t~A^_½Ä¶v{J ùñ3ÑY%ªD›QޒЍ£d_W°‘0 ´ï& øÈ({��E\�ÍÏÏ[ –½**þ+ÀСJ Üñ ­CF)˜×ïQÀ«×à ?~ýÖ &°ñË¢üdh‚I­íI‹þü?R°²!eÇòòÌ®¹Â“e) (ÝR�íž÷•e~VÖ“uš’‹žƒJ´òÙ;Øõ©�ììîÂs›î«Hï2¿F¼ÅÉ ž‘Õëq„„ç‘‘fŸ4}8qªÜ�“€= À‘y6Q™‹›s­gÿ.¤^´T†A«PxŸœž€"0Ù�N­.¯yGj¤ºJ\  u*R¶v+|\zˆ V~=‘7Îj ŸÔ ‹4P‡�ûWëëëžõõ‡/²£)ªó3‚Hù5ÕjÌÍÏyïµ·Wk*¬ ßôœ#@¡„F§Ð<Èûèð:Δ’(&#B„"•Ñüù‘�´Œ O€3JÎîj¼¿üªBP6±3G*™¬Î€É¿Ûü›w :õZ^\M-øwwv2åîõUð˜Æ:.’9xŽÓy§øžáêOóo”’<“si8Nv$€Ï,>»:Ïýéɼ‹É¹³Iæûåî`/}Ägq~ÆwÞ?*'“kTV.€…Z1)6@ ‰{Á8í¯C‚ñ;Ý4Jƒ¹%1p²e¢zqqI»¸‰ P\Åji×R”,THÍLÓDòJ-\ 8Àˆw"zän˜²˜/p¦ˆÓó�=Ô¿x€:Õ!„ââÂ"wrãã“�9õ;`àT‘ÎçYXI6­sîË?™üqa�\¤îîíõ•Qð ž§þuÿá‰oIò5tfv†NÍ4S¬á×T—œFQøìâ3¬šNáK½ôx�ïÃ÷çôèß~ÄÒ411 ~T¬lTY«úèH�°a׌fJ=TÀF$/9?pïãÍ¢ t…³±±N.Òc8PdBG½6’§˜�³ÚYØ�j0Ýòª¡S¢�œ·žú*#A„çL--"C¾öu€%Œ“V3!óÛ¥¥á×JJú³©%ÀàMæÝT6�Z¹³à•².ÑI ¦À“èã£ßÏ^/êvaaFC49Öüð„`,ãªv{]¼®�ÖGk×Oðîd=;ø 㳜UÆË; æb½á;œ-‚HÁ«: Žë\¢—ÎUÓS¾lYSüÒSjb×8§Ký�}P6­&¬»y#D3ÔkT”ÛX'£cÒÙY+¡oþÝßý�‰°íGÐÁJÀùЧü…µ6»Ù·kìm"­©œa˜•2&[!‡Å+NV ¡éDÝ‘ =��Ÿƒ“Æ£v‰^è¹# •Î8ôc4:OÄ)œÿ÷·E ‚l´úðVa‚:®ÈI ô€¿ŸCÜxÙ„ÇæÎ;ç MžuSÖûzp€G’Ƥ[ÃëÊ7¯~@9j>‘þäçȰçò°‹Ëå1Šá ¯4ä|}�Þ ž‘o�´ØÌŒ•Ͳ”˜ð�G” -RgìD¨‚ÕÄ õ×”ô:X×€S _æ´‰BzÊÐaE¢v@BAÂh�Gtê´\²2ü…S«Lm~�ìE…9ôÑsȧüâ– çêJ¥Êööv_ ¤¥!ѵèÐûô) 4¬XÅ/CÙUXÉ]9 Œ€tžå³Ó3Ö}Ê´¿¡ûïmÞ{4vÉŽ<ÿ:wœÌ­à(5JUÊ'IR$rØ$�IV[·pQã1€B „ÐW÷e ?žÌÙ±€uƒ Ð)²E?2¹N02]•M(UÀhüß h" š¯®e ˆ^dOZô>VWVa—œÐ£Ý ôŒzÒà:“å¤$Щ’Kv$€&Æ^T€�þùݾmõ�pÀ ³q r ¡ˆ\áˆHŸå世G::©�/Þ:‰÷¾=��¼ø¥±Æªnó¯#Zé6�ï2�õ¼N�Q$QIêA”ªTˆ"a"ØH¨°a] K«�P€ø ÎÍÛ‰šÔe=çP¶sK\Ï)JÜ92¤Á;~O¦^^�ÉÉp£åE�˜Kò`¶Á¬‰l–ÈC¦PŽŽÉlX&ø ªê( ‚¶Ñß1T$ Û%{@ùîíí꿦‚qâ´…GÔ“çïAn ªܵp¶4žeô€ :Ê„ÊøÃÃ'li =©kEIŒƒUŒ¤)©J��Ü3&‰Í“†YFkU꣥Í&8T!„¸÷º=;kåÙ¹YVƒ£�6À'LÕŠ H(ÎÂ@9@a¢'CL¢ˆZL+„ ñ]T”0µZôæÿÈæ 0™CD²”ã*¨X’’߬„òÝÙ݉,~Â'8¼RŒwŽâ™ëì3Æ@�¥.0×b¬%€ï’Ê×`/L­Æ ˜×¢À†|¬;P‘¹^¯‹ž»0žóé"­ž÷°årß§&Wa<'¿¶¶Ê=zLÊÊñQÁ‘ˆ7è�pð˜Ï,ø8<?„ üŽÝ'ç@$§(Hðhæ÷›"†JîH�˜¹bŠWXߨ W7%zK¥GÔêê~,Ë[ª¡ÒÙYzëVܵ..emZ·“¥dq|‰êNtowÆž‚&ÀÛ°gvÛ•Y¬ý?ÿ\Sùä_©ÊoyÙL@ESØAOèÿÑ„³“á_ÇRŒ\#¾Æ¨1Ægƒû¯ìà7¿Û@e•b$ø×BÈI÷Ï}¡£‡ì‚³‚ϲê{pHTÎùã'0gPÏ_}P‡‡à°ŸNÏèýà°/úï Ê6Úg\�D+é_ë·Œ“‰pÊž6,DÀy°èõ**dßã ^^_Õ š*ÈS…]þö·¿mÇÁ9± ONÀ“ÌäW:"ó4 §�ƆC ïÕ®CÁax±2&GæfçØ„çµYUne”PH“*7¼ú!Dµ+\»dGèìÓ­-o—l܃ßüw^@<ž ³ J^Q6M.9 ŒƒtžeÏQ-(Ñxãi2ß—S27}�G Í“b͞ǡo³xÀ¾k�@lùhJwÅ ‚þ�ú�ÂŒ]S!À@�´d»y„76"`Ÿ5Á`‘É'’R̸WÐ&ÖàȰ‘ÚíÉþÃm¦œpççÒˆJ ZéÝ×7³ ™NzZŸš¢ Øéà"°r% €•Uøú¯Åj¿êƉˆ1Y´D€Îá9NÎQ •,ø.O]'ðÕcow7õÜêöU”‚“&XÙ Nmˆ›Žæ‹´Dér×NÔŽÈø,ë¼ x @æë�Ê;r±@Gj¸Q�öþÔÊxã:nÙó± ÒÙ"1 — 4Hlo°å­�Ì/,Фfµ¬ ~"¾¸¸äû§ 3ðqÊF‚x"x àŸlÀv0‡#§Žœ­`¡ 8u0fqcŠ( :‘«¬È( ¨œÿç±*œky�ƒr©_"…, �<$RåÁ×”™i;Zl)"ưR ×½=¢óÿ ƒ£M“§éÃ("¨pÉI`T%€Ï°gʯÁZÑ�L ܧ ʹ™_Éÿäè6 ç "#“Ëóó3vt GªÊž¨,á Oƒ$©HØÜ\·"Ýãã£B׫6"`,.Ú±Ô‡~q~¬<‰ÃêÓ59&Ø«“˜à € áæŸbØîâæ¿¤ÉŠ@pþŸBž(Já\ÈšYˆÂqÖ¯1h«Õd““ Ø ºá*KÊ:y¸HųÉ; ŠÇ¿,R„i:ƒÅtµ:ÅP)é’“À¨I7þø ë&ô£á Ðy3öÁ,�ù¡a8=Øe+K«ð®¹¡Qçᘇ²ÄäÒç6� â9R\*ßr��G—Ù8’ù¹6 ¬Z™¦c°(2u=‹Yz PV¸Æ÷SÒÁw‘èdY$7â%y¼4 €z¨˜&3ÿ/çùŒ‡J=bW.C@Û §AŠ?i:�‘oóf¨‹¤Ã'û°a-ç3%-Ï’Tl�LßIU…í÷#ÈŸýÏïËV6.¸˜#øÃxè¸IqÉI`”$€Ï,>»&ï@« @ôÆéÈdÿ tˆíÑÑêÎÞ6Ì‹úÊ :Æ© :‘Ûª;/õn0*ËüŠ×#!k±(b n¬Ó[x ªmÉá1*öfçè�Tz.`�� j÷I�DŒÃòº�õ@IDAT½ï}•7·)0×#SÓÓ4 ÍÄñ¼léL“m¤••U«Ãñð77:TfÉLšrc„ÏŒϯ.Ì/€¹ª¶²úC&¯Ù:fÇð‡ 7(Þu¿Œ»©O@ ÒÏF¿ ï7R'@'Z€ÔªTò°¹|'s t ÄÞÙÙ©¶é@*P÷Uá<÷ºoÃáA:&8*Ww÷w<3à4ÌY[½ þ=oŸ1<ó�þT¼$êªA1“$bÕ• ŸP~I&QÖ21àÇ’Â?'€b)’¢KðÙ8ßQ[¼á1º¹Ù¶Çýˆ“;@,Ðaƒs+éaKœ߯ÞûÕup‘5e ãGMÿ¦4çÿ‹©Â“ nNl¤UO ™rÊîô¡ü’È*.Í‚æm€€:¡e(úȘœ4ެ­M*à¥ÓnŸÕ?˜ RÏBZþÛ¿ýÛµø‡ضÛ�‚œ`B¶�¢Ú­ÞË`eðNFb}´ˆ¯d ¤ëø¦pjÓE }~U±‘ô£$¨1™c«º\¥[#"”0•£OÊÃ>`oüè‡ 5ò¼455Åž}æYvçÎ]¶¶ºœ¦•Wÿ¢åcÎÃÃCö‡�þÀÞ}ïøZòA¶úJ”¨ü³+Êç¢ÏŠ„ŠIüz� <â% h‹yND!iCWÞúþ³ˆÃoTõ‰šS•¦© ~0Ö RÿE5y_?úø#öýçÿ<é.ìK`qq‘½ôòKìêÕkæÈL:?ŠÝú4/&T\*&tcƒþb<:j²•š�`Qd¯1µ…c­Z³ry:ï�£€SÈvpÉê9@É:Dšœ�@ÔôG“§�™£Mc׸ÁQÙÒ Ñæ$Ê:X™››Õb75SÉ,8JqÞu±Dܼu‹=¸t‰ýôg?aï¾ón&±§°0þÇzÓû +PÑB̼Ò@£Ñ$µn&-~1´ãc8 ùüáׯéé)O¡39š¤Ë·kç$P”¼ã+'p̦Ùô¾,êÐ�kW–—tšº6%�@£1Å^xñöôSO,)âCcün¨$‹‡q5R¬²‘&tsý’}’µ› `eÕ¾?'9h娵�ä„üj­Êææé} J8œ‰ €ŸàµK£'§½>ó)îõîQ `æ$<{Du@ÝN@…Ý_Ðü_×L2=µé󛃩ǀªÒ¦OÄ8£üÚÿøØgž~†ýà�°ð\-Jú˜DP‹*#âFŒD%!  ¥îñ4½\ZZô¾ú+!u•�J(�@™… -<#¾¿¨ì$ŽÚÓy Å4–$¡õÒýû÷Ù‹/¾ÈP Mñ17~­'sNÏá•L;ò:fl„äH²±ø~¡’�2a(À“­uóÒÂ’yÁi˜ûŠË¡6”€S °¨æ0öÁó„gjƒÔ€óÿºÙþâ F=0Gá›\ÛPxæÿÚÚº ÌÊl¨|^¾|™=øÖö替`¿üå[DÏS’`$«É ¥Ä5ýcêÏ4áÀxž¬ÔÛ—X$Ž4'O±¾¶¶ÊvÁï†JˆÌ¦‹5×v%påòöò+/ƒ2Óþ—äpn ¯t¸ GÜðJŽqI6&“0_,‚RMüa@•ž¢CªŒ *¼MÀQßIXߣå$eª°*Z¸4¢w#ÊÀ&›HZàWWÉñW(vt^DGˆD³ÐF_ I™¤è&nª¾‹Qd´V¯±W^}•݃/*o€5ÀÖÖVLØ cpFïF�óŒªYÆ"@€ªe¦áŒ3.æ*ª �©uœ†'�:|^YYaÛÛ;ÒV2xœÆ¥Ñ�Àâœó™]»~]LplbWVi8„W:¸ã¬Åïtà‰Ú #@rÀÑÙŠLD;¯¬Û±êØ�0K®€IÛYð:sò�;åè¤$‰ð•Gé;É|�û‰z¨šžA€æIר³‚w&3×#®ªTº²¼B~nK‰¯2>TêØ³ZPS³ººÊþâ/þ’}å+_eøÜð³pÓä$~ñ•Lüákj÷/…2IBxIrA#£8”c/@™^û?_»ÊÚÌØí9žS«ÂÀ$ÿü?áùÉSApªu!²?€s*”}æO>m´¬ÀÆÔ“ZDtFºo£Æ¼E1GÃH”š<³Ï?÷»yó&û1D øè£�Œ(¼�#ì_FrHE zfyqaž<î1)C˜“±¦À�Ûá|$“ð�š‡÷Ä¥rI•»wîÞa/¿ô2CëI½ÙN¯•' ªAÜ€[=g-~à´¡Àã«)ªÈ¨B  Nì·V›`³3VNÿêý_]�>ùCÐ/îwt$à£ÓWJ«š€è½>º‘G €è="„ß`÷/DsFY-lœÿGi Yg2eOYÉZœûHc‘œ9­Ãl£Æ!ý«#ú@ eš4·üÆ7þŒ½ÿþ{ì'?ù);µ`Ù!Or„Ïþe$GÌÔ<†3˼1#É~•h€ƒ4 <’¨Ü½“@©$077ÃZ'ræýÇ À)JÕ}ì2œóÎù/£õ_?é�ùéVéÙ.À úÕkåAL“ BÄ/3 �”_² Gl¤cpX¤#hMTâѹLùÄ'³³V,X­ÛC?N`ÚI´w €„nŒ’(à$|‰ˆn®|-6ŽâÑ!z-Gy\ƒÆ@È5³^Ë–£è3,ž5Õ¥c¤±K¤ ïÒ°L~„‘Dõû÷Ÿ†ØÊ×Ù�ÿÛ�ØÃ>H”ºÛaJ74øz!›¦Á;v¥ÐçJ–RWÏI€V}ãsË(Ë›FÐ¥rH`Ïù¿ô»qã¦5‚ø³�eº•ÞЪ×Ê£,M‚ˆ`q™ë›bš¥èÈvÝŽnAŠ¢î  �@+µ ['»6R¥ã9üÿlÀv0íJÀ)ìÊ× t~r—°Ðdð¤VœqéodCŠÊzf±eÁ£2šâ™NýD5È‹gLc,}b,R0�Æ�Úúÿåþ�ìá‡Ùûñ�˜ï‰>]Ï娕€ªe ûç’“ÀE”§è™»ÓÊW˜µšrG.¢‡Ås|Î< GÏžîy†–K£�ôæüìVzÓ¶^+O¶ÙdÄľº¶ÂÐA°ŠÒ9€ssŽ‹L�6|@³ðma¢ìÀz•:BPë"ùÀàv áÖ´W¹Gñé¬^ÍŒðLb-iÿ¯Á`�u- ]„d4‘5¹ÌhÊÍBïå¸8(>I̘ÒDò'nc,|,„f4¾ _e.ýÕ%öóŸÿŒ½ûî;@\J  ÇòŒnᢨ֜Z®p\ÁØKýïȼ/ÇΠ°g7L÷ŸzнøâÙôtà$Y¦×rH&‘ƒ�´82m+ÀM·Òc;»ú°Z[]c�?V )¿êñÑq¡kØN§mÅ).†üž�g˜z<�Wä^¾T]�2J  ;—2Ê¥Ü4Uz$&a@šÿS¤Nµ—åL6Žx!£ó\ö\UN�p©Š2Ä­$Q †–Hã(–ÆTƒýñŸü {êé§Ùø; Ž,Áô…­âY^(è‡NTªaEëC Tq “xþ}çµ]†—†'�õõu8çÿ*˜ƒ'íÁ#“�.9ÑÙK˜as ŒFMl+AI· ØÞÜÜ$W�€" BþÒ\›QåE˜�#WUÎÀ¬k kî #Á!Ö�‰ êit œqQ 9¾€zèÒ�PFUå RYxþ?Æmì&UÝË&¯ìÒH®tÅH›R]JCŠ^± ²°\¾t‰ýõ_ý5{ë­·àÎÚJ‘â*q% zô¢—]r¸¨èvÄãZ ôLŽJ€"“´\„ßÙÙ/¬ß½{÷ÉØå÷tÖ쥀vМ�Ai\†¶mDÀw ×±EõèZÀ_Ÿ#^(À-Cá§›ßùÚ×¾Vÿþ÷¿ßN¹œ2KÀ)ÊÜ;´½öÚk«õɪɡs*š¶EM×§½óÿ³dœ)‚$«nÃcÓ«*N‚ÉËgL0ëÆ+fÊAÐ:¬/U)¬^¾+ Ad]«Õ½…ÜíÛ·Ùoü€mmÑÏ|h/dnRš®L%�·Ûl bh»ä$p%p~~.Ív"8€´¸´*âzè98ãÿüóôçüõf°l6²§s ˆlèÙ”pr @p §²m(ðŒ<*³ÑácQ #P+ ðù¶ `¬en�¬~[”¼^= 8€žÜ k/184ÿr†!£i Ï·Ì!¸ €¥�â[6œf…T£×LèakÁ¬Vâ’&h¶‘ªV/ËÕ Xi|ó›ÉÞþõÛìÿýçVžƒ²ðZ-øjÒ•0gŽÒwzvÆæàk›KNM8ƒŸÁœ$;¤¢ƒÍ5OÙ|Ñ$5~QIüÒK¯°¹9;¡Ò(¹�˜Î¥Ðe?{Ð=ÙÐ¥ *I€°¡@ôÇÍ#k^óöD¿¨ ^G㢹¹9Zí²J¥‹Çœ@[‚Å4t €bä®�µWíÝ!¢YJ@dà…0ÑæÎ^CæÿH-n.Ë‘ÌžŠ°µ`Ö +qY´ÛHU «S\áä÷ܳÏ1tø£ý�}ôÑ(À:} A¨2Å~=i�©åb¡I‘�ŵ±™ôÏ@#Ä£'àà2 ,(6,iÑêwÑ(bŸ“n¥‡ €ÿ›_Õ”QNPÆ^ÐD0D4 €Áxf+_áÙ% 8ʈs´N`ƒb!yN-À-¤YïÅ[s&îx¥LV9-Óu¥+úMѬïßø3öÞûﱟýô'ÞÙÚ4P—£*�ãæ14Që 4µÄ…Öââ¼*:WßI`d%Ð…÷äðÉ _þ}izï×Ȳ\:Âñ8Å^x‘= Îbqc¤”Të‹€��2AQz\iðß>†……EïhL“8Ràcå²@V`Ë?ÎüܼçLÔ³Ô%£–1pOJç0ƒ�.J,§˧t¥½ »Sá�”ÒôNFŽLL4X� ¶-’E=°H3”SÑ–ÀêÊ , |ä±n‰Ýä7¶ÅúSh¼¥@˜ñŠ™’Ìjý„vº~í:ûÙÏÂÞïýÌv.S^G:)KÎyž€Çå©™)�òÚ¸r'�Q–À“Ãc†þ/TõG÷8ÕÅsÕÏ>û,ûüç¿�²‚ÔáSgÌ‹á ±L�› ¬HÀh >Ô¿ÉÑQÁ‘ÚpÀBªAè]ôðÄS@Ò!èõªN@'ΡAr €¡‰š‘o ±ëÉAW�†œnäÔ–+îBø’²&Ñjbf&<3ë•Ø�H*é%D,'v#‚3ÎeÒÂÌBØ:.Ì™™iö§_ûìÞ�{p,àG¬èð?™Ä�H¦Îd ­v¶w!ÔÖ«ƒ—b—œÆY­“–¿øŽE¹,ŸŸwÀgÀ(ÊœÓÌ\aq*\¿q�}åÕ¯°¹y:‹£pná •ÌV|ÒP/°"aÂ4Ž�} R‡ª_ûOŒ üP+˜ ý\ºœRKÀ)JÝ=Äu+w)l“&" ?€ùTVæ€èE™:­®QœÿOË=–»q�^BÄrb7"8ã\–-Ì7o±o]¾Â~ñ‹_°·ßþoS:ÎR æ­Ž?Ñ  nÂóŽ;Û;àOc…M€§b—œÆQÇ­&Ûß;Ð_Ž�[lrÉ)TŸ�ÕÕ5öåW¿Ì6/)œóWEbX?{fRj<ÍSm8‡h‘° ê„N£ÏÎÎÙädqï[|„à{Ê„‘‘æÀÀBºþàÁƒé×_�~¡m�XÒ—€[m�Гðõ¯½1·0sÅ”ädÀ©iðÄM0uzôžKMy ÚÛ8°ºL¡(4ýMw`,'v#•½„ˆåÆnD°F§ 'ú¯~õ«ìî�;ì�þ€íïï�ñSÚ„ó’ÝžYdôˆþøÓO!ôÒ<›Ÿ�£þðQ°„ú‹,�6øÅÙ?8`¦VhMˆ°¼´x‘E©Ä{,ô^øÂ ì™Ï}.~Îßxþ2 Ä‡Jeéi^¨—TФ3G™ äÀQ‚ï  Z®4V‚Û¡ÿ¶;mV«ÓZÅUáÌ,̯øYnC„2ö¯`;�–$à–kìÂÂÔXf«1ht|žjаṔJŽ-b'1H×êZq“•\Òp²—±ÜØMB˜“^BÄrb7a«"¯.]¾Ìþú¯°7ÿéMö«_¾UJŸègデ¿‡PEèx¯øtz*Ï\D-888dèO�g4@)ƒ¾IpÑâ’“À¨HŸc´ŠÁ¯ˆ§§§àhô”dùýG±Ýý½BÄP�/hj½¾^&¥w¶(ªðÕ#¾|6ÿÉhG^ éù+þàk‰ñüe €G I¾±˜úTs]¬fp¶i+ø0óœA°dV�'Œ†„Ž�m¤«a$§°!\K0�À’`m€íV*w´…/ÓeJ8‰ßºy�ýþ±û·‡$›‹2ð‡ç½ ?øèÀ™çæ¹³&,C¿8Ì$05 v£P)Ðé¶Y[ÓÁúÙð|m˜‘£Üz¬ržº—--ÚÙ8($hpýÆ 8çÿe6o°É‘žN¤ç/Á}Òyp0Ÿˆ�v™±˜ú˜ynn^ò,)„b½lc"ੲ 8žBí·çIÁþ�¤ScòªÁš"û9‚ðäN“tùoœ ü}¥�l'/æv|žšš†¯ÿÆnúfÈôƒ•1³ÀF¤k¾»t1$0?¿À¾ñgÿ;ûÝï~ë… <�p^eI¸`¿zå2[[]aï¼û[¶µµ]Ò„tàBd 6ÿÞP4Üõ¢�.Wè$0 Š÷sâ/q&´¯Áÿ§ÁÜ?8hËf[üpñÔSO³/}é%†áxåÕúdøƒÕÐ)7FØ`,*9èà½÷Þ“{ $kƒ@ó»|iƒýú7ïZyt{M*'ˆãëÒâÚ9 -< S«UÀ)àð¬Ð„þÚÕ+ìî�[žC°¢e Â�–MÏ?ÿyöÜsÏ�œj¢ªÂ2ÁžHØ.YÛ°RE$1ÀI¬y÷T„‘Gdf9å—6é-�Ø£ã'lþ•lXÖâü»¸`ÇzµêGp €¢E¼N (°¢ªW*ˆ`>T&ãâRè‚©’9uv¤Ûj5ɯ.»óÿäBA€¨P{å•WÙÍ›·ÀIà÷Ùî^1qºy¢[^Zb¯¾ü%ö;øðÃß—BI�‹ø¸ vÉIÀIÀ—@¾ÊµÛgCZ}öé{lΗ9áFåî½{ì˯|™y–ŠÆÄÒln©F®5Ö«®D¨ìÕEL6<Ûº®ÅHT=ê’¥ã«Vu~¢‚.ùµS”¼ƒòº•ê]ˆ³ÜjÿFÍÿ:¤H64•talXGVÀášKN�.]ºÄþú[ÿ‰ýò—o±7ßüE©B|á†û©ûwØåÍuö6X>)öÈB-Û(£ûu¸� pÄ›'¸YˆòôS÷ØÚZù•×W®\e_†°~´¡v©¶Ræë0ì+kÔXœ÷„EˇKÎq¨xüøq”ãë¢�÷ÙŠêÓÇ™“>ñìŒVéëm Àø©§ž¬�0ÁšùŽÈ±‡,p/@¤2š×™OE'EªA?B áåÙù™•͘ hØ1cØÍj1$Z¼ñƒïƒ3>Úň©ÈææÙ+/ѳøíï>(Ìñ~ÙsÉIÀI ”@ôX^˜Ks…Ç�nß¾ÁnÞ¸^úsþ ‹ 쥗^fwîüÓì¿ „K5~Ñ0BE $FàPU±Ë�~èÇ@°< ªÜåÕï sP”1 µj�ÍC(À�ÝÝ<TË�@UbÖw €…¯‚ºËz``ž& þp�0†nÓT´cý­czóÄçBФ~±ËP9ôùûÍo~Í~úÓŸ@¼oø]’„›ï[7o° pøö¯ßa»»ûP†¶LF«Âhv(�ìIçv´V£Lø®cxÐû`ý“TüSâ¡€…æÛŸÿüؾðœóÏ {YKÌ€ ÷FLÀŽ¥V(¢Š2ô×&Dø#9¦7›ÇÞ$´0("á˜Ð…?j!ò3cGàŽñ hât MÁ »,™A%n>’bØ¡ M£@s�þ—D8]”¿Mãb#Ñš&Ú ÐÁ,R¸ø~æ™Ï±ë×o°þð öûßX$9)Ü3ýãK/~�}òè±2ðü¼�ªc+ƒz£c‹N×I`X ÞûC8¿eöôÓ÷á+_ùÏùß»ü”|…MÃ…tìø ²ñ²£@Œ%MU*Ç@ ¢B†±"¸h¡¢(E�ðæ¦g}ÇÇÇ -SŠJ�n>Ô…ëv :jà„wÒâkßymã¿üýùÔl’XN@,Pkàzììè—ªíÎð6ª2hZpˆ4¸#ª=q1ëÏÏϳo|ãÏØï~÷[öãÿÈŠ? ]É¢’âÊåK^ìïß¼û˜Oné‚Rj‡Ç… ú ¢D§«ì$0, ôz4ðœÿý{wÙ&øû({ÚØØ`_ùÊ�5’hã&Øñ)3˜ÚN c‰àc ã_5惕/‰0"ß(£& #*Ôln^Ê Ã< ýªÛ‘wæ1/„µ%«ŸÔÑ À)(:Ê2 §°,` ðßþö·AýXY3……f?xN9HÓàPe€ Ú%{° §€“„KqbÁNßw…ÆE�ÀÝ»÷صk×ÙÏþ3öë_¿]*–�Iö…çŸeŸnoCÈÀ÷Øé‰Ý˜ä]ø¢Á˜›zJõ8b •@Û0 Îíx´çœõ�Îñ…2ÅAŽÑ^zùpL:lkaªUŠxsk„%ÒXŒ…#Ü Û¨qÄä7ˆ ‘Íî&„¼µ‘ŽŽžº¾µa #ÙH•J_ìÛ€í`ÒJÀ­ÂhåiÚIçän¸m×G�^?£‰*@·Û‰‚-ÕuÓ‚gþ_ª.bPiôÇü'ìÖíÛ2ð † ‹2¥�µ5¶üê{÷ýß±�>ú„üLrÀëy» 4¸+|‘Pä~�Š•Ày» ï› W®\bŸyê®çÕ[ÂpZ ÎùÎùƒ òè¦áln�°Dk>Va÷A©_EÉi¼¸°Q­¦ØÉÉINMµâ#Xd²aka~Ñs J}$"ü:G€E>0 ¸�@AXEU­�ÿŸ˜ˆŸ#š†sÀ¦ ¬ˆÁI Îòµ)N•öÍz'€. J¸ºI Ü¿ó7ÿûÇ|“½õÖ?[Ûh'ñÊÜOLÔÙ³Ï<Í®^‡Jà$І é8…ðC�‰¸BR†>WÇI`Ü$€Gèt”è++K쳟Ásþó¥ÉÝ;wÙ«ÖOÖìXjÏ)U©Ì¢¡Z3‰aŒ¥@ŒEBÎÆòql€€ÒúÛ9:FE½±ó‰çÔèxÖAføq�žL“ …ŸGà­r?YN�vIï� ¤% ŽÞÉz�£ud®Ñ`:݉ÞËÀHÖÁ¯:‹—$[÷660«+å�£lKž.�ðkØ+¯¼ÊnÞºÍÞxãûl�>�¡ËËK쫯¾Ä0\à° ¢þJ€‘0Ï^xñ‹,yäP¦½½:‘]£’xëä¶³0^) §eº®tÅtÓräHCŠP± ’X.mÐ+�LŒ°Ò(îÃ:dÄ €j­Êf턜~ûí·¯�ØhÏbH=/®’Šœ@EZÅÕ½M�'ä`8�F@pcÜ–‡R’M[" pçp bwA(�ÅÅ%öçþM/JÀOú†fòeI¸ ¹uóÛX_eÿúö;l‡èÈBˆƒ¢•°y(o<‘²ô„£cÔ%€›~ô�!£<_‚øãŸýÌSlii±ôl߸y“ýÑWÿ† ÃñNÉm§<·ñ–‚X¼b&Aë°¾T¥°zù®$!zÃZ(À'¬HËÏ(ËÕ$ ç²^­±…0f!õ#8€ÙR‚t JiZ€…¦4ö†)hüÊX«…Ý=Eàiò4“¦ÄYjß²I-r"°$*¶Dxæ™ÏÁf"üø öÁ¿ÿ{‰(clü†¼ô¥/°�?yÄ~óÎ{p~Cú™§Öé)lŠ*ð‘Ž&�©3ŽW¦þIÌ©rœ($'xÁY†úCoÿ2_ýp4æ3÷ñøÍ%Cz úÅ0ÖÖÖaãÿGìRÌ'Azçˉ݈á�o©Ù–.l-fX‰+FAë°�T¥°zQW›à•ÕÔ>kРÈ$3fèзl¤n¥÷Àý¯6`;˜tw„t0$B ¼óÎ?_Û še”’æxž�N±ëÅ.’˜eŒ¨×kÜ´`€rœŸ·£5ÕãÒµG ÌÌΰÿõ?þoìw¿û-ûñ�g…É=õj‹ XW¯\fkà ã׿~�}òøSmXц8–xç-£™îÚI`Ä%€³£ìþ sݼy�Ý¿wö啇a„¿øÅ/²gŸ}.CI‘^Ärb7¢NK.–»Áç2iaf !l-fX)f Z‡m¤*…ÕU¯ðh+~Õ>88Pm*¬ÿÄSHAE¿•‡q�4‚DYÙP˜TYÕEÐït €¡‰ZQ·[»M°OOÅnÀäM‘li&)hk5éã·®,¯d,v(¨u0œÒ¸{÷»ví:ûùÏæ H×(.§fû/¼ð»^„Þ~û]òøËÅqæ0; ÐI@f©Ž‹ðÍÍuöÙ§ï1Šð¼tÔ§!¡uÎçžy–½ôÊËlÒz(Ïô¦+–»IÓæd÷B,7v¶¼8WÒÂÌI¼5G˜ñJ™p8-Óu9ѱ.µàèˆÖnšqNB…b$ê„JÆYøØptD»V]¾SPw–xN`A¨” {Uv‡â½ŸÓÚhšFïÛ©Ì <[L�VÖ\@j™:xb àFû�ÿøOØ­ÛwØ�~øöäI±‹‘$µ›ëklõ�–Ø»ïÿ{ˆ!“ܽ“€“W‹pÎÿ™Ï>ÅV–ì˜ãrkÜ„sþ_sÿ……òû$ˆ³—½óŒåÆnâ­ãwé.–»‰·¼8wÒÂL‰$ÞR ÌxÅœMððî{ï î).މ7Ȫ4ᑆnBBH0ʄǂg! µ€ùG(Iu°,HÀ),•d�>~mhüÿì½é“Gv'ø"ïºï…ªpß ÄÕ$Än²ÕÙMvóèÖ˜Ìú“ô_ìÊlGvv>�Öl¥ÙOÉfÕ³ÇØîš�V’©�l‚H€@6ðÀÁ $ îÊ#öý<2ª"³²ª2ÓÝ3#³ž“‰8ý¹û/<³â=ï÷@¨ù[be•Ô”c¯€°™i €Þú1Á†&»k��6ÐK/ýŠ.\x›.^|×xœ£¤1f(ÞÃJ̺áAzïòTï&�±H]A  ÎûÖÍ´aÃzÝ?ÅÖ»ÛÓÓCÇOœ"üIYúÂSp¦à`%´–*·g V’ÓÌ×Ês„Aö0]°Ð5Í J­­­¦E—-/çf)BfU¶(gèàL_•Ý‹²oÜôgögñ¿þë¿“qÙ]_;7š�Mk·ZŽt“‰ÆâW=¤2‘v«c}¯VÆÌÌlµU—­‡)‚@½€¢ýØcÇ¡gÎü޾ùæn½ºR²ÝÞžnúÞ‰£tããϘ¿àò8BJÞ*'�5‰@„ ð9ΓüÅ8óE˜K2™¢G}t™8ÿ0÷¼ú¶T¹-8Sp°Òx–Z ά$§9®!ÀF�ç]= 0BÄDÞ&ƈ€öŽv¢ŠeÄ<¸ ÝŬ+Fq+r¬…€´à«AeÇÝDnÙ –íP‚Éëüò}‰œ€Sx™�ã÷Ëä¤i6 }}`ò9‰¬êèïï§çŸÿ]ºt‘Þ~û­²˜Å«k©òZx©Ø¾m3 Ãàý«tÿÁÃÊ…H A  ìçøùÔj( �-ˆâü�aþ 3á‚¶ú*r—¾…œ)8X ­Ò–‚‚³+ɪß5[©§˜бd\(-)·á ÜÝe'œ'KQd@9·N÷ˆ NÀ—ݬ�|ɱrèÿv{ÊîÁ²7f‹˜I—½±¦§í°¦K À:|`T®#©�âiCX¸ÿ2Ùq#ÉŒe6QN[ÿ§  †o*Ïp†s“†•`zÚl^SàÖÕÙÉX.r)àœA L´1KñŸþcNÇ÷)�;wFÄ0u�Ö­¢Þ¾ºúÁ5º}ç‹PõM:#˜@ ¿¿—öíÞIíím&ÄY“#ÅÎ�»èÈ‘£ÆÞ ¬uV  àñ¼WA�ÕoÅßR,zá{S�‚Œ[àÝ2Ý~”‰H;8\Ï´€1’€zL” Ú@`ÕúV7çl^ÆÀYQWŠW –ó€q±ÜÂ)IC•†¬¸ß68Äý¿e9+ccãœ2p�?^…„©ŸIöH:øÈ^acÀ{—?¤Y Ù:Â4^éËÚ@ ½­Uü 2Ñ_ØËúõ£*οWHmÃþ¨¤U 0d‰¬o’3tZ"Í+g˜@ê¾¥¥‚—÷¢ÊÊ€SZ(£ú§Úö·û·áZ…°0ÐF)€0?9Vÿ½^Y"çøCï)ˆT@uX½p—Wÿ«¯½Ð5k;68zû%þßÚÁÆ{÷ã�?Á1ÈÛèì™3ÌÆßx:‡èôã=ô‡k7è“Oo†Ú ¨3N©ÛÜÀ+ )ýÀu�0±0ð…=ú˜J#æ~Jßl¥D¾Cõ*^æ­Â�^ݾÀ Å™ÎLoa¹f]1,tt­Š@ˆŸ<¿Kl2ѽ¸âð^LR-m¬¸ë¿¤dsá�ÿfV =½&‡ÈjŠÀºu#ô /ÑÛÞ¢‹ß •¢ 綠œ Þ—Þ»B“²XPÓÉ!�U�”ý�ÖÓ®[9UžÙ—òª;µLE)yä€Ê‚~)‚@3#0<1Øx^"Ó>ˆó{ì±c´uËV:sæwt÷›»ö­ …Þžnzâ{Çé£ëŸÐG7>áXÇp+šÜÚ„ ðß‚½{vÚZ93†þþÃèØ±ÔÊü R�µ€@Ww7!ÔuvvÖèp''&Y^ýÞ{3™¬•ö»»l¥Œ€ÑhVØ¢fhV®H3ƒ€¶ Æ+lN€´d¹øÿ•º ò‘âb+%Iq;ÕÛ">ë“€Õ<©"ú8=ÒsÏÿ‚®\¹Lo¼qž`, K¹XÓAxéÒúî¾YVâ°ŒSúѸ ÎçÎmÊc%ì£X72B§Nœ"|ç¥k �ÁÁ!ºyó3£Ãžà€z[†ñTK«"¸6½p溮¤¬ç„Y¥m1¬P½.¿øâ‹]œPÛ/ÇsM\Tàa(¥ÐW:Î0l¸ÿ1T:Käþ0"E{ß¾ý466FgÏž¥;wn‡ª›��xêäQºyë½åC óoM¨€“ÎXC �Ñö­›ió&ÎÌ0¨[kPCp;z!ÎûöR¤ª ÐØ š7<|XWPð·�•jã\# ÂïÆwß}gz|b0�¨Ayb0¦IQ¹hnsdQo¯Zt¾q#Tokm¡�¦Ï?ÿŠÞ{ÿ ÍͧCÕ?éLó"ÐÏqþûöî ÎÎÎÐr s{?~B­â…¾³ÒAA  p€�2Å©“uLŸ™å°½XDÏÀWüî€N6X(ò'Òó÷ÿ÷Æ] ,ôu͉@HyÄ¡ÍEFºªz�®Z$Ù [�Š$ÌezfÆx÷ú„Ð8¦"0<´²¢ýôS?¤Ï¶Jç^9KS!c㢾¾ºüÁèÖ­ÏÜô¤éQî.Žóß8:ú±  Ò‰“§Èëyè� Ë 0•˜ÄÿÛ{^"9<Œ��ÓKëÖÑùóçéêÕ+áé÷$™LÐáûhtd˜.¾w•f,úB5`éLMˆñ*ØÖ-›hû¶Í¡�ó‡7²zHœM§ˆ4Ö@ ÙIXïLÙ,2ä˜ÿîµ” Ðqr@¿;báCA—˜æƒ ú%H©€ú2Cï0mÞ ž_ý'&�òH$’ôøãO°"´�Μ=C÷ï‡Ë{ohp€¾ÿG§è×nÐuNXÏXþHåNA€_¥çÏ«ý{vm§‡z…¹ �çþ}�СC‡�óæÛv±»?M°�*$ô2dŸé™i++‚â`b戌FC+Ž�>z„~ñü „xã°•Þžnzò‰´w÷öлm‡ »µÜxÃ>´�žøÞ±Ð+ÿýýôóŸ=G?úÑODù_Ë“VÆ^16¸1&˜ žï½ÈÀ¥Ry~÷ÆßúöÎöŠ1^­wS2¬R�®‹@�€_©Ù—_~v‡Ù·¬tO9׊ [Zõ3 Ý0g˜™š.šŠïéëí­¸ŽTš�>NÇ÷óçž§+W.Óoœ§ 3‡¥ /û6ÎюЀw/]¦{ßÝKפ!Cl×›7�Ñ®[ ûa.m­­t˜�o»ví6ž÷;Ìã–¾ ¦4o´Æ üÌô4µò÷³^%Ç}ˆrÚO“%�QG{;}c:å®KÛ±¨ÉÞ¹æÙ M°e™�Ak@CÎåb›”…QSx"@Qˆ‰2QÂä \<žéó€h£W²C-Çk (Úûöí'ž;w–nß¾*:;;èñSÇè³›·é½Ë†ÊH* Ö`gàT7²nˆöíÙÉ/îÚ¶u«Â0±{×:òØQJeñ±Ú°š €aõ4€ JfÕ7ü}oo·’  ã…^@J†/›lz5üpÌΠ†‡#È9‘q‡�|tK"Q˜°EqÀo¨ú’ͱû‘êšžœê{°rÍ) øaìî ŒèÊÈÈUAÀ È‹þÓŸ>C×®ý�~ÿû×hvvÖŒ`R è��màp…~å ðÕ×ß�*"�nŽódß.êëí ý0çòä÷ß1)‚€  ‡8lpÎ Y’]NKeÀß>�‚êÝÖ2d �d¡®,€ª+’•ÿq]¨ç þo‚Ã.*`Å߈/ª.…yõ€éÒÝÝzwQÓcy‚Àj ÙèèFNø®­v{M¯c•÷äñ#tçó/éâ¥+47?_Óö¥±ú#�·Ý;·ÒØÆÑлÐ÷ôôЉ'iÆ�õNz 4 ƒêÝÍ4q^½‰AÄ­«ð—zÄ]�v2ð¢& çJµ)çꇀê‡ý²-3k昣ïÀi‚àà)õHíqôcCŸЂ€¸ÿ/;UåÂGŠö“Oþ€¶lÙFç^9CS“vBpª…yýÈ0{ôÑûW>¤O> WÈBµc’z+#�­ÍãU–d sI2ëö£�>J{÷î³òBæ±KßÛ œ¦¯¯�¾þúk£MÕÛ`+`—¥T€¼¨ €”�!!«VÝqrÎ8iZðÃ�.&yH!îqõþU‡¨~x ÈYµ¡*o˜¶`è—€U> ©¶V€ëòKë~Eo½ù&]¾ò¾•LÕb‰l(‡ìãðaö¸LSÓæÓ„VÛ7©g¤…|dÿj yœ?Œ{vï¥#G�ŒóRAÀÃìªoÚ0‰T€u|Æ{82˜öˆ'ãŠÛ`šI�×ÙjTž3‚€ŒÀhXˆ£°$þŸWL”°‡LY ”€&fŽÈhvð›sòÔ)öØBgÏþ޾».6þá¡zêû�ÓÕ?¢�®*#E³Ï ÛãSqþ{wQø³µŒŽn “ìîß#™elO ‘/ÐàóϽoˆÉ©I»0 yõ( ÔWÙ¸x¡ÏdÁÂaGG78â`ò9™’UŸÙkª÷M(‡-z°+nÐZ</‘jiQK×ù¸ˆ=Ò”a«~znž²™lÁ¸Môöô™#2�5�Àðºuô‹_¾Ä®ÍGBÇ��0ÁŸæüïíŠjÑVòiL âltÚ¿o7=ùĉÐ+ÿ ØúÑ�¢4Eù_?…2È eЉm½Ë–#©Ë¹¯’{¢Ï`¯­/¾ø¢Yk……N®5‘â²'þòËϮ˺¤½\À`iQø)XZ*Iω,uõ}ZÚý…3SÓvâ�…`bÙÊB ‹*ÀæM›éÌÙ3ì‚ùUYõjuS/3Âÿà�NÑG7>¥«W¯‘÷»V«Ö¥]Tœÿ&Äùoc®›Â¿uº²M×O&“tàÀAÚ¿ÿ‘ÐÄL�Uä aC`ˆÃ‚l”‡“Ôi)f¾œþ"€é_>‹€d4šÆÂæ§åŒMî© b¨ Îe·’Í&Ç(%[¯(À‘`²%¥íò —§JŒz#¨¼ö”鸥|@"#E*Gá3?ÿùståÊezóÍ7(�NW.ÄR (‘;¶m¦õœþÂÅ÷èîÝ{–Z±&짃ûöPGg»I±Æea~íܹ‹Ž9J-ì�'Ej�€­t}Š öÃYhÑ q¨›3’Ø(7"ÀOmÈ™Õ! €êp³VËqrã½½êvŠWER8r®¾a¢ê•QÑ ší•XÍ2З[�Ò@Ú·o?���37ÀYº};\lüíímôøÉc*KÀ¥÷? L&Sz r¶® dãÀ¾]4î¨È½&’Á¦€n�ËQ˜Ë´…€ÖÖVB>i)‚€  ‡@GG§Š¾qã:½òÊ+4;6~P¯ uXäß½t…î|þ¥Þ`¥¶1ü·l÷Îí´uó˜qÖkc�Ì êêꦣG�2æVÓ¢Ež T�~Û‡‡èæ­›UÔ^¾ÊÄÄÃå/ÖàŠ t»½­]ýΚööeÞÂm5€Eš¨1TV-neœ1]¬¸E£‹�6O0[iéøÿJÆ”ãÔ#l•4YѽƙK¹õ¾~qÿ¯è!ÈÍ‚À*@9Z·n„Οÿ=]»vm•»k{¹…�}';L·ï|Aï^¼L³L,*¥>àÅ}ÓøÚ»{'¯à™Žv5;&xÜ=òÈ:xð�Äù›…V¤ Ú 7L>œ¬ëû°-øÃÚÛÛi°‡ÿœ‹@{&›°¨%š•+ÒªDÀu#¼Ì¡gXâþÏñÿúê?³Ds€ 9UB³jµ©)ùÖãßO@IDATK¹Eɰ*ìrƒ P1ð¬yòÉЖ­Ûè•sgir’_¦BTFׯ#Äš¿ùúäÓ[¡æ> lƺ¢âü÷ï¡ÎÎc2m‚‘bÛ¶mtìØ •?ÛF"Sô²64;7Kéùy*N¹­×ÓÊjg9\-3«ÆÅ8@'§4mà‘�@Jˆ0;sB4°†íŠãŽëö½Ø�Tîÿúª» /Gw|ËÕ·â ñÿËÁ-çmÆ6ŽÑº—^¦·Þx“._y?TŠv‚WuÜO6¬§ ï¼G“v²ŒhƒØDç¿o÷‚&ìedd=�´"\„VŒ€*†Ì^…L&9F}¦}�xK‹b�Â\¬¥ä<æRA 6€ÀDj 8{ö }õõWµi¸ÌVº»»èOž¢?|tƒÞ¿ò‚ç‘”êè§�ì¦î®®êÔ°ˆ+�?¡È±jج4%há0ØÎÎ.zøÐ¬áöa�3ØJØÕeÅ€ýç"Â``>›!SH�ã8¹qkìK<Rœ°(LH¬Øç 9&,–±bõ_œš±Ÿ+yœkü ¥9A€À÷îçÏ=O|p•^ý÷”N§Cƒ V­wnߪâÔßfo€¯î~š¾5BGZ[[hßž4¾qCè»;80H'N}�†-¹‡é  Ð$€д`€ÂW嚢•uídæjã0'x3m>§8~»ìÄB­:¹AXëà7k÷î=L·‘Îq¦€[†ó8ëâÛÞÖFO|ïg ¸Iï¾w•2ép{IéŽW·>~OwlßB»wnå´´á¦jãgûØcÇhûöºÃ–ú‚€ ðÑGí‰bÊçÐXÝ÷ëj;岼Т†OãœF\'÷ï߯¶kËÕÀrÈÔá¼êúrMF8R× @ùç†@°å/³n±Á6ªÛ§`ý) ݈ƒª×{pl²/¬e:˜‘ø'?ù)ݸq�^yåš�5oì«_ü>lÞ4F#ÃCtáâeºuç‹jE5m=`„8ÿÌ¡�JéóÑØ*ʱýû÷=B‡V†t›m‰lA@¨ƒƒƒÆËd²433Ã)@ÛŒË.W g0ËI�w-$ Ü[ƒûô5Ãtr­4ẑ1rôLÅ9IMÿ°ÇºNMOŸ&’À8¤"P¨Äa�ŽŽÒo¼AW¯^©ZŽ�Šø�=yüQºóÅ—ô6§ œ™1Ÿ‘ÄF¿mËìëí¦CöQ_o�í¦´å���Ñ©S�«_ma"@B…Àð°�Ô¢™°ž¤çæ$*F <´,eáT€íœ °¾ì‰FÑj\a†§M㊞;î¸n?–Æÿ›Yq #w)Œ¦¦ÌsH€RHË9A ~$“)züñ'hl|½rî MN†ë=býºaxª�.½•>þ䦕Ì$õC¿ü–[9ìlÿ¾�¼ò?z/ª�þ:yò ¯³£ ”�šÜ)¶°™ °ž!9 ©aèêê¶ñ(I¼•?m™•! €Êð²}÷FÝâìò,­ Ô,~œ‘¦kÕ™X„WÜÌ{ôõÙI…b ,¬Æ6n¤õ/ÿš.\x›.^|7TŠv‚³°9üˆR~ß¼p‰ê�*ª–Sé·oÝD{wï}ª¼¶ÖV:üèÚµkwè�µ|†Ò– ÐŒ p8lÔxzÙ‰‡u…ËZ*Àn+þ­Í! @ u�5^ãbÁC@~ýë_÷ó¦C·;Å!I¤ÔdïcšmºãZ©þìܬñu´×Û+€•p—k‚@=©ˆÚÆÆÆéÜÙßѽᆱgw–´=8ÐG?zê •.ðÃk7Be¤XÒY͈ó]¿ŽîßCmmúFgÍî¬XJÀî]{èÈcGGΊ7ËEA@h ð÷¢··�îÞýÚèxT*@Íwl�e‘ŠÖBû–<ÈuHˆu¸Áºb0¦Ž¨yw~Ü/r‚3K ¼B®S�kÔÂï‹N— êNO™_ýGÂP³¡Dìο|á%ºté"½ýö[VŒ�ÕÊæ�ý» Þ›o_b#ÅýjE…¶^oO7>°—úûÃo0Eœÿɓ߳ßÚg$¢a&k5m€@=ß�]ÎÐ/XÓ„ÕH׊Åùùy£SG FáÔ&-øÌUŽæ8€æ¯~bÌT÷“BlÄ™CŽhÚBôO8L>%‘%ØC1‹¢qö8ÃÞ_}õ•½Æª�ÜÓÝEOÿ{tí£OèÒåŒçW®¢KÚU`\Þ·g'mÙ´ÑøË§vçŠôôôЉ'UJÉ¢Kr(k�¡¡azÿý÷�ŽvjjR�aì­G�ò¯2n?Âò� àÛo¿5:,‡\É`Ñê…‰ zìŒÖÌ‘;îhÚáâä8…~-Lš¥ë”SšÖ £h ³‘Ɣޞ¾Â†äHB�@‡í<÷Ü/T–€×_ÿ=¥ÓéÐô¿);¶o¦‘‘!z‹½¾üúnhúVIG`lÙ¶eœIþv1ût¸_!�vððáGiïÞ}¡7RTò ä^A@¨D€PÀABÝÙÙUy‡ Õð f‹ýT€¦ °ÃаEŒ&fgŒfgÖrõˆs Öî+G£8þáQ/h™L¦òÎÔ°Æô´ù íg2/)‚€ ÐxìÞ½‡½6ѹWÏÒ§Ÿ|ªt´·Ñ=qœ>½y›Þy÷}š›�‘b5 Ö� Óáƒû¨½­uµ[ëzFŠ=»÷Ò‘£çÏn¬RA@°aª9  ždé*ŠþÕ~Ø‘ˆc+%j/§ìåT€÷´;)´€|æ*³ò?®+-Q” …W?L7 *0!ѬŒ) !½}²úoö)‰4A ¶´²’úÇ?üݸq�^}õÎ2S۬м6�m á¡ºðÎûtóöç+Ü]ÿK=ù8†½ŒŽn œÖ¯—Ýþ¥‚€ à#`+]ßǸ‰Q¿™šo³ÌÓeºÀ›¸·Ç¯K$’EÀyÓ}y•! €Êð²y÷˜®ðx‘ 0ÉiŽô‹K¶ÒŒè÷Í“0eÁ@M=‘#Ô�-[¶”Â7Þ8¯BêÛ›ÂÖa¤=uâÝùüKzëÂ{4"#zšäÕs¤ôÛ¾mSè]è»»{è8Çù#E¤A@Šèf£`2™¤¹¹¹âKZÇ“Zõu+ç˜ÐF^6J–"b°l…2ÅP!`o×7¹¬+ÍÐ}µú¯)Ã"fJ´ €Ú~j"_¨xé{üñ'h|ÓfNx†&'ëûÂV­=‰g£n@1S³Ê ‰$¼sr®vQÍ®-TŸš6O”Õ×k'÷éB§eGB‰ÀîÝ{hl|½úê9úäã�CÕÇö¶6ú£Ç�Ó§7oÓ;ï^¦¹ùÊ�í*ÎDa/##ëéä‰SÔÇÙ¤‚€ `;;:™dÕ¬Âþ�‰5×ñ´†×h©ó™Îh Z*k! -øÌTv\Ú¨+©8 ÅÌ�šQùtåèŽm¥úS8zûúWjR® ‚@#ÐÆ/ˆ?|ú�é³Ï>Ua¶xFª�+â›Æ6кáAz÷ÒúäÓòÒY!ÃÀî�ÛhÇ¶ÍÆãD«ÇJuà}uôèc´}»ðD­„“\�êäßOÓ€‰‡ëú®ì2  �wõ®ÎNÅ%f:\Ÿ×·U÷ô¤–)Ä` I=9ú€¢\Í©Ò"éýä?‡ž =XV¯=e! @oŸx¬Ž¼Ü!47ccãôòK¿¢óçϫЀ0�6•LÒñ£‡h#§ |ë�÷hzz¦d÷`0¥ƒû÷R*•(yOXN‚ÈöÀ�ƒü9Ĥ¶Ñ°tKú!M†R^ÿèºÑQÁ €WfüæÖ£à}=—s—��ëöáÅ­­m45eÖÛ–a€îÃѬ/Mu«ÿÅ_ü¨ûGt凤’ú€6HEtǬ‹äÌLéßà}•î÷öˆ RÌä~A HpäÇ‚¶mÝFgÏž¡ûî‡j˜påGÚÀ÷.H×®Â/ üš/CƒýtèÀ>‘`Ø Xý�?!Ü+aPÒ?A  °‘ ­x­'W 8»Š³�é>.ð‰!d´€û)uD 0o\;²V›¾rå ˜0µ–fð�òêI°ÉÀ.Ea.ÓÓÓVrdK€0?ué› P{Ö�ŒÐ /¼¤XèëµÂ³Ü¨aü=|pýà�NQggu´·Ñ©ãGèû§O†^ù¤Ÿ?÷ zê©§Eù_îËyA@0Š€µLŒö³Ra6ÒvG#Qêà¿+Ê–_|Q\½,[®ÈB­±ÜZrŸ1"‘ìÝÈ�âøtÎ  �cÀ± îÿè__¯�N™|N"Kh¢ì–þØcÇhÓæÍtæÌïè޷߆jXý½ô£§OüPÞ*qþ�;®<+B¢tFš�¡!¬»™/&hØ’ìrzkÃkïîî®rš¯ôžD.žãJWZQî7ƒ€ÌàXµ×å š!CñX|Iû Ô•«RjömIÇ ž˜²�݃ID M†V­ù‹èÒ¥‹táÂ[”ɘϿ\-dÑH¸�úç¿oß~:tè0‡­U;f©'‚@% (WyÓ‹\�Y ŽïÌYxíZh¿ÇÒ¢XÄÍ! @ •L^ƒ÷ŠÀ ˜U‰R)ã6«‘g†ç`�Å.™ÒÏ`‹U4ØW�ýiä$è‹gíìÖé–Ô�&Gq–¢-[¶07ÀYºsçv“�XxccctêÔãÔÑaÅ�T¿ƒ"AÖ0DÂÓóî7w�Žw‚SZпËî£Ë6Úïí¶óNì¸þ±ìÊ�F€Q8+Ƭ�tIC‹CpŒ¸�‚Õ�OÊÆÏ‰NÏëÚHØÅ®Nøã E�Õèìì¢gžy–˜Ë…ÞxãušŸŸ_­Êš»�‰'OÑðððš» X‰Àðð�qÀäÔ¶~ïÌÈ€÷vÓ<5íL£·i� î«d¨ã×C4�:‚�¦YùߨÛ¤éðCöbƒ ß’«Sر”þ ‚@õìÙ³‡Æ7m¢×^}…>þøFõ‚š¨f[k+~ôíÚµÛø iÁ$C�: 0È©éòe£-ç²ì‚_Ç‚,0ø˜6 -kG{=xhœäP uœ/b¨#øªi‡6�¦®]ì âÿ5Ǖˆ'®u¹¡LMN-w©êóÿ_5tRQXÓ@á}úéÒgŸ}JçÎ�µFRv�A@¸{×:òØQJ§ÃÞwéŸ ¬ z{{Œ4˜†Õ¸ð2ZI˜Ï`Á€)uB ÜŒAu¥¦Íºz°ô3>›H¨k Då�Ù ìíí¯¼#RC�<ccãôÒK/ÓîÝ{Ö&ˆóé¥_ÑÉS§Dù_sO_,4pi7]Ba°°xç0V]�VxÆ~ó›ß0c¹”z õ@=ßf~âkiœXý/v÷iIµh�Ê yb‘Òé´ö8‹ôööŸ’cA@*B ‘HÒã�?AÛ¶mS$�÷ïWQýF»¹¯¿ŸN�8EëFF­ëÒ_A@XƒDØSÉt ƒÀtœ>0ŠðBcw�y� ˆæPÞͼ½Šv¤Ô1Ôï‚ÖffpüD‹1¤T*¥¤�€™\¦ž\&8•:pô`+%R�³”ïtÙöä‚ 4/ëÖ�Ð ¿|‘Þ½ø½óÎã$JõFÞf‡?J{÷î[bˆ®wߤ}A@–CÀ±@Ö§ ZoôËõ¶üóxXèCo¯þ»¡RŠ üGlìN1ƒ²rA®Ù¨ûEMòJSqñ<ô~\æ2±ñYÜ×j�‹½ª•S\/Ìc.î« ‚@øˆ2�Ò£L†·i|3�9û;º{÷ëðwz•Â}vÏî½tä(Çù³šA@ [!õ~‡Dún€în+ä:’  ^ß1Ô yn7ç8MÀR-¼*£SðâÂòb#-I#p„ü±H÷A }ý}ôÜsÏsÊÀËœ2ðÄ5ÝŠ ¦p/66ïÎoÐ%`ˆÇ—æ_V!‹ÍT¾Ç‹N�b€·ƒéòÅ矛)òA@¨�áuëè¿|‰.^|‡Þ}÷ãd§~gàÖ¹mÛ6:vì!Í“A@Ö÷î}g|˜mmZkzÆúï1|Lód�¡·ssfIk¹¯�RcÄPcÀýæ"är=gýR!©”žRÜH¹Q[,xLNNÒwß}g-ÖÉþ²A`%b±(³ï¡Í›6Ó™3¿£¯ï~½Òí_YO'Oœ¢¾~;n�wH*‚€ P¦§§iÖ@k[{ z_^YåÅß“Eeà 6wïÞ5)–U!w‡Y�"­t¡ËiCî À9UH3D&8Þ¨¸èÜ\íãN‹ÇPî±­«›·'7w¹XÉ}‚€ И€ˆïçÏ=O'Ož2âbÚÞÞNO>ù}zöÙŸ‰òߘSBz-|ýõWµ—¯ÚÖ¦··¼äʯ  é�F8`§i±Äú�x˜GuU‰bX"ý|¥[_»¼¬ó÷«ÝÆ…€x,¦mñ³‘>¤Úñ­VÏV<ÒÍÏ>]­i¹.‚@Í@J¾}ûöÓ‹/¾D££ì#Ö+¸=‘LãÀóXrºü ”ÐTOO�¿ktûÆë¯•'ÂA@°�ÀØÆ1zñ¥—ißÞýŠ$ðàÁCçÿk‰ó·¶È�†Fàúõë4??o| ½½vVÆ«í(¼çmxÐÃËáÆ¦KÄ)�Ð×½L·¿åI@O½J¥¡¥Ÿüä']|бp¢Š�x‰ ÉÔÒ°€JDgyå»ÑÊÀÀ •._¹z•îÝ»GaûQ·2X* �@‚_ÊNž:ÅqþÇ)j8õSC#�A €ÀU~·³Qz--Féô©A k² @WgÍp*E“…×€ÅѲâþ}=LBª fgFõýhˆšZžü•~lƒÅ‰µÆ´Üÿ!¬”1H:¤!�Vú)‘Xê ¡;„¼öÚ«ºb¤¾ 5C@”ÿšA- ‚@"påêe+½î ™i#¤7Âäã]]Ý60D*À ÞT¬;•l³B½¬¤ŒµzR e<ù '˜?�ƒ’ýsjBG(¢EÁ¥ÈF`6Té>”^44ÒÖÈõ#ZÑÁçT°ÿ¯ÿú¯e³lT”A@A@�Ð ðùç_Э›·Œ÷'�RwwWèÞ�‰ø�žGkòáfÝœ�ËBiýñó?.^õ»¾jsêi«Ê[ 7ˆ`…§\á„*ž¨þ1¶ÁâD]§x’¯¯º�|ŠK*…¸œ`³•í{éô*«£Óž©ºë×kÁY ãÂ12üË¿ü˱ì‚€ ‚€ 4o¼a‡Ûiˆ³­D"x'×û3Blô©·§ÏÊÃo‰Å‹½üv|`ýãe·êmËÊY İÌSÆ$ZæRñéâ‰Y|ŒûùÜiïsø°C±ˆ–ÆZÊå=Êq>‰¼Å�]éØû±XéŽp^[?:j‡•”‡ûÏÿüO*%`8G.½A@A@X ,p�?~¥[ª¾6²^Û©·ê¶Wªè-ê­tGu×z{ì:¹(x‚¥XŸ*>Þ[°_�WPo-ˆ ÄÓ.sâOÄ厂ÒxÂû ½\VËg}9÷ÿC©èÒ†4bA:,[d}ÓLtò¿ýçÿ܈°HŸA@A@Xó¼ýöEìlˆKa¨º}çг¤~gg§�E·=‚í.§_ïY²_¦.·¤ÞZ9!€À“ÆdA œ*µ»ÚDÌ_?Íu¡øóevÖÙ“ÿ�«ç�,Az§Kˆt!¶¬…jü–ÿÙº ¿vÊo¾Aï¾ûŽá"UA@AÀ x¿ýÿþñ¿Y‘�L&©¯ÏŽK¼‰çT€ I‹2bñµ··/ž0··ÍÓ™ 7Ákz!¦ÂoÁ?·Ü±~aëiu«êu ÷¯¥1äŸvŠ?îô”y¯Î2ñ4ëýPü'øÃ7²Ò¯”Þ�››s6nÜèPÄÕò²‘?�ø4jÙºekI^Sãù»ÿôw֬Ǧú(rA@A@¸xñ]ºyëæâ ƒ{¶H¨Mu1k!»W”ùà`º°š¾MéLÐ�”¥ hf}k¡AÝládp§L/X¥é÷ÅÀ�¸Œ‰Qjòù“#íô¢â�+Å++þ‘ÑÑQgffÆa�ã€$Rú½¬b›Á�DõÂR'‘LÐøø8�·RN<¤ÿðWÿ�æçç­È¡‚€ ‚€ æHϧ鷿ý­9�E’Æ6�‡úÝYyö~·w8@OwO&�MO¯_÷õ'%q©! ØFæ”þñ’mºÞ’:Í|"Ö̃[mleN†â Ë×Nó1Vû'¼ópóç=¬öoc·t¥ðç�q³÷§¸]- (»Å¥E¥ vµøŽUŽs.ëòõW_‹Ë;¶ï¤7nXkêö­[ô7ó7ôçþ狭鯎5ŒE° ‚€ &øþßÿ›¾ù梖ÈÀ»øÆ c¡~w†g¯éw{Öa¨§×†€âã¼hz`'ùs;©”«<.À5 ƒ?gø³@nà+.¾ 3Žý}ÜWP ÷1&Ë^/¸¹ÉÖ¬&Á*Ï׃÷íŸæãBWÄøcµnþþj? øðʱút taõ?(k•n^öR.U>“-HX}É5(`pÄCÃCÔÕm%?éB3—.]¤¿þëÿ…2™ÌÂ9ÙA@A@—¯\¦üÇ´Ö¡ññMVCOMtD€6Šô4» ú>Ð¥ SA·* €îµÈà/¨Wa?xìߣ¶ÐÿP N®Áƒ5i(ãÁ'FñDâãÓùøž1ù‰YJñŸ˜˜ˆ@ñŸššŠ¤Óigvv6�Æ53ÄKNÓ–$‡h[?]ª¸*¾Ï9Pq½J+\¼x‘þç¿ú+xwTZUîA@A@°ˆÀÝ»_Óÿúÿ£Un«­[·Z�ѶÞí»{¬xEœmþÂ)<¨aÀ1<«—N¤Uô5ÜRº”¡ –®Ø$gל Œ^<™üG ÅŸ?ùUœÍ»ûCù®øûŠ¿¯ôg³½<�;"sííÇuF}�Õl%2@Žn€FÎÄqó–-Ôcë‡)Ð,Ëù—ÿ–¾øòËÀYÙA@A@¨pùÿ÷ÿÓ¿'^|³ÖvU§¡Á!kòM †·{΂Ç;Há‘l¾8[±hŠÅSßý øð*ýÿûÿI7o~V“‘ìÞ½‡X!­I[&q9Ë—ŽŽPª¼ROÝ=½ôÙÍ›¥.WÎqÖ÷µ··|K“3YÊ8Ñûn,vA1‡ÎÅz� #@ “ŸÏ}ôÑb¶xh«Lо_ÊQ©}ÜSP  ­%#Àš0<რÖ\bÊ?WXeÕŠ?Ñ{ÌD¨/S°âï+þÙl+‡ð}Ò3”øÑIéf`ë`³•‘‘Ú¶m;]»ö‡š d+¯¾ú ½ñÆy:úØctú‰'h||SÍÚ—†A@A@hf ôßá`p1ýþµ×jÊÅÅÏPÒ5N±•å«·§Ï‘þTtËWßÎ]�D£.!±×}ügïê»óøÊ¼j»¤7ÀÖÝHpî÷õ;¿@†´ÿiz,:Ë<×àù¢ýÓK\þwFÊ¥«þ‹îþ¹»úóªpÅÆ�t:ÉÞóN®†‚ª›{ ÜnŠ‹vü6W,²)Ž�}Œnß¹EÓSÓ5{ƒÐk¯¾ª>ccctìØ1:pà õ÷÷×´Ò˜ ‚€ Ð àï*>º…Ã3in~^W e3Y„êËᘹ™Ym9Pòf Èq•ý Gp¦ž™Ñ÷B¶¥V°¹<`ws°ûùÕW499©�Y5°ú_Ê·YµªƒÅ)¥·×N&€Dªm[&�þ0’ͺøäÚ3n6–qƒaOРtHÀLèw¾Òåïû[ˆ)(Ð׊ÀRm²ŠÆ>Àƒ,cÁ{¼}Å,É5yå_¥÷+rù_ÎÝ_Åù³«ÿ»ú/*þlh™w\>‹Ä¢äæ«uÅÁ�N©!éf@|P3–d2A'Oœ¢þçªÛð@HƒÏ?üÃ?Ðè† ´oï^Úºe+mÚ¼Ù �JÝ* e ²QðfèÈ™™ÕIå!šž6 ‡4mˆqzš_šñò¬[ð⌗yÝ‚çe‚$6ø"¯Ó'ö´ã• }€æx\&þöA ÌxÁN³œtF?v9�ž§ô¼¾œyV”Óå´’£¯¸ëÌ©+˜@ ­­�yä¢j*¿ßðš(¥?èt¤·¯W½Çføw ¿ï¦ ÷sËP*ÅQ÷ó©)e€!€½rÙo3N2ÙËêV¡7€Àq /@*Ï @bXî¹4­€'PP±/¿Íßâ:ïŸ.Xù¤÷+ úƒrïÅú{ÌþJñÏÇùçž«‹Š.“sø èôõw r·ªÆ|9«c ’èü04kaÏ Ú¹Zo\¿^÷!Þ¾u‹ðñËàà ­[7BCCCü¤ÖÖ6ÿÒÂv~ž_. ¼²¥Ô˜œ´‚¼X{Ù5°*„1Íó‹³nÁ³š7±*Ä Åܬ™Õ%S · 9ºøJ}A@A ãÐÎFŠý÷1Â;¾ @gg���Ó½{÷ØpÇoN{ËÚÛöœŽärQ·=û0‡dƒüÉÌÍE"]Qw.æ“.% Ä;?xÔÿ9à#€ßGè€% è�ŒYÉk~åFßV­Œ†yàxp+ôÏ¿æoq«·_´òÏ%uÞwû÷Vþ}—ÿŒƒÉX¼êŸËq¬b>‚_ñWûnÊI9 ½€±Ò¤#©TË Ã]ý+V¿©�ïøÞÉï)×1ä† Sùúë¯ )‚€ ‚€ áD`=“Î�oÚÎΕÑ+ÖU(1Ÿù½�®`0YXñÚ’M¦#öŸk¡îö´Û–÷`¥5½ Þ>72 ¼8M€g€'Àad(0@·ó•ûà~P <&šÚД€‚'Xx€]\øÜé’+ÿ¸qbbB}cxõ‹·LôÇé)|†LB�ü¯úg3¶\å”ÛÎuy›tRnÆIÅô2Ä¥ `Â\ÑäQ<âÀ±oàZÙf¨Ð€»±xŒ~ðƒ§è¿þ×ÿˈûqB ]A@A@¨�Gïž:qÒ¸ }…ÝкÕê +5ÜÖ¾Ôsu¥û˺æ8›Ù›Ré^¹óÌ$yuÁ 2‰¸jÜ2.B‚FæEC–g© ƒ•þ3¾ÂïoÑ¥à~Y]l†›ÀÍ0 °ØøûEÛày?¿�ð¶ù˜L¬ú•¸üƒå_)ÿ¬ø3AEd¸¥%’J DZZ"w�Å Ê¿úd³�ˆ$³ñHG,�²ùʉF´’Ä/�L%‹†Zþa®I ‹hoo§§žzÊŠ´¸-9A@A@h|Ž?Iœó¾‘‹O¤hz œÞÜ´H*z::º8T4ªô)Ö­ cAׂÎÝKy_#Ôº+£t³l¶×ÁB-ˆ¡¿¡SÐåÊ >7¯“y]Ï÷ú^ÜârþïNÿßtJÿ–†Ý6•`…|°þ¾·…kH>ÕŸó�§‰I—L¨…xžh˜tPþ=¢?¤ö›�Àå?›Ìð}¾òï)þ�ñL$gC@<ŽÉÊi3£Æ 0 0QFÕйSÕ�׸âÐÐ0“6¶·Æ�Is‚€ ‚€ ¬IvîØA;øÓè…ýæ­ ¡Ý‚íloß‚ET [¹-9^­[Yƒº˜2ÀÀÞÙÐÕV4@×#Öù Kð8¸¿p× ºåÂ=�¸ÓT€2@áÃ…ò�XùÇ!Vþ ™þ=²¿Ìü¼špPþAP±Üª¿¯øgØo(ÃÚy.co€Xĉ8�_M�cC,º4b#•¬~õý0Á‚\ÍxêUgÇÎ�tâÄ©†våªvÒ® ‚€ k�þ¾>:Æ«ÿÍP²–¼}[Ù»ÖFI%"[R.B¬ÙÀ ¨þ+Ò©{úWR-Ä.2'ÛÍÜlå‚ú`p߯pB)³i +Xhüëoñ xyå7 ®aåŸ'”Äû·óù ò�XÏåqÕ+þ˜°Xõ‡â�ýl6��;ÙÕ0�:%ï�¹µsjD…WL7!t ª¥ö½ü�èhÐÅ “µ±'t4„h¯d؃Ž"`Ñ ¨úÃ(uúBÉó~¥FÜ6…`…Sê�áÜ‚Û?&ĶmÛÔ³óãF ÜþóÊ?ýç¥Tñþ`ùgwß"•M$"°TÁÝ«þPüsñv¥üó—.’‹e#m±\„9ªöX.õˆNü?Í_"5öµöÏî]»éÔÉSkmØ2^A@A@�eàw}úÑ�BàŽj¦"@Ó%Î$ÛÉd´XŠD£› KMó‚*t«d–C¬Y×òCÀ¹†ðkèd0@G[ÍÀ)�B#€ê¶¯'ú[œ î/Œm]sážFÚYêSÞH½/¿¯þÃô¶‡Ùü“'…À„˜™™Q¤ˆû/¥ücåÊ0ÅŸRþyr‚á¿ñ)q×ñcýs¹8OÆt„öœoͰ€Ûæ�Ë!~OÊﻺ3/ýK%S,²J¡ù>4¡a«,twîÚEÑXŒ^yåÜš5„””Ü$‚€ ‚@“#åÿÇ?ù)õõö6ÝHáàDõô…R tX0”8ìê8eˆu+7NiJdrîÃ,óžÑC‚ã¸ãÐ¥2L¤æ…S³ÎÅ‹ª òp~@ð<O¾_épÈ܆ìÐíüìK3,ê„ùYéëŒþ¶`²® sÜ× oXäRŽÏñƒ.bü‡Û¿Ïø�‰&I?Õ&’7¡˜í?Ïô�sPþ“¢&bžá¿ÐåŸÿh–•NUÁÊ?ÏÞ3eös_«Æ;Á¥ <ª-k]ù÷q çž{ކ‡µ’4øâd+‚€ ‚€ Ð trš¿Ÿþ´9WþýG`+ §££¯¥Fxx:ï³ÎÝ °ÐÅà àg@8t¶bÀ…�à XÆP€�g£o«VHÃ0ð2,1¾!ÀÛæ•Ä€À „1,*ÿ ýK+«¬G~ª¿…˜X•”òï‘ý%9±)žeÊ#úóâýó«þ¹VVþg9lž¹H<™ÔÁly1èàê×mii¥Ÿ°Û×þýû…ÐE¶‚€ ‚€ 41cãã¼ô<õô4ŸÛð±±><4¶ßÑÞaLVPP_¢u3<©=#€Ç­¦t®#@Â3°~æ{ S[РÝY¯ó¼¼½àý»À°Øh¡Î¸x¾`¯ ݳàþ°4#@éè§üã'á“ýù[H,0þÏ-*ÿ`˜Ä5¸˜ø1ÿ ¢ð•ÿ8[Ÿ”e J>»¦`¢¶ñ½pùǪ¿›‹:.“梹H2¯š“'ž(í�`ò ¸òWS²fu««[M{a¯Ã_j:zô1Ú¼e+½zî,}óí·aï²ôOA@A B"œYëÀ�ƒtèÐ!U³Ù߇YŸ©Z_X Úö޶•.W}-–Jlb�ê�©{Og<1Žc¥…Ø+º“y¦clÖȪÅìX ï³ÇAþœ"�r_2¿j!¬›î3)ÝW‹¾ž$¥ z|X¾€P€ ¥"È(uÞÓ°Û†5T`�ᇗ�ñ`â?Äýcõ?èúï+ÿ™¹9¬è;°e³­ü™wÀ4é+ÿp9I$2jå¿”ò�xÿ\¼Íñ”ÿ\Ä�1 ¹h„ ªö€bº )%%l¨rj"¨Ùðªùf‚üå™gF/¾K—.]Zó< Õ`(uA@A@#=½=tòÄ)Z3ïÁ0 ô— öŒ>’6N=Å´>�Dǰ Úšžr§¡­²ÞÁò*‡’9×™W µ”äûæ(‰´:Ã-a#@aÝ¥HY$º}›®�Ã)7oÀ´ªè <Þ*µ/4QÿÒ°€e ÃC)Ü\ÿ7²Ûÿ¸þg³é”0þCù‡+ ”¸ýà Hø·Dùo�EZg¢ÊåßÍ)�¥üCñçùá´ð'’ˆôy]«ü_Ì1|q™ñ² rœ�£ÎQZå´ñ…-è`›ƒѦM›éÂÛoÑÍ[·Œÿ¸50<ÒuA@A@ ýë?êý«ÿ¹®Y�5R)ÿ`‘ä&P†�L0�Ü“Hõç§ù ºý·±òŸõ•7cA.ÿœnƒ­C¥üÏóqĉ€°êÂñ,K §øæÍ¡¹Øä€›NßóuE�ÂSòî]Wy¨P€¼loƒûšÒ  á °¸<šWÿáæ×0Abb”rýGÜ?RJ`õ?ÇJ>¬Jñ¸—z‚×ñA ¨)Aø§bþ™éß ®ü/(ÿ®“få? CspÀŠ– Ãn;Å%•Ðóˆ²Õóz-ÿcºÚñÐÐ =õM<œ ?\û]¿~¤’«U“ë‚€ ‚€ –ˆÅc42¼Ž¶mß®”;¿9^”ów×ì™l¼ów³Àtau/ÙÙÖ9ô]ö»/"¬yF€8æÙÀ­-Xûg¯†8“âÿ9výWe�žÝ9'�ºà@Æ7„À Àï³òà�ïXbwáº_ñ:)ã¹ê}ÅõÂpÜp€e@ó��íi&u˜ Êÿù®ÿHûå®ÿÔ—as‘71p.èúïÇýƒô/[L÷‡t>Û?ÿü˜åöŸ7ð\(PþÉ�ÂlQ5ÆœöÝZ$‘@sÞÁu‡IRe·Ú;ÚéðáÃÌ {€>ûì3úô“Oé‹/¿P\ åÔ—{A@A@ôÀ v_­¡u¬ø 0YœGt§ûެ׳ðÕ   Lzºz¬ ¶-¿çº_BÇò�YöhËL;~vÇam'ãÌ$3¸9³é¤RÀE>€V&w�:ŸOM©P?+@:=á댞¡¨€°`8¸J¾¿-¸Ø¨Íb(�ÿâƒõWÿ¡üûy 1` *pýO0ñÜþó®ÿ�q/îßUÆ�¸çúÏó¬-Lõ—�ùÏ+ÿ)_ùŸç•ÿ”Ë>öQž„óˆÐJ0Š€âWô OÿH²§C±h9.à7¾i“úÀ�Ž€[7oÑ�;w@4Y¦¹MA@�ú#€qqâ�Óˆ7[},á©)ñXœÃV#L|íP+³Îwttð‡·í�„™b’ly¯-=�¢Ì¤gÃÐÞÙQºAͳ©dt§T“"Ù¼©)“õ9qvb#@nšS¬å2Hc€ 1W¥æ³î,‡o;‘Œë‡ +§$¤TYØI ¿è^"‘(¡•ï 9ĺUo([ù¬5Äüs…Û|ì?ûg—&þËÿa"`Õ#ßõŸ¿$qÿ|M¹ýÏåZ‘ˆ‚‰þ<œ¸Ò€@IDAT÷~EøÇû¥”×M;-©¶Nî¯VÐ�¾7OZ½Èâoqÿƒbe¿J�‘aÃèFõÁ샇è›»wéë¯ïÒ½{ßÒÇ­üðVÙ]©&‚¿èóK5è•5 ~ÿ¢1}r)¥|˜�ƒþ˜ã££Äø°‚ÂÚŒReæyÅcýa·Z3ø¨ù£?cP�Ì3Ï+ÂËb†æ¡‰ùÌnÏŠýÞŸ—Ͳ•÷Ø2Ÿ$¿îÛ0°­¼.àa`²ðïæFèV³¼ÈÊùü"ü}âà~—‰æœy·Íi�g�©tFñ²ù¤€ñ8ÿh§áà�eG�§bcoÎ 05õ�m ¼(ËúžO�N¸XFŸÁp�¹à9î¥,9Ü[ 4A7eL Ε¬²“ú¿¶!�×Xn¼ò£|ìÿ’ÕÿBâ?fýgwŽû_âúϳ‡�*î¿�Wÿ³Ù6'Ÿåû¢¬Q0 €\*æ?°òåße€–Ö¤–û?F‚ÕeöLà?l‹�Ì È´Ú�¥ƒj»ÓLõº:»Ÿ-[¶ªaÍÎÌÒý÷ibr’&'ÒÄÄ„úLò1ϱe‡®^ˆ5¬è¾àˆz‘]œ?þùJ·p­+¶¶W*÷‡­?X]À*ƒna1Œ�¾Ø;ãq¦²Õ,žýçÎË/”PL;zÂ�3¼—L¼tè–°õãiÖ—tÝg%õA@�ÔUô- 7­ª2ŸAñokkSï´&¡ŽEb¡[¥Ø3{6ãõYVæU8 γ”�a/æüHf~GaòuW…Ä’Nv~Þ‹áW… ŸPy0mAv²Ð „ñ׸”Ë?†å~‰âorœõ�eà­°Ý.£Í¢ØÔÀ*¿JûÇ«ÿ8æ ›'\fõ_y,ºþçØõ_eÈ�ÙŸ÷á!ÀÊ oÁö¯ÿònÿ¾òϳÆi�Å´ÑW”9v)gYÞÿ«^~5¿ÉøC   µº“L%i(5DCCCVÛá‚€ ÐøÈïrã?C� aB@ý]ÑÔŠÇ’rÈíàŒ XØ2YxÝa#ËæV—Zxå&ÇôåàsÄú×|<ê´1©B\�·çâN<›á…Y×E·¹(Ó°.· àñ¾)/6@',öXÈ@‡Y…»`r8¡’µ¨M†ª[K;‹¥g¬3þ5Þòê¿rÿ/¼Û‹ý l÷ Ç�°òÏÆ€‘¶6æHòÜðÈ"üÕÿx<á8’bÿ1ɰÊÆŽ9Ypý‡*Ç‘g½3ÏûHõ§âSxÅ?¨üÇøz<îh{¨Ñ}i±:Èý+h…Gž5POF…MÊí‚€ ‚€ ‚€ PC 3èê ÅÝEvÈlg^2› �à œu--±8'qKÏG3b‡l,¼æx¤€Š ×Jí©œ» ÀÞÚ¬Ã%œŒ[@˜ƒòï.pä½Jf€.ééÿÐ1¡(o €ŽÊ4”2Õ0€”ËÙáÕÿ=|ßÀ訃Ô~|‡—úÁSþ VÿÒþ¥Ø•Ÿ‰™IÒ'þC̉çúïÅœÀ¥оË^<ï¤xž÷ci>Ç¡&AåÆ�(œHÜŒ 0vfd7ã(œyg+ßÅ|m°9[ù ¥† ‚€ ‚€ °†€Šª«7çdò? e´P¢�©øúôÃôgNï˜â€YÖ«@úçÆ²ŽâˆÏ9Ù™ gh‹’£ºÙ€³°n£Â³ç ‡Á €u½,‡³å »@Þ ÀÏ�d÷Ç'oÞ¤+j0Š POÑ²Š ‘�l€%fiɯþ«œŽ�«pÿ/Áü_ûŸãÕúl$‹Õ|ž0Hù—¸þÏsÊ?&þcå<Pô½Oš-Q±4Ÿc×ï<`%âëIÜıXĸ€')7`•»Pþñ‘"‚€ ‚€ MŠ€zç7<¶¼ÑÞnÅ@‰dë75u“Cþ)�àÅWxgó|=Ì Hr(À´3s"«n¸VÒ @z0�)¯ùfYÿ‹»¾îÈ[¤Ã)·Y£Â׊¦«�·ùVÿ7.!ÿà³3Êõ–0²'½¬´2ÿû«ÿÙlŒgPÖiçI4ËL“1—³pJ¾›Ï{«ÿl8ò ¼Eº?Ü_¬ü£ßÜ}ãñÿè”|¤‚€ ‚€ ‚@s" Þ÷ ¿óûzD;§g´Q’‰èFVT~ï²ÆÊmqÌ~ŽÉÙ½P€/Î à"+€;ãLñâ­ãðêm /?O t@?#‡c“n:}Oé“ðÀ8 Kz^§yï ®á|ñ–O5fi@yОfý‚Tú®Àió”û?êzäi•û±ÿ8×Ö6ìÌÅ�+úø$Ùúñÿjõ?Á÷0©E~õß'þ£83Mæâ<ù~¬î«ÕÞ&±Ÿæ:jv*·—‰'ÐN†·<{±o²$”€¾âŽßõƒ`²s"KA@A@BƒÜÿM¿óû2[Z[UjQÖ¹ŒŽ—YÿF¡s9¬½Çæ\¡Xpåòº˜OÈž×Ðé@È„�¨³àÀ8ߟÒ÷ Bœœå”\�!âœlKà )ï"% Êá ‡½¼}u¢ô?X¬æ6W½¯tíÚŸm"€,6¾ GNNS¡â=p®�ÉÿriÏ€ãœëÅþcÁ#îÿ*¦¿põŸmŠø�Ó �õßãP“0ïúÏ2ªËÿBùÇÏ ã¤‰Â—N»(ò³y;µû$A@A@AÀŠ°Ï„îè‘GèémìððáƒÀUýÝX$²ükÈÏë®àcãexÞf•’ÏYÙ|.€¼€Ë^X¨EF€/€|Fx€;* €=¼YœæÅâH4Ê!•®˜ Py¤8 €Nó@Îè&DÕ V×ÇÅ-,4ùö �ÿ�ý?ÃÖÿ~[Òüñ•}7™sl5šŽ'œß «VýÛ8öŽ·Qþñê?,J�ãÿñ>òNbõœè ×óãþ}åßͰ@ÄN€ Cd˜�ãã+[A@A@A@6Þùƒ2ÛÛÛŒX‰…ŽÅí(/‚·<Æ! Îããq@7ƒs¹±þ…óíN8i˜Ø}!%`“N²N¨R2ž”—1ŽU»|ÀÂÓ;Í{g–¸ÿC'Tõîk �ÐØ|³ ¼/�ëi¾´¼û?Q?åº8†>�ú¯…SÿåÝÿ‘+ ‚-A™h+O6¨ g#fZ”'�ûßÂç狈ÿ¼56 p/rXõ÷\°Bï´Ä¨…Oãc´$8 kîÚ2}×mA"@A@A@B‰€R èÁÁAëLê<L^Dh�ÇR3i&rãðlNÀ‹µ¬¬ƒ­„@‚cfT¨vŽõG&LŸ™T„ìè—¿ðëqtPÍÒŸÏuqÀd7‡L,(VXL¾ÉÙT™À"ói¾vÆ;n‚Co¨ã•ÜÿKÉ�7Üÿ9RŸ|ò?ßbÄ �4Ÿ‰xÌÿ<‘üúža€'Š2,®þ㌙ü–2Jt´wÿGÓñDœCæ)NUU Äœª—J‚€ ‚€ ‚€ `(ê¦ßùƒ2ÛØÀBqZ;ÛÖOûà+Yp[æU~^›]Î @‘²Ïª…\åòÏLÇvöêæºPþsù0€6˜È‡ ßÅÙø”»‡SË_Qa¸cå‚EkÆC_9[¹#W›Ãpš±`€`ñÝÿ‰˜ý¿�Ùÿ9÷c+üx¸øxļòŸwÿÏ‘ÿµñÏr ?¬þó³Tµú�ø¸üçÓþù«ÿjÑ?¸úÏ®'˜¤IN5ì—©}�@_æiƒÌUýÁŠA@A@A` " Þù �;¨G°BmXº'®=ù†èt2ß €X•[ôðô4VÖ8Hƒ pš#¸oê3ÃF€Öa33¥³p¦5¥ÄƒSn!à4÷åŒ×Ÿfø· ÞêûBüÿi‡”k†÷8üøÿá€ÙÑß÷·¹\.ýÞyv(©g`÷}¸ÿ³Q ?aÀò_xŒþ8ÿ�l؇)Ê‹ý�¢N~õߥüå뉸Óí·kr ÖܵEB¾¼RA@A@A 9Pïû†ßù!Ó×#ÚÚZ­Ç ëHÁæ0«º§°ð~,ÍD€‰¸ÛÂa3 z~„nó–C»]ærC@b™0xDgg]„ øÙ°ˆ¼ ø{—òÿ²ÎIgÐè€ ­<5¢ àQø‡yg–?Åéÿ8üŸr³^<è4“@PÛ<¼þáÃ$ž!Àeë�ïþ߯Æ�¹8OŠ\>•ëÚ-Y6Äy’cõŸëa寴_û¯ÈÕ=D"?í$n5VbQ´o`Æ:'‚A@A@A 4°cFwŒ((3‘H®˜Ý…Âʪ{WGÙqÝ»€€q[³qw6Î:ÿ�ÅÛyNÍÞÁ¡ÜSÓ¬ÿ±NçÂÒùny‹Á­œ0ªÂ�0Ús³÷³œ>îbõŸ•ÝB€ŽbÅ«a �o8ÍORñÿÍ:KÓÿ P63£»òŸÛDb‘ý_]Ë+î¬ó+&”~\Kûä*= < X·çû}ƒÀB쿲XÒüÑ‘|1a€ rü>ÉVA@A@Â…€�w~2— !žÖH¾/—|b|.ÍŸD^ƒÎæ c[¢�§YOS‹¥JO×¥³J·c«€E´¯K–�1`%ß‹+Ç¢2_¯ˆ (/Ìû¡6€L¡ZðüT~}X€Tü??› ü{äüLÔÇ,±¢ßÎ.#^nI¸�p ¾ÖŠ. øüs˜Pó^ê?¿�ü–Õiû¯¹nÑec‡&ÿÑõÅÍ´ŒuS ‚€ ‚€ ‚@�pYÙ1¥?øCQ2k¨G°Æå�Õ-·ŽsÃY,Ùf ºÚ,gqC¿�Á [ŽpÒÌ€}èsXô��±'8“´C„^€æ|›âýHl1`µ<Ð]¹­Ð+V¡6à�­\‹±´ vg3ݼJ¯ÌEþ=¼3O~ü?Üÿ1!ãÿ3ª^šÚ)âbf�|Q.ÞòÎxкÿó¹Œ÷Ì­øýû}ÁÖÐÜÂm€y¹ì ‚€ ‚€ ‚@%@w0¤?,4[#=Âe¢õ«mPë@ÈVV_„]? ’á8Üã[„à:‚XáÇ>&à-€}_–¿¿¼|¸�u÷Õ_CJ{�\w|ìi‰D¨¥¥•’Ì$Ê?ªëÙl†Òóišž™&Žªûp˜©”ÚÚÚ‰Ó•P4Z‹™W÷!KA@A@¨)¼ZLsóó455Iì^Ó¶M5Æú tSâ”2Wë �Àã(º:›ÿf¾ÀÀç¼°�„3ÇF‚VÖýæø&€ú¬Ò\¡ lvž3�Ê�¿²hÐWº^(<$GKtØ�ô«ân,Íà‰€{òÇ”èUlŽŠø¡—˜b±°ûœÝG²<\Žq9ЄrùÀÿü]ˆ9ñžñ"lˆIÉðL“ð(X(‹·,œ2ºcè \�/®Q,CŠÅžžjoïP¿0¥šÀ7}zjŠî}w�˜p²Ô-Vϱµ’©�•)‚€ ‚€ µA`zzšî~ó5G!{lñµiU¿¥:ÒüÞ@œi£‚/{qaŠ5^”�ºìÕ_È€{¸}µPëñïqèv,ë€ÝÝu㬟©°ÞFé;îO±^˜F"7ÞâØ/нLð((,³ð&H¥\:ÍçÏ^kÄ#Ûjj]0)΀N´µe„ß¡&�€ÛÈ ã­ðÏó$ ®£bR!¶Dé÷j2-MÿÇSÈ[³­©/[]]wj†Vù A¡î ˆQ•Vú�lmm£–ÖVºwï[zðàAù�hÜ Ö“‘‘õÊ@1æ�¹4:'UA@A@hrZZ[hãÆ1zøà!ݾs «É 1bè¦ôÀ6dú² ¶ÐX³pçg.·Â?ÖÉ\Nï‡lxm÷—ÿyWñ°òwgœtžã-¥Ÿõ:Èõ•dˆd[y±xÊaWÚ’ ™ÙÓ¶ä5ÈjÄZè«ÔÏ@\›•üââÅÿ·òð8ÜD“Cäg »z« ”ÿû“2àVRü䞎¿R5Iÿç�Ço×?®v[³/nµ¬a½®®nêëëS-V¢X÷ööñoFŒ¾ýö«½EHÂøø&jcÃÃÂijڢA@A@(…@gg'mNl¦�?ù8a¡¥úXp€‚,Š{ÔÇÔ5(ñy#Ž¡£�¬\nÁ{±¨ Æ·…s³³ìà/ó¶²áf¾`ÑWÉÎÿƒÅd?`ð|³ì‡Ö  p†\±Œ¶GäïËëý˜I>Ì� æ¯üz"OÆB@ÏjT&±ß„+îˆ�c–m@ �™åà³Ö ”êÞÞ^üHTþ &ìÁC{žG7P+sTÛǪ&•A@A@J"�L¦”7Œa?Ãû¾é>ÖZ�PŽ%ŸÄâI£·ŠÌëºãò1ßÞ¢/§ dX¬ØÃ"ò½Àq…»XÄæ>T§PTØVµ·7©`1 €ñSú åò^±AÜEp=˜ȯ³ê6þ¿ê½n05¯0=MÉ20¬ºˆyâéu¿ªð˜šž²Â ãDGG§yËm]—FA@A@hÚß×O_ßý:ÔÂú¿éw~2Ë‘Õ(ù{àõXåXR‹»IÖvá!°ä"Ÿ(XÎ߀EdD À«¼Ù\ÿ} ØpºàA"Mà û‹J©¼ÅWò·^ÜÓʶl´ãßà@ˆû–” jì lÁ¸��Ÿèf×ÿˆJ¡o¬ëíé¥/¿úÒèˆ 348K�Q¹"LA@A@�æ�ú†CAÃ�!j¹Ñ@w0¦?ä±!s¹þŸç¶ñŠ\ú嘯Á$P\Ôâo4K¹tnÁý©ýûr ©±˜ü­ºx‹ûK·[|gH�k ªZ¹ÊǸ(ßÏ€ô ‹g ÷<¢3?NóÊoIxƸœÀ¯‰ æï³/Éâ.ï�•2xÂ�, ž³±ojÖÕó‹k—Jeâ—C­¬R®ÁÎ�‚LAЉJ{UxWgV2ñIcÿÖJŽA@A@hÀÓ„%ÂZðªkJðǨdz‡öeV»…¾ÔÙ‚iÝ!3x­¸ x‹OÌÌ8‘D€Xd¾yófq•†>.Ôfz(åw^1?4u?ÄŸÞV,ýÿÛ;³Y²ô®GDîµÝ­{¦»§=3ØÆ‹æ¡y²@ÈÁ+<#!„ˆåÅâ „c �0²Í dƒeð 1ÞCûÁ – x<wOwÏí»×r«*÷Œ�ÿÿDœÌȬȪ̬ˆºuÿ¡©'Nœ8ñ‹S=õÿÎ÷}Ç^³É9ñÙ_¥…X[±¬}\LÆQ¼;~!Ši«¬W-³Ýbê�Q�ÿñêtÚÎùùyaÝÞ?Ø/Üb[XçÔ�ˆ€ˆ€ˆ€ˆ€s€¿×¸<àÖnü{¿è¿ùoPG,.h'e©ÉR�fDÙ¢f[ö=8)œ‘ƒËªÍ—'“ÏVüÍ_«ÐY• s3틌áþéuÖ·K>Ø{™-òJé;oHn]˜õ·íÝ䃻�ǽªØ�B:_@#ˆíA+Ű´ÝaB˜8..`§Í¦ÚD@D@D@D@¶•@¯õ÷yïȾÝ¿2Ú¼øîWªµ‹·,)£|ŸûË6z“7+ jÐbÅÄe�+àZ• öu¯ú¬"†ÃÞpaO=µ�´¯Q:Mübð¾ü�Ç\Yࢭ_|ù³.<¼˜‚¢~�oæ·˜w.£•-È–2í^!E}6jÜÿ‹îä´·:¸.†€r+òoÀëö){ó—Ñf¶Ï›£_tðå·0-ñö®9œüe›‹ÞàËžcÃËyKNóˆœò­/ª¢`m¨tÇn6ïc‰¿µo]š5rý–н#onòêJþò¾®›ÉRðûÞ¦ùH¯ëÒ{‹€ˆ€ˆ€ˆ@5l« &=ãõ[0Æ›ò$†R1»tû�c$íÃJ{^…^ÛkFd/z‡_hó–l ‰«MÂ|h,×a�œ¨¶c,A|+?^æõ¬Õ(S´5‡qtqÜoÒ9¶ST[›<ÿUßøp)Øà£Í"™ú¾�U .zž¼jvz¾ˆ€ˆ€ˆ€ˆ@B€+Da¾ñ60*ãoþ2Ú\‰gò󌙛ã¸�WwñХƛÛr¼Áíuz‘ó.{~÷·ÚÀ%mÖÿ[÷ñŠ­hçuö ¸. ¥c¾?)”é`0p’\ö Ú‹€ˆ€ˆ€ˆ€lþ½¶ÍS›¿w þ£—ͽÊwfx¶ëz·Z¬—1Æ«o�¦3øËqiè¼µ¶ììV¦uGVöþµ¼bå¢~ÙØNQm­Øõ­ªÖë÷œ7°vk‘[¿ß/”ééÙ™s÷î½"»¨¶D@D@D@D@ $pvvZèßvÍ4UÆßüe´¹É{S«ùi|ÿº÷ÃsÃÏá^s9yìÕëëʾuùÊëWß„™ Åà5F»ß¨{Ó†ƒÏ޾꾨‘yS±;«¾×M×ÃØq&øi6›…<:„ëWÑ€—/Oœw?ó®S«+  ��¤FD@D@D@D @t…?>>Þj:—›ÉîZ¶m"qA‡qþ×þõL¯pÏ91„¾ÃpðZtå$òN:·ÛEI°¹¦oúäVnZîó\n� iq+b‹Ð^çíðøÈyû­w Ap|rì„Q±ñ_°F:Ož>qÞ}÷; é£(ŽÀ³Ï�1B@·yãßûEéûž¯‹Ž0“Í­Ö­L·Î€˜îõ¬9vWl_Ô/0Û)ª­Š!œv÷Œ.öwî:�3‚l¾M&‡€2x>{þ̹ƒ>îïímÞAÝ)" " " "P(�Ápè<~ü¨”¿ÿŠìh^¿e´yá�ox’õÂóoaÁ­3ÜÂo”ûJE‰LNþÕVnG+Røÿáþüç>ïÔë›ýJ�áã'�¸•öÆ|ë}ç{¿ç{a¨Ø)íjXD@D@D@D`5“ÉØyÿƒ?*õï¿Õzru-þ­Zôßüe´yõ›¨Æu l¦v®ûÔÛv€¥äabcLnâõhq+d‹#üÇ <ÑZHo fîøÉ·�}½Î…DWßÂ(4–ßáp°úMÔd¿ñ‡à|çûNx,()àu‹ˆ€ˆ€ˆ€B Ûë:ßúð‡+JUa3b½(ý�¾pb¸a´Vy²• ã{£QAâjûGCy$·ðÝ/]�IàŒÀèû4Q ãÌ_{UïÉ©û6YîfG£‘óÑÇ9o½õöÊ®ö\òåé³§Ýÿob ðÀ?zÿ�œû÷î;ï¼ýŽÓÙ‘7ÀMp×3D@D@D@D€ø÷âÓgOœ£££jIÜ~‹ís ^×íà‡óÊ®ûœ*ßÿZì‡jÇIÖǸÙt�Ж.Ûs6ø,[Ë¿¤œÂ½ˆM€yŠ´â~òÉCgÂú–ÞÛÛÛw<�–¡ÙF�‰–úãr/Ýnwvá�˜k€?»;»ÎÁÁ’’vœF£î`-Ôì…%" " " ·›–‰ƒ‹h„ÿùù9þìUò…Ëø›¿Œ6W… %äbV–q ®`Åöœ½Ð¿í˜¸]õ¥n¨Þki¸!¶¥>¦ ýÏßZÅJíkçR~üqñŸ.ÈÜ<¦�€?eÆú¯Ã‹ÿ'TÕÿ#Zç=UWD@D@D@D`s‰Xßüþ¼;_¥ ¯?*[�€ «qÚºZEåÐ/î埖|èê¥MD@D@D@D@ªJÀLú�@‰•2Tò³¡ÓÍÚ³Š\m" " " " " ·“€ùs¿à¿ù¥#ª9Vd¨æw+L´Ë ¢@Ý� ”ñ7m®ø:ªv 2\Þ«¼µ0’À«ü’z¶ˆ€ˆ€ˆ€ˆ€”LÀüÍ_ð3¤# z3ÍÉp3œ ŠrŽT Š€ˆ€ˆ€ˆ€ˆÀ­$`fë‹Î @%ÇŠ •ülètTLܾù�AAmU¥ú-" " " " ·™€ñø-øo~éˆjŽªùÝ ÿ‹1DXÓž?ÚD@D@D@D@D@n'j‡¢ôƒ%”´)ayTe/@U¾ÔB?‹ Ð* hu*" " " " ·Š€BnÕç¼ÖËÈp-|¯ðæ‚<Ì jëÒУE@D@D@D@D@–àßûEÿÍ�öLhÁ²gª|+ Ȱ•ŸåêNö˦_Ü«a«†ˆ€ˆ€ˆ€ˆ€T˜@“~e´YaÄ•éºW™žª£" " " " " " " �`ctºQD@D@D@D@D@D@ªC@€ê|+õTD@D@D@D@D@D@6& ÀÆèt£ˆ€ˆ€ˆ€ˆ€ˆ€ˆ€T‡€ ÕùVꩈ€ˆ€ˆ€ˆ€ˆ€ˆ€lL@€�ÑéF¨ªó­ÔSؘ€ £Ó�" " " " " " "P2Tç[©§" " " " " " "°16Fwó7ÆN\üCÝâ›T‹" " " " " " ÛG@€íû&K{GÅ V«Ýôkèy" " " " " " ¯€€ ¯ú¦�,# òØô‹è>¨ªó­œ¨„¾~£Ñ¬uUD@D@D@D@D@D`2lBíÝ3™LJyr½®0€RÀªQØ"2lÑǸª+4Äq|Uµµ¯7�µïÑ " " " " " " Õ" @…¾Åàû…÷¸ÙlÞ¦Ø.2l×÷¸²7~ €V«å¸®{å³UAD@D@D@D@D@D ºd¨Ø·OÆ…÷¸æyH¨0€ÂÁªAØ"2lÑÇX¥+exð¹-…¬‚_uD@D@D@D@D@D ²d¨Ø§�F¥ô¸Ýé”Ò®Ø2lÇwX¹Ãá+D+×_µb§Ýv<„h¸�¤ø*ö]¹@^ÿm´‰€ˆ€ˆ€ˆ€ˆ€ˆ€ÜN2”ü]!Ø Ÿ® ¥ôzwg·”vÕ¨ˆ€ˆ€ˆ€ˆ€ˆÀ+$ ¿^!üíz´ %�(ŠúE?‚ah·èf�ÎÎŽÂ §ªE@D@D@D@DàÕ(cÁoz&k«Jþfe°x€Ëàîî^ÉDÔ¼ˆ€ˆ€ˆ€ˆ€ˆÀMpKÈõ%ÀM~Áâž%@q,s[†Aáa:£q9«ìï羇 E@D@D@D@D@D@ªM@€’¿_†…Øån·[JÏ�†ÓikIÀRàªQxèé[ôŸê¬è.ª½2ä@)²h2òK1ôºçEvs®­ƒƒƒ¹s�ˆ€ˆ€ˆ€ˆ€ˆ€T—@½^/¼ó (é�4(@ɘƒ ð‘cRôcúX `4ݬi¯ÓéÈ  ²jTD@D@D@D@n–—û®Õj…?´Œœd…wR ^ À$ÅÀP¸¿~àûNYË’ÀÝ»w‹¡E@D@D@D@D@n”@£Ñ,åya”Ò®-—€ åòMZ�ÃçE?ÆŸøÎp8(ºÙi{­V +ìNÏu " " " " "P=ü»¾Œmâîä\F7Õæ€”qêGÎã¢Û�¾ãà `8Ýô´½û÷”â.4}€D@D@D@D@D@J%Ði· oŸñÿ\™L[õÈpß, Â'E?mâ—.rz½^ÑMOÛ«!^èÞ½{Ósˆ€ˆ€ˆ€ˆ€ˆ€T‡ãÿËð E”°:ã ÛS²4J:ö¿p»:�œÂ|¿¼ø›½Ý=g§³S5+" " " " "PþO#@Ñòœݤڻ!Å�†êx•3û…‡ðýGѱ¼u{…çœÃ{ÿÁ§Œ¥Cæ¢�B ìíïÚžm¬¬ÕÈlûÚ—G@€òØN[>íÊ1Œ’e{0DQ4}^Ñu,òƃ7×u‹nZ퉀ˆ€ˆ€ˆ€ˆ€”@€®ÿ­f9+ÐY[5 ÈpßÍÂ!bdŽŠ~Ôh44ÿý~¿èæçÚk#yˆòÌ!щˆ€ˆ€ˆ€ˆ€l-�»wî–2�!àxœLDníË«cK ȰM±‚0üF±-:FüÛ_¾Ó³ÓR½Ø÷ƒýóSô{¨=�âììì8�N§¸3-�GIr¦H‡" À }¬ÉÄÿƒ2Õï%3ÿ\†ã¼[n.öŸ^L ¨MD@D@D@D@D`ûÔ¾ûËy—µ aÐV]2Üзë'_/ãQ½ÔÀ¶ÏÏÏJ_�“y¼ñ†C«¢6�í"pâŸF€26.ý7”z\F¿Õ挀 3¥¿<ÿ(Ü\Æ%8Æi2@æ8??/õ=Ø8S¾ùæ§œý’²Š–þz€ˆ€ˆ€ˆ€ˆ€ÜBwïÞuvKœ¨cöÿ (o òÅO:þ§­H2Ió’¶`- ƒ(úæ%U6¾”MxÞ=¿‘_Jîß»ïð?2ÚD@D@D@D@D@^-�½½=‡‰ÿÊÜú½^nóô (csÃxRF»¯s›2Üà×ü ”<ÙØþòßÈ[1€ÿ‘yðàA)Foä%ô¨8�}$ë.3îŸxÌÊcKÜÿKÒÿNèE2<6e(èeÍ�÷G¿{ÙõM¯…pÃÉæbyÀÞëܦϸì¾ý½}çí·Þv�ÆeÕtMD@D@D@D@D `ôÈ}pÿ~ér½~ϬB–×ý8ŽòН]ŽíFÔÀæp”{rò²û�8ŠOÊxÊ9–Ìn'/OJO˜}^³ÙtÞ‚@+d©èXD@D@D@D@Ê!ÀDŸþÔ§Kwûgï#Lñ_–kŒ×ËØÐ® ƒ• ` —5Eb üß¼¬Î¦×†Ã‘3™ÌãܹsG†€2a«m�[M€ÂŸž¶œõß¹¡Y ôüüÌ$´çy{ß/ei@¿Û÷�òž§²Í Ȱ9»�î„k~øÑootó 7�œ¼œ«ÅP€Ã£Ã+içn*ð„±I÷îÞsh¸wïžB d«¦D@D@D@D@n/N¨ÑÕŸáµþ̵Ų›ÜÂ0¼4ößöe<ÙÃÂöÐ1Oðür’ ÖËê5T¯^—7ï1ÜÑã·�\/n8¡ãN&±SK0~>h;Nñ‹•h;©×6hÎ�çƒÁÐ<ø 9—®]Ôï÷�ñhä´à†o7ºäÐð©7?uãÿѰ} !àÎÁçK”ŒÐ?ö“«ð?*ÚD@D@D@D@D@ãÖßnµ���ßÿªÃi™SŒŠ—m\†|<š÷B¾¬þª×‚(zº¬.Ì ‰^£nË(Ú¬Ž³Çv¿¬­×­<ƒëu{õ5ß—ƒ+ŽiscŽËk™ÞžžþŸ»û×<÷óköb¥ê‡‡‡ÎgÞ}wNì‡Cç î;wïÜ]©�²*áÐüÇŒ Kø[KcÅ?4 øþäÊÿÀ”Õ/µ+" " " " 7A€SØó‡®ýuLHrE­V«µUÞ²]„3§ØUÿŽ¿ÊHpUy×£0zbÊë�¬¥D$OÅÄ$&…‹�ðÍ{Ÿm)“ ˆ/ÁAéÇð)€ÕnÅö†£ñ/íí´ÿæŠÕת6Oœó³sçÎÝ;s÷1yG}Ý–¥úhEiÃS�?Ühˆä`Xè�[HâŸ+gmû�óòå|hñ²>úW –Ý{Y9<Àe•tmm·Î+Z y­úµ)np¼¾º÷¹·þ n-eíºëìîí@ð7æzÇrÏõŒ[ÑÜ…-8áGc¨€6�WC€“rGÇG+Ïêw»å$êŒüáÒÌîh+�Àö›Ÿ |ÙR›ZspŽ'“~~­¬>1çðÅá…æM9ò0$@›ˆ€ˆ€ˆ€ˆ€ˆ€d œ`Â�ný«lý^9½JñÏ�œœÿá*}(»B3n•âV®ûQlb»¿Ö ZÃp|Þý¯×zÖ7Ã\×^¾0±÷W4¡Ë" " " " " ¯ �³ós‡±ÿ«nggg«V]«^Dÿaײ,dý ™ qÇI²wø_»<_gk·×«¿NÛ7\·ú€>†Iì°&ØëV¹ÿôeÿÛ~þÏ5»¶Võ“ãÌö_ŒË1ž4È`-žª," " " " ·‘@0ÀäáÉʯ6@ý²´ÄÄ÷oÕŽ˜€mpv�‰XÛ6Â�ッ&dŒ=ÛòÛ¼¯¾à’¯Ã�h¬=—ÔYåÕ*õ.¯“߯ñËó1^@ûËŸþüÙ¡æИ­“žüe×&" " " " "ðzàª\G^gãDcYÛ ;¼Òàº�R5TYïöªÛ½Õ€<¸ÆÝƒnõpɲn"¼w•Yü¼g¬SvrÚýÈÃ__çžuë2.çéÓgHäqñwÄzp‰@m" " " " " ¯�<‚_¼x޵Î/j…e$¸ºØx<^vùºåç'ç½–6²Âä¬ëx™‰ãçÕ½$Z­^_ÂÒn÷…×Â@·ºwlò)Œ;É&7ÎÝsyøÊ‹ã³ŸFu®"XÚÆ_ÐgOŸ.ýÅæ2/ñ‹¼Î/~i�UÃ" " " " " ¥èõ{Î!<‚×Ñâ”9û�ö= évÃJaéq°|‚Ö_ÕÚyÖÄVÞQEÀt0lL4Çê“X‡&T´åoËÜLfƒ2ÿ¾«JÏÏû�'ÿ¿]Uïº×£óâù‹¥Íœ��Âú÷  MD@D@D@D@Dàö8ïžÃíÿh-ñOCõÄ:ƒu ž� ~eÝ{òê[on×MþMRo€U…?Íw:�eÚpYy^W¶ª¬J€K!/~œ<÷�Å�mÁ¥_$Ïš”çJri#öb~^=<<ùxÁÒühlz½žóìÙrŸáhˆp�'+/ýaÛÕ^D@D@D@D@D`û P¼Ÿœ8'øYw£ø�Ê“,a}ëèôôýY¿j³Ã�¬ð_¬ÞBÁdÝìÿ‹�\<¿T£^¬þêKªd(ŒÖ¢!ÀZ…ì`IöóÊß^³�Hγ®ýëNÛ÷gƒñáh4þ©lYYÇ}˜`™åŽ+n{êt1û¿îv|tìp2±Ì zè—/k¦¾’£|�vY ɵl¸ÅÚµZ3_Ôïïç—/6°åçÕ6,|ºi�÷Ò�†köcÓúC+Ð*ÛüÀš ;Þ[_ð¸N ÿÇ�^|9ŒâVéÓuë ‘ùÿÉãÇNæ»ûSø3/À³çÏþ‡B›ˆ€ˆ€ˆ€ˆ€ˆ@u `it#þ'“ÉÚ/AñÏÄenГG§ÿÃ�ÜZÔTÔ\n˜ïMM­flÿ\7Ñ…VÇÙ½½žÝsÉøeK¾ÿ~Æ!{S…�+lxoγìã,û˜ö›ÙÁ0Ûc°@ïòœ‹ãÙr[ßîiX69h7Ù0èÓãÓý‹c~“殼6ç“O>�ÏhiÝñxäyúØ 9µ¹ÞFOá§Ož8çgËuÂz-^Z{ôüèåÏ\¨‘£±¦ Ø!ô“úY7kÁk{1³,ÓoyÅõFôÙl. ˜5_Ù£[jxæØ�—ý2y™×9(888HLýé`2gø'TiùtÐÙËï=~ñSQ?-¸Ù¥ÍQà3ès$ Ãåî xöü™óK…lâ:´´º " " " " "Pþmßg¸ï“Ç&¤w“Yÿ!&ýè)<ä,ŸVH/çŽÆ_FŸç³.h²å*eÖÖTì3Ê!êàNºFçÙÜo³;ŽÎoáéÖ0pS‹ÎzÔWµÖØ�oƒy IÃÁ3@¸, @ÖÏB×�ÊÖI÷ÙûMû+þãGÑøèèå—@`ý�Ÿ‘W�I=>yø fû/O 2àPà ÀD�2ä‘T™ˆ€ˆ€ˆ€ˆ€Ü,ʧ–ýf’¿CLØm’NjƂ#¸ü?yôØ¡§ð m½g‡'ÿqú,«©P`4Öô ì¬RH�ÖÀÏ¢v³:Ìî³M8Î`šn¾<9k¤^y×®*ÛTÃ^Õn‘×·Öp�—4mÁzÓï'ñIÀ�Óé¤î í¶vp4ËÚÔ q ™ ¾U1V­—¼ùÑYïÃ~ô¯®Ãa“{C¸ú¾8„ÅðÉ•K| í`O4—·È&ï¡{D@D@D@D@Dàu Aøw{]#ü_l8AGãÁùy×yˆÉÀ³›qùŸ~šþpô³ð8H–˜“Lé*k¦,0^Ûö&×¥&Ãj´…× âæQ}ê:L§ýÍgõž]®V«ÅÔˆ6I¼m;»·Iæ³eU?Ήª¨Ð+½‡¾~1éoúqà�?¿Ñíß Ãxw·ëúµVÂ��ýÞ,�L] ¿îÆF‰ëF±À`àq4Í–÷sÝ¿žÁQX7ƒÈEº>¡z€rÖMGmR%ó{H«U|¡Ÿöjvÿð鋯|÷çßþB³Ñø³Ùò›8ÂÝçÑà‘³»·ç ñž …ï:�ŠÍºæ4jÆÚ4 `òûo»ÝN<Óß³%ÕÞWÛ�²çGArºé�°Ÿ„1 fCÅ�]Ém&ƒ€�KÖÒA“ Žh:XØÆÔJ„:Éq"Üy ÝërèÍÌ8!MØ 8hçÊq¾éöíG‡?þ]Ÿûôw!Cåg7mã:÷õñ•ÄýîîŽspç.<'ÚK›c8Àñɱóòô%êï9»;;N‹Ö¶¥w肈€ˆ€ˆ€ˆ€ˆÀ*(@Æø{{4"6p­PÜîýœñ?Cè/�_ņ÷ ŽŽ�ÿ™Ÿk@�–£T¡+æÂ´Ùoj3†Ì´—Ìi9.ÿ>™@B¤Mgüés��fcéÆÉåN§“[xqÕõ‹wlAIÖ-èÕš]øê?ö#AˆºµZ3àø‘×?zÂÔ3j8^cG°øP²Ó2„ÈÄû·`Í¢¬Sø§Þ8wí�Á‡ Û7×QŸ{3>i,°3þKÝòºiÊð 1zq|þ¥·Þ¼÷/P°»´b‰ø›^¯o~Z˜å?¸sàììí:õZþð¡²Û=7?p­qv:;ü6濘%öTM‹€ˆ€ˆ€ˆ€ˆÀí!@m<Â²Ü ·eømžV^õmy¯ñÜÅßp¸|ðUÛ»n=$þû÷G§ÝÙŽJZ¾ÿŸ} ´–ýF£y‰R£þÂU[ÄC¼o2Ù;ÆêoaBïyî-Ñ0\�«Åõó_Ogÿ©!íc¸Ì< µ¦³¿?-ÇYöØV¯Ì>_Ámo÷ › Òî“�ÑíUÉ�ôî»ï¦½çú�û—ì" ÂÇm·w¦ .êÓc˺5\cŒH–!ì9.\Äþ»µÖ¤šI.1Áy ®þŒ9‰ãȈü¤3�[wë1j¢c5¥V´¥z?1¤½rwrÚý¤Q÷~øþ݃ÁÃó}ñ¯l¥˜ ´:r-P?ð¼€»ÿ.~Çiâ—#o ÃÐÄ$1. †&Ðj¶L}îëõ¢|%òž®2Ø~ü›™Ý|ìCÌV��ègù&—ð› D`Ã= ¶�çÛ²EQøÍ‡OžÿÜ\ �B£êXJ��¸ÿso¼µSý6‹ÿ÷œB\�³ÿr¶!½þçúý؉àþ2¤ç\ú“’6€­›ÕáRÉÆsnõSä¨QC.q pÞœêGsCEÿ©š`ó{ø?8&ö"W@LŒË�îeBˆPäˆlÂ_¥bBtØg"ÀÐGqÐtû�÷uÌ{gr$N÷ÿDä×ÚÎZxèˆëÔ0Š(hQ·È0¾×ó£³ßÇlú?¹{°÷÷qšé¯¾š aøÊÈ9Æã)îÛ0xµõºk›1$À÷j^BO�$1âP ‡Qhb÷)ø!ˆ¯ÕŠÓÚ °¬7ÛßÖ 3©½çG'ÿ^Ãæ¥ófÿ§îÿ�WŒÿ·ÁÞÌÉF)Ä{¦÷¥/š”1 äXj,ð<äh $9â!\½ë�—$ýƒûò “»5ÈòUå~~[‘®Õ¯Š–¿+W€Álj Û?3=FÃZÖ“D€ÂhA²I!hêùQ¼SK¼ìÀ¢Ԇ€dCLó·Ò,”~FøgºAWTÄ…«ÂÖóà#žj¦��õe£xP¬3-BØz8çàá â5“ М'ef�áœu­+J2ÈRK•CÃ@O’õ°‘÷%9¸O8ר>~ôüç{½Ñ¿\ÉKb�vUUD@D@D@D@D@Š$0ñƒ¯~ûñÑ¿�i ΗÎtPÞì?ëÚÙ«¹Ìäª bOT¿ÑjS�F÷è5Ÿš�ß~<Áä°}Žõç{Qüg=Ä©Ynãÿ5¥ã0÷œÙì>=­Þ®Š€EʱóÞbQòñ’¸r K=ºø´ìð‡ÀüŒ½$&Ä z ƒ†ž!Ó0€tPqI@ºšÐå„“øÉ`2ñ¢€±\±·´¾9žûgjÏÚh =|úâ+gýþ—Ðdj÷šk\'" " " " " "ðJ LüÉ/~ôÉ‹2q£Êh¦ìì?=¬­Z¢1Àˆ{Ìþ÷xfS›™2Þ�qÿýÔýŸ�áµ¼¼Mü¿76ÀÔÔ‚Œÿçä¯Y €Í�C'ÿÏ’éöÞôèVÜÀôC|GY— {¡Vƒ[GÎf>ºY †dÂ0"⦱%^öö©7Ãì £ HzÌÁYÇ1÷fEÜh,Z¦Ì lkÜÛa� ²—V8~òôä7_žuÿªÒ¡MD@D@D@D@D@¶‚Àhü§>~ö‹â?™HE¡‘¦ZÉ,-ÅÐêTð�­eêSOaöŸ^Ú#[ñŸìéþ�Œÿ©û¿)ƒ¶#„¬û?ãÿé nÝÿy�ZѺÿó܆”Ûs–]µUÉ+{ë «Á|/ù&¹yËqäxgI2À'&B°€uù_ 0^fPõaaJ“¦ƒŒyît& ´»Œ©ˆ¿à@÷~,'ÈÎ-àÕëž¾xù»/^œüV7|ÊÖ´‰€ˆ€ˆ€ˆ€ˆ€ˆÀ«$€˜ÿŸþðáÓ %dô�íK¢Ÿp ”uýÏfþ7Kÿ¥70ößúÌì¿1 0\;�”¥fÀ}]æþOñï`ixëþO�X¯ŸNûÇÉä9÷ÿý}\{ÏvýVì·Þ°„²ýHv� ü8Éf?š�áÀZóÓkýµÚ 7  æû0i2@† .ÃØj2ó˜™þš„p°P™ãÔ €q*nÆÖreÖ°DÛ01,©ÖŸ|^0[Ö�Ô·WVÙ�õ>üè“ç;ðƒßX¥¾êˆ€ˆ€ˆ€ˆ€ˆ€ˆ@ѰÞä¼?üÑ>ÿéÙDgò”©¢øO5'L×ÿdöŸZjÌkÔU6Ù½2³ÿ¶ÏF‡Á@Ïmzp;Hʲîÿ³à8ÓßÇ�ñÇÞ¶C÷«m™Ù¿gþµõ÷sU«rRUÀ"_û1œÅ0ËA×ã�;­1at á¹MaNÓåÊ5R¡ŸÆ õ˜†Ð2…je<;C\Z¼èÎÂṵ̂¿Ô`[`ýôsãÕÿŒ'“Á7?zü#é ÑÕw¨†ˆ€ˆ€ˆ€ˆ€ˆ€C ŽâÇ/ŽOÿî£'Ï~u5ñ½ao´Sêú?UCÆõ?�ýÇÄ+cÿƒtÆŸ3ýü¡Æâì¿ ÍF½FÝÆä&×Î÷ÿ‘Ña‰x-îÑ#<ÿ‹îÿì ½¨)“í={pkö·Å€‚�Ãu–„ØÕìÇ¦ë‡ 0#ß·B– �‚€%  ©3=æ€5^̉áŠz´XM—dQÆ0�qIG6*ZXØì°g1¯çÕY¸%=e{\!àä´÷`RZä×T©ˆ€ˆ€ˆ€ˆ€ˆ€CÀ÷ÃßüðÑ‹¿süòåÇ‹-N5dÎlæ¥ÔJÖ{šÊ\7‰‰ÿ ±PÆðëé mÔZ©û?÷œý§fK²ÿóšÓ³Û&ÿ³¼fö¿ÿlª½¸üŸÍþÏþRø?zô(¹NMIm¹Â¶ZÈú ÝP•JV‡š„Ðb“ ˜®€r8@8øc„·� °V ̬²$`ê�ZšJõ¥Mžø·õ)þ³®ÿÆh@/kh-ÛÙ+þ9ñjgÿ©Ù²³ÿlÓ&ÿ3Æè?–e³ÿ×N&ždÿϺÿÏfÿ¿Æ{¬ŽZܳ¹Jn•0,!»øpþÞ´êâjFðã#ÓÒC#@0˜&¤!€ƒÄ Òt½H»$`bQ¢[I’hÂdfŸ´ Ý1Npp&¡�àÓ|ðY#;j~0ÐùË`Ûœ¾ÀÜ�5po·¬1 {l¯;ÎÄ÷ûßúøé�žœÿ½(ŠÍ®èHD@D@D@D@D@®G ‚ÿùÉÓgãá³ç¿„–¬>3�RߘJ;³Ï–§3ÿÔAœý_ŒûŸKü‡Ùj.«—f{Îþs¢v~öŸ×�g7–þ›NöšðƒDrB8ÍG�È.Q;ÚìÿÆ£|ÅÙÞ[µ­Ê€|Öi-7táàÇ´�¬‹ÇœÀ0I Q«M¢s¿™%/Ä�Ô¢ÜJ8�¦++T6!`º*êÐMÅ’±ž8б]0˜B\Kµ½Ð,ÎßX1û³X‹Ï™ÿ9<>ûý?zú·ÆcÿçQÛ_¼Cç" " " " " "°*�(Ž_¼ìvø›}ò»½áQö¾9=CÙ4ÍöOM”ÿÔO Iÿ¬øç}ÖõŸIØyn'dÍ5Ÿ±ÿHÜ0À,öŸZnnöZÏzSÚ�p›ýßjEz�Ïfÿg æ³ïvŽ+cX�X•fÖ&œÃU#ÇbcÃèêa²^XÂXŽh=ÂÏ|.SqE€V0À,?¬O 9Ðf¡f ›�ÉD©Àu#Žùåž³%³ÞXö‡ûËZÖ°üxMÆßzøèß<~òô¯NüàѦ’^VWE@D@D@D@D@2â8: G?ùÑÃ'í鳣߶—.h—D�è™ø‡öÉÿFQÜCÓ%âqÿ@¼Ó1-M&7qñh8a=ˆ£0ÄØI¬AQT‹1šŒ ޏ"@3>@âd…@P€�ï £nñêŽ7 ÚQ­>òù80ayõ¨ž_¯GµQäÅí3ý 'Ž'¨R�‚8F}7ŽÃc+à· ¨áÐ�“E0~±§¸<Ày’31˜—0ÅüÝØ|;ë�ÏúŸüøý»û_~pïî_nÔë­]«ÍÍ{£;E@D@D@D@D@¶�Qw<ñÿËãçGÿyŒ@ö—Zh®ßýÜPfÕëYá�Ë&á®çÏüSü'qÿ̵Ö‚xÈIZ�†kóØç¤nêú�ëÌüï×êQYÐ9û_Ã3íïòÙÿšÃÙ zŽÐj¥ åsf“͋ݎn©€ ¾h†]9Þ}÷]óqùÉÂðЭ�Þ�£=¸ðcc.€O·!þ‡­8nEPùHñ8žLq#ö#Üà8�ôxä2¹Ä–˜»;TÉÀôêÂëoƒŒŽßœ<¯‰kÏR‘]G{1lõ°Ã0€K!–ʬ¹ðêGð¬EC;Ic7|­ ¿hæòúF�“ÓîCüüÈ›î}ß݃½¿ÃÄÐZaž£D@D@D@D@D@^{ÐÓ£áW^�þ*—'h†—Œè產™Ð´W)üdù‡ÞIf@!gLY‰•äK£±·A%^ÔFü�p­™ˆÿ®CðCÌ›ÙÖàúßòúqÏ7IÝ#üÈó‡Q�� Ì­1ö?ñð6«¾58û?Šë�†‰ý7Éÿ z²³ÿœ8ÎlÔ‰üáf÷ÉÙ-ø·R‚�.p™g¾? Ëæ÷4Ü|ñr/Þí!æ„V¡y/XŽv¼¸D±Ë„€áƒ®1ô~b;ÁÀ×w0œ±‘çÖ0¸1(GQäµÝÀ™4jq}„Á€F€8ö]¨~ŽlgŒ[’ÉÿŒ7£bÐn®!€ÏÀ@7Ïâo”5°<ýz@²ñÀ<:9ý~¾´·»{ïÁÝý?ßi·þ"Ú{ƒÍkx½PsÁ[úwº½Á/<9<þ½9­a?‘Pƒ,Š~[N¥­bòŸá#üYÚË”š{¯ÿ¨Ãg›†Z{0 d\ÿ1ïŸ&aÇîÔl¾?�ý¯Ó3»ÑˆjA²ò½Lìÿ1úÒ:5ú/;ûÿÿøŠäô9Uqd¤²Š]¿ªÏLÜÐ5Æ‚e^ÎÝÄ@/€½ð<ê8o:N3mw2qÎæ³7~‡Ä6ÀED@¿îÅ;È81{$ „9F—Çã ízÁàY#ÃLî½Ô@o,ð€íÁ­»ð`bK¬�™ˆú:BìxBmëÀ®ñ—%ÝL¨€=á>kÈ–/ç|õ^¿ÿ??O€_xû�¦½Óús5Ïûø…Ë©½Ø ÎE@D@D@D@D@*LÀ÷Ãð�†“ß899ÿ­Á`xÆwáªhÙwšsíÏ^0ÅŠ~ê¨þŒðç gý)æ� �ÂêeéÌ?¯ÛÙÿLÜ9Ù˜›Íc’öŒëÿ·™Ì�IÝkÿ¼VCæÿáð0:œ`rÖÎþ×.Æþg_Ãä“›/¸ug·EÜq`M=Û¿‡ó/š8ŽP˜› À à÷z ²Gše³¡¾ßˆLœŸ† š̉'@àDÈ �5Àç +`GˆÃán �¸žä°FŠxTaS?zðð9 ÷±¦e·ùeÃ?H’¦•§BŸ†„¹ íÎ�gNŒ�=¿p£½€gÃvñɓï¢ä«;�öÞý{?Ðî4þ _Ä/,c´‰€ˆ€ˆ€ˆ€ˆ€T�@Å�ƒ üz2ùÝã³ß�ó^œ®Þvª;ÑO�b&3Y BÇÎøó$þ^’€Âj†åffÉÓ§1ÿ©Û¿)GÂ?äS‡NK’þ™þ(îS»¥YÿkZpÿï ëÿ2×zzÛÙºþ'™ÿ/Äþ/Îþ[Mµ¸_€PÍSd¦³ïU�À HÄó|—mÙüþ‹È0¹ß=»Èà"i…Ë„€¾ïãxß ïn0{�ï{ïìåù͉‡5-“ ´ÿßh„°„^PÛ©�™E¡·»S÷ÂaÍ‹�0Ž�…‚åAAÒ@·�pž#1 »pp™`€]fH@Œ, �Ѐ$~�š~Ó#ÀP�9æ^ös +éÔüâ=s l~ÒiÔÛÞ¸ó'[ÍÖ÷7êµï¯×½ïAkI†ÅÍ›Õ�" " " " " %€‰±ü× Ä#jxõ‚TìC© ‘Ÿí“Öº Ãk³II;㟓'ÀÎú{AÜàŠk¸‡âß31ÿXŽ=ÿœåwóoÜþI¶;óï#îß&ýs½ù%ÿèúø÷à‚]Ë$þ³�Iâ?hAèÑõÿâfËìþb� —¼ÊÎX\~àð±lB@ûÝÂð4vN±$À]'b>® ð=ìCðE ãzKbShC¸wä@&ÉT¾µ¡ÄƒÀÔk#¯EŒ�ár– à€ÆÊ“TðîÔ c\O õñË3õàÃŒy9X5LRjðP›ˆ€ˆ€ˆ€ˆ€ˆ€Hkš¥â~*öÙzZf¡^’&¢?¹œþ¨kbý›nlgý]L±ñ/ãòÏöÒ¥þL¶Îâ#á�‹â2�vª�¹œfcÜ?ôê×0‘K-ǬÿY×ÿF£çºþ›N¿>³ÿæ»@”&ߪ‚ÿæ„Ø·˜Ú¡Ò‚ä-»Ü`—\Õ€t®]€‚œ+`¡ #ô¹B@8z“Æ.Ò÷Á ]%Àz˜s#âV€=ïçŠÈ�x Çsƾ·ôžä$ñ X4$×®þ—ϼº–jˆ€ˆ€ˆ€ˆ€ˆ€T™€ç¼ÀLäóæ40¢Ý6uQôóšï48³�zý¬Ëc›ånÖßŬ0`ÜtÜ3Ÿnÿ÷ÌôÑ�%Úâ™ø÷éêoê.ŠÞ_‡8cÜÞ’WˆvѾ“ݳlºA7å–O+Tä`æW^‘/v“ƒé2Ë•¹Ž„Nל#ñC|I>‡Iù<,µÛŽ×˜ìDNH è`e€1ò^Žü–ËÕ úc„ ÃË�ã€iKU¸òìˆû]ø½À€�áªè6WàÞ<#“#€ 2XNc@ØÄ@ 3ÖO-hIÈ@hîç/ Û™ÛÒ¶çÊNø‹¸P¤S�ב@®6HÒ�Ý04QÔÆ 9È{§éÁeE?ü�!âùy�‡Ù~¢ä13þ3Ã?.;!ý5!xZ˜õïC¼3ÞßÃ=ôðG¶ÿlÌ¿uû÷¼�qñÏŠ.ùWk�ÌÍüÓõŸÚoöIMÜÿìô5ÿæ;@hf_¼’ÇLFçt>[n�±¿èÀ{Çã±Ëå!ø½ ¸ë†w €¤€Ì °€e\®À=ó´1ëϼôHÊè �ä ðß3ÇXà @C@cÊŒ ÁIýÔ(`öuÓW"À¾Ùz>®ÓCÀn¦Üž`�Åí{N t™Ë:�|™éa+ðmE øäYѱ™Ä€8œ•Ï\üéÀrûÃDêFøã> ü3ýM3ëO#�IöÇYÿÄ域³þ¾_‹F;8žLL¬6æŸâÿ9&jÅ?ãþÛívd“þ­÷Ï×ÊÇÐ\¹å¼¡jhÕúœÛ_ ®™ð�¯aËížWqœoÈ&\4ìCìc0%9|dŸèä0Úàë2t‘‰uFìS¨C@çXŽÐâ44ÀÀ²~²OVà1· F�¤xjH ¶Ðîg[¢½ˆ€ˆ€ˆ€ˆ€ˆ€$QŸ¥a3ÿS¼s£˜OŽ’­k¿½F÷~{löinL]ÿ[ñ¢ð·±þlÛÎúÓåß›Lb#þM(À$¢»?…¾;DÂ?,õ7š¬ÿÝŒÛ?gþÅ?û!×R˜m¯ƒ€okÅ¿Ý'e\€ùwcöÿÝwßÅJ‰€]`Ñ@O€=xt:o"B tÖo’†ðˆ Ú[aÃc^ xë þÅðæ €×¬GC&(³9Lp}j€AÀóZrœúl¹¹ÿd ¶L{X‡@VàÛûfF€d†Ÿå¶Ì&ú3穱€F�1þMÌø3ÁŸ�ñgŠÿ$Ö~֟׌˿7FøVHÅ¿]ê�â¿Ç%sÄ6ã?û&ñO óÅå|I…Ï0X²?û&Ùr{œì×0d¾³»‹p”–1Dh€qÀ¸ÿ70HB¬7žëŒ!€"?5pÙ@v–^,[4ðÚTì§áse<ÉlY#A¦X‡" " " " " "p�™¸ÏV¤0·çæxÑ3€¢>õ è§À§›?ïaŒ?ïIDÿEáÏk6Ñ_ÖåŸ"߆\ʯ‰YÿAœ]ê� ÿìÌÿŠâŸÝ±ïa÷Ù2O7èªl�iy•n•€ƒÇ üÅï’-·Çéþb8À2O˜ ð}�F€0Ü¡‹¿Y&�âŸFë G-׿È3°ƒÆ+ 5X¯€¤<6aL:Hƒ@R–äà1n´ï03˜ é?©×ÿÔp�½¦cÈ Ÿž.DÌ]ËÔË ~gâ8ü¶ît¶ßÌô'¢ŸíOãüáæ¿èîï.ÌúÏ’ýÕ�`¹øgÌ?Û¶qÿKfþYž£ÝgËx<Ýn£øçËeÒ÷åÓdÿ¼fE~rÜŠ�mbŸB¿áDØûqÍ8ÞN;îNj¦ÜP‡.üñ<§AÀÅ'Âú±‡ ?Üø™áâ×âq­ÕÜ®%®ývÖŸ³úsñþÐe¼Ï;Ãê­NäÔ+þ÷÷÷�`Nü›—£Ö3Ûâž…¶,©‘þ Í•[>W©¢'·Î€ßì2qk¯Ù½¹Åq~y».ó°Àæàñ¼7À}ä zÑ�ÄÀ®°Ì`�®`ó�ŠðÑÈa²@Çáò�Iîzð¹VìsoŽQ¶ßlN�¬;WR{k¨³ui(¸Î–:\§ Ý+" " " " "p¬¸Ö£ Ú¹�}¢&éyzå^›b?9g]S§Éq²<9�§¢³ýPêæ:üñÚ9ÕGi’? X×ÔǾ1i"éßlÖß\ƒð¯·ZQâòßÀÜêüRì÷ñÿ5Šÿ÷x)+èí±ÝóúÜ=µôÚ\Å žÜJ¿UVà/~{ÍîÍ-S#Ï2‰yºh�Â’Ë.  7sذ€¬!€í14`ÑÀº¼–xØãd¦~Î(ÀJ÷Vè·pÚțч±ÀnÖ“Àžk/" " " " "ðz �Ÿ#�Šúl™±>N (ÜéêÏÍ›ýLð³ÜŠ~OgÿD¿üfÆ^ø{ˆõ_œõ73þœõŸÆû¯*þ¿fßÍîÙ%{l÷,›Ûn³øç‹ÞZ€y¹åF€á?ýæ(ÿÁÄ€E—� ¸;g˜÷€ Úaöÿ\CW €ßŠËd�|Ô¢1€žLÈk‰ÐO<fç{uAèóÜú'ð  G2]ûYÄvéÞÏcºøÛÙ~smÁÕ&üwÿ>„—u8ã¿dÖŸídÅÿÇ㯳p*þ/uûgÍÄHÀ£ÌvÛgþí«Þj_ð2Aœ½–s<[€FŠ$4õ¬! 1¤Þaà:'�MÚŒË ìÛbxˬ1€ÇYƒÏ³â�9X65ð„<7k0X,×¹ˆ€ˆ€ˆ€ˆ€ˆ€˜™ûE ÷v³"ŸçVè󨲂ߔ§Ùü“ãy7–­"ü�c®P�ëõ‹³þlƒñþ-ÌúOÅÿ×XzéÌ?+äŠsb‹ûÛ¾Ýz?`ÙF>cÎ�É Àkó†€$4€åY¯S³÷vßæ A€×âfö‡ŽÓáqê)Àrz po·¬‘À–i/" " " " " «°Â>[ߊ|–1$ÒĈwœÌö{ˆé7×LX@’ Àä°çi|?ëXWÿ쌿)Ÿ›õŸ ^³³þ<–ø'…ͶÿH†Ë% ŒuIEND®B`‚ic11¼‰PNG  IHDR szzôsRGB®ÎéDeXIfMM*‡i     ¬†bóIDATX µWÛOœÇÿ}{e/`X®ËÝc¨ˆkLMj5އ·H¦IãÊ4Jþ‚:êCûPõѱ\©’_ª´R_šŠ¤¾Tí u['•ÕšH@äÚÆ§\ Ëk³Ë^Ø]v¿¯¿3ßî²k%­”A³3ß™™s~snsІ††ð¿¶S§_ ºQ_|N³hk»ñqC1íËÌm{m·g€ŸZ€W²@¯¸òûœßþÎ Ÿ�ÅÂlº¡ãÚ•kõGÆÇ�&E14`Û ÜÕ�?SÐÏ® í-«éž(ô×>¯÷µçÏžu¹ÝnXs‚äD8™ÄŸ865´ �N+&‡C�§Î�Cwm­šËOV×]‰XìØ§cc½›ñx+Ios“=ñÈ+hŽím Á;g )×tz .—Ng™¢æ5á …°/CÝå›Õ çu®ßݹ3Âé—@•k˼yúÆ d"©Tž%¶d6Ø]בÞÉiÀnjàáøxaŸL4§¶}ûàèè€ð,YÌ}ì©îüíäÌÌw.sz<°•™7•3ÙÓŒ‰í8? Á0 ÔV×)vÙ'�­¨È±24W*Gàþý$y^),Mö`F‘H೉‰—‰üðÓNˆÓ'‡±¶Tì6S³·n!VÄ\œ�ýÏÿ…<R´T˜î ç­?*ìzjòa#´Žµu€“ª–6=4¤M?µ÷‹>÷ðE‡d=XÁúÚºÒ€ÍÊûýŸíuuužd:ñsò;ÅÞùy|�"â ôT4[E¥—Ï´ 4cŠ{Þß Ç~ñôê3’©í÷këê^mlðÃÂ0Ò‚Ù¬Ž Ã+£gùm†ÙÖVÔNŽ‘-èéî�Õf…ÕºËÖ0t[*™XX\РеbïƒØÝIªÏç«�¦¿z|`ss099�õõuØÍrųß{�a¸KËÏÿpý�\ÝU‚ËåBc“‡BCñîÝ»oqÃçÈ Ù_SY‡òr/‚Áuøý~ŒŒ|_¥\¥jž^z´Lfü󳤽þÆØßÖV ÅbQ\¿~ëè{¾š¦!ϲÕÕÕd~S‰Œ¬ÖßÜܛͦnxîÜ‘wª¤#Z�$³T*�Š¢xf[4C³c___ž·åÌÄ'·QÆ\âõzÑH¼Ÿ ·ò›Ì×$÷¥ÁrìàÁN$™@²FÏ=×Mm”ËA5Ê|qé‘Úí«ò¡¸ q‰kv»½¤wuu©ït:…Z¾†–}!'N % Çzº{!1ÞèoDUUó½S¡—QúƒÙu°ŠŠ»çæþ­ÖŠ›xÞ‰­Èš›š%³x½ÀSëñS€¿’BS©$ºu=#\ÌÍ=DŒé5CÕîdôB�Æâ˜ÿÏ|á‰Î ±2’ZZZfÈ6Ÿ„ÁüšŒÐÒú7Úê‹F“Ž*ô,Mù„¨v~~^…åÒÒ ¼å¦&3:þâÒ2ÃqS�{Ëc%öçmoß�(Rêg™³¶Â°tÒg–àÚ@KK+«õnù|Õd�!#�Þk!( ÷gfà¦:—X @£è�A.>Ï�–—ñµÞ^%\�úú,,Î#A͉YW¶WOPx) 9ÖÂ;éTû|ê6n·GÂG]TZ­üÕ|åM’¢Ë�x²¶ª´%Ζo’GÄŸ¤ ˜†z?VWâ¿ZÁ(㈠W¡QˆØ[²ß‡~€æf:7 Ÿ9�·.¯.Oiw{=ÊY_:yR™°³³cccŠ�8«TMt4Ó¨Óã"\šà¨pì§Ýª‰‘N•F5È�$Ã�>ý2&&ncttRžm†#¼ÁŸâPI_XXb}°©BöÂ… ’õ0<<\ˆ ?µ FB“¤øîššU8(VÇ+++OÄQÆ›×SMÅÍíváÒ¥‹¸zõ ÒÌÓSÓx´ÀZè1‚! »‡OnOàÔÉ1;;‹óç¨n.Ž+š§lkmãõtšÐ"9Å’HGU>P4Xûkkk¨ò4mhÑË/Ì�ý:þù�[ýÁ›øìÁ –1wÿZ›ññG7qùòe:Y¥2]&³£FÑ¢˜¯¹¹•á�¢)œôVïº&Žhš€Ee#UÃÚv¦áPh£ðÒÉ&1‡i¹�ï¼s7oþG�àWïý×®]A{{»ÊÛ,dÍžà˜`M›@œÞ/Õ“Óa‡‹Ú�‡‰•¶ÊâËÚkß޼ýö[åV›Ñh ª7‹¼_@ä[>"ˆ‰'%sáÀ!?—=jž£ÉV/�´§§5|p#ÌŠ/½ùýW«$ÙyÀ+öÊêµ±ëÐAŠ)–àiª­t$�t±­x¶¨Ô©FsnÒ¤å×IÏíš”ðê”)ÿìPßf6Ô?ºùwë ƒƒIo…—T«f³Ú4z«ÆØ¡ú%†™zYŒ±Cœ´3—¨Æ7,kCìÌ*>mD°EºaH1âFß‘‚&“5›OìÁ`°œrEuv¥ÚWgyeý×½;e++Ë ¥R£¨2—Ío3# M¾%;îÎMšI7çâ7âòºJ— TWW ®¼�)–�õÐ$ŸÜãû~:ÈA:ˆ/'\F%h÷›€ÚÆ@â(ª•+)O�ýü)>Ãy3JbÛatƒs¬´6¦¸Ù"HNMN¿K%üøñã'�|:+…ÍWÕ’ÉT8?œšüô"eÈ?, ¸ügáa¯bP_eË�ù&{œ=ý_‰‘PÏ ÛûIEND®B`‚info>bplist00Ô X$versionY$archiverT$topX$objects† _NSKeyedArchiverÑ Troot€§ U$nullÓ WNS.keysZNS.objectsV$class¢€€¢€€€Tname_assetcatalog-referenceTiconÓ   €Ò !"Z$classnameX$classes\NSDictionary¢!#XNSObject$)27ILQS[ahp{‚…‡‰ŒŽ�’—°µ¼½¾ÀÅÐÙæé$òtremotesf-2.10.1/data/icons/status/000077500000000000000000000000001523411710200171465ustar00rootroot00000000000000tremotesf-2.10.1/data/icons/status/active.svg000066400000000000000000000020741523411710200211450ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/checking.svg000066400000000000000000000032201523411710200214370ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/downloading.svg000066400000000000000000000012671523411710200222020ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/errored.svg000066400000000000000000000012451523411710200213330ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/paused.svg000066400000000000000000000012501523411710200211460ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/priority-high.svg000066400000000000000000000003311523411710200224620ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/priority-low.svg000066400000000000000000000003311523411710200223440ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/priority-normal.svg000066400000000000000000000003311523411710200230330ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/queued.svg000066400000000000000000000014231523411710200211570ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/seeding.svg000066400000000000000000000012671523411710200213130ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/stalled-downloading.svg000066400000000000000000000012571523411710200236270ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/stalled-seeding.svg000066400000000000000000000012571523411710200227400ustar00rootroot00000000000000 tremotesf-2.10.1/data/icons/status/status.qrc000066400000000000000000000011121523411710200211730ustar00rootroot00000000000000 active.svg checking.svg downloading.svg errored.svg paused.svg priority-high.svg priority-low.svg priority-normal.svg queued.svg seeding.svg stalled-downloading.svg stalled-seeding.svg tremotesf-2.10.1/data/org.equeim.Tremotesf.appdata.xml.in000066400000000000000000000402631523411710200232770ustar00rootroot00000000000000 org.equeim.Tremotesf CC0-1.0 GPL-3.0+ Tremotesf Remote GUI for Transmission BitTorrent client

Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully control your transmission-daemon instance, manage torrents, set Transmission settings, view server statistics.

You can add several servers profiles and switch between them (only one can be connected at a time). Tremotesf has support of HTTPS, including self-signed certificates and client certificate authentication. You can set mounted directories for servers and quickly open torrents' files and choose download directory from file dialog.

org.equeim.Tremotesf.desktop tremotesf https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-1.png https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-2.png https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-3.png https://github.com/equeim/tremotesf-screenshots/raw/master/desktop-4.png https://github.com/equeim/tremotesf2 https://github.com/equeim/tremotesf2/issues https://www.transifex.com/equeim/tremotesf Alexey Rochev

Fixed

  • Crash on startup on Windows
  • Missing icon for clearing search field on Windows and macOS

Changed

  • Flatpak KDE SDK updated to 6.11

Added

  • Status bar displays free space in the download directory (thanks @keizie)
  • Priority column in torrents list now shows colored icon (thanks @keizie)
  • Support for mapping remote directories to network share URLs like smb:// (thanks @Manriel). May not work correctly in Flatpak.

Changed

  • Improved performance and memory usage when dealing with torrents with large amounts of files
  • Minimum CMake version is 3.31

Fixed

  • Label/directory/tracker filter lists in the sidebar now use natural sorting
  • Jumping cursor position when editing paths
  • Issues with activation of “add torrent” dialogs on Wayland (again)
  • “Add torrent” and “Set location” dialogs being too wide when there are download directories with very long paths
  • Peers list not being updated correctly

Fixed

  • Changed maximum values of idle limits and speed limits settings to align them with Transmission limitations

Changed

  • Increased default width of some dialogs (thanks @glutenmancy)
  • Updated KDE Flatpak runtime to 6.10 branch
  • Updated translations

Added

  • The first column in torrents, trackers, and peers lists can now be reordered too (thanks @keizie)
  • Torrents can be renamed using F2 shortcut (thanks @keizie)
  • Torrent's file list show appropriate file icons based on file extensions

Fixed

  • Fixed opening torrent's directory when torrent has a single file in a directory (thanks @keizie)
  • Fixed window activation when opening torrent's file or its download directory on Linux/Wayland
  • Relocating the torrent does not save the directory for use in "Add torrent" dialog (thanks @keizie)
  • Improved support of servers that use HTTPS certificate chain with custom root CA

Changed

  • If your server uses HTTPS certificate chain with custom root CA (instead of single self-signed certificate), then you need to specify the root certificate in the connection settings, and optionally the leaf certificate if it doesn't have correct host name

Fixed

  • Crash when failing to parse server response as JSON

Fixed

  • Not working file dialogs when installed through Flatpak

Added

  • Option to show torrent properties in a panel in the main window instead of dialog
  • Ability to set labels on torrents and filter torrent list by labels
  • Option to display relative time
  • Option to display only names of download directories in sidebar and torrents list

Changed

  • Options dialog is rearranged to use multiple tabs
  • Message that's shown when trying to add torrent while disconnected from server is now displayed in a dialog instead of main window

Fixed

  • Delayed loading of peers for active torrents
  • Window activation from clicking on notification

Fixed

  • Failures to add torrents when "Delete .torrent file" option is enabled

Fixed

  • Tray icon disappearing in some X11 environments
  • Wrong translation being loaded on Windows

Fixed

  • Black screen issues when closing fullscreen window on macOS
  • File dialog being shown twice in some Linux environments
  • Crash with GCC 12
  • Torrent's details in list not being updated for most recently added torrent

Fixed

  • Opening torrent's download directory with some file managers

Fixed

  • Performance issue with torrents/files lists
  • Issues with mounted directories mapping

Added

  • Merging trackers when adding existing torrent
  • Add Torrent Link dialogs allows multiple links
  • "None" proxy option to bypass system proxy

Changed

  • Breeze is used as a fallback icon theme
  • Notification portal is used for notifications in Flatpak
  • Added workaround for Transmission not showing an error for torrent when all trackers have failed

Fixed

  • Mapping of mounted directories working incorrectly in some cases

Fixed

  • Qt 6.7 compatibility

Fixed

  • Application being closed when opening file picker in Qt 6 builds

Changed

  • Qt 6 is now used by default

Fixed

  • Sorting of directories and trackers in side panel
  • Menu items that should disabled on first start not being disabled
  • Selecting of current server via status bar context menu being broken in some cases
  • Debug logs being printed when they are disabled

Added

  • Option to open torrent's file or download directory on double click
  • Option to not activate main window when adding torrents (except on macOS where application is always activated)
  • Option to not show "Add Torrent" dialog when adding torrents
  • Right click on status bar opens menu to quickly connect to different server
  • Support of xdg-activation protocol on Wayland

Changed

  • "Open" and "Show in file manager" actions now show error dialog if file/directory does not exist, instead if being inaccessible
  • "Show in file manager" actions has been renamed to "Open download directory"
  • Progress bar's text is now drawn according to Qt style (though Breeze style still draws text next to the progress bar, not inside of it)

Fixed

  • Initial state of "Lock toolbar" menu action
  • Progress bar being drawn in incorrect column in torrent's files list

Added

  • Option to move torrent file to trash instead of deleting it (enabled by default)

Changed

  • Progress bar columns in torrent/file lists are now displayed with percent text
  • Default columns and sort order in torrents list are changed. Default sorting is now by added date, from new to old
  • When search field is focused via shortcut its contents are now selected
  • When Tremotesf is launched for the first time it now doesn't place itself in the middle of the screen, letting OS decide

Fixed

  • "Open" action on torrent's root file directory
  • Unnecessary RPC requests when torrent's limits are edited

Added

  • "Add torrent" dialog now has checkbox to remove torrent file when torrent is added

Changed

  • "Remember last download directory" is replaced with "Remember parameters of last added torrent", which also remembers priority, started/paused state and "Delete .torrent file" checkbox of last added torrent
  • "Remember last torrent open directory" setting is renamed to "Remember location of last opened torrent file"
  • When "Remember last torrent open directory" is unchecked user's home directory is always used
  • When authentication is enabled, `Authorization` header will be sent in advance instead of waiting for 401 response from server (thanks @otaconix)

Fixed

  • Errors when opening certain torrent files
  • Incorrect error message being displayed when there is no configured servers

Fixed

  • Crash on launch with some Qt styles
  • Mounted directories feature not working on Windows with UNC paths
  • Incorrect error message when adding torrent that already exists with some Transmission versions

Added

  • Torrent priority selection in torrent's context menu

Changed

  • Torrent status icons are redrawn to be more contrasting

Fixed

  • Torrent status icons being pixelated on high DPI displays
  • Torrent priority column being empty
  • Zero number of leechers
  • No icon in task switcher in KDE Plasma Wayland

Added

  • Trackers list shows number of seeders and leechers for trackers, not just total number peers

Changed

  • "Seeders" and "leechers" now refer to total number of seeders and leechers reported by trackers, while number of peers that we are currently downloading from / uploading to is displayed separately
  • Tracker's error is displayed in separate column
  • Torrents that have an error but are still being downloaded/uploaded are displayed under both "Status" filters simultaneously

Fixed

  • Fixed issues with lists or torrents/trackers not being updated sometimes
  • Main window placeholder when there is no torrents in list is now displayed correctly
tremotesf-2.10.1/data/org.equeim.Tremotesf.desktop.in000066400000000000000000000006311523411710200225320ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 [Desktop Entry] Type=Application Name=Tremotesf Comment=Remote GUI for Transmission BitTorrent client Categories=Network;P2P; Icon=org.equeim.Tremotesf Exec=tremotesf %U StartupNotify=true StartupWMClass=tremotesf X-GNOME-SingleWindow=true SingleMainWindow=true MimeType=application/x-bittorrent;x-scheme-handler/magnet; tremotesf-2.10.1/data/po/000077500000000000000000000000001523411710200151265ustar00rootroot00000000000000tremotesf-2.10.1/data/po/LINGUAS000066400000000000000000000000631523411710200161520ustar00rootroot00000000000000de en es_ES fr hu_HU it_IT nl nl_BE pl ru tr zh_CN tremotesf-2.10.1/data/po/LINGUAS.license000066400000000000000000000001221523411710200175670ustar00rootroot00000000000000SPDX-FileCopyrightText: 2015-2025 Alexey Rochev SPDX-License-Identifier: CC0-1.0 tremotesf-2.10.1/data/po/de.po000066400000000000000000000044731523411710200160660ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Steve, 2024 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Steve, 2024\n" "Language-Team: German (https://app.transifex.com/equeim/teams/72280/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Remote-GUI für den Transmission BitTorrent-Client" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf ist eine Remote-GUI für den Transmission BitTorrent-Client. Sie " "können Ihre Transmission-Daemon-Instanz vollständig steuern, Torrents " "verwalten, Übertragungseinstellungen festlegen und Serverstatistiken " "anzeigen." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Sie können mehrere Serverprofile hinzufügen und zwischen ihnen wechseln (es " "kann immer nur ein Server verbunden sein). Tremotesf unterstützt HTTPS, " "einschließlich selbstsignierter Zertifikate und Client-" "Zertifikatauthentifizierung. Sie können bereitgestellte Verzeichnisse für " "Server festlegen, Torrent-Dateien schnell öffnen und im Dateidialog ein " "Download-Verzeichnis auswählen." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/en.po000066400000000000000000000044331523411710200160740ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Alexey Rochev , 2018 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Alexey Rochev , 2018\n" "Language-Team: English (United States) (https://app.transifex.com/equeim/teams/72280/en_US/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en_US\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Remote GUI for Transmission BitTorrent client" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/es_ES.po000066400000000000000000000047451523411710200164760ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Carmen Fernández B., 2023 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Carmen Fernández B., 2023\n" "Language-Team: Spanish (Spain) (https://app.transifex.com/equeim/teams/72280/es_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: es_ES\n" "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "" "Interfaz gráfica de usuario remota para el cliente de Transmission " "BitTorrent" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf es una interfaz gráfica de usuario remota para el cliente de " "Transmission BitTorrent. Puede controlar completamente tu instancia de " "transmission-daemon, administrar torrents, establecer la configuración de " "Transmission, ver las estadísticas del servidor." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Puedes añadir varios perfiles de servidores y cambiar entre ellos (sólo es " "posible conectarse a uno a la vez). Tremotesf es compatible con HTTPS, " "incluidos los certificados autofirmados y la autenticación de certificados " "de clientes. Puedes configurar directorios montados para servidores y abrir " "rápidamente archivos torrents y seleccionar el directorio de descarga desde " "el cuadro de diálogo." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/fr.po000066400000000000000000000050361523411710200161010ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Nicolas Roudninski, 2019 # Jonathan Blanc, 2022 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Jonathan Blanc, 2022\n" "Language-Team: French (https://app.transifex.com/equeim/teams/72280/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fr\n" "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Interface graphique distante pour le client BitTorrent Transmission" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf est une interface graphique distante pour le client BitTorrent " "Transmission. Vous pouvez contrôler pleinement votre instance de " "transmission-daemon, gérer les torrents, définir les paramètres de " "Transmission, voir les statistiques du serveur." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Vous pouvez ajouter plusieurs profils de serveur et basculer entre eux (un " "seul peut être connecté à la fois). Tremotesf prend en charge le protocole " "HTTPS, notamment les certificats auto-signés et l'authentification par " "certificat client. Pour chaque serveur, vous pouvez définir des répertoires " "montés afin d'ouvrir rapidement les fichiers des torrents et choisir le " "répertoire de téléchargement à partir de la boite de dialogue lors de " "l'ajout du torrent." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/hu_HU.po000066400000000000000000000046501523411710200165030ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # György Viktor , 2025 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: György Viktor , 2025\n" "Language-Team: Hungarian (Hungary) (https://app.transifex.com/equeim/teams/72280/hu_HU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: hu_HU\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Távoli GUI a Transmission BitTorrent klienshez" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "A Tremotesf egy távoli grafikus felhasználói felület a Transmission " "BitTorrent klienshez. Teljes mértékben vezérelheti a transmission démon " "példányát, kezelheti a torrenteket, beállíthatja a Transmission-t, " "megtekintheti a szerver statisztikáit." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Több szerverprofilt is hozzáadhat, és válthat közöttük (egyszerre csak egy " "csatlakozhat). A Tremotesf támogatja a HTTPS-t, beleértve az önaláírt " "tanúsítványokat és az ügyféltanúsítvány-hitelesítést. Beállíthat csatolt " "könyvtárakat a szerverekhez, és gyorsan megnyithatja a torrent fájlokat, és " "kiválaszthatja a letöltési könyvtárat a fájl párbeszédablakban." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/it_IT.po000066400000000000000000000044731523411710200165060ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Ioma Taani, 2019 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Ioma Taani, 2019\n" "Language-Team: Italian (Italy) (https://app.transifex.com/equeim/teams/72280/it_IT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: it_IT\n" "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Interfaccia remota per il client BitTorrent Transmission" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf è un'interfaccia remota per il client BitTorrent Transmission. " "Puoi gestire la tua istanza di transmission-daemon, gestire i torrent, " "gestire le impostazioni e vedere le statistiche del server." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Puoi aggiungere diversi server e passare da uno all'altro (può esserci solo " "una connessione alla volta). Tremotesf supporta il protocollo HTTPS, inclusi" " i certificati firmati autonomamente e l'autenticazione da certificato " "client. Puoi impostare le cartelle montate per server ed aprire velocemente " "i torrent e scegliere la cartella di download." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/messages.po000066400000000000000000000030061523411710200172740ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission " "settings, view server statistics." msgstr "" #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "" tremotesf-2.10.1/data/po/nl.po000066400000000000000000000045011523411710200160770ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Nathan Follens, 2018 # Alexey Rochev , 2018 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Alexey Rochev , 2018\n" "Language-Team: Dutch (https://app.transifex.com/equeim/teams/72280/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Externe GUI voor Transmission" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf is een externe GUI voor Transmission. je kan je instantie van " "transmission-daemon volledig besturen, torrents beheren, Transmission-" "instellingen configureren en serverstatistieken bekijken." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Je kan verschillende serverprofielen toevoegen en ertussen wisselen (slechts" " één profiel kan gelijktijdig zijn verbonden). Tremotesf ondersteunt HTTPS, " "inclusief zelfondertekende certificaten en cliëntcertificaatauthenticatie. " "Je kan aangekoppelde mappen voor servers instellen en snel de bestanden van " "torrents openen, en een downloadmap kiezen via de bestandsdialoog." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/nl_BE.po000066400000000000000000000045251523411710200164530ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Nathan Follens, 2018 # Alexey Rochev , 2018 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Alexey Rochev , 2018\n" "Language-Team: Dutch (Belgium) (https://app.transifex.com/equeim/teams/72280/nl_BE/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nl_BE\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Externen GUI voor Transmission" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf is nen externen GUI voor Transmission. Ge kunt uw instantie van " "transmission-daemon volledig besturen, torrents beheren, Transmission-" "instellingen configureren en serverstatistieken bekijken." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Ge kunt verschillende serverprofielen toevoegen en dertussen wisselen " "(slechts één profiel kan tegelijk verbonden zijn). Tremotesf ondersteunt " "HTTPS, inclusief zelfondertekende certificaten en " "cliëntcertificaatauthenticatie. Ge kunt aangekoppelde mappen voor servers " "instellen en rap de bestanden van torrents openen, en een downloadmap kiezen" " via de bestandsdialoog." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/pl.po000066400000000000000000000050021523411710200160760ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Waldemar Stoczkowski, 2020 # Oliwier ., 2024 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Oliwier ., 2024\n" "Language-Team: Polish (https://app.transifex.com/equeim/teams/72280/pl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pl\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Zdalne GUI dla klienta BitTorrent Transmission" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf jest zdalnym GUI dla klienta BitTorrent Transmission. Za jego " "pomocą możesz w pełni kontrolować swoją instancję transmission-daemon, " "zarządzać torrentami, konfigurować ustawienia Transmission i wyświetlać " "statystyki serwera." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Możesz dodać kilka profili serwerów i przełączać się między nimi (tylko " "jeden może być podłączony jednocześnie). Tremotesf obsługuje protokół HTTPS," " w tym certyfikaty z podpisem własnym i uwierzytelnianie certyfikatów " "klienta. Możesz ustawić zamontowane katalogi dla serwerów i szybko otwierać " "pliki torrentów oraz wybrać katalog pobierania z okna dialogowego plików." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/ru.po000066400000000000000000000060251523411710200161170ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Alexey Rochev , 2018 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Alexey Rochev , 2018\n" "Language-Team: Russian (https://app.transifex.com/equeim/teams/72280/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ru\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Клиент удаленного управлени� дл� BitTorrent-клиента Transmission" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf - �то клиент удаленного управлени� дл� BitTorrent-клиента " "Transmission. Вы можете полно�тью контролировать ваш transmission-daemon. " "управл�ть торрентами, измен�ть на�тройки Transmission, про�матривать " "�тати�тику �ервера." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Вы можете �оздать не�колько профилей �ервероы и переключать�� между ними " "(одновременно может быть подключен только один). Tremotesf поддерживает " "работу через HTTPS, в том чи�ле �амоподпи�анные �ертификаты и авторизацию � " "помощью клиент�кого �ертификата. Вы можете указать подключенные каталоги дл�" " �ервера и бы�тро открывать файлы торрента или выбирать каталог загрузки � " "помощью диалога выбора файлов." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/tr.po000066400000000000000000000046211523411710200161160ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # Mehmet BEKGÖZ, 2023 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: Mehmet BEKGÖZ, 2023\n" "Language-Team: Turkish (https://app.transifex.com/equeim/teams/72280/tr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: tr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Transmission BitTorrent istemcisi için uzaktan uygulama" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf, Transmission BitTorrent istemcisi için uzaktan bir GUI'dir. " "Transmission-daemon örneğinizi tamamen kontrol edebilir, torrentleri " "yönetebilir, transmission ayarlarını yapabilir, sunucu istatistiklerini " "görüntüleyebilirsiniz." #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "Birkaç sunucu profili ekleyebilir ve bunlar arasında geçiş yapabilirsiniz " "(aynı anda yalnızca bir tanesi bağlanabilir). Tremotesf, kendinden imzalı " "sertifikalar ve istemci sertifikası kimlik doğrulaması dahil olmak üzere " "HTTPS desteğine sahiptir. Sunucular için monte edilmiş dizinler " "ayarlayabilir ve torrent dosyalarını hızlı bir şekilde açabilir ve dosya " "iletişim kutusundan indirme dizinini seçebilirsiniz." #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/data/po/update.sh000066400000000000000000000005641523411710200167510ustar00rootroot00000000000000#!/bin/sh # SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 sed "/Icon=/d" ../org.equeim.Tremotesf.desktop.in > tmp.desktop.in xgettext --copyright-holder="Alexey Rochev " --package-name="tremotesf" -p ./ tmp.desktop.in ../org.equeim.Tremotesf.appdata.xml.in rm tmp.desktop.in sed -i "s/CHARSET/UTF-8/" messages.po tremotesf-2.10.1/data/po/zh_CN.po000066400000000000000000000042431523411710200164720ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Alexey Rochev # This file is distributed under the same license as the tremotesf package. # FIRST AUTHOR , YEAR. # # Translators: # dumperk , 2019 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tremotesf\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-31 19:28+0300\n" "PO-Revision-Date: 2018-08-31 13:48+0000\n" "Last-Translator: dumperk , 2019\n" "Language-Team: Chinese (China) (https://app.transifex.com/equeim/teams/72280/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" #: tmp.desktop.in:4 ../org.equeim.Tremotesf.appdata.xml.in:7 msgid "Tremotesf" msgstr "Tremotesf" #: tmp.desktop.in:5 ../org.equeim.Tremotesf.appdata.xml.in:8 msgid "Remote GUI for Transmission BitTorrent client" msgstr "Transmission远程GUI客户端" #: ../org.equeim.Tremotesf.appdata.xml.in:11 msgid "" "Tremotesf is a remote GUI for Transmission BitTorrent client. You can fully " "control your transmission-daemon instance, manage torrents, set Transmission" " settings, view server statistics." msgstr "" "Tremotesf是一款Transmission远程GUI客户端,你�以完全的控制你的transmission-" "daemon实例,管����更改transmission的设置�查看�务器统计数�。" #: ../org.equeim.Tremotesf.appdata.xml.in:14 msgid "" "You can add several servers profiles and switch between them (only one can " "be connected at a time). Tremotesf has support of HTTPS, including self-" "signed certificates and client certificate authentication. You can set " "mounted directories for servers and quickly open torrents' files and choose " "download directory from file dialog." msgstr "" "你�以添加多个�务器�置并切�它们(�时�能连接一个)。Tremotesf支�HTTPS,包�自签��书和客户端�书认�。为�务器设置目录映射从而快速的打开��文件,添加��时�以选择下载目录。" #: ../org.equeim.Tremotesf.appdata.xml.in:41 msgid "Alexey Rochev" msgstr "Alexey Rochev" tremotesf-2.10.1/org.equeim.Tremotesf.json000066400000000000000000000047301523411710200205200ustar00rootroot00000000000000{ "app-id": "org.equeim.Tremotesf", "runtime": "org.kde.Platform", "runtime-version": "6.11", "sdk": "org.kde.Sdk", "command": "tremotesf", "finish-args": [ "--socket=fallback-x11", "--socket=wayland", "--share=ipc", "--share=network", "--device=dri", "--filesystem=host:ro", "--talk-name=org.kde.StatusNotifierWatcher", "--talk-name=org.freedesktop.FileManager1" ], "cleanup": [ "/include", "/lib/cmake", "/lib/pkgconfig" ], "modules": [ { "name": "cxxopts", "buildsystem": "cmake-ninja", "builddir": true, "config-opts": [ "-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DCXXOPTS_BUILD_EXAMPLES=OFF", "-DCXXOPTS_BUILD_TESTS=OFF" ], "sources": [ { "type": "archive", "url": "https://github.com/jarro2783/cxxopts/archive/refs/tags/v3.3.1.tar.gz", "sha256": "3bfc70542c521d4b55a46429d808178916a579b28d048bd8c727ee76c39e2072" } ] }, { "name": "cpp-httplib", "buildsystem": "cmake-ninja", "builddir": true, "config-opts": [ "-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DHTTPLIB_REQUIRE_OPENSSL=ON" ], "sources": [ { "type": "archive", "url": "https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.48.0.tar.gz", "sha256": "d9ed142d319c6e19a961f477257e67f846909ce15288502188df2281941be84e" } ] }, { "name": "tremotesf", "buildsystem": "cmake-ninja", "builddir": true, "build-options": { "env": { "CTEST_OUTPUT_ON_FAILURE": "1", "ASAN_OPTIONS": "detect_leaks=0" } }, "config-opts": [ "-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON", "-DTREMOTESF_WITH_HTTPLIB=system", "-DTREMOTESF_ASAN=ON" ], "run-tests": true, "sources": [ { "type": "dir", "path": "." } ] } ] } tremotesf-2.10.1/packaging/000077500000000000000000000000001523411710200155235ustar00rootroot00000000000000tremotesf-2.10.1/packaging/CMakeLists.txt000066400000000000000000000002701523411710200202620ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 if (WIN32) add_subdirectory("windows") elseif (APPLE) add_subdirectory("macos") endif() tremotesf-2.10.1/packaging/CPackCommon.cmake000066400000000000000000000031271523411710200206620ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 set(CPACK_THREADS 0) set(CPACK_PACKAGE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") if (WIN32) set(os "Windows") elseif (APPLE) set(os "macOS") else () message(FATAL_ERROR "Unsupported target platform ${CMAKE_SYSTEM_NAME}") endif () if (DEFINED VCPKG_TARGET_TRIPLET) if (VCPKG_TARGET_TRIPLET MATCHES "^x64.*$") set(arch "x86_64") elseif (VCPKG_TARGET_TRIPLET MATCHES "^arm64.*$") set(arch "arm64") else () message(FATAL_ERROR "Unsupported VCPKG_TARGET_TRIPLET ${VCPKG_TARGET_TRIPLET}") endif () elseif (DEFINED CMAKE_OSX_ARCHITECTURES) if (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64") set(arch "x86_64") elseif (CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") set(arch "arm64") else () message(FATAL_ERROR "Unsupported CMAKE_OSX_ARCHITECTURES ${CMAKE_OSX_ARCHITECTURES}") endif () else () string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" target_arch) if ((target_arch STREQUAL "amd64") OR (target_arch STREQUAL "x86_64")) set(arch "x86_64") elseif (target_arch STREQUAL "arm64") set(arch "arm64") else () message(FATAL_ERROR "Unsupported target architecture ${target_arch}") endif () endif () get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if (is_multi_config) set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${os}-${arch}-\${CPACK_BUILD_CONFIG}") else () set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${os}-${arch}-${CMAKE_BUILD_TYPE}") endif () tremotesf-2.10.1/packaging/macos/000077500000000000000000000000001523411710200166255ustar00rootroot00000000000000tremotesf-2.10.1/packaging/macos/!!! ATTENTION !!!.txt000066400000000000000000000004671523411710200213500ustar00rootroot00000000000000After installing this app to Applications you need to run following command in the terminal: xattr -dr com.apple.quarantine /Applications/Tremotesf.app The reason why this command is needed is that this app is unsigned and macOS will prevent it from running. This command tells macOS to allow this app to run.tremotesf-2.10.1/packaging/macos/!!! ATTENTION !!!.txt.license000066400000000000000000000001211523411710200227540ustar00rootroot00000000000000SPDX-FileCopyrightText: 2015-2025 Alexey Rochev SPDX-License-Identifier: CC0-1.0tremotesf-2.10.1/packaging/macos/CMakeLists.txt000066400000000000000000000012021523411710200213600ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 configure_file("Info.plist.in" "${CMAKE_CURRENT_BINARY_DIR}/Info.plist") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Info.plist" DESTINATION "${TREMOTESF_MACOS_BUNDLE_NAME}.app/Contents") install(FILES "!!! ATTENTION !!!.txt" DESTINATION ".") include("../CPackCommon.cmake") set(CPACK_GENERATOR DragNDrop) get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if (is_multi_config) set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/StripExecutable.cmake") else() include(StripExecutable.cmake) endif() include(CPack) tremotesf-2.10.1/packaging/macos/Info.plist.in000066400000000000000000000067661523411710200212210ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable tremotesf CFBundleIconFile tremotesf CFBundleIdentifier org.equeim.Tremotesf CFBundleInfoDictionaryVersion 6.0 CFBundleName ${TREMOTESF_MACOS_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${PROJECT_VERSION} CFBundleVersion ${PROJECT_VERSION} CSResourcesFileMapped NSHumanReadableCopyright 2015-2025 Alexey Rochev LSMinimumSystemVersion ${TREMOTESF_MACOS_DEPLOYMENT_TARGET} CFBundleDocumentTypes CFBundleTypeExtensions torrent CFBundleTypeIconFile tremotesf CFBundleTypeName BitTorrent Document CFBundleTypeRole Viewer LSHandlerRank Owner LSItemContentTypes org.bittorrent.torrent NSExportableTypes org.bittorrent.torrent UTExportedTypeDeclarations UTTypeConformsTo public.data public.item com.bittorrent.torrent UTTypeDescription BitTorrent Document UTTypeIconFile tremotesf UTTypeIdentifier org.bittorrent.torrent UTTypeReferenceURL https://www.bittorrent.org/beps/bep_0000.html UTTypeTagSpecification com.apple.ostype TORR public.filename-extension torrent public.mime-type application/x-bittorrent CFBundleURLTypes CFBundleTypeRole Viewer CFBundleURLSchemes magnet CFBundleURLName BitTorrent Magnet URL tremotesf-2.10.1/packaging/macos/StripExecutable.cmake000066400000000000000000000010401523411710200227250ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 if (DEFINED CPACK_BUILD_CONFIG) string(TOLOWER "${CPACK_BUILD_CONFIG}" config) elseif (DEFINED CMAKE_BUILD_TYPE) string(TOLOWER "${CMAKE_BUILD_TYPE}" config) else () message(FATAL_ERROR "Unknown build type") endif () if (config STREQUAL "release") message(STATUS "Stripping executable when packaging") set(CPACK_STRIP_FILES ON) else() message(STATUS "Not stripping executable when packaging") set(CPACK_STRIP_FILES OFF) endif() tremotesf-2.10.1/packaging/rpm/000077500000000000000000000000001523411710200163215ustar00rootroot00000000000000tremotesf-2.10.1/packaging/rpm/tremotesf.spec000066400000000000000000000141201523411710200212030ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 %bcond asan 0 %global app_id org.equeim.Tremotesf Name: tremotesf Version: 2.10.1 Release: 1%{!?suse_version:%{?dist}} Summary: Remote GUI for transmission-daemon %if %{defined suse_version} Group: Productivity/Networking/Other License: GPL-3.0-or-later %else License: GPLv3+ %endif URL: https://github.com/equeim/tremotesf2 Source0: https://github.com/equeim/tremotesf2/releases/download/%{version}/%{name}-%{version}.tar.zst Requires: hicolor-icon-theme BuildRequires: cmake BuildRequires: desktop-file-utils BuildRequires: gettext BuildRequires: make BuildRequires: zstd BuildRequires: cmake(Qt6) BuildRequires: cmake(Qt6Core) BuildRequires: cmake(Qt6DBus) BuildRequires: cmake(Qt6LinguistTools) BuildRequires: cmake(Qt6Network) BuildRequires: cmake(Qt6Test) BuildRequires: cmake(Qt6Widgets) BuildRequires: cmake(fmt) BuildRequires: cmake(KF6WidgetsAddons) BuildRequires: cmake(KF6WindowSystem) BuildRequires: cmake(cxxopts) BuildRequires: pkgconfig(libpsl) BuildRequires: openssl-devel %if %{defined fedora} BuildRequires: cmake(httplib) BuildRequires: libappstream-glib %if "%{toolchain}" == "clang" BuildRequires: clang %if %{with asan} BuildRequires: compiler-rt %endif %else BuildRequires: gcc-c++ %if %{with asan} BuildRequires: libasan %endif %endif Requires: qt6-qtsvg Requires: breeze-icon-theme %global tremotesf_with_httplib system %endif %if %{defined suse_version} BuildRequires: pkgconfig(cpp-httplib) BuildRequires: appstream-glib # OBS complains about not owned directories if hicolor-icon-theme isn't installed at build time BuildRequires: hicolor-icon-theme Requires: libQt6Svg6 Requires: kf6-breeze-icons %global _metainfodir %{_datadir}/metainfo %global tremotesf_with_httplib system %endif %if %{defined mageia} BuildRequires: appstream-util Requires: qtsvg5 Requires: breeze-icons %global tremotesf_with_httplib bundled %endif %description Remote GUI for Transmission BitTorrent client. %prep %autosetup %build %cmake -D TREMOTESF_WITH_HTTPLIB=%{tremotesf_with_httplib} -D TREMOTESF_ASAN=%{with asan} %cmake_build %check export ASAN_OPTIONS=detect_leaks=0 %ctest %install %cmake_install appstream-util validate-relax --nonet %{buildroot}/%{_metainfodir}/%{app_id}.appdata.xml desktop-file-validate %{buildroot}/%{_datadir}/applications/%{app_id}.desktop %files %{_bindir}/%{name} %{_datadir}/icons/hicolor/*/apps/%{app_id}.* %{_datadir}/applications/%{app_id}.desktop %{_metainfodir}/%{app_id}.appdata.xml %changelog * Tue Jul 28 2026 Alexey Rochev - 2.10.1-1 - tremotesf-2.10.1 * Mon Jul 20 2026 Alexey Rochev - 2.10.0-1 - tremotesf-2.10.0 * Tue Dec 09 2025 Alexey Rochev - 2.9.1-1 - tremotesf-2.9.1 * Fri Oct 03 2025 Alexey Rochev - 2.9.0-1 - tremotesf-2.9.0 * Wed Apr 16 2025 Alexey Rochev - 2.8.2-1 - tremotesf-2.8.2 * Sat Apr 12 2025 Alexey Rochev - 2.8.1-1 - tremotesf-2.8.1 * Wed Apr 09 2025 Alexey Rochev - 2.8.0-1 - tremotesf-2.8.0 * Tue Jan 14 2025 Alexey Rochev - 2.7.5-1 - tremotesf-2.7.5 * Fri Dec 06 2024 Alexey Rochev - 2.7.4-1 - tremotesf-2.7.4 * Wed Nov 20 2024 Alexey Rochev - 2.7.3-1 - tremotesf-2.7.3 * Sun Sep 15 2024 Alexey Rochev - 2.7.2-1 - tremotesf-2.7.2 * Fri Sep 13 2024 Alexey Rochev - 2.7.1-1 - tremotesf-2.7.1 * Sat Aug 31 2024 Alexey Rochev - 2.7.0-1 - tremotesf-2.7.0 * Mon Apr 22 2024 Alexey Rochev - 2.6.3-1 - tremotesf-2.6.3 * Mon Apr 01 2024 Alexey Rochev - 2.6.2-1 - tremotesf-2.6.2 * Sun Mar 17 2024 Alexey Rochev - 2.6.1-1 - tremotesf-2.6.1 * Mon Jan 08 2024 Alexey Rochev - 2.6.0-1 - tremotesf-2.6.0 * Sun Oct 15 2023 Alexey Rochev - 2.5.0-1 - tremotesf-2.5.0 * Tue May 30 2023 Alexey Rochev - 2.4.0-1 - tremotesf-2.4.0 * Sun Apr 30 2023 Alexey Rochev - 2.3.0-1 - tremotesf-2.3.0 * Tue Mar 28 2023 Alexey Rochev - 2.2.0-1 - tremotesf-2.2.0 * Sun Mar 12 2023 Alexey Rochev - 2.1.0-1 - tremotesf-2.1.0 * Sat Nov 05 2022 Alexey Rochev - 2.0.0-1 - tremotesf-2.0.0 * Fri Mar 18 2022 Alexey Rochev - 1.11.3-1 - tremotesf-1.11.3 * Wed Mar 16 2022 Alexey Rochev - 1.11.2-1 - tremotesf-1.11.2 * Mon Feb 28 2022 Alexey Rochev - 1.11.1-1 - tremotesf-1.11.1 * Sun Feb 13 2022 Alexey Rochev - 1.11.0-1 - tremotesf-1.11.0 * Mon Sep 27 2021 Alexey Rochev - 1.10.0-1 - tremotesf-1.10.0 * Mon May 10 2021 Alexey Rochev - 1.9.1-1 - tremotesf-1.9.1 * Tue May 04 2021 Alexey Rochev - 1.9.0-1 - tremotesf-1.9.0 * Sun Sep 06 2020 Alexey Rochev - 1.8.0-1 - tremotesf-1.8.0 * Sat Jun 27 2020 Alexey Rochev - 1.7.1-1 - tremotesf-1.7.1 * Fri Jun 05 2020 Alexey Rochev - 1.7.0-1 - tremotesf-1.7.0 * Sat Jan 11 2020 Alexey Rochev - 1.6.4-1 - tremotesf-1.6.4 * Sun Jan 05 2020 Alexey Rochev - 1.6.3-1 - tremotesf-1.6.3 * Sun Jan 05 2020 Alexey Rochev - 1.6.2-1 - tremotesf-1.6.2 * Tue Jul 16 2019 Alexey Rochev - 1.6.1-1 - tremotesf-1.6.1 * Sat Jan 26 2019 Alexey Rochev - 1.6.0-1 - tremotesf-1.6.0 * Sat Dec 08 2018 Alexey Rochev - 1.5.6-1 - tremotesf-1.5.6 * Wed Sep 26 2018 Alexey Rochev - 1.5.5-1 - tremotesf-1.5.5 * Mon Sep 10 2018 Alexey Rochev - 1.5.4-1 - tremotesf-1.5.4 * Mon Sep 03 2018 Alexey Rochev - 1.5.3-1 - tremotesf-1.5.3 * Sat Aug 18 2018 Alexey Rochev - 1.5.2-1 - tremotesf-1.5.2 * Tue Aug 14 2018 Alexey Rochev - 1.5.1-1 - tremotesf-1.5.1 * Mon Aug 13 2018 Alexey Rochev - 1.5.0-1 - tremotesf-1.5.0 tremotesf-2.10.1/packaging/windows/000077500000000000000000000000001523411710200172155ustar00rootroot00000000000000tremotesf-2.10.1/packaging/windows/CMakeLists.txt000066400000000000000000000013701523411710200217560ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 include("../CPackCommon.cmake") set(CPACK_GENERATOR ZIP WIX) set(CPACK_PACKAGE_NAME "Tremotesf") set(CPACK_PACKAGE_VENDOR "Tremotesf") set(CPACK_PACKAGE_EXECUTABLES "tremotesf;Tremotesf") set(CPACK_PACKAGE_INSTALL_DIRECTORY "Tremotesf") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/license.rtf") set(CPACK_WIX_VERSION 4) set(CPACK_WIX_INSTALL_SCOPE perMachine) set(CPACK_WIX_UPGRADE_GUID "67e0511b-d9de-4b3c-a604-0dd47522d451") set(CPACK_WIX_PRODUCT_ICON "${TREMOTESF_WINDOWS_ICON}") set(CPACK_WIX_EXTRA_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/registry.wxs") set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/registry_feature_patch.wxs") include(CPack) tremotesf-2.10.1/packaging/windows/license.rtf000066400000000000000000001103201523411710200213510ustar00rootroot00000000000000{\rtf1\ansi\deff0\nouicompat{\fonttbl{\f0\fswiss Helvetica;}{\f1\fswiss\fcharset0 Helvetica;}{\f2\fnil Consolas;}} {\colortbl ;\red0\green0\blue255;} {\*\generator Riched20 10.0.22621}\viewkind4\uc1 \pard\sa180\b\f0\fs22\lang9 GNU GENERAL PUBLIC LICENSE\par \b0\fs20 Version 3, 29 June 2007\par Copyright \f1\'a9 2007 Free Software Foundation, Inc. <{{\field{\*\fldinst{HYPERLINK "http://fsf.org/"}}{\fldrslt{http://fsf.org/\ul0\cf0}}}}\f1\fs20 >\par \f0 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\par \b\fs22 Preamble\par \b0\fs20 The GNU General Public License is a free, copyleft license for software and other kinds of works.\par The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.\par When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.\par To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.\par For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\par Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.\par For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.\par Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.\par Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.\par The precise terms and conditions for copying, distribution and modification follow.\par \b\fs22 TERMS AND CONDITIONS\par \fs20 0. Definitions.\par \b0\ldblquote This License\rdblquote refers to version 3 of the GNU General Public License.\par \ldblquote Copyright\rdblquote also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.\par \ldblquote The Program\rdblquote refers to any copyrightable work licensed under this License. Each licensee is addressed as \ldblquote you\rdblquote . \ldblquote Licensees\rdblquote and \ldblquote recipients\rdblquote may be individuals or organizations.\par To \ldblquote modify\rdblquote a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a \ldblquote modified version\rdblquote of the earlier work or a work \ldblquote based on\rdblquote the earlier work.\par A \ldblquote covered work\rdblquote means either the unmodified Program or a work based on the Program.\par To \ldblquote propagate\rdblquote a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.\par To \ldblquote convey\rdblquote a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.\par An interactive user interface displays \ldblquote Appropriate Legal Notices\rdblquote to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.\par \b 1. Source Code.\par \b0 The \ldblquote source code\rdblquote for a work means the preferred form of the work for making modifications to it. \ldblquote Object code\rdblquote means any non-source form of a work.\par A \ldblquote Standard Interface\rdblquote means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.\par The \ldblquote System Libraries\rdblquote of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A \ldblquote Major Component\rdblquote , in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.\par The \ldblquote Corresponding Source\rdblquote for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.\par The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.\par The Corresponding Source for a work in source code form is that same work.\par \b 2. Basic Permissions.\par \b0 All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.\par You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.\par Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.\par \b 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\par \b0 No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.\par When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.\par \b 4. Conveying Verbatim Copies.\par \b0 You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.\par You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.\par \b 5. Conveying Modified Source Versions.\par \b0 You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:\par \pard\fi-360\li360\tx360\bullet\tab a) The work must carry prominent notices stating that you modified it, and giving a relevant date.\par \bullet\tab b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to \ldblquote keep intact all notices\rdblquote .\par \bullet\tab c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.\par \pard\fi-360\li360\sa180\tx360\bullet\tab d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.\par \pard\sa180 A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an \ldblquote aggregate\rdblquote if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.\par \b 6. Conveying Non-Source Forms.\par \b0 You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:\par \pard\fi-360\li360\tx360\bullet\tab a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.\par \bullet\tab b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.\par \bullet\tab c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.\par \bullet\tab d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.\par \pard\fi-360\li360\sa180\tx360\bullet\tab e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.\par \pard\sa180 A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.\par A \ldblquote User Product\rdblquote is either (1) a \ldblquote consumer product\rdblquote , which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, \ldblquote normally used\rdblquote refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.\par \ldblquote Installation Information\rdblquote for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.\par If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).\par The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.\par Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.\par \b 7. Additional Terms.\par \b0\ldblquote Additional permissions\rdblquote are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.\par When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.\par Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:\par \pard\fi-360\li360\tx360\bullet\tab a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or\par \bullet\tab b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or\par \bullet\tab c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or\par \bullet\tab d) Limiting the use for publicity purposes of names of licensors or authors of the material; or\par \bullet\tab e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or\par \pard\fi-360\li360\sa180\tx360\bullet\tab f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.\par \pard\sa180 All other non-permissive additional terms are considered \ldblquote further restrictions\rdblquote within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.\par If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.\par Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.\par \b 8. Termination.\par \b0 You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).\par However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.\par Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.\par Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.\par \b 9. Acceptance Not Required for Having Copies.\par \b0 You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.\par \b 10. Automatic Licensing of Downstream Recipients.\par \b0 Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.\par An \ldblquote entity transaction\rdblquote is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.\par You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.\par \b 11. Patents.\par \b0 A \ldblquote contributor\rdblquote is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's \ldblquote contributor version\rdblquote .\par A contributor's \ldblquote essential patent claims\rdblquote are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, \ldblquote control\rdblquote includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.\par Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.\par In the following three paragraphs, a \ldblquote patent license\rdblquote is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To \ldblquote grant\rdblquote such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.\par If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. \ldblquote Knowingly relying\rdblquote means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.\par If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.\par A patent license is \ldblquote discriminatory\rdblquote if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.\par Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.\par \b 12. No Surrender of Others' Freedom.\par \b0 If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.\par \b 13. Use with the GNU Affero General Public License.\par \b0 Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.\par \b 14. Revised Versions of this License.\par \b0 The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\par Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License \ldblquote or any later version\rdblquote applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.\par If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.\par Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.\par \b 15. Disclaimer of Warranty.\par \b0 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \ldblquote AS IS\rdblquote WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\par \b 16. Limitation of Liability.\par \b0 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\par \b 17. Interpretation of Sections 15 and 16.\par \b0 If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.\par END OF TERMS AND CONDITIONS\par \b\fs22 How to Apply These Terms to Your New Programs\par \b0\fs20 If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\par To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the \ldblquote copyright\rdblquote line and a pointer to where the full notice is found.\par \f2 \line Copyright (C) \line\line This program is free software: you can redistribute it and/or modify\line it under the terms of the GNU General Public License as published by\line the Free Software Foundation, either version 3 of the License, or\line (at your option) any later version.\line\line This program is distributed in the hope that it will be useful,\line but WITHOUT ANY WARRANTY; without even the implied warranty of\line MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\line GNU General Public License for more details.\line\line You should have received a copy of the GNU General Public License\line along with this program. If not, see <{{\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f2\fs20 >.\par \f0 Also add information on how to contact you by electronic and paper mail.\par If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:\par \f2 Copyright (C) \line This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\line This is free software, and you are welcome to redistribute it\line under certain conditions; type `show c' for details.\par \f0 The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an \ldblquote about box\rdblquote .\par You should also get your employer (if you work as a programmer) or school, if any, to sign a \ldblquote copyright disclaimer\rdblquote for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <{{\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs20 >.\par The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <{{\field{\*\fldinst{HYPERLINK "http://www.gnu.org/philosophy/why-not-lgpl.html"}}{\fldrslt{http://www.gnu.org/philosophy/why-not-lgpl.html\ul0\cf0}}}}\f0\fs20 >.\par } tremotesf-2.10.1/packaging/windows/license.rtf.license000066400000000000000000000001641523411710200227760ustar00rootroot00000000000000SPDX-FileCopyrightText: 2007 Free Software Foundation, Inc. SPDX-License-Identifier: CC-BY-ND-4.0 tremotesf-2.10.1/packaging/windows/registry.wxs000066400000000000000000000104531523411710200216330ustar00rootroot00000000000000 tremotesf-2.10.1/packaging/windows/registry_feature_patch.wxs000066400000000000000000000005601523411710200245230ustar00rootroot00000000000000 tremotesf-2.10.1/src/000077500000000000000000000000001523411710200143665ustar00rootroot00000000000000tremotesf-2.10.1/src/CMakeLists.txt000066400000000000000000000414571523411710200171410ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 include(CTest) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) find_package(Threads REQUIRED) find_package(fmt 10.1.0 REQUIRED) find_package(cxxopts 3.2.1 REQUIRED) find_package(Qt6 ${TREMOTESF_MINIMUM_QT_VERSION} REQUIRED COMPONENTS Core Network Widgets) set(minimum_kf_version 6.11.0) find_package(KF6WidgetsAddons ${minimum_kf_version} REQUIRED) find_package(PkgConfig REQUIRED) if (WIN32 OR APPLE) set(registrable_domain_qt ON) else () set(registrable_domain_qt OFF) pkg_check_modules(libpsl REQUIRED IMPORTED_TARGET "libpsl >= 0.21.2") endif () add_library(tremotesf_objects OBJECT) target_sources(tremotesf_objects PRIVATE bencodeparser.cpp desktoputils.cpp filemanagerlauncher.cpp fileutils.cpp formatutils.cpp magnetlinkparser.cpp settings.cpp torrentfileparser.cpp coroutines/coroutines.cpp coroutines/scope.cpp coroutines/waitall.cpp log/demangle.cpp log/formatters.cpp log/log.cpp rpc/addressutils.cpp rpc/mounteddirectoriesutils.cpp rpc/pathutils.cpp rpc/peer.cpp rpc/qsslcertificateformatter.cpp rpc/requestrouter.cpp rpc/rpc.cpp rpc/servers.cpp rpc/serversettings.cpp rpc/serverstats.cpp rpc/torrent.cpp rpc/torrentfile.cpp rpc/tracker.cpp startup/commandlineparser.cpp ui/itemmodels/baseproxymodel.cpp ui/itemmodels/basetorrentfilesmodel.cpp ui/itemmodels/stringlistmodel.cpp ui/itemmodels/torrentfilesmodelentry.cpp ui/itemmodels/torrentfilesproxymodel.cpp ui/notificationscontroller.cpp ui/savewindowstatedispatcher.cpp ui/screens/aboutdialog.cpp ui/screens/addtorrent/addtorrentdialog.cpp ui/screens/addtorrent/addtorrenthelpers.cpp ui/screens/addtorrent/droppedtorrents.cpp ui/screens/addtorrent/localtorrentfilesmodel.cpp ui/screens/connectionsettings/connectionsettingsdialog.cpp ui/screens/connectionsettings/servereditdialog.cpp ui/screens/connectionsettings/serversmodel.cpp ui/screens/mainwindow/alltrackersmodel.cpp ui/screens/mainwindow/basetorrentsfilterssettingsmodel.cpp ui/screens/mainwindow/downloaddirectoriesmodel.cpp ui/screens/mainwindow/downloaddirectorydelegate.cpp ui/screens/mainwindow/editlabelsdialog.cpp ui/screens/mainwindow/labelsmodel.cpp ui/screens/mainwindow/mainwindow.cpp ui/screens/mainwindow/mainwindowsidebar.cpp ui/screens/mainwindow/mainwindowstatusbar.cpp ui/screens/mainwindow/mainwindowviewmodel.cpp ui/screens/mainwindow/statusfiltersmodel.cpp ui/screens/mainwindow/torrentsmodel.cpp ui/screens/mainwindow/torrentsproxymodel.cpp ui/screens/mainwindow/torrentsview.cpp ui/screens/serversettings/serversettingsdialog.cpp ui/screens/serverstatsdialog.cpp ui/screens/settingsdialog.cpp ui/screens/torrentproperties/peersmodel.cpp ui/screens/torrentproperties/torrentfilesmodel.cpp ui/screens/torrentproperties/torrentpropertiesdialog.cpp ui/screens/torrentproperties/torrentpropertieswidget.cpp ui/screens/torrentproperties/trackersmodel.cpp ui/screens/torrentproperties/trackersviewwidget.cpp ui/stylehelpers.cpp ui/widgets/basetreeview.cpp ui/widgets/editlabelswidget.cpp ui/widgets/listplaceholder.cpp ui/widgets/progressbardelegate.cpp ui/widgets/remotedirectoryselectionwidget.cpp ui/widgets/textinputdialog.cpp ui/widgets/tooltipwhenelideddelegate.cpp ui/widgets/torrentfilesview.cpp ui/widgets/torrentremotedirectoryselectionwidget.cpp resources.qrc PRIVATE FILE_SET HEADERS FILES bencodeparser.h desktoputils.h filemanagerlauncher.h fileutils.h formatutils.h itemlistupdater.h magnetlinkparser.h pragmamacros.h settings.h stdutils.h target_os.h macoshelpers.h torrentfileparser.h unixhelpers.h windowshelpers.h coroutines/coroutinefwd.h coroutines/coroutines.h coroutines/hostinfo.h coroutines/network.h coroutines/qobjectsignal.h coroutines/scope.h coroutines/threadpool.h coroutines/timer.h coroutines/waitall.h ipc/ipcclient.h ipc/ipcserver.h log/demangle.h log/formatters.h log/log.h rpc/addressutils.h rpc/jsonutils.h rpc/mounteddirectoriesutils.h rpc/numbervaluerange.h rpc/pathutils.h rpc/peer.h rpc/qsslcertificateformatter.h rpc/requestrouter.h rpc/rpc.h rpc/servers.h rpc/serversettings.h rpc/serverstats.h rpc/torrent.h rpc/torrentfile.h rpc/tracker.h startup/commandlineparser.h startup/signalhandler.h ui/iconthemesetup.h ui/itemmodels/baseproxymodel.h ui/itemmodels/basetorrentfilesmodel.h ui/itemmodels/modelutils.h ui/itemmodels/stringlistmodel.h ui/itemmodels/torrentfilesmodelentry.h ui/itemmodels/torrentfilesproxymodel.h ui/itemmodels/torrentfilestreebuilder.h ui/notificationscontroller.h ui/savewindowstatedispatcher.h ui/screens/aboutdialog.h ui/screens/addtorrent/addtorrentdialog.h ui/screens/addtorrent/addtorrenthelpers.h ui/screens/addtorrent/droppedtorrents.h ui/screens/addtorrent/localtorrentfilesmodel.h ui/screens/connectionsettings/connectionsettingsdialog.h ui/screens/connectionsettings/servereditdialog.h ui/screens/connectionsettings/serversmodel.h ui/screens/mainwindow/alltrackersmodel.h ui/screens/mainwindow/basetorrentsfilterssettingsmodel.h ui/screens/mainwindow/downloaddirectoriesmodel.h ui/screens/mainwindow/downloaddirectorydelegate.h ui/screens/mainwindow/editlabelsdialog.h ui/screens/mainwindow/labelsmodel.h ui/screens/mainwindow/mainwindow.h ui/screens/mainwindow/mainwindowsidebar.h ui/screens/mainwindow/mainwindowstatusbar.h ui/screens/mainwindow/mainwindowviewmodel.h ui/screens/mainwindow/statusfiltersmodel.h ui/screens/mainwindow/torrentsmodel.h ui/screens/mainwindow/torrentsproxymodel.h ui/screens/mainwindow/torrentsview.h ui/screens/serversettings/serversettingsdialog.h ui/screens/serverstatsdialog.h ui/screens/settingsdialog.h ui/screens/torrentproperties/peersmodel.h ui/screens/torrentproperties/torrentfilesmodel.h ui/screens/torrentproperties/torrentpropertiesdialog.h ui/screens/torrentproperties/torrentpropertieswidget.h ui/screens/torrentproperties/trackersmodel.h ui/screens/torrentproperties/trackersviewwidget.h ui/stylehelpers.h ui/systemcolorsprovider.h ui/widgets/basetreeview.h ui/widgets/editlabelswidget.h ui/widgets/listplaceholder.h ui/widgets/progressbardelegate.h ui/widgets/remotedirectoryselectionwidget.h ui/widgets/textinputdialog.h ui/widgets/tooltipwhenelideddelegate.h ui/widgets/torrentfilesview.h ui/widgets/torrentremotedirectoryselectionwidget.h ) target_link_libraries(tremotesf_objects PUBLIC cxxopts::cxxopts Threads::Threads fmt::fmt Qt::Core Qt::Network Qt::Widgets KF6::WidgetsAddons) # We need these as CMake variables for configure_file() call below set(TREMOTESF_APP_NAME "Tremotesf") set(TREMOTESF_EXECUTABLE_NAME "tremotesf") target_compile_definitions( tremotesf_objects PUBLIC TREMOTESF_EXECUTABLE_NAME="${TREMOTESF_EXECUTABLE_NAME}" TREMOTESF_APP_ID="org.equeim.Tremotesf" TREMOTESF_APP_NAME="${TREMOTESF_APP_NAME}" TREMOTESF_VERSION="${PROJECT_VERSION}" FMT_VERSION_MAJOR=${fmt_VERSION_MAJOR} ) if (registrable_domain_qt) target_compile_definitions(tremotesf_objects PUBLIC TREMOTESF_REGISTRABLE_DOMAIN_QT) else () target_link_libraries(tremotesf_objects PUBLIC PkgConfig::libpsl) endif () if (UNIX) target_sources( tremotesf_objects PRIVATE unixhelpers.cpp startup/signalhandler_unix.cpp ) if (TREMOTESF_UNIX_FREEDESKTOP) find_package(Qt6 ${TREMOTESF_MINIMUM_QT_VERSION} REQUIRED COMPONENTS DBus) find_package(KF6WindowSystem ${minimum_kf_version} REQUIRED) target_link_libraries(tremotesf_objects PUBLIC Qt::DBus KF6::WindowSystem) qt_add_dbus_adaptor( dbus_generated ipc/org.freedesktop.Application.xml ipc/ipcserver_dbus_service.h tremotesf::IpcDbusService tremotesf_dbus_generated/ipc/org.freedesktop.Application.adaptor OrgFreedesktopApplicationAdaptor ) qt_add_dbus_interface( dbus_generated ipc/org.freedesktop.Application.xml tremotesf_dbus_generated/ipc/org.freedesktop.Application ) qt_add_dbus_interface( dbus_generated org.freedesktop.portal.Notification.xml tremotesf_dbus_generated/org.freedesktop.portal.Notification ) qt_add_dbus_interface( dbus_generated org.freedesktop.Notifications.xml tremotesf_dbus_generated/org.freedesktop.Notifications ) qt_add_dbus_interface( dbus_generated org.freedesktop.FileManager1.xml tremotesf_dbus_generated/org.freedesktop.FileManager1 ) target_sources( tremotesf_objects PRIVATE filemanagerlauncher_freedesktop.cpp ipc/ipcclient_dbus.cpp ipc/ipcserver_dbus.cpp ipc/ipcserver_dbus_service.cpp ui/iconthemesetup_freedesktop.cpp ui/notificationscontroller_freedesktop.cpp ${dbus_generated} PRIVATE FILE_SET HEADERS FILES coroutines/dbus.h ipc/ipcserver_dbus.h ipc/ipcserver_dbus_service.h ) else () target_sources( tremotesf_objects PRIVATE ipc/ipcclient_socket.cpp ipc/ipcserver_socket.cpp ui/notificationscontroller_fallback.cpp PRIVATE FILE_SET HEADERS FILES ipc/ipcserver_socket.h ) if (APPLE) target_sources( tremotesf_objects PRIVATE macoshelpers.mm filemanagerlauncher_macos.mm ipc/fileopeneventhandler.cpp PRIVATE FILE_SET HEADERS FILES ipc/fileopeneventhandler.h ) else () target_sources( tremotesf_objects PRIVATE filemanagerlauncher_fallback.cpp ) endif () endif () elseif (WIN32) find_library(Dwmapi Dwmapi REQUIRED) find_library(RuntimeObject RuntimeObject REQUIRED) target_link_libraries(tremotesf_objects PUBLIC Qt::Svg "${Dwmapi}" "${RuntimeObject}") target_sources( tremotesf_objects PRIVATE filemanagerlauncher_windows.cpp ipc/ipcclient_socket.cpp ipc/ipcserver_socket.cpp startup/signalhandler_windows.cpp startup/windowsfatalerrorhandlers.cpp ui/darkthemeapplier_windows.cpp ui/notificationscontroller_fallback.cpp ui/systemcolorsprovider_windows.cpp windowshelpers.cpp PRIVATE FILE_SET HEADERS FILES ipc/ipcserver_socket.h startup/windowsfatalerrorhandlers.h ui/darkthemeapplier_windows.h ) set(TREMOTESF_WINDOWS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/tremotesf.ico" PARENT_SCOPE) else () message(FATAL_ERROR "Unsupported target platform ${CMAKE_SYSTEM_NAME}") endif () add_executable(tremotesf startup/main.cpp) target_link_libraries(tremotesf PRIVATE tremotesf_objects) # Executable configuration on Windows if (WIN32) include("${CMAKE_SOURCE_DIR}/cmake/WindowsMinimumVersion.cmake") configure_file(tremotesf.rc.in tremotesf.rc @ONLY) configure_file(tremotesf.manifest.in tremotesf.manifest @ONLY) target_sources( tremotesf PRIVATE startup/main_windows.cpp startup/windowsmessagehandler.cpp "${CMAKE_CURRENT_BINARY_DIR}/tremotesf.rc" PRIVATE FILE_SET HEADERS FILES startup/main_windows.h startup/windowsmessagehandler.h ) if (MSVC) target_sources(tremotesf PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/tremotesf.manifest") else () target_sources(tremotesf PRIVATE tremotesf.manifest.rc) endif () if (MSVC) target_link_options(tremotesf PRIVATE "/PDBALTPATH:%_PDB%") endif() set_target_properties(tremotesf PROPERTIES WIN32_EXECUTABLE $>) endif () # Executable installation if (WIN32) set(bindir ".") elseif (APPLE) set(bindir "${TREMOTESF_MACOS_BUNDLE_NAME}.app/Contents/MacOS") else () set(bindir "${CMAKE_INSTALL_BINDIR}") endif () install(TARGETS tremotesf DESTINATION "${bindir}") if (WIN32 AND MSVC) install(FILES $ DESTINATION "${bindir}" OPTIONAL) endif () # Icons and Qt translations bundling if (WIN32 OR APPLE) target_sources( tremotesf PRIVATE startup/recoloringsvgiconengineplugin.cpp ui/iconthemesetup_bundled.cpp ui/recoloringsvgiconengine.cpp PRIVATE FILE_SET HEADERS FILES startup/recoloringsvgiconengineplugin.h ui/recoloringsvgiconengine.h ) target_compile_definitions( tremotesf PUBLIC TREMOTESF_USE_BUNDLED_QT_TRANSLATIONS QT_STATICPLUGIN ) find_package(Qt6 ${TREMOTESF_MINIMUM_QT_VERSION} REQUIRED COMPONENTS GuiPrivate Svg) target_link_libraries(tremotesf PUBLIC Qt::GuiPrivate Qt::Svg) set(exclude_plugins bearer iconengines imageformats) if (WIN32) list(APPEND exclude_plugins styles) endif () qt_import_plugins(tremotesf EXCLUDE_BY_TYPE ${exclude_plugins}) endif () if (BUILD_TESTING) find_package(Qt6 ${TREMOTESF_MINIMUM_QT_VERSION} REQUIRED COMPONENTS Test) add_executable(itemlistupdater_test itemlistupdater_test.cpp) add_test(NAME itemlistupdater_test COMMAND itemlistupdater_test) target_link_libraries(itemlistupdater_test PRIVATE tremotesf_objects Qt::Test) add_executable(log_test log/log_test.cpp) add_test(NAME log_test COMMAND log_test) target_link_libraries(log_test PRIVATE tremotesf_objects Qt::Test) add_executable(demangle_test log/demangle_test.cpp) add_test(NAME demangle_test COMMAND demangle_test) target_link_libraries(demangle_test PRIVATE tremotesf_objects Qt::Test) if (NOT TREMOTESF_WITH_HTTPLIB STREQUAL "none") include("${CMAKE_SOURCE_DIR}/cmake/FindCppHttplib.cmake") find_cpp_httplib() add_executable(requestrouter_test rpc/requestrouter_test.cpp) target_compile_definitions(requestrouter_test PRIVATE TEST_DATA_PATH="${CMAKE_CURRENT_SOURCE_DIR}/rpc/test-data") add_test(NAME requestrouter_test COMMAND requestrouter_test) target_link_libraries(requestrouter_test PRIVATE tremotesf_objects Qt::Test) if (TARGET PkgConfig::httplib) target_link_libraries(requestrouter_test PRIVATE PkgConfig::httplib) else () target_link_libraries(requestrouter_test PRIVATE httplib::httplib) endif () endif() add_executable(pathutils_test rpc/pathutils_test.cpp) add_test(NAME pathutils_test COMMAND pathutils_test) target_link_libraries(pathutils_test PRIVATE tremotesf_objects Qt::Test) add_executable(tracker_test rpc/tracker_test.cpp) add_test(NAME tracker_test COMMAND tracker_test) target_link_libraries(tracker_test PRIVATE tremotesf_objects Qt::Test) add_executable(servers_test rpc/servers_test.cpp) add_test(NAME servers_test COMMAND servers_test) target_link_libraries(servers_test PRIVATE tremotesf_objects Qt::Test) add_executable(servers_migration_test rpc/servers_migration_test.cpp) target_compile_definitions(servers_migration_test PRIVATE TEST_DATA_PATH="${CMAKE_CURRENT_SOURCE_DIR}/rpc/test-data") add_test(NAME servers_migration_test COMMAND servers_migration_test) target_link_libraries(servers_migration_test PRIVATE tremotesf_objects Qt::Test) add_executable(coroutines_test coroutines/coroutines_test.cpp) add_test(NAME coroutines_test COMMAND coroutines_test) target_link_libraries(coroutines_test PRIVATE tremotesf_objects Qt::Test) add_executable(magnetlinkparser_test magnetlinkparser_test.cpp) add_test(NAME magnetlinkparser_test COMMAND magnetlinkparser_test) target_link_libraries(magnetlinkparser_test PRIVATE tremotesf_objects Qt::Test) add_executable(torrentfileparser_test torrentfileparser_test.cpp) add_test(NAME torrentfileparser_test COMMAND torrentfileparser_test WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test-torrents") target_link_libraries(torrentfileparser_test PRIVATE tremotesf_objects Qt::Test) add_executable(torrentfilesmodel_test ui/itemmodels/torrentfilesmodel_test.cpp) add_test(NAME torrentfilesmodel_test COMMAND torrentfilesmodel_test) target_link_libraries(torrentfilesmodel_test PRIVATE tremotesf_objects Qt::Test) endif () set_common_options_on_targets() tremotesf-2.10.1/src/authors.html000066400000000000000000000010321523411710200167350ustar00rootroot00000000000000

Alexey Rochev <equeim@gmail.com>
%1

LuK1337 <priv.luk@gmail.com>
%2

Buck Melanoma
%2

Alex Bell
%2

otaconix
%2

Jeong-Hee Kang (keizie)
%2

tremotesf-2.10.1/src/bencodeparser.cpp000066400000000000000000000230221523411710200177050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "bencodeparser.h" #include #include #include #include #include #include #include #include #include "fileutils.h" namespace tremotesf::bencode { namespace { constexpr char integerPrefix = 'i'; constexpr char listPrefix = 'l'; constexpr char dictionaryPrefix = 'd'; constexpr char terminator = 'e'; constexpr char byteArraySeparator = ':'; // digits10 + 1 is maximum number of character needed to hold integer, +1 for minus sign, +1 for terminator character constexpr int integerBufferSize = std::numeric_limits::digits10 + 3; } template Expected Value::takeValue() && { return std::visit( [](auto&& value) -> Expected { using T = std::decay_t; if constexpr (std::same_as) { return std::forward(value); } else if constexpr (std::same_as && std::same_as) { auto string = QString::fromStdString(value); value.clear(); return string; } else { throw Error( Error::Type::Parsing, fmt::format("Value is not of {} type", getValueTypeName()) ); } }, std::move(mValue) ); } template Integer Value::takeValue() &&; template ByteArray Value::takeValue() &&; template List Value::takeValue() &&; template Dictionary Value::takeValue() &&; template QString Value::takeValue() &&; namespace { class Parser { public: explicit Parser(QFile& file, ReadRootDictionaryValueCallback onReadRootDictionaryValue) : mFile{file}, mOnReadRootDictionaryValue(std::move(onReadRootDictionaryValue)) {}; Value parse() { return parseValue(true); } private: Value parseValue(bool root = false) { const char byte = peekByte(); if (byte == integerPrefix) { return parseInteger(); } if (byte == listPrefix) { return parseList(); } if (byte == dictionaryPrefix) { return parseDictionary(root); } return parseByteArray(); } Dictionary parseDictionary(bool root) { return parseContainer([&](Dictionary& dict) { ByteArray key(parseByteArray()); const auto valuePos = mFile.pos(); Value value(parseValue()); if (root) { mOnReadRootDictionaryValue(key, valuePos, mFile.pos() - valuePos); } dict.emplace(std::move(key), std::move(value)); }); } List parseList() { return parseContainer([&](List& list) { list.push_back(parseValue()); }); } template ParseNextElement> Container parseContainer(ParseNextElement parseNextElement) { const auto containerPos = mFile.pos(); try { skipByte(); Container container{}; while (true) { if (peekByte() == terminator) { skipByte(); return container; } parseNextElement(container); } } catch (const Error& e) { std::throw_with_nested(Error( e.type(), fmt::format("Failed to parse {} at position {}", getValueTypeName(), containerPos) )); } } ByteArray parseByteArray() { const auto byteArrayPos = mFile.pos(); try { Integer size{}; try { size = readIntegerUntilTerminator(byteArraySeparator); } catch (const Error& e) { std::throw_with_nested(Error(e.type(), "Failed to parse byte array size")); } if (size < 0) { throw Error(Error::Type::Parsing, fmt::format("Incorrect byte array size {}", size)); } ByteArray byteArray(static_cast(size), 0); if (size != 0) { try { readBytes(mFile, byteArray); } catch (const QFileError&) { std::throw_with_nested(Error( Error::Type::Reading, fmt::format("Failed to read byte array data with size {}", size) )); } } return byteArray; } catch (const Error& e) { std::throw_with_nested( Error(e.type(), fmt::format("Failed to parse byte array at position {}", byteArrayPos)) ); } } Integer parseInteger() { const auto integerPos = mFile.pos(); try { skipByte(); return readIntegerUntilTerminator(terminator); } catch (const Error& e) { std::throw_with_nested( Error(e.type(), fmt::format("Failed to parse integer at position {}", integerPos)) ); } } Integer readIntegerUntilTerminator(char integerTerminator) { std::span peeked{}; try { peeked = peekBytes(mFile, mIntegerBuffer); } catch (const QFileError&) { std::throw_with_nested(Error(Error::Type::Reading, "Failed to read integer buffer")); } Integer integer{}; const auto result = std::from_chars(std::to_address(peeked.begin()), std::to_address(peeked.end()), integer); if (result.ec != std::errc{}) { throw Error( Error::Type::Parsing, fmt::format( "std::from_chars() failed with: {} (error code {} ({:#x}))", std::make_error_condition(result.ec).message(), std::to_underlying(result.ec), static_cast>>(result.ec) ) ); } if (result.ptr == std::to_address(peeked.end())) { throw Error( Error::Type::Parsing, fmt::format( "Didn't find integer terminator \"{}\", file is possibly truncated", integerTerminator ) ); } if (*result.ptr != integerTerminator) { throw Error( Error::Type::Parsing, fmt::format( R"(Integer terminator doesn't match: expected "{}", actual "{}")", integerTerminator, *result.ptr ) ); } const auto terminatorIndex = result.ptr - peeked.data(); skip(terminatorIndex + 1); return integer; } char peekByte() { std::array buffer{}; try { [[maybe_unused]] const auto peeked = peekBytes(mFile, buffer); } catch (const QFileError&) { std::throw_with_nested(Error(Error::Type::Reading, "Failed to peek byte")); } return buffer[0]; } void skipByte() { skip(1); } void skip(qint64 size) { try { skipBytes(mFile, size); } catch (const QFileError&) { std::throw_with_nested(Error(Error::Type::Reading, fmt::format("Failed to skip {} bytes", size))); } } QFile& mFile; ReadRootDictionaryValueCallback mOnReadRootDictionaryValue; std::array mIntegerBuffer{}; }; } Value parse(const QString& filePath, ReadRootDictionaryValueCallback onReadRootDictionaryValue) { QFile file(filePath); try { openFile(file, QIODevice::ReadOnly); } catch (const QFileError&) { std::throw_with_nested(Error(Error::Type::Reading, "Failed to open file")); } return parse(file, std::move(onReadRootDictionaryValue)); } Value parse(QFile& device, ReadRootDictionaryValueCallback onReadRootDictionaryValue) { return Parser(device, std::move(onReadRootDictionaryValue)).parse(); } } tremotesf-2.10.1/src/bencodeparser.h000066400000000000000000000077221523411710200173630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_BENCODEPARSER_H #define TREMOTESF_BENCODEPARSER_H #include #include #include #include #include #include #include #include #include class QFile; namespace tremotesf::bencode { class Value; using Integer = int64_t; using ByteArray = std::string; using List = std::list; struct DictionaryComparator { using is_transparent = void; inline bool operator()(const ByteArray& key1, const ByteArray& key2) const { return key1 < key2; } inline bool operator()(std::string_view key1, const ByteArray& key2) const { return key1 < key2; } inline bool operator()(const ByteArray& key1, std::string_view key2) const { return key1 < key2; } }; using Dictionary = std::map; template concept ValueType = std::same_as || std::same_as || std::same_as || std::same_as || std::same_as; template inline constexpr const char* getValueTypeName() { if constexpr (std::same_as) { return "Integer"; } else if constexpr (std::same_as) { return "ByteArray"; } else if constexpr (std::same_as) { return "List"; } else if constexpr (std::same_as) { return "Dictionary"; } else if constexpr (std::same_as) { return "String"; } } class Value { public: Value() = default; inline Value(Integer value) : mValue{value} {} inline Value(ByteArray&& value) : mValue{std::move(value)} {} inline Value(List&& value) : mValue{std::move(value)} {} inline Value(Dictionary&& value) : mValue{std::move(value)} {} inline Integer takeInteger() &&; inline ByteArray takeByteArray() &&; inline QString takeString() &&; inline List takeList() &&; inline Dictionary takeDictionary() &&; template Expected takeValue() &&; private: using Variant = std::variant; Variant mValue{}; }; extern template Integer Value::takeValue() &&; extern template ByteArray Value::takeValue() &&; extern template List Value::takeValue() &&; extern template Dictionary Value::takeValue() &&; extern template QString Value::takeValue() &&; inline Integer Value::takeInteger() && { return std::move(*this).takeValue(); } inline ByteArray Value::takeByteArray() && { return std::move(*this).takeValue(); } inline QString Value::takeString() && { return std::move(*this).takeValue(); } inline List Value::takeList() && { return std::move(*this).takeValue(); } inline Dictionary Value::takeDictionary() && { return std::move(*this).takeValue(); } class Error : public std::runtime_error { public: enum class Type { Reading, Parsing }; inline explicit Error(Type type, const char* what) : std::runtime_error(what), mType(type) {} inline explicit Error(Type type, const std::string& what) : std::runtime_error(what), mType(type) {} inline Type type() const { return mType; } private: Type mType; }; using ReadRootDictionaryValueCallback = std::function; Value parse(const QString& filePath, ReadRootDictionaryValueCallback onReadRootDictionaryValue); Value parse(QFile& device, ReadRootDictionaryValueCallback onReadRootDictionaryValue); } #endif // TREMOTESF_BENCODEPARSER_H tremotesf-2.10.1/src/coroutines/000077500000000000000000000000001523411710200165605ustar00rootroot00000000000000tremotesf-2.10.1/src/coroutines/coroutinefwd.h000066400000000000000000000007551523411710200214500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINEFWD_H #define TREMOTESF_COROUTINEFWD_H #include namespace tremotesf { namespace impl { template concept CoroutineReturnValue = std::same_as || std::movable; class StandaloneCoroutine; } template class Coroutine; } #endif // TREMOTESF_COROUTINEFWD_H tremotesf-2.10.1/src/coroutines/coroutines.cpp000066400000000000000000000067021523411710200214630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "coroutines.h" #include "log/log.h" namespace tremotesf::impl { void CoroutinePromiseBase::setParentCoroutineHandle(std::coroutine_handle<> parentCoroutineHandle) { mParentCoroutineHandle = parentCoroutineHandle; } void CoroutinePromiseBase::interruptChildAwaiter() { std::visit( [&](auto& callback) { using Type = std::decay_t; if constexpr (std::same_as) { mOwningStandaloneCoroutine->completeCancellation(); } else if constexpr (std::same_as>) { callback(); } else if constexpr (std::same_as) { } }, mChildAwaiterInterruptionCallback ); } bool CoroutinePromiseBase::onStartedAwaiting(JustCompleteCancellation) { if (mOwningStandaloneCoroutine->completeCancellation()) { return false; } mChildAwaiterInterruptionCallback = JustCompleteCancellation{}; return true; } bool CoroutinePromiseBase::onStartedAwaiting(std::function&& interruptionCallback) { if (mOwningStandaloneCoroutine->completeCancellation()) { return false; } mChildAwaiterInterruptionCallback = std::move(interruptionCallback); return true; } void CoroutinePromiseBase::abortNoParent(std::coroutine_handle<> handle) { fatal().log("No parent coroutine when completing coroutine {}", handle.address()); Q_UNREACHABLE(); } void CoroutinePromise::invokeCompletionCallbackForStandaloneCoroutine() { // Completion callback will destroy Coroutine<> object, but coroutine itself will be destroyed later by compiler's injected machinery // because CoroutinePromiseFinalSuspendAwaiter::await_ready will return false // Pass true for coroutineWillBeDestroyedAutomatically parameter here so that Coroutine<>'s destructor won't destroy coroutine resulting in double free mOwningStandaloneCoroutine->invokeCompletionCallback(mUnhandledException, true); } void StandaloneCoroutine::cancel() { if (mCancellationState != CancellationState::NotCancelled) { return; } mCancellationState = CancellationState::Cancelling; mCoroutine.mHandle.promise().interruptChildAwaiter(); } bool StandaloneCoroutine::completeCancellation() { switch (mCancellationState) { case CancellationState::NotCancelled: return false; case CancellationState::Cancelling: mCancellationState = CancellationState::Cancelled; invokeCompletionCallback({}, false); return true; case CancellationState::Cancelled: return true; } return false; } void StandaloneCoroutine::invokeCompletionCallback( const std::exception_ptr& unhandledException, bool coroutineWillBeDestroyedAutomatically ) { if (coroutineWillBeDestroyedAutomatically) { mCoroutine.mHandle = nullptr; } mCompletionCallback(unhandledException); } void StandaloneCoroutine::setCompletionCallback(std::function&& callback) { mCompletionCallback = std::move(callback); } } tremotesf-2.10.1/src/coroutines/coroutines.h000066400000000000000000000265731523411710200211400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_H #define TREMOTESF_COROUTINES_H #include #include #include #include #include #include #include #include #include "coroutinefwd.h" namespace tremotesf { namespace impl { template class CoroutinePromise; template class CoroutineAwaiter; } template class [[nodiscard]] Coroutine { public: using promise_type = impl::CoroutinePromise; inline explicit Coroutine(std::coroutine_handle> handle) : mHandle(std::move(handle)) {} inline Coroutine(Coroutine&& other) noexcept : mHandle(std::exchange(other.mHandle, nullptr)) {} inline Coroutine& operator=(Coroutine&& other) noexcept { mHandle = std::exchange(other.mHandle, nullptr); return *this; } Q_DISABLE_COPY(Coroutine) inline ~Coroutine() { if (mHandle) { mHandle.destroy(); } } inline impl::CoroutineAwaiter operator co_await(); inline void* address() const { return mHandle.address(); } private: std::coroutine_handle> mHandle; friend class impl::StandaloneCoroutine; }; namespace impl { class CoroutinePromiseBase { public: inline ~CoroutinePromiseBase() = default; Q_DISABLE_COPY_MOVE(CoroutinePromiseBase) // promise object contract begin inline std::suspend_always initial_suspend() { return {}; } inline void unhandled_exception() { mUnhandledException = std::current_exception(); } // promise object contract end void interruptChildAwaiter(); struct JustCompleteCancellation {}; bool onStartedAwaiting(JustCompleteCancellation); bool onStartedAwaiting(std::function&& interruptionCallback); inline void onAboutToResume() { mChildAwaiterInterruptionCallback = std::monostate{}; } inline StandaloneCoroutine* owningStandaloneCoroutine() const { return mOwningStandaloneCoroutine; } inline void setOwningStandaloneCoroutine(StandaloneCoroutine* root) { mOwningStandaloneCoroutine = root; } void setParentCoroutineHandle(std::coroutine_handle<> parentCoroutineHandle); void rethrowException() { if (mUnhandledException) { std::rethrow_exception(mUnhandledException); } } protected: inline CoroutinePromiseBase() = default; [[noreturn]] static void abortNoParent(std::coroutine_handle<> handle); std::coroutine_handle<> mParentCoroutineHandle{}; std::variant> mChildAwaiterInterruptionCallback{}; std::exception_ptr mUnhandledException{}; StandaloneCoroutine* mOwningStandaloneCoroutine{}; }; class CoroutinePromiseFinalSuspendAwaiter final { public: explicit CoroutinePromiseFinalSuspendAwaiter(std::coroutine_handle<> parentCoroutine) : mParentCoroutine(parentCoroutine) {} // If there is no parent coroutine then await_ready returns false which causes our coroutine to be destroyed // Otherwise control is transferred to parent coroutine, which destroys CoroutineAwaiter and therefore our coroutine inline bool await_ready() noexcept { return !mParentCoroutine; } std::coroutine_handle<> await_suspend(std::coroutine_handle<>) noexcept { return mParentCoroutine; } inline void await_resume() noexcept {} private: std::coroutine_handle<> mParentCoroutine; }; template class CoroutinePromise final : public CoroutinePromiseBase { public: inline CoroutinePromise() : mCoroutineHandle(std::coroutine_handle>::from_promise(*this)) {} // promise object contract begin inline Coroutine get_return_object() { return Coroutine(mCoroutineHandle); } inline void return_value(const T& valueToReturn) { mValue = valueToReturn; } inline void return_value(T&& valueToReturn) { mValue = std::move(valueToReturn); } inline CoroutinePromiseFinalSuspendAwaiter final_suspend() noexcept { if (mParentCoroutineHandle) { return CoroutinePromiseFinalSuspendAwaiter(mParentCoroutineHandle); } abortNoParent(mCoroutineHandle); } // promise object contract end inline T takeValueOrRethrowException() { rethrowException(); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) T value = std::move(mValue).value(); mValue.reset(); return value; } private: std::coroutine_handle> mCoroutineHandle; std::optional mValue{}; }; template<> class CoroutinePromise final : public CoroutinePromiseBase { public: // promise object contract begin inline Coroutine get_return_object() { return Coroutine(std::coroutine_handle>::from_promise(*this)); } inline void return_void() {} inline CoroutinePromiseFinalSuspendAwaiter final_suspend() noexcept { if (mParentCoroutineHandle) { return CoroutinePromiseFinalSuspendAwaiter(mParentCoroutineHandle); } invokeCompletionCallbackForStandaloneCoroutine(); return CoroutinePromiseFinalSuspendAwaiter(nullptr); } // promise object contract end void invokeCompletionCallbackForStandaloneCoroutine(); inline void takeValueOrRethrowException() { rethrowException(); } }; template class CoroutineAwaiter final { public: inline explicit CoroutineAwaiter(std::coroutine_handle> handle) : mHandle(handle) {} inline ~CoroutineAwaiter() = default; Q_DISABLE_COPY_MOVE(CoroutineAwaiter) inline bool await_ready() { return false; } template inline std::coroutine_handle<> await_suspend(std::coroutine_handle parentCoroutineHandle) { if constexpr (std::derived_from) { mParentCoroutinePromise = &parentCoroutineHandle.promise(); if (!mParentCoroutinePromise->onStartedAwaiting([this] { mHandle.promise().interruptChildAwaiter(); })) { return std::noop_coroutine(); } mHandle.promise().setOwningStandaloneCoroutine( mParentCoroutinePromise->owningStandaloneCoroutine() ); } mHandle.promise().setParentCoroutineHandle(parentCoroutineHandle); return mHandle; } inline T await_resume() { if (mParentCoroutinePromise) { mParentCoroutinePromise->onAboutToResume(); } return mHandle.promise().takeValueOrRethrowException(); } private: std::coroutine_handle> mHandle; CoroutinePromiseBase* mParentCoroutinePromise{}; }; template requires(!std::same_as) [[nodiscard]] bool startAwaiting(std::coroutine_handle handle) { if constexpr (std::derived_from) { return handle.promise().onStartedAwaiting(CoroutinePromiseBase::JustCompleteCancellation{}); } else { return true; } } inline void resume(std::coroutine_handle<> handle, CoroutinePromiseBase* promise) { if (promise) { promise->onAboutToResume(); } handle.resume(); } template requires(!std::same_as) void resume(std::coroutine_handle handle) { CoroutinePromiseBase* promise{}; if constexpr (std::derived_from) { promise = &handle.promise(); } resume(handle, promise); } class StandaloneCoroutine { public: inline explicit StandaloneCoroutine(Coroutine&& coroutine) : mCoroutine(std::move(coroutine)) { mCoroutine.mHandle.promise().setOwningStandaloneCoroutine(this); } inline ~StandaloneCoroutine() = default; Q_DISABLE_COPY_MOVE(StandaloneCoroutine) inline void* address() const { return mCoroutine.address(); } inline void start() { mCoroutine.mHandle.resume(); } inline StandaloneCoroutine* rootCoroutine() const { return mRootCoroutine; } inline void setRootCoroutine(StandaloneCoroutine* coroutine) { mRootCoroutine = coroutine; } void cancel(); bool completeCancellation(); void invokeCompletionCallback( const std::exception_ptr& unhandledException, bool coroutineWillBeDestroyedAutomatically ); void setCompletionCallback(std::function&& callback); private: Coroutine mCoroutine; StandaloneCoroutine* mRootCoroutine{}; std::function mCompletionCallback{}; enum class CancellationState : char { NotCancelled, Cancelling, Cancelled }; CancellationState mCancellationState{CancellationState::NotCancelled}; }; class [[nodiscard]] CancellationAwaiter final { public: inline bool await_ready() { return false; } template Promise> inline void await_suspend(std::coroutine_handle handle) { if (startAwaiting(handle)) { handle.promise().owningStandaloneCoroutine()->rootCoroutine()->cancel(); } } inline void await_resume() {} }; } template impl::CoroutineAwaiter Coroutine::operator co_await() { return impl::CoroutineAwaiter(mHandle); } } #define cancelCoroutine() \ do { \ co_await tremotesf::impl::CancellationAwaiter{}; \ qFatal("CancellationAwaiter resumed"); /* CancellationAwaiter must never resume */ \ } while (false); #endif // TREMOTESF_COROUTINES_H tremotesf-2.10.1/src/coroutines/coroutines_test.cpp000066400000000000000000000220641523411710200225210ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include "coroutines/coroutines.h" #include "coroutines/scope.h" #include "coroutines/timer.h" #include "coroutines/waitall.h" using namespace std::chrono_literals; using std::chrono::duration_cast; using std::chrono::milliseconds; // NOLINTBEGIN(cppcoreguidelines-avoid-reference-coroutine-parameters) namespace tremotesf { class CoroutinesTest final : public QObject { Q_OBJECT private slots: void checkImmediateCoroutine() { CoroutineScope scope{}; bool executed{}; scope.launch(immediateCoroutine(executed)); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(executed); } void checkImmediateCoroutineCancellationFromInside() { CoroutineScope scope{}; bool executed{}; scope.launch(immediateCoroutineCancellingFromInside(executed)); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(!executed); } void checkImmediateCoroutineWithNested() { CoroutineScope scope{}; int value{}; scope.launch(immediateCoroutineWithNested(value)); QCOMPARE(scope.coroutinesCount(), 0); QCOMPARE(value, testReturnValue); } void checkImmediateCoroutineWithNestedCancellationFromInside() { CoroutineScope scope{}; int value{}; scope.launch(immediateCoroutineWithNestedCancellingFromInside(value)); QCOMPARE(scope.coroutinesCount(), 0); QCOMPARE(value, 0); } void checkSuspendingCoroutine() { CoroutineScope scope{}; bool executed{}; scope.launch(suspendingCoroutine(executed)); QCOMPARE(scope.coroutinesCount(), 1); waitTestTime(); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(executed); } void checkSuspendingCoroutineCancellation() { CoroutineScope scope{}; bool executed{}; scope.launch(suspendingCoroutine(executed)); QCOMPARE(scope.coroutinesCount(), 1); scope.cancelAll(); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(!executed); } void checkSuspendingCoroutineCancellationFromInside() { CoroutineScope scope{}; bool executed{}; scope.launch(suspendingCoroutine(executed)); QCOMPARE(scope.coroutinesCount(), 1); scope.cancelAll(); waitTestTime(); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(!executed); } void checkSuspendingWithNested() { CoroutineScope scope{}; int value{}; scope.launch(suspendingCoroutineWithNested(value)); QCOMPARE(scope.coroutinesCount(), 1); waitTestTime(); QCOMPARE(scope.coroutinesCount(), 0); QCOMPARE(value, testReturnValue); } void checkSuspendingWithNestedCancellation() { CoroutineScope scope{}; int value{}; scope.launch(suspendingCoroutineWithNested(value)); QCOMPARE(scope.coroutinesCount(), 1); scope.cancelAll(); QCOMPARE(scope.coroutinesCount(), 0); QCOMPARE(value, 0); } void checkWaitAllCoroutineImmediate() { CoroutineScope scope{}; WaitAllCoroutineSideEffects sideEffects{}; scope.launch(waitAllCoroutineImmediate(sideEffects)); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(sideEffects.executed1); QVERIFY(sideEffects.executed2); QVERIFY(sideEffects.executedAll); } void checkWaitAllCoroutineImmediateCancellationFromInside() { CoroutineScope scope{}; WaitAllCoroutineSideEffects sideEffects{}; scope.launch(waitAllCoroutineImmediateCancellingFromInside(sideEffects)); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(sideEffects.executed1); QVERIFY(!sideEffects.executed2); QVERIFY(!sideEffects.executedAll); } void checkWaitAllCoroutineSuspending() { CoroutineScope scope{}; WaitAllCoroutineSideEffects sideEffects{}; scope.launch(waitAllCoroutineSuspending(sideEffects)); QCOMPARE(scope.coroutinesCount(), 1); waitTestTime(); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(sideEffects.executed1); QVERIFY(sideEffects.executed2); QVERIFY(sideEffects.executedAll); } void checkWaitAllCoroutineSuspendingCancellation() { CoroutineScope scope{}; WaitAllCoroutineSideEffects sideEffects{}; scope.launch(waitAllCoroutineSuspending(sideEffects)); QCOMPARE(scope.coroutinesCount(), 1); scope.cancelAll(); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(!sideEffects.executed1); QVERIFY(!sideEffects.executed2); QVERIFY(!sideEffects.executedAll); } void checkWaitAllCoroutineSuspendingCancellationFromInside() { CoroutineScope scope{}; WaitAllCoroutineSideEffects sideEffects{}; scope.launch(waitAllCoroutineSuspendingCancellingFromInside(sideEffects)); QCOMPARE(scope.coroutinesCount(), 1); waitTestTime(); QCOMPARE(scope.coroutinesCount(), 0); QVERIFY(!sideEffects.executed1); QVERIFY(!sideEffects.executed2); QVERIFY(!sideEffects.executedAll); } private: static constexpr int testReturnValue = 42; static constexpr auto testWaitTime = 40ms; static constexpr auto testWaitTimeReduced = 20ms; void waitTestTime() { // Default QTimer guarantees 5% accuracy QTest::qWait(duration_cast(testWaitTime * 1.05).count() + 1); QCoreApplication::processEvents(); } Coroutine<> immediateCoroutine(bool& executed) { executed = true; co_return; } Coroutine<> immediateCoroutineCancellingFromInside(bool& executed) { cancelCoroutine(); executed = true; co_return; } Coroutine immediateCoroutineReturningValue() { co_return testReturnValue; } Coroutine<> immediateCoroutineWithNested(int& value) { value = co_await immediateCoroutineReturningValue(); } Coroutine immediateCoroutineReturningValueCancellingFromInside() { cancelCoroutine(); co_return testReturnValue; } Coroutine<> immediateCoroutineWithNestedCancellingFromInside(int& value) { value = co_await immediateCoroutineReturningValueCancellingFromInside(); } Coroutine<> suspendingCoroutine(bool& executed) { co_await waitFor(testWaitTime); executed = true; } Coroutine suspendingReturningValue() { co_await waitFor(testWaitTime); co_return testReturnValue; } Coroutine<> suspendingCoroutineWithNested(int& value) { value = co_await suspendingReturningValue(); } Coroutine<> suspendingCoroutineCancellingFromInside(bool& executed) { co_await waitFor(testWaitTimeReduced); cancelCoroutine(); executed = true; } struct WaitAllCoroutineSideEffects { bool executed1; bool executed2; bool executedAll; }; Coroutine<> waitAllCoroutineImmediate(WaitAllCoroutineSideEffects& sideEffects) { co_await waitAll(immediateCoroutine(sideEffects.executed1), immediateCoroutine(sideEffects.executed2)); sideEffects.executedAll = true; } Coroutine<> waitAllCoroutineImmediateCancellingFromInside(WaitAllCoroutineSideEffects& sideEffects) { co_await waitAll( immediateCoroutine(sideEffects.executed1), immediateCoroutineCancellingFromInside(sideEffects.executed2) ); sideEffects.executedAll = true; } Coroutine<> waitAllCoroutineSuspending(WaitAllCoroutineSideEffects& sideEffects) { co_await waitAll(suspendingCoroutine(sideEffects.executed1), suspendingCoroutine(sideEffects.executed2)); sideEffects.executedAll = true; } Coroutine<> waitAllCoroutineSuspendingCancellingFromInside(WaitAllCoroutineSideEffects& sideEffects) { co_await waitAll( suspendingCoroutine(sideEffects.executed1), suspendingCoroutineCancellingFromInside(sideEffects.executed2) ); sideEffects.executedAll = true; } }; } // NOLINTEND(cppcoreguidelines-avoid-reference-coroutine-parameters) QTEST_GUILESS_MAIN(tremotesf::CoroutinesTest) #include "coroutines_test.moc" tremotesf-2.10.1/src/coroutines/dbus.h000066400000000000000000000025021523411710200176650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_DBUS_H #define TREMOTESF_COROUTINES_DBUS_H #include #include "qobjectsignal.h" class QDBusPendingCallWatcher; namespace tremotesf { namespace impl { template class DbusReplyAwaitable final : public SignalAwaitable { public: inline explicit DbusReplyAwaitable(const QDBusPendingReply& reply) : SignalAwaitable(nullptr, &QDBusPendingCallWatcher::finished), mReply(reply) { mSender = &mWatcher; } inline ~DbusReplyAwaitable() = default; Q_DISABLE_COPY_MOVE(DbusReplyAwaitable) inline bool await_ready() { return mReply.isFinished(); } inline QDBusPendingReply await_resume() { return mReply; }; private: QDBusPendingReply mReply; QDBusPendingCallWatcher mWatcher{mReply}; }; } template inline impl::DbusReplyAwaitable operator co_await(const QDBusPendingReply& reply) { return impl::DbusReplyAwaitable(reply); } } #endif // TREMOTESF_COROUTINES_DBUS_H tremotesf-2.10.1/src/coroutines/hostinfo.h000066400000000000000000000032331523411710200205630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_HOSTINFO_H #define TREMOTESF_COROUTINES_HOSTINFO_H #include #include #include "coroutines.h" namespace tremotesf { namespace impl { class HostInfoAwaitable final { public: inline explicit HostInfoAwaitable(QString name) : mName(std::move(name)) {} inline ~HostInfoAwaitable() { if (mLookupId) { QHostInfo::abortHostLookup(*mLookupId); } }; Q_DISABLE_COPY_MOVE(HostInfoAwaitable) inline bool await_ready() { return false; } template inline void await_suspend(std::coroutine_handle handle) { if (!startAwaiting(handle)) { return; } mLookupId = QHostInfo::lookupHost(mName, &mReceiver, [this, handle](QHostInfo hostInfo) { mLookupId = std::nullopt; mResult = std::move(hostInfo); resume(handle); }); } // NOLINTNEXTLINE(bugprone-unchecked-optional-access) inline QHostInfo await_resume() { return std::move(mResult).value(); }; private: QString mName; QObject mReceiver{}; std::optional mLookupId{}; std::optional mResult{}; }; } inline impl::HostInfoAwaitable lookupHost(QString name) { return impl::HostInfoAwaitable(std::move(name)); } } #endif // TREMOTESF_COROUTINES_HOSTINFO_H tremotesf-2.10.1/src/coroutines/network.h000066400000000000000000000017351523411710200204300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_NETWORK_H #define TREMOTESF_COROUTINES_NETWORK_H #include #include "qobjectsignal.h" namespace tremotesf { namespace impl { class NetworkReplyAwaitable final : public SignalAwaitable { public: inline explicit NetworkReplyAwaitable(QNetworkReply* reply) : SignalAwaitable(reply, &QNetworkReply::finished) {} inline ~NetworkReplyAwaitable() = default; Q_DISABLE_COPY_MOVE(NetworkReplyAwaitable) inline bool await_ready() { return mSender->isFinished(); } private: QObject mReceiver{}; }; } inline impl::NetworkReplyAwaitable operator co_await(QNetworkReply& reply) { return impl::NetworkReplyAwaitable(&reply); } } #endif // TREMOTESF_COROUTINES_NETWORK_H tremotesf-2.10.1/src/coroutines/qobjectsignal.h000066400000000000000000000030231523411710200215540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_QOBJECTSIGNAL_H #define TREMOTESF_COROUTINES_QOBJECTSIGNAL_H #include #include "coroutines.h" namespace tremotesf { namespace impl { template class SignalAwaitable { public: inline explicit SignalAwaitable(const Object* sender, PointerToMemberFunction signal) : mSender(sender), mSignal(signal) {} inline ~SignalAwaitable() = default; Q_DISABLE_COPY_MOVE(SignalAwaitable) inline bool await_ready() { return false; } template inline void await_suspend(std::coroutine_handle handle) { if (startAwaiting(handle)) { QObject::connect(mSender, mSignal, &mReceiver, [handle] { resume(handle); }); } } inline void await_resume() {}; protected: const Object* mSender; PointerToMemberFunction mSignal; QObject mReceiver{}; }; } template Object, typename PointerToMemberFunction> requires(std::is_member_function_pointer_v) inline Coroutine<> waitForSignal(const Object* object, PointerToMemberFunction signal) { co_await impl::SignalAwaitable(object, signal); } } #endif // TREMOTESF_COROUTINES_QOBJECTSIGNAL_H tremotesf-2.10.1/src/coroutines/scope.cpp000066400000000000000000000043461523411710200204040ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "coroutines/scope.h" #include "coroutines/coroutines.h" #include "log/log.h" namespace tremotesf { namespace { void handleException(const std::exception_ptr& unhandledException) { if (!unhandledException) return; warning().log("Unhandled exception in coroutine"); // Make sure we terminate immediately try { std::rethrow_exception(unhandledException); } catch (...) { std::terminate(); } } } CoroutineScope::~CoroutineScope() { if (mCoroutines.empty()) return; for (auto* coroutine : mCoroutines) { // NOLINTNEXTLINE(bugprone-unhandled-exception-at-new) coroutine->setCompletionCallback([coroutine](const std::exception_ptr& unhandledException) { handleException(unhandledException); delete coroutine; }); coroutine->cancel(); } } void CoroutineScope::launch(Coroutine<> coroutine) { auto* root = new impl::StandaloneCoroutine(std::move(coroutine)); mCoroutines.push_back(root); root->setRootCoroutine(root); root->setCompletionCallback([this, root](const std::exception_ptr& unhandledException) { onCoroutineCompleted(root, unhandledException); }); root->start(); } void CoroutineScope::cancelAll() { // Copy vector to handle the case when coroutine completes immediately and is erased from vector while we are iterating for (auto* coroutine : std::vector(mCoroutines)) { coroutine->cancel(); } } void CoroutineScope::onCoroutineCompleted( impl::StandaloneCoroutine* coroutine, const std::exception_ptr& unhandledException ) { handleException(unhandledException); const auto found = std::ranges::find(mCoroutines, coroutine); if (found == mCoroutines.end()) { fatal().log("Did not find completed coroutine {} in CoroutineScope", coroutine->address()); Q_UNREACHABLE(); } mCoroutines.erase(found); delete coroutine; } } tremotesf-2.10.1/src/coroutines/scope.h000066400000000000000000000015051523411710200200430ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_SCOPE_H #define TREMOTESF_COROUTINES_SCOPE_H #include #include #include #include "coroutinefwd.h" namespace tremotesf { class CoroutineScope { public: CoroutineScope() = default; ~CoroutineScope(); Q_DISABLE_COPY_MOVE(CoroutineScope) void launch(Coroutine<> coroutine); void cancelAll(); inline size_t coroutinesCount() const { return mCoroutines.size(); } private: void onCoroutineCompleted(impl::StandaloneCoroutine* coroutine, const std::exception_ptr& unhandledException); std::vector mCoroutines{}; }; } #endif // TREMOTESF_COROUTINES_SCOPE_H tremotesf-2.10.1/src/coroutines/threadpool.h000066400000000000000000000124671523411710200211040ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_THREADPOOL_H #define TREMOTESF_COROUTINES_THREADPOOL_H #include #include #include #include #include "coroutines.h" #ifdef Q_OS_WIN # include "startup/windowsfatalerrorhandlers.h" #endif namespace tremotesf { namespace impl { template> class ThreadPoolAwaitable final { public: inline explicit ThreadPoolAwaitable(QThreadPool* threadPool, Function&& function) : mThreadPool(threadPool), mFunction(std::move(function)) {} inline ~ThreadPoolAwaitable() { if (mSharedData) { mSharedData->cancelled = true; } } Q_DISABLE_COPY_MOVE(ThreadPoolAwaitable) inline bool await_ready() { return false; } template inline void await_suspend(std::coroutine_handle handle) { if (!startAwaiting(handle)) { return; } mSharedData = std::make_shared(); mSharedData->coroutineHandle = handle; if constexpr (std::derived_from) { mSharedData->coroutinePromise = &handle.promise(); } mThreadPool->start(new Runnable(std::move(mFunction), mSharedData)); } T await_resume() { if (mSharedData->unhandledException) { std::rethrow_exception(mSharedData->unhandledException); } if constexpr (!std::is_void_v) { // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return std::move(mSharedData->result).value(); } } private: struct Empty {}; using ResultValue = std::conditional_t, Empty, T>; struct SharedData { QObject receiver{}; std::atomic_bool cancelled{}; std::optional result{}; std::exception_ptr unhandledException{}; std::coroutine_handle<> coroutineHandle{}; CoroutinePromiseBase* coroutinePromise{}; }; class Runnable : public QRunnable { public: explicit Runnable(Function&& function, const std::shared_ptr& sharedData) : mFunction(std::move(function)), mSharedData(sharedData) {} inline ~Runnable() override = default; Q_DISABLE_COPY_MOVE(Runnable) void run() override { if (mSharedData->cancelled) { return; } #ifdef Q_OS_WIN windowsSetUpFatalErrorHandlersInThread(); #endif try { if constexpr (std::is_void_v) { mFunction(); } else { mSharedData->result = mFunction(); } } catch (...) { mSharedData->unhandledException = std::current_exception(); } if (mSharedData->cancelled) { return; } QMetaObject::invokeMethod(&(mSharedData->receiver), [sharedData = mSharedData] { if (!sharedData->cancelled) { resume(sharedData->coroutineHandle, sharedData->coroutinePromise); } }); } private: Function mFunction; std::shared_ptr mSharedData; }; QThreadPool* mThreadPool; Function mFunction; std::shared_ptr mSharedData{}; }; } template inline auto runOnThreadPool(QThreadPool* threadPool, Function&& function) { return impl::ThreadPoolAwaitable(threadPool, std::forward(function)); } template inline auto runOnThreadPool(Function&& function) { return runOnThreadPool(QThreadPool::globalInstance(), std::forward(function)); } template Function> requires(sizeof...(Args) != 0) inline auto runOnThreadPool(QThreadPool* threadPool, Function&& function, Args&&... args) { return impl::ThreadPoolAwaitable( threadPool, [function = std::forward(function), ... args = std::forward(args)]() mutable { return function(std::forward(args)...); } ); } template Function> requires(sizeof...(Args) != 0) inline auto runOnThreadPool(Function&& function, Args&&... args) { return runOnThreadPool( QThreadPool::globalInstance(), std::forward(function), std::forward(args)... ); } } #endif // TREMOTESF_COROUTINES_THREADPOOL_H tremotesf-2.10.1/src/coroutines/timer.h000066400000000000000000000023021523411710200200460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_TIMER_H #define TREMOTESF_COROUTINES_TIMER_H #include #include #include "coroutines.h" namespace tremotesf { namespace impl { class TimerAwaitable final { public: inline explicit TimerAwaitable(std::chrono::milliseconds duration) : mDuration(duration) {} inline ~TimerAwaitable() = default; Q_DISABLE_COPY_MOVE(TimerAwaitable) inline bool await_ready() { return mDuration.count() == 0; } template inline void await_suspend(std::coroutine_handle handle) { if (startAwaiting(handle)) { QTimer::singleShot(mDuration, &mReceiver, [handle] { resume(handle); }); } } inline void await_resume() {}; private: std::chrono::milliseconds mDuration; QObject mReceiver{}; }; } inline impl::TimerAwaitable waitFor(std::chrono::milliseconds duration) { return impl::TimerAwaitable(duration); } } #endif // TREMOTESF_COROUTINES_TIMER_H tremotesf-2.10.1/src/coroutines/waitall.cpp000066400000000000000000000062361523411710200207300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "coroutines/waitall.h" #include "log/log.h" namespace tremotesf::impl { namespace { template inline std::vector getPointers(std::list& list) { std::vector pointers{}; pointers.reserve(list.size()); for (auto& item : list) { pointers.push_back(&item); } return pointers; } } void MultipleCoroutinesAwaiter::await_resume() { if (mUnhandledException) { std::rethrow_exception(mUnhandledException); } } void MultipleCoroutinesAwaiter::awaitSuspendImpl() { if (mParentCoroutinePromise && !mParentCoroutinePromise->onStartedAwaiting([this] { cancelAll(); })) { return; } // Copy pointers to handle the case when coroutine completes immediately and is erased from list while we are iterating for (auto* coroutine : getPointers(mCoroutines)) { if (mParentCoroutinePromise) { coroutine->setRootCoroutine(mParentCoroutinePromise->owningStandaloneCoroutine()->rootCoroutine()); } coroutine->setCompletionCallback([this, coroutine](const std::exception_ptr& unhandledException) { onCoroutineCompleted(coroutine, unhandledException); }); coroutine->start(); } } void MultipleCoroutinesAwaiter::onCoroutineCompleted( StandaloneCoroutine* coroutine, const std::exception_ptr& unhandledException ) { if (unhandledException && !mUnhandledException) { mUnhandledException = unhandledException; } const auto found = std::ranges::find_if(mCoroutines, [coroutine](auto& c) { return &c == coroutine; }); if (found == mCoroutines.end()) { fatal().log("Did not find completed coroutine {} in MultipleCoroutinesAwaiter", coroutine->address()); Q_UNREACHABLE(); } mCoroutines.erase(found); if (mCancellingCoroutines) { return; } if (mCoroutines.empty()) { onAllCoroutinesCompleted(); } else if ((mUnhandledException || mCancelAfterFirst) && !mCancelledCoroutinesAndWaitingForCompletionCallbacks) { cancelAll(); } } void MultipleCoroutinesAwaiter::onAllCoroutinesCompleted() { if (mParentCoroutinePromise && mParentCoroutinePromise->owningStandaloneCoroutine()->completeCancellation()) { return; } resume(mParentCoroutineHandle, mParentCoroutinePromise); } void MultipleCoroutinesAwaiter::cancelAll() { mCancellingCoroutines = true; // Copy pointers to handle the case when coroutine is cancelled immediately and is erased from list while we are iterating for (auto* coroutine : getPointers(mCoroutines)) { coroutine->cancel(); } mCancellingCoroutines = false; mCancelledCoroutinesAndWaitingForCompletionCallbacks = true; if (mCoroutines.empty()) { onAllCoroutinesCompleted(); } } } tremotesf-2.10.1/src/coroutines/waitall.h000066400000000000000000000063741523411710200204000ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COROUTINES_WAITALL_H #define TREMOTESF_COROUTINES_WAITALL_H #include #include "coroutines.h" namespace tremotesf { namespace impl { class MultipleCoroutinesAwaiter final { public: inline explicit MultipleCoroutinesAwaiter(std::vector>&& coroutines, bool cancelAfterFirst) : mCancelAfterFirst(cancelAfterFirst) { for (auto&& coroutine : std::move(coroutines)) { mCoroutines.emplace_back(std::move(coroutine)); } } template inline explicit MultipleCoroutinesAwaiter(bool cancelAfterFirst, Coroutines&&... coroutines) : mCancelAfterFirst(cancelAfterFirst) { (mCoroutines.emplace_back(std::forward(coroutines)), ...); } inline ~MultipleCoroutinesAwaiter() = default; Q_DISABLE_COPY_MOVE(MultipleCoroutinesAwaiter) inline bool await_ready() { return mCoroutines.empty(); } template inline void await_suspend(std::coroutine_handle parentCoroutineHandle) { mParentCoroutineHandle = parentCoroutineHandle; if constexpr (std::derived_from) { mParentCoroutinePromise = &parentCoroutineHandle.promise(); } awaitSuspendImpl(); } void await_resume(); private: void awaitSuspendImpl(); void onCoroutineCompleted(impl::StandaloneCoroutine* coroutine, const std::exception_ptr& unhandledException); void onAllCoroutinesCompleted(); void cancelAll(); std::list mCoroutines{}; bool mCancelAfterFirst; std::coroutine_handle<> mParentCoroutineHandle{}; CoroutinePromiseBase* mParentCoroutinePromise{}; // NOLINTNEXTLINE(bugprone-throw-keyword-missing) std::exception_ptr mUnhandledException{}; bool mCancellingCoroutines{}; bool mCancelledCoroutinesAndWaitingForCompletionCallbacks{}; }; } inline impl::MultipleCoroutinesAwaiter waitAll(std::vector>&& coroutines) { return impl::MultipleCoroutinesAwaiter(std::move(coroutines), false); } template>... Coroutines> requires(sizeof...(Coroutines) != 0) inline impl::MultipleCoroutinesAwaiter waitAll(Coroutines&&... coroutines) { return impl::MultipleCoroutinesAwaiter(false, std::forward(coroutines)...); } inline impl::MultipleCoroutinesAwaiter waitAny(std::vector>&& coroutines) { return impl::MultipleCoroutinesAwaiter(std::move(coroutines), true); } template>... Coroutines> requires(sizeof...(Coroutines) != 0) inline impl::MultipleCoroutinesAwaiter waitAny(Coroutines&&... coroutines) { return impl::MultipleCoroutinesAwaiter(true, std::forward(coroutines)...); } } #endif // TREMOTESF_COROUTINES_WAITALL_H tremotesf-2.10.1/src/desktoputils.cpp000066400000000000000000000203521523411710200176260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "desktoputils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TREMOTESF_UNIX_FREEDESKTOP # include # include # include # include "coroutines/coroutines.h" # include "coroutines/qobjectsignal.h" # include "coroutines/scope.h" # include "coroutines/timer.h" # include "coroutines/waitall.h" #endif #include #include "log/log.h" #include "rpc/pathutils.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QUrl) using namespace Qt::StringLiterals; namespace tremotesf::desktoputils { const QIcon& statusIcon(StatusIcon icon) { using enum StatusIcon; switch (icon) { case Active: { static const QIcon qicon(":/active.svg"_L1); return qicon; } case Checking: { static const QIcon qicon(":/checking.svg"_L1); return qicon; } case Downloading: { static const QIcon qicon(":/downloading.svg"_L1); return qicon; } case Errored: { static const QIcon qicon(":/errored.svg"_L1); return qicon; } case Paused: { static const QIcon qicon(":/paused.svg"_L1); return qicon; } case Queued: { static const QIcon qicon(":/queued.svg"_L1); return qicon; } case Seeding: { static const QIcon qicon(":/seeding.svg"_L1); return qicon; } case StalledDownloading: { static const QIcon qicon(":/stalled-downloading.svg"_L1); return qicon; } case StalledSeeding: { static const QIcon qicon(":/stalled-seeding.svg"_L1); return qicon; } } throw std::logic_error(fmt::format("Unknown StatusIcon value {}", std::to_underlying(icon))); } const QIcon& priorityIcon(Priority priority) { switch (priority) { case Priority::High: { static const QIcon qicon(":/priority-high.svg"_L1); return qicon; } case Priority::Normal: { static const QIcon qicon(":/priority-normal.svg"_L1); return qicon; } case Priority::Low: { static const QIcon qicon(":/priority-low.svg"_L1); return qicon; } } throw std::logic_error(fmt::format("Unknown Priority value {}", std::to_underlying(priority))); } const QIcon& standardFileIcon() { static const auto icon = qApp->style()->standardIcon(QStyle::SP_FileIcon); return icon; } const QIcon& standardDirIcon() { static const auto icon = qApp->style()->standardIcon(QStyle::SP_DirIcon); return icon; } namespace { void showOpenFileError(const QUrl& url, std::optional error, QWidget* parent) { const auto pathToDisplay = url.isLocalFile() ? QDir::toNativeSeparators(url.toLocalFile()) : url.toString(); auto dialog = new QMessageBox( QMessageBox::Warning, //: Dialog title qApp->translate("tremotesf", "Error"), //: File opening error, %1 is a file path qApp->translate("tremotesf", "Error opening %1").arg(pathToDisplay), QMessageBox::Close, parent ); if (error.has_value()) { dialog->setText(dialog->text() % "\n\n"_L1 % *error); } dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } void openFileImpl(const QUrl& url, QWidget* parent) { info().log("Executing QDesktopServices::openUrl() for {}", url); if (!QDesktopServices::openUrl(url)) { warning().log("QDesktopServices::openUrl() failed for {}", url); showOpenFileError(url, {}, parent); } } #ifdef TREMOTESF_UNIX_FREEDESKTOP class DelayedUrlOpener : public QObject { Q_OBJECT public: using QObject::QObject; static DelayedUrlOpener* instance() { static const auto instance = new DelayedUrlOpener(qApp); return instance; } void openUrlAfterFocusWindowChange(QUrl url, QPointer parent) { mScope.launch(openUrlAfterFocusWindowChangeImpl(std::move(url), std::move(parent))); } private: Coroutine<> openUrlAfterFocusWindowChangeImpl(QUrl url, QPointer parent) { co_await waitAny( []() -> Coroutine<> { debug().log("Waiting for focusWindowChanged signal"); co_await waitForSignal(qApp, &QGuiApplication::focusWindowChanged); debug().log("Received focusWindowChanged signal"); }(), []() -> Coroutine<> { using namespace std::chrono_literals; constexpr auto timeout = 100ms; co_await waitFor(timeout); debug().log("Did not receive focusWindowChanged signal in {}", timeout); }() ); openFileImpl(url, parent); } CoroutineScope mScope; }; #endif } void openFile(const QString& filePath, QWidget* parent) { const auto parsedUrl = parsePathAsUrl(filePath); if (!parsedUrl.has_value() && !QFile::exists(filePath)) { warning().log("Can't open file {}, it does not exist", filePath); showOpenFileError(filePath, qApp->translate("tremotesf", "This file/directory does not exist"), parent); return; } auto url = parsedUrl.has_value() ? *parsedUrl : QUrl::fromLocalFile(filePath); #ifdef TREMOTESF_UNIX_FREEDESKTOP // If focusWindow returns null in this moment (which is possible when we've just closed a context menu) then Qt will not pass activation token to launched app: // https://bugreports.qt.io/browse/QTBUG-138892 // Wait for QGuiApplication::focusWindowChanged signal first so that token is requested and passed along if (KWindowSystem::isPlatformWayland() && !QGuiApplication::focusWindow()) { DelayedUrlOpener::instance()->openUrlAfterFocusWindowChange(std::move(url), parent); } else { openFileImpl(url, parent); } #else openFileImpl(url, parent); #endif } namespace { QRegularExpression urlRegex() { constexpr auto protocol = "(?:(?:[a-z]+:)?//)"_L1; constexpr auto host = R"((?:(?:[a-z\x{00a1}-\x{ffff0}-9][-_]*)*[a-z\x{00a1}-\x{ffff0}-9]+))"; constexpr auto domain = R"((?:\.(?:[a-z\x{00a1}-\x{ffff0}-9]-*)*[a-z\x{00a1}-\x{ffff0}-9]+)*)"; constexpr auto tld = R"((?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))\.?)"; constexpr auto port = R"((?::\d{2,5})?)"; constexpr auto path = R"((?:[/?#][^\s"\)']*)?)"; const auto regex = QString("(?:"_L1 % protocol % R"(|www\.)(?:)" % host % domain % tld % ")"_L1 % port % path); return QRegularExpression(regex, QRegularExpression::CaseInsensitiveOption); } } void findLinksAndAddAnchors(QTextDocument* document) { auto baseFormat = QTextCharFormat(); baseFormat.setAnchor(true); baseFormat.setFontUnderline(true); baseFormat.setForeground(qApp->palette().link()); const auto regex = desktoputils::urlRegex(); auto cursor = QTextCursor(); while (true) { cursor = document->find(regex, cursor); if (cursor.isNull()) { break; } auto format = baseFormat; format.setAnchorHref(cursor.selection().toPlainText()); cursor.setCharFormat(format); } } } #include "desktoputils.moc" tremotesf-2.10.1/src/desktoputils.h000066400000000000000000000016311523411710200172720ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_DESKTOPUTILS_H #define TREMOTESF_DESKTOPUTILS_H class QIcon; class QString; class QTextDocument; class QWidget; namespace tremotesf::desktoputils { constexpr int defaultDbusTimeout = 2000; // 2 seconds enum class StatusIcon { Active, Checking, Downloading, Errored, Paused, Queued, Seeding, StalledDownloading, StalledSeeding }; const QIcon& statusIcon(StatusIcon icon); enum class Priority { High, Normal, Low }; const QIcon& priorityIcon(Priority priority); const QIcon& standardFileIcon(); const QIcon& standardDirIcon(); void openFile(const QString& filePath, QWidget* parent = nullptr); void findLinksAndAddAnchors(QTextDocument* document); } #endif // TREMOTESF_DESKTOPUTILS_H tremotesf-2.10.1/src/filemanagerlauncher.cpp000066400000000000000000000112201523411710200210620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "filemanagerlauncher.h" #include #include #include #include #include #include #include #include #include "log/log.h" #include "rpc/pathutils.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QUrl) using namespace Qt::StringLiterals; namespace tremotesf { namespace impl { void FileManagerLauncher::launchFileManagerAndSelectFiles(const std::vector& files, QWidget* parentWidget) { std::vector filesToSelect{}; std::vector nonExistentDirectories{}; for (const QString& filePath : files) { const auto parsedUrl = parsePathAsUrl(filePath); QString dirPath = [&] { if (parsedUrl.has_value()) { QUrl dirUrl = *parsedUrl; dirUrl.setPath(QFileInfo(dirUrl.path()).path()); return dirUrl.toString(); } return QFileInfo(filePath).path(); }(); if (std::ranges::contains(nonExistentDirectories, dirPath)) { continue; } if (!parsedUrl.has_value() && !QFileInfo::exists(dirPath)) { warning().log("FileManagerLauncher: directory {} does not exist", dirPath); nonExistentDirectories.push_back(dirPath); continue; } const auto found = std::ranges::find(filesToSelect, dirPath, &FilesInDirectory::directory); auto& dirFiles = [&]() -> std::vector& { if (found != filesToSelect.end()) { return found->files; } filesToSelect.push_back({.directory = std::move(dirPath), .files = {}}); return filesToSelect.back().files; }(); dirFiles.push_back(filePath); } if (!nonExistentDirectories.empty()) { const auto error = qApp->translate("tremotesf", "This directory does not exist"); for (const auto& dirPath : nonExistentDirectories) { showErrorDialog(dirPath, error, parentWidget); } } if (!filesToSelect.empty()) { launchFileManagerAndSelectFiles(std::move(filesToSelect), parentWidget); } else { emit done(); } } void FileManagerLauncher::launchFileManagerAndSelectFiles( // NOLINTNEXTLINE(performance-unnecessary-value-param) std::vector filesToSelect, QWidget* parentWidget ) { for (const auto& [directory, _] : filesToSelect) { fallbackForDirectory(directory, parentWidget); } emit done(); } void FileManagerLauncher::fallbackForDirectory(const QString& dirPath, QWidget* parentWidget) { const auto url = QUrl::fromLocalFile(dirPath); info().log("FileManagerLauncher: executing QDesktopServices::openUrl() for {}", url); if (!QDesktopServices::openUrl(url)) { warning().log("FileManagerLauncher: QDesktopServices::openUrl() failed for {}", url); showErrorDialog(dirPath, {}, parentWidget); } } void FileManagerLauncher::showErrorDialog( const QString& dirPath, const std::optional& error, QWidget* parentWidget ) { auto dialog = new QMessageBox( QMessageBox::Warning, //: Dialog title qApp->translate("tremotesf", "Error"), //: Directory opening error, %1 is a file path qApp->translate("tremotesf", "Error opening %1").arg(QDir::toNativeSeparators(dirPath)), QMessageBox::Close, parentWidget ); if (error.has_value()) { dialog->setText(dialog->text() % "\n\n"_L1 % *error); } dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } } void launchFileManagerAndSelectFiles(const std::vector& files, QWidget* parentWidget) { auto launcher = impl::FileManagerLauncher::createInstance(); QObject::connect(launcher, &impl::FileManagerLauncher::done, launcher, &impl::FileManagerLauncher::deleteLater); launcher->launchFileManagerAndSelectFiles(files, parentWidget); } } tremotesf-2.10.1/src/filemanagerlauncher.h000066400000000000000000000024141523411710200205340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_FILEMANAGERLAUNCHER_H #define TREMOTESF_FILEMANAGERLAUNCHER_H #include #include #include #include class QWidget; namespace tremotesf { namespace impl { class FileManagerLauncher : public QObject { Q_OBJECT public: static FileManagerLauncher* createInstance(); void launchFileManagerAndSelectFiles(const std::vector& files, QWidget* parentWidget); protected: FileManagerLauncher() = default; struct FilesInDirectory { QString directory{}; std::vector files{}; }; virtual void launchFileManagerAndSelectFiles(std::vector filesToSelect, QWidget* parentWidget); void fallbackForDirectory(const QString& dirPath, QWidget* parentWidget); void showErrorDialog(const QString& dirPath, const std::optional& error, QWidget* parentWidget); signals: void done(); }; } void launchFileManagerAndSelectFiles(const std::vector& files, QWidget* parentWidget); } #endif tremotesf-2.10.1/src/filemanagerlauncher_fallback.cpp000066400000000000000000000004111523411710200227010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "filemanagerlauncher.h" namespace tremotesf::impl { FileManagerLauncher* FileManagerLauncher::createInstance() { return new FileManagerLauncher(); } } tremotesf-2.10.1/src/filemanagerlauncher_freedesktop.cpp000066400000000000000000000135041523411710200234640ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "filemanagerlauncher.h" #include #include #include #include #include #include #include #include "coroutines/dbus.h" #include "coroutines/scope.h" #include "log/log.h" #include "tremotesf_dbus_generated/org.freedesktop.FileManager1.h" #include "desktoputils.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QDBusError) using namespace std::views; using std::ranges::to; using namespace Qt::StringLiterals; namespace tremotesf { namespace { class ActivationTokenAwaitable final { public: inline explicit ActivationTokenAwaitable(QWindow* window) : mWindow(window) {} ~ActivationTokenAwaitable() = default; Q_DISABLE_COPY_MOVE(ActivationTokenAwaitable) inline bool await_ready() { return false; } template inline void await_suspend(std::coroutine_handle handle) { if (!startAwaiting(handle)) { return; } #if KWINDOWSYSTEM_VERSION >= QT_VERSION_CHECK(6, 19, 0) KWaylandExtras::xdgActivationToken(mWindow, {}).then(&mReceiver, [=, this](QString token) { mToken = std::move(token); resume(handle); }); #else const auto requestedSerial = KWaylandExtras::lastInputSerial(mWindow); QObject::connect( KWaylandExtras::self(), &KWaylandExtras::xdgActivationTokenArrived, &mReceiver, [=, this](int serial, const QString& token) { if (static_cast(serial) == requestedSerial) { mToken = token; resume(handle); } } ); KWaylandExtras::requestXdgActivationToken(mWindow, requestedSerial, {}); #endif } // NOLINTNEXTLINE(bugprone-unchecked-optional-access) inline QString await_resume() { return std::move(mToken).value(); }; private: QWindow* mWindow; QObject mReceiver{}; std::optional mToken{}; }; class FreedesktopFileManagerLauncher final : public impl::FileManagerLauncher { Q_OBJECT public: FreedesktopFileManagerLauncher() = default; protected: void launchFileManagerAndSelectFiles( std::vector filesToSelect, QWidget* parentWidget ) override { mCoroutineScope.launch(launchFileManagerAndSelectFilesImpl(std::move(filesToSelect), parentWidget)); } private: Coroutine<> launchFileManagerAndSelectFilesImpl( std::vector filesToSelect, QPointer parentWidget ) { const auto uris = filesToSelect | transform(&FilesInDirectory::files) | join | transform([](const QString& path) { return QUrl::fromLocalFile(path).toString(QUrl::FullyEncoded); }) | to(); QString startupId{}; if (KWindowSystem::isPlatformWayland()) { info().log("FreedesktopFileManagerLauncher: requesting XDG activation token"); const auto window = parentWidget.data() ? parentWidget->windowHandle() : nullptr; if (window) { startupId = co_await ActivationTokenAwaitable(window); info().log("FreedesktopFileManagerLauncher: received XDG activation token '{}'", startupId); } else { warning().log( "FreedesktopFileManagerLauncher: platform window is null, can't request XDG activation " "token" ); } } info().log( "FreedesktopFileManagerLauncher: executing org.freedesktop.FileManager1.ShowItems() D-Bus call " "with: uris = {}, startupId = {}", uris, startupId ); OrgFreedesktopFileManager1Interface interface( "org.freedesktop.FileManager1"_L1, "/org/freedesktop/FileManager1"_L1, QDBusConnection::sessionBus() ); interface.setTimeout(desktoputils::defaultDbusTimeout); const auto reply = co_await interface.ShowItems(uris, startupId); if (!reply.isError()) { info().log( "FreedesktopFileManagerLauncher: executed org.freedesktop.FileManager1.ShowItems() D-Bus call" ); } else { warning().log( "FreedesktopFileManagerLauncher: org.freedesktop.FileManager1.ShowItems() D-Bus call failed: " "{}", reply.error() ); for (const auto& [dirPath, dirFiles] : filesToSelect) { fallbackForDirectory(dirPath, parentWidget.data()); } } } CoroutineScope mCoroutineScope{}; }; } namespace impl { FileManagerLauncher* FileManagerLauncher::createInstance() { return new FreedesktopFileManagerLauncher(); } } } #include "filemanagerlauncher_freedesktop.moc" tremotesf-2.10.1/src/filemanagerlauncher_macos.mm000066400000000000000000000045121523411710200221010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "filemanagerlauncher.h" #include #include #include #include "log/log.h" namespace tremotesf { namespace { class MacosFileManagerLauncher final : public impl::FileManagerLauncher { Q_OBJECT public: MacosFileManagerLauncher() = default; Q_DISABLE_COPY_MOVE(MacosFileManagerLauncher) protected: void launchFileManagerAndSelectFiles( std::vector filesToSelect, QWidget* parentWidget ) override { auto* const workspace = [NSWorkspace sharedWorkspace]; NSMutableArray* const fileUrls = [NSMutableArray arrayWithCapacity:filesToSelect.size()]; std::vector fallbackDirectories{}; for (const auto& [dirPath, files] : filesToSelect) { for (const auto& filePath : files) { // activateFileViewerSelectingURLs won't work is file does not exist if (QFileInfo::exists(filePath)) { auto* const url = [NSURL fileURLWithPath:filePath.toNSString()]; [fileUrls addObject:url]; } else { if (std::find(fallbackDirectories.begin(), fallbackDirectories.end(), dirPath) == fallbackDirectories.end()) { fallbackDirectories.push_back(dirPath); } } } } if (const auto count = [fileUrls count]; count != 0) { info().log("Executing NSWorkspace.activateFileViewerSelectingURLs for {} files", count); [workspace activateFileViewerSelectingURLs:fileUrls]; } for (const auto& dirPath : fallbackDirectories) { fallbackForDirectory(dirPath, parentWidget); } emit done(); } }; } namespace impl { FileManagerLauncher* FileManagerLauncher::createInstance() { return new MacosFileManagerLauncher(); } } } #include "filemanagerlauncher_macos.moc" tremotesf-2.10.1/src/filemanagerlauncher_windows.cpp000066400000000000000000000110351523411710200226400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "filemanagerlauncher.h" #include #include #include #include // Workarounds for winrt headers #include #include #include #include #include #include #include #include "log/log.h" using namespace winrt::Windows::Storage; using namespace winrt::Windows::System; namespace tremotesf { namespace { class WindowsFileManagerLauncher final : public impl::FileManagerLauncher { Q_OBJECT public: WindowsFileManagerLauncher() = default; ~WindowsFileManagerLauncher() override { if (mCoroutine) { mCoroutine.Cancel(); } } Q_DISABLE_COPY_MOVE(WindowsFileManagerLauncher) protected: void launchFileManagerAndSelectFiles( std::vector filesToSelect, QWidget* parentWidget ) override { mCoroutine = selectFiles(std::move(filesToSelect), parentWidget); } private: winrt::Windows::Foundation::IAsyncAction selectFiles(std::vector filesToSelect, QPointer parentWidget) { (co_await winrt::get_cancellation_token()).enable_propagation(); for (auto&& [dirPath, dirFiles] : filesToSelect) { co_await selectFilesInFolder(std::move(dirPath), std::move(dirFiles), parentWidget); } emit done(); } winrt::Windows::Foundation::IAsyncAction selectFilesInFolder(QString dirPath, std::vector dirFiles, QPointer parentWidget) { auto options = FolderLauncherOptions(); for (const QString& filePath : dirFiles) { const auto nativeFilePath = winrt::hstring(QDir::toNativeSeparators(filePath).toStdWString()); if (QFileInfo(filePath).isDir()) { try { options.ItemsToSelect().Append( co_await StorageFolder::GetFolderFromPathAsync(nativeFilePath) ); } catch (const winrt::hresult_canceled&) { throw; } catch (const winrt::hresult_error& e) { warning().logWithException( e, "WindowsFileManagerLauncher: failed to create StorageFolder from {}", nativeFilePath ); } } else { try { options.ItemsToSelect().Append(co_await StorageFile::GetFileFromPathAsync(nativeFilePath)); } catch (const winrt::hresult_canceled&) { throw; } catch (const winrt::hresult_error& e) { warning().logWithException( e, "WindowsFileManagerLauncher: failed to create StorageFile from {}", nativeFilePath ); } } } auto nativeDirPath = winrt::hstring(QDir::toNativeSeparators(dirPath).toStdWString()); info().log( "WindowsFileManagerLauncher: opening folder {} and selecting {} items", nativeDirPath, options.ItemsToSelect().Size() ); try { co_await Launcher::LaunchFolderPathAsync(nativeDirPath, options); } catch (const winrt::hresult_canceled&) { throw; } catch (const winrt::hresult_error& e) { warning().logWithException(e, "WindowsFileManagerLauncher: failed to select files"); fallbackForDirectory(dirPath, parentWidget); } } winrt::Windows::Foundation::IAsyncAction mCoroutine{}; }; } namespace impl { FileManagerLauncher* FileManagerLauncher::createInstance() { return new WindowsFileManagerLauncher(); } } } #include "filemanagerlauncher_windows.moc" tremotesf-2.10.1/src/fileutils.cpp000066400000000000000000000275321523411710200171030ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "fileutils.h" #include #include #include #include #include #include #include #include #include "macoshelpers.h" #include "target_os.h" #include "log/log.h" using namespace Qt::StringLiterals; namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(QFile::FileError e, format_context& ctx) const { const std::string_view string = [e] { using namespace std::string_view_literals; switch (e) { case QFileDevice::NoError: return "NoError"sv; case QFileDevice::ReadError: return "ReadError"sv; case QFileDevice::WriteError: return "WriteError"sv; case QFileDevice::FatalError: return "FatalError"sv; case QFileDevice::ResourceError: return "ResourceError"sv; case QFileDevice::OpenError: return "OpenError"sv; case QFileDevice::AbortError: return "AbortError"sv; case QFileDevice::TimeOutError: return "TimeOutError"sv; case QFileDevice::UnspecifiedError: return "UnspecifiedError"sv; case QFileDevice::RemoveError: return "RemoveError"sv; case QFileDevice::RenameError: return "RenameError"sv; case QFileDevice::PositionError: return "PositionError"sv; case QFileDevice::ResizeError: return "ResizeError"sv; case QFileDevice::PermissionsError: return "PermissionsError"sv; case QFileDevice::CopyError: return "CopyError"sv; } return std::string_view{}; }(); if (string.empty()) { return fmt::format_to(ctx.out(), "QFileDevice::FileError::", std::to_underlying(e)); } return fmt::format_to(ctx.out(), "QFileDevice::FileError::{}", string); } }; } namespace tremotesf { namespace { std::string fileDescription(const QFile& file) { if (const QString fileName = file.fileName(); !fileName.isEmpty()) { return fmt::format(R"(file "{}")", fileName); } return fmt::format("file with handle={}", file.handle()); } std::string errorDescription(const QFile& file) { return fmt::format("{} ({})", file.errorString(), file.error()); } enum class ReadErrorType { FileError, UnexpectedEndOfFile }; void throwReadError(const QFile& file, ReadErrorType type) { switch (type) { case ReadErrorType::UnexpectedEndOfFile: throw QFileError(fmt::format("Failed to read from {}: unexpected end of file", fileDescription(file))); case ReadErrorType::FileError: throw QFileError( fmt::format("Failed to read from {}: {}", fileDescription(file), errorDescription(file)) ); } throw std::logic_error("Unknown ReadErrorType value"); } struct ReadWholeBuffer {}; struct ReadUntilEndOfFile { qint64 bytesRead{}; }; using ReadResult = std::variant; [[nodiscard]] ReadResult readWholeBufferOrUntilEndOfFile(QFile& file, std::span buffer) { if (buffer.empty()) { // If buffer's size is 0 then file.read() will return 0 which we will confuse with EOF condition // Just return early, there is nothing for us to do return ReadWholeBuffer{}; } std::span emptyBufferRemainder = buffer; while (true) { const qint64 bytesRead = file.read(emptyBufferRemainder.data(), static_cast(emptyBufferRemainder.size())); if (bytesRead == -1) { // Error, throw throwReadError(file, ReadErrorType::FileError); } if (bytesRead == 0) { // End of file, return const auto filledBufferSize = static_cast(buffer.size() - emptyBufferRemainder.size()); return ReadUntilEndOfFile{.bytesRead = filledBufferSize}; } if (bytesRead == static_cast(emptyBufferRemainder.size())) { // Read whole buffer, return return ReadWholeBuffer{}; } // Read part of buffer, continue emptyBufferRemainder = emptyBufferRemainder.subspan(static_cast(bytesRead)); } } } void openFile(QFile& file, QIODevice::OpenMode mode) { if (!file.open(mode)) { throw QFileError(fmt::format("Failed to open {}: {}", fileDescription(file), errorDescription(file))); } } void openFileFromFd(QFile& file, int fd, QIODevice::OpenMode mode) { if (!file.open(fd, mode)) { throw QFileError(fmt::format("Failed to open file from handle={}: {}", fd, errorDescription(file))); } } void readBytes(QFile& file, std::span buffer) { const auto result = readWholeBufferOrUntilEndOfFile(file, buffer); if (std::holds_alternative(result)) { throwReadError(file, ReadErrorType::UnexpectedEndOfFile); } } void skipBytes(QFile& file, qint64 bytes) { if (bytes < 0) { throw std::invalid_argument(fmt::format("Argument bytes has invalid value {}, can't be negative", bytes)); } if (bytes == 0) { // Nothing to do return; } auto remainingBytes = bytes; while (remainingBytes > 0) { const auto bytesSkipped = file.skip(remainingBytes); if (bytesSkipped == -1) { // Error, throw throwReadError(file, ReadErrorType::FileError); } if (bytesSkipped == 0) { // End of file, throw throwReadError(file, ReadErrorType::UnexpectedEndOfFile); } remainingBytes -= bytesSkipped; } } std::span peekBytes(QFile& file, std::span buffer) { if (buffer.empty()) { return buffer; } const auto peeked = file.peek(buffer.data(), static_cast(buffer.size())); if (peeked == -1) { throwReadError(file, ReadErrorType::FileError); } if (peeked == 0) { throwReadError(file, ReadErrorType::UnexpectedEndOfFile); } return buffer.subspan(0, static_cast(peeked)); } void writeBytes(QFile& file, std::span data) { std::span remainingData = data; while (true) { const qint64 bytesWritten = file.write(remainingData.data(), static_cast(remainingData.size())); if (bytesWritten == -1) { // Error, throw throw QFileError( fmt::format("Failed to write to {}: {}", fileDescription(file), errorDescription(file)) ); } if (bytesWritten == static_cast(remainingData.size())) { // Written whole buffer, return break; } // Written part of buffer, continue remainingData = remainingData.subspan(static_cast(bytesWritten)); } } QByteArray readFile(const QString& path) { QFile file(path); openFile(file, QIODevice::ReadOnly); auto data = file.readAll(); if (file.error() != QFileDevice::NoError) { throwReadError(file, ReadErrorType::FileError); } return data; } namespace { void deleteFileImpl(QFile& file) { info().log("Deleting {}", fileDescription(file)); if (file.remove()) { info().log("Succesfully deleted file"); } else { throw QFileError(fmt::format("Failed to delete {}: {}", fileDescription(file), errorDescription(file))); } } } void deleteFile(const QString& filePath) { QFile file(filePath); deleteFileImpl(file); } void moveFileToTrashOrDelete(const QString& filePath) { QFile file(filePath); info().log("Moving {} to trash", fileDescription(file)); if (file.moveToTrash()) { if (const auto newPath = file.fileName(); !newPath.isEmpty()) { info().log("Successfully moved file to trash, new path is {}", newPath); } else { info().log("Successfully moved file to trash"); } } else { warning().log("Failed to move {} to trash: {}", fileDescription(file), errorDescription(file)); deleteFileImpl(file); } } QString resolveExternalBundledResourcesPath(QLatin1String path) { const QString root = [&] { if constexpr (targetOs == TargetOs::UnixMacOS) { return bundleResourcesPath(); } else { return QCoreApplication::applicationDirPath(); } }(); return root % '/' % path; } namespace impl { QString readFileAsBase64String(QFile& file) { QString string{}; string.reserve(static_cast(((4 * file.size() / 3) + 3) & ~3)); static constexpr qint64 bufferSize = 1024 * 1024 - 1; // 1 MiB minus 1 byte (dividable by 3) QByteArray buffer(bufferSize, '\0'); while (true) { const auto result = readWholeBufferOrUntilEndOfFile(file, buffer); if (std::holds_alternative(result)) { string.append(QLatin1String(buffer.toBase64())); continue; } if (const auto readUntilEndOfFile = std::get_if(&result); readUntilEndOfFile) { buffer.resize(static_cast(readUntilEndOfFile->bytesRead)); string.append(QLatin1String(buffer.toBase64())); break; } } return string; } namespace { constexpr auto sessionIdFileLocation = [] { if constexpr (targetOs == TargetOs::Windows) { return QStandardPaths::GenericDataLocation; } else { return QStandardPaths::TempLocation; } }(); constexpr QLatin1String sessionIdFilePrefix = [] { if constexpr (targetOs == TargetOs::Windows) { return "Transmission/tr_session_id_"_L1; } else { return "tr_session_id_"_L1; } }(); } bool isTransmissionSessionIdFileExists(QByteArrayView sessionId) { const auto file = QStandardPaths::locate(sessionIdFileLocation, sessionIdFilePrefix % sessionId); if (!file.isEmpty()) { info().log( "isSessionIdFileExists: found transmission-daemon session id file {}", QDir::toNativeSeparators(file) ); return true; } info().log("isSessionIdFileExists: did not find transmission-daemon session id file"); return false; } } } tremotesf-2.10.1/src/fileutils.h000066400000000000000000000024741523411710200165460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_FILEUTILS_H #define TREMOTESF_FILEUTILS_H #include #include #include class QFile; namespace tremotesf { class QFileError : public std::runtime_error { public: explicit QFileError(const std::string& what) : std::runtime_error(what) {} explicit QFileError(const char* what) : std::runtime_error(what) {} }; void openFile(QFile& file, QIODevice::OpenMode mode); void openFileFromFd(QFile& file, int fd, QIODevice::OpenMode mode); void readBytes(QFile& file, std::span buffer); void skipBytes(QFile& file, qint64 bytes); [[nodiscard]] std::span peekBytes(QFile& file, std::span buffer); void writeBytes(QFile& file, std::span data); [[nodiscard]] QByteArray readFile(const QString& path); void deleteFile(const QString& filePath); void moveFileToTrashOrDelete(const QString& filePath); [[maybe_unused]] QString resolveExternalBundledResourcesPath(QLatin1String path); namespace impl { [[nodiscard]] QString readFileAsBase64String(QFile& file); [[nodiscard]] bool isTransmissionSessionIdFileExists(QByteArrayView sessionId); } } #endif // TREMOTESF_FILEUTILS_H tremotesf-2.10.1/src/formatutils.cpp000066400000000000000000000250361523411710200174510ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later /* For formatRelativeDateTime() SPDX-FileCopyrightText: 2013 Alex Merry SPDX-FileCopyrightText: 2013 John Layt SPDX-FileCopyrightText: 2010 Michael Leupold SPDX-FileCopyrightText: 2009 Michael Pyne SPDX-FileCopyrightText: 2008 Albert Astals Cid SPDX-License-Identifier: LGPL-2.0-or-later */ #include "formatutils.h" #include #include #include #include #include #include #include "settings.h" namespace tremotesf::formatutils { namespace { enum class StringType { Size, Speed }; struct ByteUnitStrings { QString (*size)(); QString (*speed)(); QString string(StringType type) const { switch (type) { case StringType::Size: return size(); case StringType::Speed: return speed(); } throw std::logic_error("Unknown StringType value"); } }; // Should be kept in sync with `enum ByteUnit` constexpr auto byteUnits = std::array{ ByteUnitStrings{ //: Size suffix in bytes .size = [] { return qApp->translate("tremotesf", "%L1 B"); }, //: Download speed suffix in bytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 B/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in kibibytes .size = [] { return qApp->translate("tremotesf", "%L1 KiB"); }, //: Download speed suffix in kibibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 KiB/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in mebibytes .size = [] { return qApp->translate("tremotesf", "%L1 MiB"); }, //: Download speed suffix in mebibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 MiB/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in gibibytes .size = [] { return qApp->translate("tremotesf", "%L1 GiB"); }, //: Download speed suffix in gibibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 GiB/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in tebibytes .size = [] { return qApp->translate("tremotesf", "%L1 TiB"); }, //: Download speed suffix in tebibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 TiB/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in pebibytes .size = [] { return qApp->translate("tremotesf", "%L1 PiB"); }, //: Download speed suffix in pebibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 PiB/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in exbibytes .size = [] { return qApp->translate("tremotesf", "%L1 EiB"); }, //: Download speed suffix in exbibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 EiB/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in zebibytes .size = [] { return qApp->translate("tremotesf", "%L1 ZiB"); }, //: Download speed suffix in zebibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 ZiB/s"); } }, //: IEC 80000 binary prefixes, i.e. KiB = 1024 bytes ByteUnitStrings{ //: Size suffix in yobibytes .size = [] { return qApp->translate("tremotesf", "%L1 YiB"); }, //: Download speed suffix in yobibytes per second .speed = [] { return qApp->translate("tremotesf", "%L1 YiB/s"); } }, }; constexpr size_t maxByteUnit = byteUnits.size() - 1; QString formatBytes(qint64 bytes, StringType stringType) { size_t unit = 0; auto bytes_floating = static_cast(bytes); while (bytes_floating >= 1024.0 && unit < maxByteUnit) { bytes_floating /= 1024.0; ++unit; } if (unit == 0) { return byteUnits[0].string(stringType).arg(bytes_floating); } // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) return byteUnits[unit].string(stringType).arg(bytes_floating, 0, 'f', 1); } } QString formatByteSize(long long size) { return formatBytes(size, StringType::Size); } QString formatByteSpeed(long long speed) { return formatBytes(speed, StringType::Speed); } QString formatSpeedLimit(int limit) { //: Download speed suffix in kibibytes per second return qApp->translate("tremotesf", "%L1 KiB/s").arg(limit); } QString formatProgress(double progress) { if (qFuzzyCompare(progress, 1.0)) { //: Progress in percents. %L1 must remain unchanged, % after it is a percent character return qApp->translate("tremotesf", "%L1%").arg(100); } //: Progress in percents. %L1 must remain unchanged, % after it is a percent character return qApp->translate("tremotesf", "%L1%").arg(std::trunc(progress * 1000.0) / 10.0, 0, 'f', 1); } QString formatRatio(double ratio) { if (ratio < 0) { return {}; } int precision = 2; if (ratio >= 100) { precision = 0; } else if (ratio >= 10) { precision = 1; } return QLocale().toString(ratio, 'f', precision); } QString formatRatio(long long downloaded, long long uploaded) { if (downloaded == 0) { return formatRatio(0); } return formatRatio(static_cast(uploaded) / static_cast(downloaded)); } QString formatEta(int seconds) { if (seconds < 0) { return "\u221E"; } const int days = seconds / 86400; seconds %= 86400; const int hours = seconds / 3600; seconds %= 3600; const int minutes = seconds / 60; seconds %= 60; if (days > 0) { //: Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" return qApp->translate("tremotesf", "%L1 d %L2 h").arg(days).arg(hours); } if (hours > 0) { //: Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" return qApp->translate("tremotesf", "%L1 h %L2 m").arg(hours).arg(minutes); } if (minutes > 0) { //: Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" return qApp->translate("tremotesf", "%L1 m %L2 s").arg(minutes).arg(seconds); } //: Remaining time string. %L1 is seconds, "10 s" return qApp->translate("tremotesf", "%L1 s").arg(seconds); } namespace { // Adapted from KCoreAddons' KFormat QString formatRelativeDate(QDate date, QLocale::FormatType format, const QLocale& locale) { if (!date.isValid()) { return {}; } const qint64 daysTo = QDate::currentDate().daysTo(date); if (daysTo > 0 || daysTo < -2) { return locale.toString(date, format); } // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (daysTo) { case 0: return qApp->translate("tremotesf", "Today"); case -1: return qApp->translate("tremotesf", "Yesterday"); case -2: return qApp->translate("tremotesf", "Two days ago"); } Q_UNREACHABLE(); } QString addTimeToDate(const QString& dateString, QTime time, QLocale::FormatType format, const QLocale& locale) { //: Relative date & time return qApp->translate("tremotesf", "%1 at %2") .arg( dateString, locale.toString( time, format == QLocale::FormatType::LongFormat ? QLocale::FormatType::ShortFormat : format ) ); } QString formatRelativeDateTime(const QDateTime& dateTime, QLocale::FormatType format, const QLocale& locale) { const QDateTime now = QDateTime::currentDateTime(); const auto secsToNow = dateTime.secsTo(now); constexpr int secsInAHour = 60 * 60; if (secsToNow >= 0 && secsToNow < secsInAHour) { const auto minutesToNow = static_cast(secsToNow / 60); if (minutesToNow <= 1) { //: Relative time return qApp->translate("tremotesf", "Just now"); } //: @item:intext %1 is a whole number //~ singular %n minute ago //~ plural %n minutes ago return qApp->translate("tremotesf", "%n minute(s) ago", nullptr, minutesToNow); } return addTimeToDate(formatRelativeDate(dateTime.date(), format, locale), dateTime.time(), format, locale); } } QString formatDateTime(const QDateTime& dateTime, QLocale::FormatType format, bool displayRelativeTime) { if (!dateTime.isValid()) { return {}; } const QLocale locale{}; if (displayRelativeTime) { return formatRelativeDateTime(dateTime, format, locale); } return addTimeToDate(locale.toString(dateTime.date(), format), dateTime.time(), format, locale); } QString formatDateTime(const QDateTime& dateTime, QLocale::FormatType format) { return formatDateTime(dateTime, format, Settings::instance()->get_displayRelativeTime()); } } tremotesf-2.10.1/src/formatutils.h000066400000000000000000000014471523411710200171160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_FORMATUTILS_H #define TREMOTESF_FORMATUTILS_H #include #include class QDateTime; namespace tremotesf::formatutils { QString formatByteSize(long long size); QString formatByteSpeed(long long speed); QString formatSpeedLimit(int limit); QString formatProgress(double progress); QString formatRatio(double ratio); QString formatRatio(long long downloaded, long long uploaded); QString formatEta(int seconds); QString formatDateTime(const QDateTime& dateTime, QLocale::FormatType format, bool displayRelativeTime); QString formatDateTime(const QDateTime& dateTime, QLocale::FormatType format); } #endif // TREMOTESF_FORMATUTILS_H tremotesf-2.10.1/src/ipc/000077500000000000000000000000001523411710200151415ustar00rootroot00000000000000tremotesf-2.10.1/src/ipc/fileopeneventhandler.cpp000066400000000000000000000043161523411710200220520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "fileopeneventhandler.h" #include #include #include "log/log.h" namespace tremotesf { FileOpenEventHandler::FileOpenEventHandler(QObject* parent) : QObject(parent) { QCoreApplication::instance()->installEventFilter(this); } FileOpenEventHandler::~FileOpenEventHandler() { QCoreApplication::instance()->removeEventFilter(this); } bool FileOpenEventHandler::eventFilter(QObject* watched, QEvent* event) { // In case of multiple files / urls, Qt send separate QFileOpenEvent per file in a loop // Call processPendingEvents() in the next event loop iteration to process all events at once if (event->type() == QEvent::FileOpen) { auto* const e = static_cast(event); if (!e->file().isEmpty() || e->url().isValid()) { info().log("Received QEvent::FileOpen"); auto pendingEvent = [&] { if (!e->file().isEmpty()) { info().log("file = {}", e->file()); return PendingEvent{.fileOrUrl = e->file(), .isFile = true}; } auto url = e->url().toString(); info().log("url = {}", url); return PendingEvent{.fileOrUrl = std::move(url), .isFile = false}; }(); if (mPendingEvents.empty()) { QMetaObject::invokeMethod(this, &FileOpenEventHandler::processPendingEvents, Qt::QueuedConnection); } mPendingEvents.push_back(std::move(pendingEvent)); } } return QObject::eventFilter(watched, event); } void FileOpenEventHandler::processPendingEvents() { QStringList files{}; QStringList urls{}; for (auto& event : mPendingEvents) { if (event.isFile) { files.push_back(std::move(event.fileOrUrl)); } else { urls.push_back(std::move(event.fileOrUrl)); } } mPendingEvents.clear(); emit filesOpeningRequested(files, urls); } } tremotesf-2.10.1/src/ipc/fileopeneventhandler.h000066400000000000000000000016131523411710200215140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_FILEOPENEVENTHANDLER_H #define TREMOTESF_FILEOPENEVENTHANDLER_H #include #include namespace tremotesf { class FileOpenEventHandler : public QObject { Q_OBJECT public: explicit FileOpenEventHandler(QObject* parent = nullptr); ~FileOpenEventHandler() override; Q_DISABLE_COPY_MOVE(FileOpenEventHandler) bool eventFilter(QObject* watched, QEvent* event) override; private: void processPendingEvents(); struct PendingEvent { QString fileOrUrl{}; bool isFile{}; }; std::vector mPendingEvents{}; signals: void filesOpeningRequested(const QStringList& files, const QStringList& urls); }; } #endif // TREMOTESF_FILEOPENEVENTHANDLER_H tremotesf-2.10.1/src/ipc/ipcclient.h000066400000000000000000000012331523411710200172630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_IPCCLIENT_H #define TREMOTESF_IPCCLIENT_H #include #include namespace tremotesf { class IpcClient { public: static std::unique_ptr createInstance(); virtual ~IpcClient() = default; Q_DISABLE_COPY_MOVE(IpcClient) virtual bool isConnected() const = 0; virtual void activateWindow() = 0; virtual void addTorrents(const QStringList& files, const QStringList& urls) = 0; protected: IpcClient() = default; }; } #endif // TREMOTESF_IPCCLIENT_H tremotesf-2.10.1/src/ipc/ipcclient_dbus.cpp000066400000000000000000000061241523411710200206370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "ipcclient.h" #include #include #include #include "log/log.h" #include "tremotesf_dbus_generated/ipc/org.freedesktop.Application.h" #include "ipcserver_dbus.h" #include "ipcserver_dbus_service.h" #include "unixhelpers.h" namespace tremotesf { namespace { constexpr auto desktopStartupIdEnvVariable = "DESKTOP_STARTUP_ID"; inline bool waitForReply(QDBusPendingReply<> pending) { pending.waitForFinished(); const auto reply(pending.reply()); if (reply.type() != QDBusMessage::ReplyMessage) { warning().log("D-Bus method call failed: {}", reply.errorMessage()); return false; } return true; } } class IpcClientDbus final : public IpcClient { public: IpcClientDbus() = default; [[nodiscard]] bool isConnected() const override { return mInterface.isValid(); } void activateWindow() override { info().log("Requesting window activation"); if (mInterface.isValid()) { waitForReply(mInterface.Activate(getPlatformData())); } } void addTorrents(const QStringList& files, const QStringList& urls) override { info().log("Requesting torrents adding"); if (mInterface.isValid()) { QStringList uris; uris.reserve(files.size() + urls.size()); for (const QString& filePath : files) { uris.push_back(QUrl::fromLocalFile(filePath).toString()); } uris.append(urls); waitForReply(mInterface.Open(uris, getPlatformData())); } } private: static inline QVariantMap getPlatformData() { QVariantMap data{}; if (qEnvironmentVariableIsSet(desktopStartupIdEnvVariable)) { const auto startupId = qgetenv(desktopStartupIdEnvVariable); info().log("{} is '{}'", desktopStartupIdEnvVariable, startupId); data.insert(IpcDbusService::desktopStartupIdField, startupId); } else { info().log("{} is not set", desktopStartupIdEnvVariable); } if (qEnvironmentVariableIsSet(xdgActivationTokenEnvVariable)) { const auto activationToken = qgetenv(xdgActivationTokenEnvVariable); info().log("{} is '{}'", xdgActivationTokenEnvVariable, activationToken); data.insert(IpcDbusService::xdgActivationTokenField, qgetenv(xdgActivationTokenEnvVariable)); } else { info().log("{} is not set", xdgActivationTokenEnvVariable); } return data; } OrgFreedesktopApplicationInterface mInterface{ IpcServerDbus::serviceName, IpcServerDbus::objectPath, QDBusConnection::sessionBus() }; }; std::unique_ptr IpcClient::createInstance() { return std::make_unique(); } } tremotesf-2.10.1/src/ipc/ipcclient_socket.cpp000066400000000000000000000036611523411710200211750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "ipcclient.h" #include #include #include #include #include "log/log.h" #include "ipcserver_socket.h" namespace tremotesf { class IpcClientSocket final : public IpcClient { public: IpcClientSocket() { mSocket.connectToServer(IpcServerSocket::socketName()); mSocket.waitForConnected(); } bool isConnected() const override { return mSocket.state() == QLocalSocket::ConnectedState; } void activateWindow() override { info().log("Requesting window activation"); sendMessage(IpcServerSocket::activateWindowMessage); } void addTorrents(const QStringList& files, const QStringList& urls) override { info().log("Requesting torrents adding"); sendMessage(IpcServerSocket::createAddTorrentsMessage(files, urls)); } private: void sendMessage(const QByteArray& message) { const qint64 written = mSocket.write(message); if (written != message.size()) { warning().log("Failed to write to socket ({} bytes written): {}", written, mSocket.errorString()); } waitForBytesWritten(); } void sendMessage(char message) { const bool written = mSocket.putChar(message); if (!written) { warning().log("Failed to write to socket: {}", mSocket.errorString()); } waitForBytesWritten(); } void waitForBytesWritten() { if (!mSocket.waitForBytesWritten()) { warning().log("Timed out when waiting for bytes written"); } } QLocalSocket mSocket; }; std::unique_ptr IpcClient::createInstance() { return std::make_unique(); } } tremotesf-2.10.1/src/ipc/ipcserver.h000066400000000000000000000014131523411710200173130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_IPCSERVER_H #define TREMOTESF_IPCSERVER_H #include #include namespace tremotesf { class IpcServer : public QObject { Q_OBJECT public: static IpcServer* createInstance(QObject* parent = nullptr); protected: inline explicit IpcServer(QObject* parent = nullptr) : QObject(parent) {}; signals: void windowActivationRequested(const std::optional& windowActivationToken); void torrentsAddingRequested( const QStringList& files, const QStringList& urls, const std::optional& windowActivationToken ); }; } #endif // TREMOTESF_IPCSERVER_H tremotesf-2.10.1/src/ipc/ipcserver_dbus.cpp000066400000000000000000000003711523411710200206650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "ipcserver_dbus.h" namespace tremotesf { IpcServer* IpcServer::createInstance(QObject* parent) { return new IpcServerDbus(parent); } } tremotesf-2.10.1/src/ipc/ipcserver_dbus.h000066400000000000000000000014301523411710200203270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_IPCSERVER_DBUS_H #define TREMOTESF_IPCSERVER_DBUS_H #include "ipcserver.h" #include "ipcserver_dbus_service.h" namespace tremotesf { class IpcServerDbus final : public IpcServer { Q_OBJECT public: static constexpr auto serviceName = QLatin1String("org.equeim.Tremotesf"); static constexpr auto objectPath = QLatin1String("/org/equeim/Tremotesf"); static constexpr auto interfaceName = QLatin1String("org.freedesktop.Application"); inline explicit IpcServerDbus(QObject* parent = nullptr) : IpcServer(parent) {}; private: IpcDbusService mDbusService{this, this}; }; } #endif // TREMOTESF_IPCSERVER_DBUS_H tremotesf-2.10.1/src/ipc/ipcserver_dbus_service.cpp000066400000000000000000000067421523411710200224150ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "ipcserver_dbus_service.h" #include #include #include "log/log.h" #include "tremotesf_dbus_generated/ipc/org.freedesktop.Application.adaptor.h" #include "ipcserver_dbus.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QDBusError) SPECIALIZE_FORMATTER_FOR_QDEBUG(QStringList) SPECIALIZE_FORMATTER_FOR_QDEBUG(QVariantMap) SPECIALIZE_FORMATTER_FOR_QDEBUG(QVariantList) namespace tremotesf { namespace { std::optional platformDataToWindowActivationToken(const QVariantMap& platformData) { QLatin1String key{}; switch (KWindowSystem::platform()) { case KWindowSystem::Platform::X11: { key = IpcDbusService::desktopStartupIdField; break; } case KWindowSystem::Platform::Wayland: { key = IpcDbusService::xdgActivationTokenField; break; } default: warning().log("Unknown windowing system"); return {}; } auto token = platformData.value(key).toByteArray(); if (token.isEmpty()) { return {}; } return token; } } IpcDbusService::IpcDbusService(IpcServerDbus* ipcServer, QObject* parent) : QObject(parent), mIpcServer(ipcServer) { new OrgFreedesktopApplicationAdaptor(this); auto connection(QDBusConnection::sessionBus()); if (connection.registerService(IpcServerDbus::serviceName)) { info().log("Registered D-Bus service"); if (connection.registerObject(IpcServerDbus::objectPath, this)) { info().log("Registered D-Bus object"); } else { warning().log("Failed to register D-Bus object: {}", connection.lastError()); } } else { warning().log("Failed to register D-Bus service", connection.lastError()); } } /* * org.freedesktop.Application methods */ void IpcDbusService::Activate(const QVariantMap& platform_data) { info().log("IpcDbusService: window activation requested, platform_data = {}", platform_data); emit mIpcServer->windowActivationRequested(platformDataToWindowActivationToken(platform_data)); } void IpcDbusService::Open(const QStringList& uris, const QVariantMap& platform_data) { info().log("IpcDbusService: torrents adding requested, uris = {}, platform_data = {}", uris, platform_data); QStringList files; QStringList urls; for (const QUrl& url : QUrl::fromStringList(uris)) { if (url.isValid()) { if (url.isLocalFile()) { files.push_back(url.toLocalFile()); } else { urls.push_back(url.toString()); } } } emit mIpcServer->torrentsAddingRequested(files, urls, platformDataToWindowActivationToken(platform_data)); } void IpcDbusService::ActivateAction( const QString& action_name, const QVariantList& parameter, const QVariantMap& platform_data ) { info().log( "IpcDbusService: action activated, action_name = {}, parameter = {}, platform_data = {}", action_name, parameter, platform_data ); emit mIpcServer->windowActivationRequested(platformDataToWindowActivationToken(platform_data)); } } tremotesf-2.10.1/src/ipc/ipcserver_dbus_service.h000066400000000000000000000021451523411710200220530ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_IPCSERVER_DBUS_SERVICE_H #define TREMOTESF_IPCSERVER_DBUS_SERVICE_H #include class OrgFreedesktopApplicationAdaptor; namespace tremotesf { class IpcServerDbus; class IpcDbusService final : public QObject { Q_OBJECT public: static constexpr auto desktopStartupIdField = QLatin1String("desktop-startup-id"); static constexpr auto xdgActivationTokenField = QLatin1String("activation-token"); IpcDbusService(IpcServerDbus* ipcServer, QObject* parent = nullptr); private: /* * org.freedesktop.Application methods */ friend OrgFreedesktopApplicationAdaptor; void Activate(const QVariantMap& platform_data); void Open(const QStringList& uris, const QVariantMap& platform_data); void ActivateAction(const QString& action_name, const QVariantList& parameter, const QVariantMap& platform_data); IpcServerDbus* mIpcServer; }; } #endif // TREMOTESF_IPCSERVER_DBUS_SERVICE_H tremotesf-2.10.1/src/ipc/ipcserver_socket.cpp000066400000000000000000000123671523411710200212300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "ipcserver_socket.h" #include #include #include #include #include #include #include "fileopeneventhandler.h" #include "log/log.h" #ifdef Q_OS_WIN # include # include "windowshelpers.h" #endif #ifdef Q_OS_MACOS # include "ipc/fileopeneventhandler.h" #endif using namespace Qt::StringLiterals; SPECIALIZE_FORMATTER_FOR_QDEBUG(QCborValue) namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { fmt::format_context::iterator format(QCborParserError error, fmt::format_context& ctx) const { return fmt::format_to( ctx.out(), "{} (error code {})", error.errorString(), static_cast(error.error) ); } }; } namespace tremotesf { namespace { constexpr QStringView keyFiles = u"files"; constexpr QStringView keyUrls = u"urls"; QStringList toStringList(const QCborValue& value) { QStringList strings{}; if (!value.isArray()) return strings; const auto array = value.toArray(); strings.reserve(static_cast(array.size())); for (const auto& v : array) { if (v.isString()) { strings.push_back(v.toString()); } } return strings; } } IpcServerSocket::IpcServerSocket(QObject* parent) : IpcServer(parent) { auto* const server = new QLocalServer(this); const QString name(socketName()); if (!server->listen(name)) { if (server->serverError() == QAbstractSocket::AddressInUseError) { // We already tried to connect to it, removing warning().log("Removing dead socket"); if (QLocalServer::removeServer(name)) { if (!server->listen(name)) { warning().log("Failed to create socket: {}", server->errorString()); } } else { warning().log("Failed to remove socket: {}", server->errorString()); } } else { warning().log("Failed to create socket: {}", server->errorString()); } } if (server->isListening()) { listenToConnections(server); } #ifdef Q_OS_MACOS const auto* const handler = new FileOpenEventHandler(this); QObject::connect( handler, &FileOpenEventHandler::filesOpeningRequested, this, [this](const QStringList& files, const QStringList& urls) { emit torrentsAddingRequested(files, urls, {}); } ); #endif } QString IpcServerSocket::socketName() { QString name("tremotesf"_L1); #ifdef Q_OS_WIN try { DWORD sessionId{}; checkWin32Bool(ProcessIdToSessionId(GetCurrentProcessId(), &sessionId), "ProcessIdToSessionId"); name += '-'; name += QString::number(sessionId); } catch (const std::system_error& e) { warning().logWithException(e, "IpcServerSocket: failed to append session id to socket name"); } #endif return name; } IpcServer* IpcServer::createInstance(QObject* parent) { return new IpcServerSocket(parent); } QByteArray IpcServerSocket::createAddTorrentsMessage(const QStringList& files, const QStringList& urls) { return QCborMap{{keyFiles, QCborArray::fromStringList(files)}, {keyUrls, QCborArray::fromStringList(urls)}} .toCborValue() .toCbor(); } void IpcServerSocket::listenToConnections(QLocalServer* server) { QObject::connect(server, &QLocalServer::newConnection, this, [this, server]() { QLocalSocket* socket = server->nextPendingConnection(); QObject::connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); QTimer::singleShot(30000, socket, &QLocalSocket::disconnectFromServer); QObject::connect(socket, &QLocalSocket::readyRead, this, [this, socket]() { const QByteArray message(socket->readAll()); if (message.size() == 1 && message.front() == activateWindowMessage) { info().log("IpcServerSocket: window activation requested"); emit windowActivationRequested({}); } else { QCborParserError error{}; const auto cbor = QCborValue::fromCbor(message, &error); if (error.error != QCborError::NoError) { warning().log("IpcServerSocket: failed to parse CBOR message: {}", error); return; } info().log("Arguments received: {}", cbor); const auto map = cbor.toMap(); const auto files = toStringList(map[keyFiles]); const auto urls = toStringList(map[keyUrls]); emit torrentsAddingRequested(files, urls, {}); } }); }); } } tremotesf-2.10.1/src/ipc/ipcserver_socket.h000066400000000000000000000013031523411710200206610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_IPCSERVER_SOCKET_H #define TREMOTESF_IPCSERVER_SOCKET_H #include "ipcserver.h" class QLocalServer; namespace tremotesf { class IpcServerSocket final : public IpcServer { Q_OBJECT public: static QString socketName(); explicit IpcServerSocket(QObject* parent = nullptr); static constexpr char activateWindowMessage = '\0'; static QByteArray createAddTorrentsMessage(const QStringList& files, const QStringList& urls); private: void listenToConnections(QLocalServer* server); }; } #endif // TREMOTESF_IPCSERVER_SOCKET_H tremotesf-2.10.1/src/ipc/org.freedesktop.Application.xml000066400000000000000000000020421523411710200232240ustar00rootroot00000000000000 tremotesf-2.10.1/src/itemlistupdater.h000066400000000000000000000152171523411710200177640ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_ITEMLISTUPDATER_H #define TREMOTESF_ITEMLISTUPDATER_H #include #include #include #include #include #include #include #include namespace tremotesf { class ItemBatchProcessor { public: inline explicit ItemBatchProcessor(std::function&& action) : mAction(std::move(action)) {} void nextIndex(size_t index) { if (!firstIndex.has_value()) { reset(index); return; } if (!lastIndex.has_value()) throw std::logic_error("lastIndex is empty"); if (index == *lastIndex) { lastIndex = index + 1; } else { commit(); reset(index); } } std::optional commitIfNeeded() { if (firstIndex) { return commit(); } return std::nullopt; } std::optional firstIndex = std::nullopt; std::optional lastIndex = std::nullopt; private: void reset(size_t index) { firstIndex = index; lastIndex = index + 1; } size_t commit() { if (!firstIndex.has_value()) throw std::logic_error("firstIndex is empty"); if (!lastIndex.has_value()) throw std::logic_error("lastIndex is empty"); mAction(*firstIndex, *lastIndex); const size_t size = *lastIndex - *firstIndex; firstIndex = std::nullopt; lastIndex = std::nullopt; return size; } std::function mAction; }; template> class ItemListUpdater { using NewItem = std::ranges::range_value_t; public: ItemListUpdater() = default; virtual ~ItemListUpdater() = default; Q_DISABLE_COPY_MOVE(ItemListUpdater) void update(std::vector& items, NewItemsRange newItems) { if (!items.empty()) { auto removedBatchProcessor = ItemBatchProcessor([&](size_t first, size_t last) { onAboutToRemoveItems(first, last); items.erase( items.begin() + static_cast(first), items.begin() + static_cast(last) ); onRemovedItems(first, last); }); auto changedBatchProcessor = ItemBatchProcessor([&](size_t first, size_t last) { onChangedItems(first, last); }); for (size_t i = 0, max = items.size(); i < max; ++i) { Item* item = &items[i]; auto found = findNewItemForItem(newItems, *item); if (found == newItems.end()) { changedBatchProcessor.commitIfNeeded(); removedBatchProcessor.nextIndex(i); } else { if (auto size = removedBatchProcessor.commitIfNeeded(); size) { i -= *size; max -= *size; item = &items[i]; } if (updateItem(*item, std::forward(*found))) { changedBatchProcessor.nextIndex(i); } else { changedBatchProcessor.commitIfNeeded(); } newItems.erase(found); } } removedBatchProcessor.commitIfNeeded(); changedBatchProcessor.commitIfNeeded(); } if (!newItems.empty()) { const size_t count = newItems.size(); onAboutToAddItems(count); items.reserve(items.size() + count); for (auto&& newItem : newItems) { items.push_back(createItemFromNewItem(std::forward(newItem))); } onAddedItems(count); } } protected: /** * @brief Find NewItem for corresponing Item with the same identity * @param container container of NewItems * @param item Item that should be found in container * @return iterator to the NewItem with the same identity as Item, or end iterator * Default implementation simply checks for equality of items or throws logic_error if they are not comparable */ inline virtual std::ranges::iterator_t findNewItemForItem(NewItemsRange& newItems, const Item& item) { if constexpr (std::equality_comparable_with) { return std::ranges::find(newItems, item); } else { throw std::logic_error("findNewItemForItem() must be implemented"); } }; virtual void onAboutToRemoveItems(size_t first, size_t last) = 0; virtual void onRemovedItems(size_t first, size_t last) = 0; /** * @brief Update Item from the NewItem with the same identity * @param item Item that will be updated * @param newItem NewItem, guaranteed to have the same identity as the Item * @return true if Item was changed, otherwise false * Default implementation simply returns false * (with default implementation of findNewItemForItem() Item and NewItem will be always equal) */ // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) inline virtual bool updateItem([[maybe_unused]] Item& item, [[maybe_unused]] NewItem&& newItem) { return false; }; virtual void onChangedItems(size_t first, size_t last) = 0; /** * @brief Create Item from NewItem * @return new Item instance * Default implementation performs implicit conversion from NewItem to Item, * or throws logic_error if types are not convertible */ inline virtual Item createItemFromNewItem(NewItem&& newItem) { if constexpr (std::convertible_to) { return std::move(newItem); } else { throw std::logic_error("createItemFromNewItem() must be implemented"); } }; virtual void onAboutToAddItems(size_t count) = 0; virtual void onAddedItems(size_t count) = 0; }; } #endif // TREMOTESF_ITEMLISTUPDATER_H tremotesf-2.10.1/src/itemlistupdater_test.cpp000066400000000000000000000256721523411710200213640ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #define QTEST_THROW_ON_FAIL #include #include #include "log/log.h" #include "itemlistupdater.h" #include "stdutils.h" struct Item { int id; QString data; bool operator==(const Item& other) const = default; bool operator<(const Item& other) const { return id < other.id; } bool operator<=(const Item& other) const { return id <= other.id; } bool operator>(const Item& other) const { return id > other.id; } bool operator>=(const Item& other) const { return id > other.id; } }; namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(const Item& item, format_context& ctx) const { return fmt::format_to(ctx.out(), "Item(id={}, data={})", item.id, item.data); } }; } namespace tremotesf { class Updater final : public tremotesf::ItemListUpdater { public: std::vector> aboutToRemoveIndexRanges; std::vector> removedIndexRanges; std::vector> changedIndexRanges; std::optional aboutToAddCount; std::optional addedCount; protected: typename std::vector::iterator findNewItemForItem(std::vector& container, const Item& item) override { return std::ranges::find(container, item.id, &Item::id); }; void onAboutToRemoveItems(size_t first, size_t last) override { aboutToRemoveIndexRanges.emplace_back(first, last); } void onRemovedItems(size_t first, size_t last) override { removedIndexRanges.emplace_back(first, last); } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) bool updateItem(Item& item, Item&& newItem) override { if (item != newItem) { item = newItem; return true; } return false; } void onChangedItems(size_t first, size_t last) override { changedIndexRanges.emplace_back(first, last); } Item createItemFromNewItem(Item&& newItem) override { return std::move(newItem); } void onAboutToAddItems(size_t count) override { if (aboutToAddCount) { QFAIL("onAboutToAddItems() must be called only once"); } aboutToAddCount = count; } void onAddedItems(size_t count) override { if (addedCount) { QFAIL("onAddedItems() must be called only once"); } addedCount = count; } }; class ItemListUpdaterTest final : public QObject { Q_OBJECT private slots: void emptyListDidNotChange() { checkUpdate({}, {}); } void singleItemListDidNotChange() { checkUpdate({{42, "666"}}, {{42, "666"}}); } void multipleItemsListDidNotChange() { checkUpdate({{42, "666"}, {1, "Foo"}, {11, "Bar"}}, {{42, "666"}, {1, "Foo"}, {11, "Bar"}}); } void addedSingleItemToEmptyList() { checkUpdate({}, {{42, "666"}}); } void addedMultipleItemsToEmptyList() { checkUpdate({}, {{42, "666"}, {1, "Foo"}}); } void addedSingleItemToNonEmptyList() { checkUpdate({{42, "666"}}, {{42, "666"}, {1, "Foo"}}); } void addedMultipleItemsToNonEmptyList() { checkUpdate({{42, "666"}}, {{42, "666"}, {1, "Foo"}, {11, "Bar"}}); } void removedItemFromSingleItemList() { checkUpdate({{42, "666"}}, {}); } void removedLastItemFromMultipleItemsList() { checkUpdate({{42, "666"}, {666, ""}}, {{42, "666"}}); } void removedFirstItemFromMultipleItemsList() { checkUpdate({{42, "666"}, {666, ""}}, {{666, ""}}); } void removedMiddleItemFromMultipleItemsList() { checkUpdate({{42, "666"}, {666, ""}, {1, ""}}, {{42, "666"}, {1, ""}}); } void removedTwoConsecutiveItemsFromEnd() { checkUpdate({{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}}, {{42, "666"}, {666, ""}}); } void removedTwoConsecutiveItemsFromBeginning() { checkUpdate({{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}}, {{1, ""}, {18, "Nope"}}); } void removedTwoConsecutiveItemsFromMiddle() { checkUpdate({{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}}, {{42, "666"}, {18, "Nope"}}); } void removedTwoSeparateItems1() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "666"}, {666, ""}, {18, "Nope"}} ); } void removedTwoSeparateItems2() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{666, ""}, {18, "Nope"}, {77, "Foo"}} ); } void removedTwoSeparateItems3() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{666, ""}, {1, ""}, {18, "Nope"}} ); } void changedSingleItemList() { checkUpdate({{42, "666"}}, {{42, "42"}}); } void changedFirstItemInMultipleItemList() { checkUpdate({{42, "666"}, {666, ""}, {1, ""}}, {{42, "42"}, {666, ""}, {1, ""}}); } void changedMiddleItemInMultipleItemList() { checkUpdate({{42, "666"}, {666, ""}, {1, ""}}, {{42, "666"}, {666, "666"}, {1, ""}}); } void changedLastItemInMultipleItemList() { checkUpdate({{42, "666"}, {666, ""}, {1, ""}}, {{42, "666"}, {666, ""}, {1, "nnn"}}); } void changedTwoConsecutiveItemsFromBeginning() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "AAAA"}, {666, "ok"}, {1, ""}, {18, "Nope"}, {77, "Foo"}} ); } void changedTwoConsecutiveItemsFromMiddle() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "666"}, {666, ""}, {1, "uhh"}, {18, ""}, {77, "Foo"}} ); } void changedTwoConsecutiveItemsFromEnd() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "666"}, {666, ""}, {1, ""}, {18, "arr"}, {77, "qr"}} ); } void changedTwoSeparateItems1() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "667"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Fooo"}} ); } void changedTwoSeparateItems2() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "666"}, {666, "sure"}, {1, ""}, {18, "aoa"}, {77, "Foo"}} ); } void changedTwoSeparateItems3() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "666"}, {666, ""}, {1, "uwu"}, {18, "Nope"}, {77, "www"}} ); } void removedAndAdded() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{666, ""}, {1, ""}, {18, "Nope"}, {33, "fffu"}, {-2, "ew"}} ); } void changedAndAdded() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "666"}, {666, "yeeee"}, {1, "what"}, {18, "Nope"}, {77, "now"}, {999, "big"}} ); } void removedChangedAndAdded() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "sad"}, {1, "small"}, {33, "fffu"}, {-2, "ew"}} ); } void removedChangedAndAdded2() { checkUpdate( {{42, "666"}, {666, ""}, {1, ""}, {18, "Nope"}, {77, "Foo"}}, {{42, "666"}, {666, "333"}, {18, "Nope"}, {77, "Foo"}} ); } private: void checkUpdate(const std::vector& oldList, std::vector newList) { checkThatItemsAreUnique(oldList); checkThatItemsAreUnique(newList); info().log("Checking update from {}", oldList); std::ranges::sort(newList); do { info().log(" - to {}", newList); checkUpdateInner(oldList, newList); } while (std::ranges::next_permutation(newList).found); } void checkUpdateInner(const std::vector& oldList, const std::vector& newList) { auto directlyUpdatedList = oldList; Updater updater; updater.update(directlyUpdatedList, std::vector(newList)); QVERIFY(updater.aboutToRemoveIndexRanges == updater.removedIndexRanges); QVERIFY(updater.aboutToAddCount == updater.addedCount); QCOMPARE(directlyUpdatedList.size(), newList.size()); checkThatItemsAreUnique(directlyUpdatedList); QCOMPARE( std::set(directlyUpdatedList.begin(), directlyUpdatedList.end()), std::set(newList.begin(), newList.end()) ); auto indirectlyUpdatedList = oldList; for (const auto& [first, last] : updater.removedIndexRanges) { indirectlyUpdatedList.erase( indirectlyUpdatedList.begin() + static_cast(first), indirectlyUpdatedList.begin() + static_cast(last) ); } for (const auto& [first, last] : updater.changedIndexRanges) { std::ranges::copy( slice(directlyUpdatedList, first, last), indirectlyUpdatedList.begin() + static_cast(first) ); std::ranges::copy( slice(directlyUpdatedList, first, last), indirectlyUpdatedList.begin() + static_cast(first) ); } if (updater.addedCount) { indirectlyUpdatedList.reserve(indirectlyUpdatedList.size() + *updater.addedCount); std::ranges::copy( std::views::drop( directlyUpdatedList, static_cast(directlyUpdatedList.size() - *updater.addedCount) ), std::back_insert_iterator(indirectlyUpdatedList) ); } QCOMPARE(indirectlyUpdatedList, directlyUpdatedList); } void checkThatItemsAreUnique(const std::vector& list) { const auto set = std::set(list.begin(), list.end()); QCOMPARE(set.size(), list.size()); } }; } QTEST_GUILESS_MAIN(tremotesf::ItemListUpdaterTest) #include "itemlistupdater_test.moc" tremotesf-2.10.1/src/license.html000066400000000000000000001113301523411710200166750ustar00rootroot00000000000000 GNU General Public License v3.0 - GNU Project - Free Software Foundation (FSF)

GNU GENERAL PUBLIC LICENSE

Version 3, 29 June 2007

Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

Preamble

The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS

0. Definitions.

“This License” refers to version 3 of the GNU General Public License.

“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.

To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

A “covered work” means either the unmodified Program or a work based on the Program.

To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

1. Source Code.

The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.

A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

The Corresponding Source for a work in source code form is that same work.

2. Basic Permissions.

All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

3. Protecting Users' Legal Rights From Anti-Circumvention Law.

No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

4. Conveying Verbatim Copies.

You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

5. Conveying Modified Source Versions.

You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

  • a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
  • b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
  • c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
  • d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

6. Conveying Non-Source Forms.

You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

  • a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
  • b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
  • c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
  • d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
  • e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

7. Additional Terms.

“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

  • a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
  • b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
  • c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
  • d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
  • e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
  • f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

8. Termination.

You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

9. Acceptance Not Required for Having Copies.

You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

10. Automatic Licensing of Downstream Recipients.

Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

11. Patents.

A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.

A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

12. No Surrender of Others' Freedom.

If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

13. Use with the GNU Affero General Public License.

Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

14. Revised Versions of this License.

The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

15. Disclaimer of Warranty.

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. Limitation of Liability.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

17. Interpretation of Sections 15 and 16.

If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

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:

    <program>  Copyright (C) <year>  <name of author>
    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.

The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.

You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.

The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.

tremotesf-2.10.1/src/log/000077500000000000000000000000001523411710200151475ustar00rootroot00000000000000tremotesf-2.10.1/src/log/demangle.cpp000066400000000000000000000024311523411710200174270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "demangle.h" #include #include #if __has_include() # include # define TREMOTESF_HAVE_CXXABI_H #else # include #endif namespace tremotesf::impl { #ifdef TREMOTESF_HAVE_CXXABI_H std::string demangleTypeName(const char* typeName) { const std::unique_ptr demangled( abi::__cxa_demangle(typeName, nullptr, nullptr, nullptr), &free ); return demangled ? demangled.get() : typeName; } #else namespace { using namespace std::string_view_literals; constexpr auto structPrefix = "struct "sv; constexpr auto classPrefix = "class "sv; void removeSubstring(std::string& str, std::string_view substring) { size_t pos{}; while ((pos = str.find(substring, pos)) != std::string::npos) { str.erase(pos, substring.size()); } } } std::string demangleTypeName(const char* typeName) { std::string demangled = typeName; removeSubstring(demangled, structPrefix); removeSubstring(demangled, classPrefix); return demangled; } #endif } tremotesf-2.10.1/src/log/demangle.h000066400000000000000000000007411523411710200170760ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_LOG_DEMANGLE_H #define TREMOTESF_LOG_DEMANGLE_H #include #include namespace tremotesf { namespace impl { std::string demangleTypeName(const char* typeName); } template std::string typeName(const T& t) { return impl::demangleTypeName(typeid(t).name()); } } #endif // TREMOTESF_LOG_DEMANGLE_H tremotesf-2.10.1/src/log/demangle_test.cpp000066400000000000000000000025011523411710200204640ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "demangle.h" using namespace tremotesf; struct Foo {}; class Bar {}; namespace foobar { struct Foo {}; class Bar {}; } template struct What {}; class DemangleTest final : public QObject { Q_OBJECT private slots: void checkInt() { const int foo{}; QCOMPARE(typeName(foo), "int"); } void checkStruct() { const Foo foo{}; QCOMPARE(typeName(foo), "Foo"); } void checkClass() { const Bar bar{}; QCOMPARE(typeName(bar), "Bar"); } void checkNamespacedStruct() { const foobar::Foo foo{}; QCOMPARE(typeName(foo), "foobar::Foo"); } void checkNamespacedClass() { const foobar::Bar bar{}; QCOMPARE(typeName(bar), "foobar::Bar"); } void checkTemplatedStruct() { const What what{}; QCOMPARE(typeName(what), "What"); } void checkTemplatedStruct2() { const What what{}; QCOMPARE(typeName(what), "What"); } void checkTemplatedStruct3() { const What what{}; QCOMPARE(typeName(what), "What"); } }; QTEST_GUILESS_MAIN(DemangleTest) #include "demangle_test.moc" tremotesf-2.10.1/src/log/formatters.cpp000066400000000000000000000113731523411710200200460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "formatters.h" #include // If we don't include it here we will get undefined reference link error for fmt::formatter #include #include #include #include #include #ifdef Q_OS_WIN # include # include #endif #include "demangle.h" namespace { template fmt::format_context::iterator formatQEnumImpl(QMetaEnum meta, Integer value, fmt::format_context& ctx) { const auto named = #if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) meta.valueToKey(static_cast(value)); #else meta.valueToKey(static_cast(value)); #endif std::string unnamed{}; const auto string = [&] { if (named) return std::string_view(named); unnamed = fmt::format("", value); return std::string_view(unnamed); }(); return fmt::format_to(ctx.out(), "{}::{}::{}", meta.scope(), meta.enumName(), string); } } namespace tremotesf { fmt::format_context::iterator formatQEnum(QMetaEnum meta, std::intmax_t value, fmt::format_context& ctx) { return formatQEnumImpl(meta, value, ctx); } fmt::format_context::iterator formatQEnum(QMetaEnum meta, std::uintmax_t value, fmt::format_context& ctx) { return formatQEnumImpl(meta, value, ctx); } } namespace { fmt::string_view toFmtStringView(QByteArrayView str) { return {str.data(), static_cast(str.size())}; } fmt::format_context::iterator formatSystemError(std::string_view type, const std::system_error& e, fmt::format_context& ctx) { const int code = e.code().value(); return fmt::format_to( ctx.out(), "{}: {} (error code {} ({:#x}))", type, e.what(), code, static_cast(code) ); } } namespace fmt { format_context::iterator formatter::format(const QString& string, format_context& ctx) const { return formatter::format(toFmtStringView(string.toUtf8()), ctx); } format_context::iterator formatter::format(QStringView string, format_context& ctx) const { return formatter::format(toFmtStringView(string.toUtf8()), ctx); } format_context::iterator formatter::format(QLatin1String string, format_context& ctx) const { return formatter::format(std::string_view(string.data(), static_cast(string.size())), ctx); } format_context::iterator formatter::format(const QByteArray& array, format_context& ctx) const { return formatter::format(toFmtStringView(array), ctx); } format_context::iterator formatter::format(QByteArrayView array, format_context& ctx) const { return formatter::format(toFmtStringView(array), ctx); } format_context::iterator formatter::format(QUtf8StringView string, format_context& ctx) const { return formatter::format(toFmtStringView(string), ctx); } format_context::iterator formatter::format(QAnyStringView string, format_context& ctx) const { return formatter::format(string.toString(), ctx); } format_context::iterator formatter::format(const std::exception& e, format_context& ctx) const { const auto type = tremotesf::typeName(e); const auto what = e.what(); if (auto s = dynamic_cast(&e); s) { return formatSystemError(type, *s, ctx); } return fmt::format_to(ctx.out(), "{}: {}", type, what); } format_context::iterator formatter::format(const std::system_error& e, format_context& ctx) const { return formatSystemError(tremotesf::typeName(e), e, ctx); } #ifdef Q_OS_WIN format_context::iterator formatter::format(const winrt::hstring& str, format_context& ctx) const { return formatter::format( QString::fromWCharArray(str.data(), static_cast(str.size())), ctx ); } format_context::iterator formatter::format(const winrt::hresult_error& e, format_context& ctx) const { const auto code = e.code().value; return fmt::format_to( ctx.out(), "{}: {} (error code {} ({:#x}))", tremotesf::typeName(e), e.message(), code, static_cast(code) ); } #endif // Q_OS_WIN } tremotesf-2.10.1/src/log/formatters.h000066400000000000000000000127601523411710200175140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_LOG_FORMATTERS_H #define TREMOTESF_LOG_FORMATTERS_H #include #include #include #include #include #include #include #include #include #include #if FMT_VERSION_MAJOR >= 11 # include #else # include #endif namespace tremotesf { struct SimpleFormatter { constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context& ctx) { return ctx.begin(); } }; } template<> struct fmt::formatter : formatter { format_context::iterator format(const QString& string, format_context& ctx) const; }; template<> struct fmt::formatter : formatter { format_context::iterator format(QStringView string, format_context& ctx) const; }; template<> struct fmt::formatter : formatter { format_context::iterator format(QLatin1String string, format_context& ctx) const; }; template<> struct fmt::formatter : formatter { format_context::iterator format(const QByteArray& array, format_context& ctx) const; }; template<> struct fmt::formatter : formatter { format_context::iterator format(QByteArrayView array, format_context& ctx) const; }; template<> struct fmt::formatter : formatter { format_context::iterator format(QUtf8StringView string, format_context& ctx) const; }; template<> struct fmt::formatter : formatter { format_context::iterator format(QAnyStringView string, format_context& ctx) const; }; namespace tremotesf { inline constexpr auto singleArgumentFormatString = "{}"; template concept QDebugPrintable = requires(T t, QDebug d) { d << t; }; template struct QDebugFormatter : SimpleFormatter { fmt::format_context::iterator format(const T& t, fmt::format_context& ctx) const { QString buffer{}; QDebug stream(&buffer); stream.nospace() << t; return fmt::format_to(ctx.out(), singleArgumentFormatString, buffer); } }; // This relies on private Qt API but it should work at least until Qt 7 template concept QEnum = std::is_enum_v && requires { qt_getEnumMetaObject(T{}); }; fmt::format_context::iterator formatQEnum(QMetaEnum meta, std::intmax_t value, fmt::format_context& ctx); fmt::format_context::iterator formatQEnum(QMetaEnum meta, std::uintmax_t value, fmt::format_context& ctx); } namespace fmt { template T> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(const T& object, format_context& ctx) const { QString buffer{}; QDebug stream(&buffer); stream.nospace() << &object; return fmt::format_to(ctx.out(), tremotesf::singleArgumentFormatString, buffer); } }; template struct formatter : tremotesf::SimpleFormatter { fmt::format_context::iterator format(T t, fmt::format_context& ctx) const { const auto meta = QMetaEnum::fromType(); if constexpr (std::signed_integral>) { return tremotesf::formatQEnum(meta, static_cast(t), ctx); } else { return tremotesf::formatQEnum(meta, static_cast(t), ctx); } } }; } #define SPECIALIZE_FORMATTER_FOR_QDEBUG(Class) \ namespace fmt { \ template<> \ struct formatter : tremotesf::QDebugFormatter {}; \ } #define DISABLE_RANGE_FORMATTING(Class) \ namespace fmt { \ template<> \ struct is_range : std::false_type {}; \ template<> \ struct is_range : std::false_type {}; \ } namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(const std::exception& e, format_context& ctx) const; }; template T> requires(!std::derived_from) struct formatter : formatter {}; template<> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(const std::system_error& e, format_context& ctx) const; }; template T> struct formatter : formatter {}; } #ifdef Q_OS_WIN namespace winrt { struct hstring; struct hresult_error; } namespace fmt { template<> struct formatter : formatter { format_context::iterator format(const winrt::hstring& str, format_context& ctx) const; }; template<> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(const winrt::hresult_error& e, format_context& ctx) const; }; } #endif #endif // TREMOTESF_LOG_FORMATTERS_H tremotesf-2.10.1/src/log/log.cpp000066400000000000000000000063741523411710200164460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "log.h" #include #ifdef Q_OS_WIN # include # include #endif namespace tremotesf { constexpr auto defaultLogLevel = #ifdef QT_DEBUG QtDebugMsg; #else QtInfoMsg; #endif Q_LOGGING_CATEGORY(tremotesfLoggingCategory, tremotesfLoggingCategoryName, defaultLogLevel) void overrideDebugLogs(bool enable) { constexpr auto loggingRulesEnvVariable = "QT_LOGGING_RULES"; QByteArray rules = qgetenv(loggingRulesEnvVariable); if (!rules.isEmpty()) { rules += ";"; } rules += fmt::format("{}.debug={}", tremotesfLoggingCategoryName, enable).c_str(); qputenv(loggingRulesEnvVariable, rules); } namespace { template void appendNestedException(std::string& out, const T& e) { fmt::format_to(std::back_insert_iterator(out), "\nCaused by: {}", e); } template void appendNestedExceptions(std::string& out, const T& e) { try { std::rethrow_if_nested(e); } catch (const std::system_error& nested) { appendNestedException(out, nested); appendNestedExceptions(out, nested); } catch (const std::exception& nested) { appendNestedException(out, nested); appendNestedExceptions(out, nested); } #ifdef Q_OS_WIN catch (const winrt::hresult_error& nested) { appendNestedException(out, nested); appendNestedExceptions(out, nested); } #endif catch (...) { fmt::format_to(std::back_insert_iterator(out), "\nCaused by: unknown exception"); } } template std::string formatExceptionRecursivelyImpl(const E& e) { std::string out = fmt::format(singleArgumentFormatString, e); appendNestedExceptions(out, e); return out; } } std::string formatExceptionRecursively(const std::exception& e) { return formatExceptionRecursivelyImpl(e); } std::string formatExceptionRecursively(const std::system_error& e) { return formatExceptionRecursivelyImpl(e); } #ifdef Q_OS_WIN std::string formatExceptionRecursively(const winrt::hresult_error& e) { return formatExceptionRecursivelyImpl(e); } #endif QString Logger::formatToQString(fmt::string_view fmt, fmt::format_args args) { return QString::fromStdString(fmt::vformat(fmt, args)); } void Logger::logWithFormatArgs(fmt::string_view fmt, fmt::format_args args) const { logImpl(formatToQString(fmt, args)); } void Logger::logImpl(const QString& string) const { // We use internal qt_message_output() function here because there are only two methods // to output string to QMessageLogger and they have overheads that are unneccessary // when we are doing formatting on our own: // 1. QDebug marshalls everything through QTextStream // 2. QMessageLogger::<>(const char*, ...) overloads perform QString::vasprintf() formatting qt_message_output(type, context, string); } } tremotesf-2.10.1/src/log/log.h000066400000000000000000000154161523411710200161100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_LOG_LOG_H #define TREMOTESF_LOG_LOG_H #include #include #include #include #include #include #include #if FMT_VERSION_MAJOR >= 11 # include #else # include #endif #ifdef Q_OS_WIN # include # include #endif #include "formatters.h" #if __has_cpp_attribute(gnu::always_inline) # define ALWAYS_INLINE [[gnu::always_inline]] inline #elif __has_cpp_attribute(msvc::forceinline) # define ALWAYS_INLINE [[msvc::forceinline]] inline #elifdef _MSC_VER # define ALWAYS_INLINE __forceinline #else # define ALWAYS_INLINE inline #endif namespace tremotesf { namespace impl { template concept IsException = std::derived_from, std::exception> #ifdef Q_OS_WIN || std::derived_from, winrt::hresult_error> #endif ; template concept IsQStringView = std::same_as, QStringView> || std::same_as, QUtf8StringView> || std::same_as, QAnyStringView>; template T> ALWAYS_INLINE QString convertToQString(const T& string) { return static_cast(string); } template T> requires(!std::convertible_to && !impl::IsQStringView) ALWAYS_INLINE QString convertToQString(const T& string) { const auto stringView = static_cast(string); return QString::fromUtf8(stringView.data(), static_cast(stringView.size())); } template ALWAYS_INLINE QString convertToQString(const T& string) { return string.toString(); } template concept CanConvertToQString = !std::same_as, QString> && requires(T string) { tremotesf::impl::convertToQString(string); }; inline void printNewline(FILE* stream) { std::fwrite("\n", 1, 1, stream); } } constexpr auto tremotesfLoggingCategoryName = "tremotesf"; Q_DECLARE_LOGGING_CATEGORY(tremotesfLoggingCategory) void overrideDebugLogs(bool enable); std::string formatExceptionRecursively(const std::exception& e); std::string formatExceptionRecursively(const std::system_error& e); #ifdef Q_OS_WIN std::string formatExceptionRecursively(const winrt::hresult_error& e); #endif struct Logger { ALWAYS_INLINE consteval explicit Logger(QtMsgType type, std::source_location location) : type(type), context( location.file_name(), static_cast(location.line()), location.function_name(), tremotesfLoggingCategoryName ) {} ALWAYS_INLINE void log(const QString& string) const { if (isEnabled()) { logImpl(string); } } template ALWAYS_INLINE void log(const T& string) const { if (isEnabled()) { logImpl(impl::convertToQString(string)); } } /** * Format then print */ template ALWAYS_INLINE void log(const T& value) const { if (isEnabled()) { logWithFormatArgs( fmt::format_string(singleArgumentFormatString).get(), fmt::make_format_args(value) ); } } template requires(sizeof...(Args) != 0) ALWAYS_INLINE void log(fmt::format_string fmt, const Args&... args) const { if (isEnabled()) { logWithFormatArgs(fmt.get(), fmt::make_format_args(args...)); } } /** * Special functions to print nested exceptions recursively */ template ALWAYS_INLINE void logWithException(const E& e, const T& value) const { if (isEnabled()) { const auto formattedException = formatExceptionRecursively(e); logWithFormatArgs( fmt::format_string("{}\n{}").get(), fmt::make_format_args(value, formattedException) ); } } template requires(sizeof...(Args) != 0) ALWAYS_INLINE void logWithException(const E& e, fmt::format_string fmt, const Args&... args) const { if (isEnabled()) { auto message = formatToQString(fmt.get(), fmt::make_format_args(args...)); message += '\n'; message += formatExceptionRecursively(e); logImpl(message); } } private: ALWAYS_INLINE bool isEnabled() const { return tremotesfLoggingCategory().isEnabled(type); } static QString formatToQString(fmt::string_view fmt, fmt::format_args args); void logWithFormatArgs(fmt::string_view fmt, fmt::format_args args) const; /** * Actual log function */ void logImpl(const QString& string) const; QtMsgType type; QMessageLogContext context; }; ALWAYS_INLINE consteval Logger debug(std::source_location location = std::source_location::current()) { return Logger(QtDebugMsg, location); } ALWAYS_INLINE consteval Logger info(std::source_location location = std::source_location::current()) { return Logger(QtInfoMsg, location); } ALWAYS_INLINE consteval Logger warning(std::source_location location = std::source_location::current()) { return Logger(QtWarningMsg, location); } ALWAYS_INLINE consteval Logger fatal(std::source_location location = std::source_location::current()) { return Logger(QtFatalMsg, location); } template ALWAYS_INLINE void printlnStdout(const T& value) { fmt::print(stdout, singleArgumentFormatString, value); impl::printNewline(stdout); } template requires(sizeof...(Args) != 0) ALWAYS_INLINE void printlnStdout(fmt::format_string fmt, const Args&... args) { fmt::vprint(stdout, fmt.get(), fmt::make_format_args(args...)); impl::printNewline(stdout); } } #endif // TREMOTESF_LOG_LOG_H tremotesf-2.10.1/src/log/log_test.cpp000066400000000000000000000205721523411710200175010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #ifdef Q_OS_WIN # include # include #endif #include #include #include #include "rpc/torrent.h" #include "log.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QVariant) using namespace Qt::StringLiterals; using namespace tremotesf; #ifdef Q_OS_WIN static constexpr auto E_ACCESSDENIED = static_cast(0x80070005); #endif class PrintlnTest final : public QObject { Q_OBJECT private slots: void stdoutStringLiteral() { printlnStdout("foo"); printlnStdout("{}", "foo"); printlnStdout(FMT_STRING("{}"), "foo"); printlnStdout(fmt::runtime("{}"), "foo"); } void stdoutStdString() { const std::string str = "foo"; printlnStdout(str); printlnStdout("{}", str); printlnStdout(FMT_STRING("{}"), str); printlnStdout(fmt::runtime("{}"), str); } void stdoutStdStringView() { const std::string_view str = "foo"; printlnStdout(str); printlnStdout("{}", str); printlnStdout(FMT_STRING("{}"), str); printlnStdout(fmt::runtime("{}"), str); } void stdoutQString() { const QString str = "foo"; printlnStdout(str); printlnStdout("{}", str); printlnStdout(FMT_STRING("{}"), str); printlnStdout(fmt::runtime("{}"), str); } void stdoutQStringView() { const QString _str = "foo"; const QStringView str = _str; printlnStdout(str); printlnStdout("{}", str); printlnStdout(FMT_STRING("{}"), str); printlnStdout(fmt::runtime("{}"), str); } void stdoutQLatin1String() { const auto str = "foo"_L1; printlnStdout(str); printlnStdout("{}", str); printlnStdout(FMT_STRING("{}"), str); printlnStdout(fmt::runtime("{}"), str); } void stdoutQUtf8StringView() { const QUtf8StringView str = "foo"; printlnStdout(str); printlnStdout("{}", str); printlnStdout(FMT_STRING("{}"), str); printlnStdout(fmt::runtime("{}"), str); } void stdoutQAnyStringView() { const QAnyStringView str = "foo"; printlnStdout(str); printlnStdout("{}", str); printlnStdout(FMT_STRING("{}"), str); printlnStdout(fmt::runtime("{}"), str); } void stdoutQVariant() { const QVariant value = "foo"; printlnStdout(value); printlnStdout("{}", value); printlnStdout(FMT_STRING("{}"), value); printlnStdout(fmt::runtime("{}"), value); } void stdoutQStringList() { const QStringList list{"foo"}; printlnStdout(list); printlnStdout("{}", list); printlnStdout(FMT_STRING("{}"), list); printlnStdout(fmt::runtime("{}"), list); } void stdoutTorrent() { const Torrent value{}; printlnStdout(value); printlnStdout("{}", value); printlnStdout(FMT_STRING("{}"), value); printlnStdout(fmt::runtime("{}"), value); } void stdoutThis() { printlnStdout(*this); printlnStdout("{}", *this); printlnStdout(FMT_STRING("{}"), *this); printlnStdout(fmt::runtime("{}"), *this); } void stdoutQObject() { QObject value{}; printlnStdout(value); printlnStdout("{}", value); printlnStdout(FMT_STRING("{}"), value); printlnStdout(fmt::runtime("{}"), value); } void infoStringLiteral() { info().log("foo"); info().log("{}", "foo"); info().log(FMT_STRING("{}"), "foo"); info().log(fmt::runtime("{}"), "foo"); } void infoStdString() { const std::string str = "foo"; info().log(str); info().log("{}", str); info().log(FMT_STRING("{}"), str); info().log(fmt::runtime("{}"), str); } void infoStdStringView() { const std::string_view str = "foo"; info().log(str); info().log("{}", str); info().log(FMT_STRING("{}"), str); info().log(fmt::runtime("{}"), str); } void infoQString() { const QString str = "foo"; info().log(str); info().log("{}", str); info().log(FMT_STRING("{}"), str); info().log(fmt::runtime("{}"), str); } void infoQStringView() { const QString _str = "foo"; const QStringView str = _str; info().log(str); info().log("{}", str); info().log(FMT_STRING("{}"), str); info().log(fmt::runtime("{}"), str); } void infoQLatin1String() { const auto str = "foo"_L1; info().log(str); info().log("{}", str); info().log(FMT_STRING("{}"), str); info().log(fmt::runtime("{}"), str); } void infoQUtf8StringView() { const QUtf8StringView str = "foo"; info().log(str); info().log("{}", str); info().log(FMT_STRING("{}"), str); info().log(fmt::runtime("{}"), str); } void infoQAnyStringView() { const QAnyStringView str = "foo"; info().log(str); info().log("{}", str); info().log(FMT_STRING("{}"), str); info().log(fmt::runtime("{}"), str); } void infoQVariant() { const QVariant value = "foo"; info().log(value); info().log("{}", value); info().log(FMT_STRING("{}"), value); info().log(fmt::runtime("{}"), value); } void infoQStringList() { const QStringList list{"foo"}; info().log(list); info().log("{}", list); info().log(FMT_STRING("{}"), list); info().log(fmt::runtime("{}"), list); } void infoTorrent() { const Torrent value{}; info().log(value); info().log("{}", value); info().log(FMT_STRING("{}"), value); info().log(fmt::runtime("{}"), value); } void infoThis() { info().log(*this); info().log("{}", *this); info().log(FMT_STRING("{}"), *this); info().log(fmt::runtime("{}"), *this); } void infoQObject() { QObject value{}; info().log(value); info().log("{}", value); info().log(FMT_STRING("{}"), value); info().log(fmt::runtime("{}"), value); } void warningStdException() { const std::runtime_error e("nope"); warning().log(e); } void warningWithStdException() { const std::runtime_error e("nope"); warning().logWithException(e, "oh no"); } void warningNested() { try { try { throw std::runtime_error("nope"); } catch (const std::runtime_error&) { std::throw_with_nested(std::runtime_error("higher-level nope")); } } catch (const std::runtime_error& e) { warning().log(e); } } void warningWithNested() { try { try { throw std::runtime_error("nope"); } catch (const std::runtime_error&) { std::throw_with_nested(std::runtime_error("higher-level nope")); } } catch (const std::runtime_error& e) { warning().logWithException(e, "oh no"); } } #ifdef Q_OS_WIN void warningHresultError() { winrt::hresult_error e(E_ACCESSDENIED); warning().log(e); } void warningWithHresultError() { winrt::hresult_error e(E_ACCESSDENIED); warning().logWithException(e, "oh no"); } void warningHresultErrorNested() { try { try { throw winrt::hresult_error(E_ACCESSDENIED); } catch (const winrt::hresult_error&) { std::throw_with_nested(std::runtime_error("higher-level nope")); } } catch (const std::runtime_error& e) { warning().log(e); } } void warningWithHresultErrorNested() { try { try { throw winrt::hresult_error(E_ACCESSDENIED); } catch (const winrt::hresult_error&) { std::throw_with_nested(std::runtime_error("higher-level nope")); } } catch (const std::runtime_error& e) { warning().logWithException(e, "oh no"); } } #endif }; QTEST_GUILESS_MAIN(PrintlnTest) #include "log_test.moc" tremotesf-2.10.1/src/macoshelpers.h000066400000000000000000000005431523411710200172260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_MACOSHELPERS_H #define TREMOTESF_MACOSHELPERS_H #include namespace tremotesf { void hideNSApp(); void unhideNSApp(); bool isNSAppHidden(); QString bundleResourcesPath(); } #endif // TREMOTESF_MACOSHELPERS_H tremotesf-2.10.1/src/macoshelpers.mm000066400000000000000000000012441523411710200174070ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "macoshelpers.h" #include #include namespace tremotesf { void hideNSApp() { [NSApp hide:nullptr]; } void unhideNSApp() { [NSApp unhide:nullptr]; } bool isNSAppHidden() { return [NSApp isHidden]; } QString bundleResourcesPath() { auto* const bundle = [NSBundle mainBundle]; if (!bundle) { throw std::runtime_error("[NSBundle mainBundle] returned null"); } auto* const resourcePath = [bundle resourcePath]; return QString::fromNSString(resourcePath); } } tremotesf-2.10.1/src/magnetlinkparser.cpp000066400000000000000000000037261523411710200204500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include "magnetlinkparser.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { constexpr auto xtKey = "xt"_L1; constexpr auto xtValuePrefix = "urn:btih:"_L1; constexpr auto trKey = "tr"_L1; } TorrentMagnetLink parseMagnetLink(const QUrl& url) { if (url.scheme() != magnetScheme) { throw std::runtime_error("URL scheme must be magnet"); } const QUrlQuery query(url); const auto xtValues = query.allQueryItemValues(xtKey, QUrl::FullyDecoded); const auto infoHashV1Value = std::ranges::find_if(xtValues, [](const auto& value) { return value.startsWith(xtValuePrefix); }); if (infoHashV1Value == xtValues.end()) { throw std::runtime_error("Did not find v1 info hash in the URL"); } auto infoHashV1 = infoHashV1Value->mid(xtValuePrefix.size()).toLower(); auto trackers = query.allQueryItemValues(trKey, QUrl::FullyDecoded) | std::views::transform([](auto tracker) { return std::set{std::move(tracker)}; }) | std::ranges::to(); return {.infoHashV1 = std::move(infoHashV1), .trackers = std::move(trackers)}; } QDebug operator<<(QDebug debug, const TorrentMagnetLink& magnetLink) { const QDebugStateSaver saver(debug); debug.noquote() << fmt::format(singleArgumentFormatString, magnetLink).c_str(); return debug; } } fmt::format_context::iterator fmt::formatter::format( const tremotesf::TorrentMagnetLink& magnetLink, format_context& ctx ) const { return fmt::format_to( ctx.out(), "MagnetLink(infoHashV1={}, trackers={})", magnetLink.infoHashV1, magnetLink.trackers ); } tremotesf-2.10.1/src/magnetlinkparser.h000066400000000000000000000016751523411710200201160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_MAGNETLINKPARSER_H #define TREMOTESF_MAGNETLINKPARSER_H #include #include #include "log/formatters.h" class QUrl; namespace tremotesf { inline constexpr auto magnetScheme = QLatin1String("magnet"); struct TorrentMagnetLink { QString infoHashV1; std::vector> trackers; bool operator==(const TorrentMagnetLink&) const = default; }; /** * @throws std::runtime_error */ TorrentMagnetLink parseMagnetLink(const QUrl& url); QDebug operator<<(QDebug debug, const TorrentMagnetLink& magnetLink); } template<> struct fmt::formatter : tremotesf::SimpleFormatter { format_context::iterator format(const tremotesf::TorrentMagnetLink& magnetLink, format_context& ctx) const; }; #endif // TREMOTESF_MAGNETLINKPARSER_H tremotesf-2.10.1/src/magnetlinkparser_test.cpp000066400000000000000000000042231523411710200215000ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "magnetlinkparser.h" using namespace Qt::StringLiterals; namespace tremotesf { class MagnetLinkParserTest final : public QObject { Q_OBJECT private slots: void parseWikipediaLink() { const QUrl url( "magnet:?xt=urn:btih:779D06C72A13A72DA82611F44F07543AC691DC54&dn=enwiki-20240701-pages-" "articles-multistream.xml.bz2&tr=wss%3a%2f%2fwstracker.online" ); const TorrentMagnetLink expected{ .infoHashV1 = "779d06c72a13a72da82611f44f07543ac691dc54"_L1, .trackers = {{"wss://wstracker.online"_L1}} }; QCOMPARE(expected, parseMagnetLink(url)); } void parseAltWikipediaLink() { const QUrl url( "magnet:?xt=urn:btih:GVED7WSKNQJUIBE2KYU3SRWFRDEN4JVK&dn=simplewiki-20230820-pages-articles-" "multistream.xml.bz2&xl=283045562&tr=http%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&" "tr=http%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr." "org%3A1337&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80%2Fannounce&tr=http%3A%2F%" "2Ffosstorrents.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ffosstorrents.com%3A6969%2Fannounce" ); const TorrentMagnetLink expected{ .infoHashV1 = "gved7wsknqjuibe2kyu3srwfrden4jvk"_L1, .trackers = { {"http://tracker.opentrackr.org:1337/announce"_L1}, {"http://tracker.opentrackr.org:1337/announce"_L1}, {"udp://tracker.opentrackr.org:1337"_L1}, {"udp://tracker.openbittorrent.com:80/announce"_L1}, {"http://fosstorrents.com:6969/announce"_L1}, {"udp://fosstorrents.com:6969/announce"_L1} } }; QCOMPARE(expected, parseMagnetLink(url)); } }; } QTEST_GUILESS_MAIN(tremotesf::MagnetLinkParserTest) #include "magnetlinkparser_test.moc" tremotesf-2.10.1/src/org.freedesktop.FileManager1.xml000066400000000000000000000007351523411710200224500ustar00rootroot00000000000000 tremotesf-2.10.1/src/org.freedesktop.Notifications.xml000066400000000000000000000052221523411710200230220ustar00rootroot00000000000000 tremotesf-2.10.1/src/org.freedesktop.portal.Notification.xml000066400000000000000000000127511523411710200241440ustar00rootroot00000000000000 tremotesf-2.10.1/src/pragmamacros.h000066400000000000000000000012311523411710200172100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_PRAGMAMACROS_H #define TREMOTESF_PRAGMAMACROS_H #if defined(__GNUC__) || defined(__clang__) # define SUPPRESS_DEPRECATED_WARNINGS_BEGIN \ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") # define SUPPRESS_DEPRECATED_WARNINGS_END _Pragma("GCC diagnostic pop") #else # define SUPPRESS_DEPRECATED_WARNINGS_BEGIN _Pragma("warning(push)") _Pragma("warning(disable : 4996)") # define SUPPRESS_DEPRECATED_WARNINGS_END _Pragma("warning(pop)") #endif #endif // TREMOTESF_PRAGMAMACROS_H tremotesf-2.10.1/src/resources.qrc000066400000000000000000000003771523411710200171160ustar00rootroot00000000000000 authors.html license.html translators.html tremotesf-2.10.1/src/rpc/000077500000000000000000000000001523411710200151525ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/addressutils.cpp000066400000000000000000000030641523411710200203670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "addressutils.h" #include #include #include "log/log.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QHostAddress) namespace tremotesf { bool isLocalIpAddress(const QHostAddress& ipAddress) { info().log("isLocalIpAddress() called for: ipAddress = {}", ipAddress); if (ipAddress.isLoopback()) { info().log("isLocalIpAddress: address is loopback, return true"); return true; } const auto addresses = QNetworkInterface::allAddresses(); info().log("isLocalIpAddress: this machine's IP addresses:"); for (const auto& address : addresses) { info().log("isLocalIpAddress: - {}", address); } if (addresses.contains(ipAddress)) { info().log("isLocalIpAddress: address is this machine's IP address, return true"); return true; } info().log("isLocalIpAddress: address is not this machine's IP address, return false"); return false; } [[nodiscard]] std::optional isLocalIpAddress(const QString& address) { info().log("isLocalIpAddress() called for: address = {}", address); const QHostAddress ipAddress(address); if (ipAddress.isNull()) { info().log("isLocalIpAddress: address is not an IP address"); return std::nullopt; } info().log("isLocalIpAddress: address is an IP address"); return isLocalIpAddress(ipAddress); } } tremotesf-2.10.1/src/rpc/addressutils.h000066400000000000000000000007001523411710200200260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_ADDRESSUTILS_H #define TREMOTESF_RPC_ADDRESSUTILS_H #include class QHostAddress; class QString; namespace tremotesf { [[nodiscard]] bool isLocalIpAddress(const QHostAddress& ipAddress); [[nodiscard]] std::optional isLocalIpAddress(const QString& address); } #endif // TREMOTESF_RPC_ADDRESSUTILS_H tremotesf-2.10.1/src/rpc/jsonutils.h000066400000000000000000000076111523411710200173620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_JSONUTILS_H #define TREMOTESF_RPC_JSONUTILS_H #include #include #include #include #include #include #include #include #include #include "log/log.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QJsonValue) namespace tremotesf::impl { template concept EnumConstant = std::is_enum_v; template concept JsonConstant = std::same_as || std::same_as; template struct EnumMapping { constexpr explicit EnumMapping(EnumConstantT enumValue, JsonConstantT jsonValue) : enumValue(enumValue), jsonValue(jsonValue) {} EnumConstantT enumValue; JsonConstantT jsonValue; }; template struct EnumMapper { constexpr explicit EnumMapper(std::array, EnumCount>&& mappings) : mappings(std::move(mappings)) {} EnumConstantT fromJsonValue(const QJsonValue& value, QLatin1String key) const { const auto jsonValue = [&] { if constexpr (std::same_as) { if (!value.isDouble()) { warning().log("JSON field with key {} and value {} is not a number", key, value); return std::optional{}; } return std::optional(value.toInt()); } else if constexpr (std::same_as) { if (!value.isString()) { warning().log("JSON field with key {} and value {} is not a string", key, value); return std::optional{}; } return std::optional(value.toString()); } }(); if (!jsonValue.has_value()) { return {}; } const auto found = std::ranges::find(mappings, jsonValue, &EnumMapping::jsonValue); if (found == mappings.end()) { warning().log("JSON field with key {} has unknown value {}", key, value); return {}; } return found->enumValue; } JsonConstantT toJsonConstant(EnumConstantT value) const { const auto found = std::ranges::find(mappings, value, &EnumMapping::enumValue); if (found == mappings.end()) { throw std::logic_error(fmt::format("Unknown enum value {}", value)); } return found->jsonValue; } private: std::array, EnumCount> mappings{}; }; template requires std::convertible_to, QJsonValue> inline QJsonArray toJsonArray(const FromRange& from) { QJsonArray array{}; std::ranges::copy(from, std::back_insert_iterator(array)); return array; } inline void updateDateTime(QDateTime& dateTime, const QJsonValue& value, bool& changed) { const auto newDateTime = value.toInteger(); if (newDateTime > 0) { if (!dateTime.isValid() || newDateTime != dateTime.toSecsSinceEpoch()) { dateTime.setSecsSinceEpoch(newDateTime); changed = true; } } else { if (!dateTime.isNull()) { dateTime.setDate({}); dateTime.setTime({}); changed = true; } } } } #endif // TREMOTESF_RPC_JSONUTILS_H tremotesf-2.10.1/src/rpc/mounteddirectoriesutils.cpp000066400000000000000000000045271523411710200226570ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "mounteddirectoriesutils.h" #include "rpc.h" #include "serversettings.h" #include "servers.h" #include #include using namespace Qt::StringLiterals; namespace tremotesf { bool isServerLocalOrTorrentIsMounted(const Rpc* rpc, const Torrent* torrent) { if (rpc->isLocal()) { return true; } if (!Servers::instance()->currentServerHasMountedDirectories()) { return false; } return !localTorrentDownloadDirectoryPath(rpc, torrent).isEmpty(); } QString localTorrentDownloadDirectoryPath(const Rpc* rpc, const Torrent* torrent) { const auto serverSettings = rpc->serverSettings(); const bool incompleteDirectoryEnabled = serverSettings->data().incompleteDirectoryEnabled; QString directory{}; if (incompleteDirectoryEnabled && torrent->data().leftUntilDone > 0) { directory = serverSettings->data().incompleteDirectory; } else { directory = torrent->data().downloadDirectory; } if (!rpc->isLocal() && !directory.isEmpty() && Servers::instance()->currentServerHasMountedDirectories()) { directory = Servers::instance()->fromRemoteToLocalDirectory(directory, serverSettings); } return directory; } QString localTorrentRootFilePath(const Rpc* rpc, const Torrent* torrent) { const QString downloadDirectoryPath = localTorrentDownloadDirectoryPath(rpc, torrent); if (downloadDirectoryPath.isEmpty()) { return {}; } const auto& torrentName = torrent->data().name; QString rootFilePath = downloadDirectoryPath % '/' % torrentName; if (rpc->serverSettings()->data().renameIncompleteFiles && torrent->data().leftUntilDone > 0) { const QString incompleteRootFilePath = rootFilePath % ".part"_L1; // Don't return incomplete file path unless it exists because we don't know whether it's supposed to be a directory or a file, // and in case of directory this path can't exist so we don't want to try to open it if (QFileInfo::exists(incompleteRootFilePath)) { return incompleteRootFilePath; } } return rootFilePath; } } tremotesf-2.10.1/src/rpc/mounteddirectoriesutils.h000066400000000000000000000010761523411710200223200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_MOUNTEDDIRECTORIESUTILS_H #define TREMOTESF_RPC_MOUNTEDDIRECTORIESUTILS_H #include namespace tremotesf { class Rpc; class Torrent; bool isServerLocalOrTorrentIsMounted(const Rpc* rpc, const Torrent* torrent); QString localTorrentDownloadDirectoryPath(const Rpc* rpc, const Torrent* torrent); QString localTorrentRootFilePath(const Rpc* rpc, const Torrent* torrent); } #endif // TREMOTESF_RPC_MOUNTEDDIRECTORIESUTILS_H tremotesf-2.10.1/src/rpc/numbervaluerange.h000066400000000000000000000031201523411710200206610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_NUMBERVALUERANGE_H #define TREMOTESF_RPC_NUMBERVALUERANGE_H #include #include #include namespace tremotesf { template struct NumberValueRange { T min; T max; }; inline constexpr auto nonNegativeIntegersRange = NumberValueRange{.min = 0, .max = std::numeric_limits::max()}; inline constexpr auto nonNegativeDecimalsRange = NumberValueRange{.min = 0.0, .max = std::numeric_limits::max()}; // Transmission processes it as unsigned 16-bit integer inline constexpr auto minutesRange = NumberValueRange{.min = 0, .max = static_cast(std::numeric_limits::max())}; inline constexpr auto portsRange = minutesRange; inline constexpr auto peersLimitRange = minutesRange; // Transmission processes it as unsigned 32-bit integer that's multiplied by 1000 and stored as unsigned 32-bit integer inline constexpr auto speedLimitKbpsRange = NumberValueRange{.min = 0, .max = static_cast(std::numeric_limits::max() / 1000)}; inline void setSpinBoxLimits(QSpinBox* spinBox, NumberValueRange range) { spinBox->setMinimum(range.min); spinBox->setMaximum(range.max); } inline void setSpinBoxLimits(QDoubleSpinBox* spinBox, NumberValueRange range) { spinBox->setMinimum(range.min); spinBox->setMaximum(range.max); } } #endif // TREMOTESF_RPC_NUMBERVALUERANGE_H tremotesf-2.10.1/src/rpc/pathutils.cpp000066400000000000000000000147771523411710200177130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "pathutils.h" #include "target_os.h" using namespace Qt::StringLiterals; namespace tremotesf { // We can't use QDir::to/fromNativeSeparators because it checks for current OS, // and we need it to work regardless of OS we are running on namespace { constexpr auto windowsSeparatorChar = '\\'; constexpr auto unixSeparatorChar = '/'; constexpr auto unixSeparatorString = "/"_L1; constexpr PathOs localPathOs = targetOs == TargetOs::Windows ? PathOs::Windows : PathOs::Unix; enum class PathType { Unix, WindowsAbsoluteDOSFilePath, WindowsUNCOrDOSDevicePath }; bool isWindowsUNCOrDOSDevicePath(QStringView path) { static const QRegularExpression regex(R"(^(?:\\|//).*$)"_L1); return regex.matchView(path).hasMatch(); } PathType determinePathType(const QString& path, PathOs pathOs) { switch (pathOs) { case PathOs::Unix: { return PathType::Unix; } case PathOs::Windows: // There can be ambiguity here so DOS file path takes precedence if (isAbsoluteWindowsDOSFilePath(path)) { return PathType::WindowsAbsoluteDOSFilePath; } if (isWindowsUNCOrDOSDevicePath(path)) { return PathType::WindowsUNCOrDOSDevicePath; } return PathType::WindowsAbsoluteDOSFilePath; } throw std::logic_error("Unknown PathOs value"); } void convertFromNativeWindowsSeparators(QString& path) { path.replace(windowsSeparatorChar, unixSeparatorChar); } void convertToNativeWindowsSeparators(QString& path) { path.replace(unixSeparatorChar, windowsSeparatorChar); } void capitalizeWindowsDriveLetter(QString& path) { if (path.size() >= 2 && path[1] == ':') { const auto drive = path[0]; if (drive.isLower()) { path[0] = drive.toUpper(); } } } void collapseRepeatingSeparators(QString& path, PathType pathType) { const auto& regex = [pathType]() -> const QRegularExpression& { if (pathType == PathType::WindowsUNCOrDOSDevicePath) { // Don't collapse leading '//' static const QRegularExpression regex(R"((?!^)//+)"_L1); return regex; } static const QRegularExpression regex(R"(//+)"_L1); return regex; }(); path.replace(regex, unixSeparatorString); } void dropOrAddTrailingSeparator(QString& path, PathType pathType) { const auto minimumLength = [pathType] { switch (pathType) { case PathType::Unix: return 1; // e.g. '/' case PathType::WindowsAbsoluteDOSFilePath: return 3; // e.g. 'C:/' case PathType::WindowsUNCOrDOSDevicePath: return 2; // e.g. '//' } throw std::logic_error("Unknown PathType value"); }(); if (path.size() <= minimumLength) { if (pathType == PathType::WindowsAbsoluteDOSFilePath && path.size() == 2) { path.append(unixSeparatorChar); } return; } if (path.back() == unixSeparatorChar) { path.chop(1); } } void normalizePathImpl(QString& path, PathType pathType) { if (pathType != PathType::Unix) { convertFromNativeWindowsSeparators(path); if (pathType == PathType::WindowsAbsoluteDOSFilePath) { capitalizeWindowsDriveLetter(path); } } collapseRepeatingSeparators(path, pathType); dropOrAddTrailingSeparator(path, pathType); } } bool isAbsoluteWindowsDOSFilePath(QStringView path) { static const QRegularExpression regex(R"(^[A-Za-z]:[\\/]?.*$)"_L1); return regex.matchView(path).hasMatch(); } std::optional parsePathAsUrl(const QString& path) { if constexpr (localPathOs == PathOs::Windows) { // Windows path can be parsed as url, handle it first if (isAbsoluteWindowsDOSFilePath(path)) { return std::nullopt; } } const QUrl url = path; if (url.isValid() && !url.isRelative() && !url.scheme().isEmpty() && !url.path().isEmpty()) { return url; } return std::nullopt; } QString normalizePath(const QString& path, PathOs pathOs) { if (path.isEmpty()) { return {}; } QString result = path.trimmed(); if (result.isEmpty()) { return {}; } normalizePathImpl(result, determinePathType(path, pathOs)); return result; } QString normalizeLocalPathOrNetworkShareUrl(const QString& path) { if (path.isEmpty()) { return {}; } QString result = path.trimmed(); if (result.isEmpty()) { return {}; } if (auto url = parsePathAsUrl(result); url.has_value()) { if (url->isLocalFile()) { // We shouldn't have file:// urls here, but handle them just in case auto localPath = url->toLocalFile(); normalizePathImpl(localPath, determinePathType(localPath, localPathOs)); return localPath; } auto urlPath = url->path(); normalizePathImpl(urlPath, PathType::Unix); url->setPath(urlPath); return url->toString(); } normalizePathImpl(result, determinePathType(result, localPathOs)); return result; } QString toNativeSeparators(const QString& path, PathOs pathOs) { if (path.isEmpty() || pathOs != PathOs::Windows) { return path; } QString result = path; convertToNativeWindowsSeparators(result); return result; } QString lastPathSegment(const QString& path) { const auto index = path.lastIndexOf(unixSeparatorChar); if (index == -1 || index == (path.size() - 1)) { return path; } return path.sliced(index + 1); } } tremotesf-2.10.1/src/rpc/pathutils.h000066400000000000000000000020261523411710200173400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_PATHUTILS_H #define TREMOTESF_RPC_PATHUTILS_H #include #include #include namespace tremotesf { bool isAbsoluteWindowsDOSFilePath(QStringView path); std::optional parsePathAsUrl(const QString& path); /** * We need to pass PathOs explicitly because we can't determing whether given path is Unix or Windows path from its string alone: * There is no way to distinguish whether '//foo' is Unix path with duplicate directory separator, or Windows UNC path * (we need to handle Windows paths with both '\' and '/' separators) */ enum class PathOs { Unix, Windows }; QString normalizePath(const QString& path, PathOs pathOs); QString normalizeLocalPathOrNetworkShareUrl(const QString& path); QString toNativeSeparators(const QString& path, PathOs pathOs); QString lastPathSegment(const QString& path); } #endif // TREMOTESF_RPC_PATHUTILS_H tremotesf-2.10.1/src/rpc/pathutils_test.cpp000066400000000000000000000215671523411710200207450ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "pathutils.h" using namespace tremotesf; class PathUtilsTest final : public QObject { Q_OBJECT private: struct NormalizeTestCase { QString inputPath{}; QString expectedNormalizedPath{}; PathOs pathOs; }; struct NormalizeLocalPathOrNetworkShareUrlTestCase { QString inputPathOrUrl{}; QString expectedNormalizedPathOrUrl{}; }; struct NativeSeparatorsTestCase { QString inputPath{}; QString expectedNativeSeparatorsPath{}; PathOs pathOs; }; private slots: void checkNormalize() { const auto testCases = std::array{ NormalizeTestCase{.inputPath = "", .expectedNormalizedPath = "", .pathOs = PathOs::Unix}, NormalizeTestCase{.inputPath = "", .expectedNormalizedPath = "", .pathOs = PathOs::Windows}, NormalizeTestCase{.inputPath = "/", .expectedNormalizedPath = "/", .pathOs = PathOs::Unix}, NormalizeTestCase{ .inputPath = "/", .expectedNormalizedPath = "/", .pathOs = PathOs::Windows }, // Whatever that is we leave it as it is NormalizeTestCase{.inputPath = "//", .expectedNormalizedPath = "/", .pathOs = PathOs::Unix}, NormalizeTestCase{.inputPath = "//", .expectedNormalizedPath = "//", .pathOs = PathOs::Windows}, // UNC path NormalizeTestCase{.inputPath = "///", .expectedNormalizedPath = "/", .pathOs = PathOs::Unix}, NormalizeTestCase{ .inputPath = "///", .expectedNormalizedPath = "//", .pathOs = PathOs::Windows }, // UNC path? whatever NormalizeTestCase{.inputPath = " / ", .expectedNormalizedPath = "/", .pathOs = PathOs::Unix}, NormalizeTestCase{ .inputPath = " / ", .expectedNormalizedPath = "/", .pathOs = PathOs::Windows }, // Whatever that is we leave it as it is NormalizeTestCase{ .inputPath = "///home//foo", .expectedNormalizedPath = "/home/foo", .pathOs = PathOs::Unix }, NormalizeTestCase{ .inputPath = "C:/home//foo", .expectedNormalizedPath = "C:/home/foo", .pathOs = PathOs::Windows }, NormalizeTestCase{ .inputPath = "C:/home//foo/", .expectedNormalizedPath = "C:/home/foo", .pathOs = PathOs::Windows }, NormalizeTestCase{ .inputPath = R"(C:\home\foo)", .expectedNormalizedPath = "C:/home/foo", .pathOs = PathOs::Windows }, NormalizeTestCase{ .inputPath = R"(C:\home\foo\\)", .expectedNormalizedPath = "C:/home/foo", .pathOs = PathOs::Windows }, NormalizeTestCase{ .inputPath = R"(z:\home\foo)", .expectedNormalizedPath = "Z:/home/foo", .pathOs = PathOs::Windows }, NormalizeTestCase{.inputPath = R"(D:\)", .expectedNormalizedPath = "D:/", .pathOs = PathOs::Windows}, NormalizeTestCase{.inputPath = R"( D:\ )", .expectedNormalizedPath = "D:/", .pathOs = PathOs::Windows}, NormalizeTestCase{.inputPath = R"(D:\\)", .expectedNormalizedPath = "D:/", .pathOs = PathOs::Windows}, NormalizeTestCase{.inputPath = "D:/", .expectedNormalizedPath = "D:/", .pathOs = PathOs::Windows}, NormalizeTestCase{.inputPath = "D://", .expectedNormalizedPath = "D:/", .pathOs = PathOs::Windows}, NormalizeTestCase{ .inputPath = R"(\\LOCALHOST\c$\home\foo)", .expectedNormalizedPath = R"(//LOCALHOST/c$/home/foo)", .pathOs = PathOs::Windows }, // Backslashes in Unix paths are untouched NormalizeTestCase{ .inputPath = R"(///home//fo\o)", .expectedNormalizedPath = R"(/home/fo\o)", .pathOs = PathOs::Unix }, // Internal whitespace is untouched NormalizeTestCase{ .inputPath = "///home//fo o", .expectedNormalizedPath = "/home/fo o", .pathOs = PathOs::Unix }, NormalizeTestCase{ .inputPath = R"(C:\home\fo o)", .expectedNormalizedPath = "C:/home/fo o", .pathOs = PathOs::Windows }, // Weird cases from the top of my head NormalizeTestCase{.inputPath = "d:", .expectedNormalizedPath = "D:/", .pathOs = PathOs::Windows}, NormalizeTestCase{.inputPath = "d:foo", .expectedNormalizedPath = "D:foo", .pathOs = PathOs::Windows}, NormalizeTestCase{ .inputPath = R"(c::\wtf)", .expectedNormalizedPath = R"(C::/wtf)", .pathOs = PathOs::Windows } }; for (const auto& [inputPath, expectedNormalizedPath, pathOs] : testCases) { QCOMPARE(normalizePath(inputPath, pathOs), expectedNormalizedPath); } } void checkNormalizeLocalPathOrNetworkShareUrl() { const auto testCases = std::array{ NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "SMB://HOSTNAME/PATH", .expectedNormalizedPathOrUrl = "smb://hostname/PATH" }, NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "smb://hostname//path/to/share/", .expectedNormalizedPathOrUrl = "smb://hostname/path/to/share" }, #ifdef Q_OS_WIN NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "c://local/path", .expectedNormalizedPathOrUrl = "C:/local/path" }, NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "file:///c:/local//path", .expectedNormalizedPathOrUrl = "C:/local/path" }, #else NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "file:///local//path/", .expectedNormalizedPathOrUrl = "/local/path" }, #endif // full RFC example with username, password and port - untouched NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "ftp://user:password@example:21/share", .expectedNormalizedPathOrUrl = "ftp://user:password@example:21/share" }, // same ipv6 - untouched NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "ftp://user:password@[::1]:21/path", .expectedNormalizedPathOrUrl = "ftp://user:password@[::1]:21/path" }, // weird paths - untouched NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "ftp://:@hostname/path/to/share", .expectedNormalizedPathOrUrl = "ftp://:@hostname/path/to/share" }, NormalizeLocalPathOrNetworkShareUrlTestCase{ .inputPathOrUrl = "ftp://hostname:/path/to/share", .expectedNormalizedPathOrUrl = "ftp://hostname:/path/to/share" } }; } void checkToNativeSeparators() { const auto testCases = std::array{ NativeSeparatorsTestCase{.inputPath = "/", .expectedNativeSeparatorsPath = "/", .pathOs = PathOs::Unix}, NativeSeparatorsTestCase{ .inputPath = "/home/foo", .expectedNativeSeparatorsPath = "/home/foo", .pathOs = PathOs::Unix }, NativeSeparatorsTestCase{ .inputPath = "C:/", .expectedNativeSeparatorsPath = R"(C:\)", .pathOs = PathOs::Windows }, NativeSeparatorsTestCase{ .inputPath = "C:/home/foo", .expectedNativeSeparatorsPath = R"(C:\home\foo)", .pathOs = PathOs::Windows }, NativeSeparatorsTestCase{ .inputPath = R"(//LOCALHOST/c$/home/foo)", .expectedNativeSeparatorsPath = R"(\\LOCALHOST\c$\home\foo)", .pathOs = PathOs::Windows }, NativeSeparatorsTestCase{ .inputPath = R"(C::/wtf)", .expectedNativeSeparatorsPath = R"(C::\wtf)", .pathOs = PathOs::Windows } }; for (const auto& [inputPath, expectedNativeSeparatorsPath, pathOs] : testCases) { QCOMPARE(toNativeSeparators(inputPath, pathOs), expectedNativeSeparatorsPath); } } }; QTEST_GUILESS_MAIN(PathUtilsTest) #include "pathutils_test.moc" tremotesf-2.10.1/src/rpc/peer.cpp000066400000000000000000000015401523411710200166110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "peer.h" #include #include "stdutils.h" using namespace Qt::StringLiterals; namespace tremotesf { Peer::Peer(QString&& address, const QJsonObject& peerJson) : address(std::move(address)), client(peerJson.value("clientName"_L1).toString()) { update(peerJson); } bool Peer::update(const QJsonObject& peerJson) { bool changed = false; setChanged(downloadSpeed, peerJson.value("rateToClient"_L1).toInteger(), changed); setChanged(uploadSpeed, peerJson.value("rateToPeer"_L1).toInteger(), changed); setChanged(progress, peerJson.value("progress"_L1).toDouble(), changed); setChanged(flags, peerJson.value("flagStr"_L1).toString(), changed); return changed; } } tremotesf-2.10.1/src/rpc/peer.h000066400000000000000000000012741523411710200162620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_PEER_H #define TREMOTESF_RPC_PEER_H #include class QJsonObject; namespace tremotesf { struct Peer { static constexpr auto addressKey = QLatin1String("address"); explicit Peer(QString&& address, const QJsonObject& peerJson); bool update(const QJsonObject& peerJson); bool operator==(const Peer& other) const = default; QString address{}; QString client{}; qint64 downloadSpeed{}; qint64 uploadSpeed{}; double progress{}; QString flags{}; }; } #endif // TREMOTESF_RPC_PEER_H tremotesf-2.10.1/src/rpc/qsslcertificateformatter.cpp000066400000000000000000000013451523411710200227720ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "qsslcertificateformatter.h" using namespace Qt::StringLiterals; namespace fmt { format_context::iterator formatter::format(const QSslCertificate& certificate, format_context& ctx) const { // QSslCertificate::toText is implemented only for OpenSSL backend static const bool isOpenSSL = (QSslSocket::activeBackend() == "openssl"_L1); if (!isOpenSSL) { return tremotesf::QDebugFormatter{}.format(certificate, ctx); } return formatter{}.format(certificate.toText(), ctx); } } tremotesf-2.10.1/src/rpc/qsslcertificateformatter.h000066400000000000000000000010031523411710200224260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_QSSLCERTIFICATEFORMATTER_H #define TREMOTESF_QSSLCERTIFICATEFORMATTER_H #include "log/formatters.h" class QSslCertificate; namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { fmt::format_context::iterator format(const QSslCertificate& certificate, fmt::format_context& ctx) const; }; } #endif // TREMOTESF_QSSLCERTIFICATEFORMATTER_H tremotesf-2.10.1/src/rpc/requestrouter.cpp000066400000000000000000000500341523411710200206110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later // Needed to access deprecated QSsl::SslProtocol enum values #undef QT_DISABLE_DEPRECATED_BEFORE #undef QT_DISABLE_DEPRECATED_UP_TO #include "requestrouter.h" #include #include #include #include #include #include #include #include #include #include #include "coroutines/network.h" #include "coroutines/threadpool.h" #include "log/log.h" #include "pragmamacros.h" #include "qsslcertificateformatter.h" #include "rpc.h" using namespace Qt::StringLiterals; DISABLE_RANGE_FORMATTING(QJsonObject) SPECIALIZE_FORMATTER_FOR_QDEBUG(QJsonObject) SPECIALIZE_FORMATTER_FOR_QDEBUG(QNetworkProxy) SPECIALIZE_FORMATTER_FOR_QDEBUG(QSslError) namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { fmt::format_context::iterator format(QSsl::SslProtocol protocol, fmt::format_context& ctx) const { const auto str = [&]() -> std::optional { SUPPRESS_DEPRECATED_WARNINGS_BEGIN switch (protocol) { case QSsl::TlsV1_0: return "TlsV1_0"; case QSsl::TlsV1_1: return "TlsV1_1"; case QSsl::TlsV1_2: return "TlsV1_2"; case QSsl::AnyProtocol: return "AnyProtocol"; case QSsl::SecureProtocols: return "SecureProtocols"; case QSsl::TlsV1_0OrLater: return "TlsV1_0OrLater"; case QSsl::TlsV1_1OrLater: return "TlsV1_1OrLater"; case QSsl::TlsV1_2OrLater: return "TlsV1_2OrLater"; case QSsl::DtlsV1_0: return "DtlsV1_0"; case QSsl::DtlsV1_0OrLater: return "DtlsV1_0OrLater"; case QSsl::DtlsV1_2: return "DtlsV1_2"; case QSsl::DtlsV1_2OrLater: return "DtlsV1_2OrLater"; case QSsl::TlsV1_3: return "TlsV1_3"; case QSsl::TlsV1_3OrLater: return "TlsV1_3OrLater"; case QSsl::UnknownProtocol: return "UnknownProtocol"; } SUPPRESS_DEPRECATED_WARNINGS_END return std::nullopt; }(); if (str) { return fmt::format_to(ctx.out(), tremotesf::singleArgumentFormatString, *str); } return fmt::format_to(ctx.out(), tremotesf::singleArgumentFormatString, std::to_underlying(protocol)); } }; } namespace tremotesf::impl { struct NetworkReplyDeleter { inline void operator()(QNetworkReply* reply) { reply->deleteLater(); } }; namespace { constexpr auto sessionIdHeader = "X-Transmission-Session-Id"_L1; QJsonObject getReplyArguments(const QJsonObject& parseResult) { return parseResult.value("arguments"_L1).toObject(); } bool isResultSuccessful(const QJsonObject& parseResult) { return (parseResult.value("result"_L1).toString() == "success"_L1); } QString httpStatus(QNetworkReply* reply) { const auto statusCodeAttr = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!statusCodeAttr.isValid()) { return {}; } auto status = QString::number(statusCodeAttr.toInt()); const auto reasonAttr = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute); if (!reasonAttr.isValid()) { return status; } const auto reason = QString::fromUtf8(reasonAttr.toByteArray()); if (reason.isEmpty()) { return status; } status += ' '; status += reason; return status; } QString makeDetailedErrorMessage(QNetworkReply* reply, const QList& sslErrors) { auto detailedErrorMessage = QString::fromStdString(fmt::format("{}: {}", reply->error(), reply->errorString())); if (reply->url() == reply->request().url()) { detailedErrorMessage += QString::fromStdString(fmt::format("\nURL: {}", reply->url().toString())); } else { detailedErrorMessage += QString::fromStdString( fmt::format( "\nOriginal URL: {}\nRedirected URL: {}", reply->request().url().toString(), reply->url().toString() ) ); } if (const auto status = httpStatus(reply); !status.isEmpty()) { detailedErrorMessage += QString::fromStdString( fmt::format( "\nHTTP status: {}\nConnection was encrypted: {}", status, reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool() ) ); const auto headers = reply->headers(); if (!headers.isEmpty()) { detailedErrorMessage += "\nReply headers:"_L1; for (auto i : std::views::iota(qsizetype{0}, headers.size())) { detailedErrorMessage += QString::fromStdString(fmt::format("\n {}: {}", headers.nameAt(i), headers.valueAt(i))); } } } else { detailedErrorMessage += "\nDid not establish HTTP connection"_L1; } if (!sslErrors.isEmpty()) { detailedErrorMessage += QString::fromStdString(fmt::format("\n\n{} TLS errors:", sslErrors.size())); int i = 1; for (const QSslError& sslError : sslErrors) { detailedErrorMessage += QString::fromStdString( fmt::format( "\n\n{}. {}: {}\n{}", i, sslError.error(), sslError.errorString(), sslError.certificate() ) ); ++i; } } return detailedErrorMessage; } QNetworkRequest getOriginalRequestAndDeleteReply(NetworkReplyUniquePtr reply) { return reply->request(); } } struct RpcRequestMetadata { QLatin1String method{}; }; struct NetworkRequestMetadata { QByteArray postData{}; int retryAttempts{}; RpcRequestMetadata rpcMetadata{}; }; RequestRouter::RequestRouter(QThreadPool* threadPool, QObject* parent) : QObject(parent), mNetwork(new QNetworkAccessManager(this)), mThreadPool(threadPool ? threadPool : QThreadPool::globalInstance()) {} RequestRouter::RequestRouter(QObject* parent) : RequestRouter(nullptr, parent) {} void RequestRouter::setConfiguration(RequestsConfiguration configuration) { debug().log("Setting requests configuration"); mConfiguration = std::move(configuration); mRequestHeaders.clear(); mRequestHeaders.append(QHttpHeaders::WellKnownHeader::ContentType, "application/json"_L1); mNetwork->setProxy(mConfiguration->proxy); mNetwork->clearAccessCache(); const bool https = mConfiguration->serverUrl.scheme() == "https"_L1; mSslConfiguration = QSslConfiguration::defaultConfiguration(); if (https) { if (!mConfiguration->clientCertificate.isNull()) { mSslConfiguration.setLocalCertificate(mConfiguration->clientCertificate); } if (!mConfiguration->clientPrivateKey.isNull()) { mSslConfiguration.setPrivateKey(mConfiguration->clientPrivateKey); } mExpectedSslErrors.clear(); std::visit( [&](const auto& settings) { using T = std::remove_cvref_t; if constexpr (std::same_as) { if (settings.certificate.isNull()) { warning().log("Server's self-signed certificate is null"); return; } debug().log("Server's self-signed certificate:\n{}", settings.certificate); if (!settings.certificate.isSelfSigned()) { warning().log("Server's self-signed certificate is not actually self signed"); return; } mExpectedSslErrors.push_back(QSslError(QSslError::SelfSignedCertificate, settings.certificate)); mExpectedSslErrors.push_back(QSslError(QSslError::CertificateUntrusted, settings.certificate)); mExpectedSslErrors.push_back(QSslError(QSslError::HostNameMismatch, settings.certificate)); } else if constexpr (std::same_as) { if (settings.rootCertificate.isNull()) { warning().log("Server's root certificate is null"); return; } debug().log("Server's root certificate:\n{}", settings.rootCertificate); if (!settings.rootCertificate.isSelfSigned()) { // Using setCaCertificates instead of addCaCertificate since the latter one can confuse Windows schannel TLS backend // We don't need system CA certificates here anyway warning().log("Server's root certificate is not self-signed"); return; } mSslConfiguration.setCaCertificates({settings.rootCertificate}); if (settings.leafCertificate.isNull()) { warning().log( "Server's leaf certificate is null. Certificate validation might fail if its host name " "is not configured correctly" ); return; } debug().log("Server's leaf certificate:\n{}", settings.leafCertificate); mExpectedSslErrors.push_back(QSslError(QSslError::HostNameMismatch, settings.leafCertificate)); } }, mConfiguration->serverCertificate ); } if (!mConfiguration->serverUrl.isEmpty()) { debug().log("Connection configuration:"); debug().log(" - Server url: {}", mConfiguration->serverUrl.toString()); if (mConfiguration->proxy.type() != QNetworkProxy::NoProxy) { debug().log(" - Proxy: {}", mConfiguration->proxy); } debug().log(" - Timeout: {}", mConfiguration->timeout); debug().log(" - HTTP Basic access authentication: {}", mConfiguration->authentication); if (mConfiguration->authentication) { auto base64Credentials = QString("%1:%2"_L1) .arg(mConfiguration->username, mConfiguration->password) .normalized(QString::NormalizationForm_C) .toUtf8() .toBase64(); mRequestHeaders.append(QHttpHeaders::WellKnownHeader::Authorization, "Basic "_ba + base64Credentials); } if (https) { debug().log(" - Available TLS backends: {}", QSslSocket::availableBackends()); debug().log(" - Active TLS backend: {}", QSslSocket::activeBackend()); debug().log(" - Supported TLS protocols: {}", QSslSocket::supportedProtocols()); debug().log(" - TLS library version: {}", QSslSocket::sslLibraryVersionString()); debug().log( " - Server's certificate settings: {}", std::visit( [&](const auto& settings) { using T = std::remove_cvref_t; if constexpr (std::same_as) { return "self-signed certificate"; } else if constexpr (std::same_as) { return "custom CA root"; } else { return "none"; } }, mConfiguration->serverCertificate ) ); debug().log( " - Client certificate authentication: {}", !mConfiguration->clientCertificate.isNull() && !mConfiguration->clientPrivateKey.isNull() ); } } } void RequestRouter::resetConfiguration() { debug().log("Resetting requests configuration"); mConfiguration.reset(); mRequestHeaders.clear(); mSslConfiguration = {}; mExpectedSslErrors.clear(); mNetwork->setProxy(QNetworkProxy::NoProxy); mNetwork->clearAccessCache(); } QByteArrayView RequestRouter::sessionId() const { return mRequestHeaders.value(sessionIdHeader); } Coroutine RequestRouter::postRequest(QLatin1String method, QJsonObject arguments) { co_return co_await postRequest(method, makeRequestData(method, std::move(arguments))); } Coroutine RequestRouter::postRequest(QLatin1String method, QByteArray data) { if (!mConfiguration.has_value()) { throw std::runtime_error("Requests configuration is not set"); } QNetworkRequest request(mConfiguration->serverUrl); request.setHeaders(mRequestHeaders); request.setSslConfiguration(mSslConfiguration); request.setTransferTimeout(mConfiguration->timeout); NetworkRequestMetadata metadata{}; metadata.postData = data; metadata.rpcMetadata = {method}; co_return co_await performRequest(request, metadata); } void RequestRouter::abortNetworkRequestsAndClearSessionId() { auto children = mNetwork->children(); for (auto* child : children) { if (auto* reply = qobject_cast(child); reply && reply->isRunning()) { reply->abort(); } } mNetwork->clearConnectionCache(); mRequestHeaders.removeAll(sessionIdHeader); } QByteArray RequestRouter::makeRequestData(QLatin1String method, QJsonObject arguments) { return QJsonDocument( QJsonObject{ {u"method"_s, method}, {u"arguments"_s, std::move(arguments)}, } ) .toJson(QJsonDocument::Compact); } Coroutine RequestRouter::performRequest(QNetworkRequest request, NetworkRequestMetadata metadata) { NetworkReplyUniquePtr reply(mNetwork->post(request, metadata.postData)); auto expectedSslErrors = mExpectedSslErrors; reply->ignoreSslErrors(expectedSslErrors); auto sslErrors = std::make_shared>(); QObject::connect(reply.get(), &QNetworkReply::sslErrors, reply.get(), [=](const QList& errors) { for (const QSslError& error : errors) { if (!expectedSslErrors.contains(error)) { sslErrors->push_back(error); } } }); co_await *reply; if (reply->error() == QNetworkReply::NoError) { co_return co_await onRequestSuccess(std::move(reply), metadata.rpcMetadata); } else { co_return co_await onRequestError(std::move(reply), *sslErrors, metadata); } } Coroutine RequestRouter::onRequestSuccess(NetworkReplyUniquePtr reply, RpcRequestMetadata metadata) { debug() .log("HTTP request for method '{}' succeeded, HTTP status: {}", metadata.method, httpStatus(reply.get())); const auto json = co_await runOnThreadPool( [](NetworkReplyUniquePtr reply) -> std::optional { const auto replyData = reply->readAll(); QJsonParseError error{}; QJsonObject json = QJsonDocument::fromJson(replyData, &error).object(); if (error.error != QJsonParseError::NoError) { warning().log( "Failed to parse JSON reply from server:\n{}\nError '{}' at offset {}", replyData, error.errorString(), error.offset ); return {}; } return json; }, std::move(reply) ); if (!json.has_value()) { emit requestFailed(RpcError::ParseError, {}, {}); cancelCoroutine(); } const bool success = isResultSuccessful(*json); if (!success) { warning().log("method '{}' failed, response: {}", metadata.method, *json); } co_return Response{.arguments = getReplyArguments(*json), .success = success}; } Coroutine RequestRouter::onRequestError( NetworkReplyUniquePtr reply, QList sslErrors, NetworkRequestMetadata metadata ) { if (reply->error() == QNetworkReply::ContentConflictError) { const auto replyHeaders = reply->headers(); const auto newSessionId = replyHeaders.value(sessionIdHeader); if (!newSessionId.isEmpty() && newSessionId != reply->request().headers().value(sessionIdHeader)) { debug().log("Session id is {}, retrying '{}' request", newSessionId, metadata.rpcMetadata.method); mRequestHeaders.replaceOrAppend(sessionIdHeader, newSessionId); // Retry without incrementing retryAttempts auto request = getOriginalRequestAndDeleteReply(std::move(reply)); request.setHeaders(mRequestHeaders); co_return co_await performRequest(request, metadata); } } const QString detailedErrorMessage = makeDetailedErrorMessage(reply.get(), sslErrors); warning().log("HTTP request for method '{}' failed:\n{}", metadata.rpcMetadata.method, detailedErrorMessage); RpcError error{}; bool shouldRetry{}; switch (reply->error()) { case QNetworkReply::AuthenticationRequiredError: warning().log("Authentication error"); error = RpcError::AuthenticationError; shouldRetry = false; break; case QNetworkReply::OperationCanceledError: case QNetworkReply::TimeoutError: warning().log("Timed out"); error = RpcError::TimedOut; shouldRetry = true; break; default: error = RpcError::ConnectionError; shouldRetry = true; break; } // NOLINTNEXTLINE(bugprone-unchecked-optional-access) if (shouldRetry && metadata.retryAttempts < mConfiguration.value().retryAttempts) { metadata.retryAttempts++; warning() .log("Retrying '{}' request, retry attempts = {}", metadata.rpcMetadata.method, metadata.retryAttempts); co_return co_await performRequest(getOriginalRequestAndDeleteReply(std::move(reply)), metadata); } emit requestFailed(error, reply->errorString(), detailedErrorMessage); cancelCoroutine(); } } tremotesf-2.10.1/src/rpc/requestrouter.h000066400000000000000000000067751523411710200202730ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_REQUESTROUTER_H #define TREMOTESF_RPC_REQUESTROUTER_H #include #include #include #include #include #include #include #include #include #include #include #include "coroutines/coroutinefwd.h" class QNetworkAccessManager; class QNetworkRequest; class QNetworkReply; class QSslError; class QThreadPool; namespace tremotesf { enum class RpcError; } namespace tremotesf::impl { struct RpcRequestMetadata; struct NetworkRequestMetadata; struct NetworkReplyDeleter; using NetworkReplyUniquePtr = std::unique_ptr; class RequestRouter final : public QObject { Q_OBJECT public: explicit RequestRouter(QThreadPool* threadPool, QObject* parent = nullptr); explicit RequestRouter(QObject* parent = nullptr); struct RequestsConfiguration { struct SelfSignedCertificate { QSslCertificate certificate{}; }; struct CustomRoot { QSslCertificate rootCertificate{}; QSslCertificate leafCertificate{}; }; QUrl serverUrl{}; QNetworkProxy proxy{QNetworkProxy::applicationProxy()}; std::variant serverCertificate{}; QSslCertificate clientCertificate{}; QSslKey clientPrivateKey{}; std::chrono::milliseconds timeout{}; int retryAttempts{2}; bool authentication{}; QString username{}; QString password{}; }; const std::optional& configuration() const { return mConfiguration; } void setConfiguration(RequestsConfiguration configuration); void resetConfiguration(); struct Response { QJsonObject arguments{}; bool success{}; }; Coroutine postRequest(QLatin1String method, QJsonObject arguments); Coroutine postRequest(QLatin1String method, QByteArray data); QByteArrayView sessionId() const; void abortNetworkRequestsAndClearSessionId(); static QByteArray makeRequestData(QLatin1String method, QJsonObject arguments); private: Coroutine performRequest(QNetworkRequest request, NetworkRequestMetadata metadata); Coroutine onRequestSuccess(NetworkReplyUniquePtr reply, RpcRequestMetadata metadata); Coroutine onRequestError(NetworkReplyUniquePtr reply, QList sslErrors, NetworkRequestMetadata metadata); QNetworkAccessManager* mNetwork{}; QThreadPool* mThreadPool{}; std::optional mConfiguration{}; QHttpHeaders mRequestHeaders{}; QSslConfiguration mSslConfiguration{}; QList mExpectedSslErrors{}; signals: /** * Emitted if request has failed with network or HTTP error * @brief requestFailed * @param error Error type * @param errorMessage Short error message * @param detailedErrorMessage Detailed error message */ void requestFailed(tremotesf::RpcError error, const QString& errorMessage, const QString& detailedErrorMessage); }; } #endif // TREMOTESF_RPC_REQUESTROUTER_H tremotesf-2.10.1/src/rpc/requestrouter_test.cpp000066400000000000000000000747731523411710200216700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include #include #include #include #include "coroutines/coroutines.h" #include "fileutils.h" #include "log/log.h" #include "requestrouter.h" #include "rpc.h" using namespace std::chrono; using namespace std::chrono_literals; using namespace std::string_literals; using namespace Qt::StringLiterals; using namespace tremotesf; using namespace tremotesf::impl; // NOLINTBEGIN(bugprone-unchecked-optional-access) namespace { constexpr auto testApiPath = "/"_L1; constexpr auto testTimeout = 5s; constexpr auto testRetryAttempts = 0; const auto contentType = "application/json"s; // Moc can't handle raw string literals lol // https://bugreports.qt.io/browse/QTBUG-55962 // NOLINTNEXTLINE(modernize-raw-string-literal) const auto successResponse = "{\"result\":\"success\"}"s; // NOLINTNEXTLINE(modernize-raw-string-literal) const auto invalidJsonResponse = "{\"result\":\"success}"s; const auto sessionIdHeader = "X-Transmission-Session-Id"s; template Server = httplib::Server> class TestHttpServer { public: TestHttpServer() requires std::same_as { initServer(); } TestHttpServer( const char* cert_path, const char* private_key_path, const char* client_ca_cert_file_path = nullptr, const char* client_ca_cert_dir_path = nullptr, const char* private_key_password = nullptr ) requires std::same_as : mServer{ cert_path, private_key_path, client_ca_cert_file_path, client_ca_cert_dir_path, private_key_password } { initServer(); } ~TestHttpServer() { // Wait until we've started listing or already finished because of error while (true) { if (mServer.is_running()) break; if (mListenFuture.wait_for(10ms) == std::future_status::ready) break; } if (mServer.is_running()) { info().log("Stopping server"); mServer.stop(); } else { warning().log("Server has already stopped"); } mListenFuture.wait(); } Q_DISABLE_COPY_MOVE(TestHttpServer) const QString host{QHostAddress(QHostAddress::LocalHost).toString()}; int port{}; void handle(httplib::Server::Handler&& handler) { const std::unique_lock lock(mHandlerMutex); mHandler = std::move(handler); } void clearHandler() { const std::unique_lock lock(mHandlerMutex); mHandler = {}; } private: void initServer() { info().log("Server is valid = {}", mServer.is_valid()); mServer.set_keep_alive_max_count(1); mServer.set_keep_alive_timeout(1); port = mServer.bind_to_any_port(host.toStdString()); info().log("Bound to port {}", port); mServer.Post(testApiPath.data(), [=, this](const httplib::Request& req, httplib::Response& res) { httplib::Server::Handler handler{}; { const std::unique_lock lock(mHandlerMutex); handler = mHandler; } if (handler) { handler(req, res); } else { res.status = 500; } }); mListenFuture = std::async([=, this] { info().log("Starting listening on address {} and port {}", host, port); const bool ok = mServer.listen_after_bind(); info().log("Stopped listening, ok = {}", ok); }); } Server mServer{}; std::future mListenFuture{}; httplib::Server::Handler mHandler{}; std::mutex mHandlerMutex{}; }; void success(httplib::Response& res) { res.set_content(successResponse, contentType); } void checkAuthentication( const httplib::Request& req, httplib::Response& res, const std::string& username, const std::string& password ) { const auto header = httplib::make_basic_authentication_header(username, password); if (req.get_header_value(header.first) == header.second) { success(res); } else { res.status = 401; res.set_header("WWW-Authenticate", R"(Basic realm="Do it")"); } } class RequestRouterTest final : public QObject { Q_OBJECT public: RequestRouterTest() { mThreadPool.setMaxThreadCount(1); } private slots: void init() { const int port = mServer.port; RequestRouter::RequestsConfiguration config{}; config.serverUrl.setScheme("http"_L1); config.serverUrl.setHost(mServer.host); config.serverUrl.setPort(port); config.serverUrl.setPath(testApiPath); config.timeout = testTimeout; config.retryAttempts = testRetryAttempts; mRouter.setConfiguration(std::move(config)); } void cleanup() { mServer.clearHandler(); mRouter.abortNetworkRequestsAndClearSessionId(); } void checkUrlIsCorrect() { mServer.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->arguments, QJsonObject{}); QCOMPARE(response->success, true); } void checkThatJsonIsFormedCorrectly() { const auto method = "foo"_L1; const QJsonObject arguments{{"bar", "foobar"}}; mServer.handle([&](const httplib::Request& req, httplib::Response& res) { const auto json = QJsonDocument::fromJson(req.body.c_str()); const auto expectedJson = QJsonDocument(QJsonObject{{u"method"_s, method}, {u"arguments"_s, arguments}}); if (json == expectedJson) { success(res); } else { res.status = 400; } }); const auto response = waitForResponse(method, arguments); QCOMPARE(response.has_value(), true); QCOMPARE(response->success, true); } void checkTimeoutIsHandled() { QTcpServer tcpServer{}; tcpServer.listen(QHostAddress::LocalHost); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setPort(tcpServer.serverPort()); config.timeout = 100ms; mRouter.setConfiguration(std::move(config)); } const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::TimedOut); } void checkTcpConnectionRefusedIsHandled() { { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); // It is likely that there is nothing listening on this port, so we will get ConnectionRefusedError config.serverUrl.setPort(9); mRouter.setConfiguration(std::move(config)); } const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); if (error.value() == RpcError::TimedOut) { // This is not what we test here but it can happen on some systems warning().log("Connection to port 9 timed out instead of being refused"); } else { QCOMPARE(error.value(), RpcError::ConnectionError); } } void checkTcpConnectionClosedErrorIsHandled() { QTcpServer tcpServer{}; QObject::connect(&tcpServer, &QTcpServer::newConnection, this, [&tcpServer] { tcpServer.nextPendingConnection()->close(); }); tcpServer.listen(QHostAddress::LocalHost); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setPort(tcpServer.serverPort()); mRouter.setConfiguration(std::move(config)); } const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::ConnectionError); } void checkThatRequestsAreRetried() { const int retryAttempts = 2; std::atomic_int requestsCount{}; mServer.handle([&](const httplib::Request&, httplib::Response& res) { ++requestsCount; res.status = 500; }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.retryAttempts = retryAttempts; mRouter.setConfiguration(std::move(config)); } const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::ConnectionError); QCOMPARE(requestsCount.load(), retryAttempts + 1); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT void checkSelfSignedCertificateError() { TestHttpServer server( TEST_DATA_PATH "/server-certs/self-signed.pem", TEST_DATA_PATH "/server-certs/self-signed-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); mRouter.setConfiguration(std::move(config)); } const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::ConnectionError); } void checkSelfSignedCertificateSuccess() { TestHttpServer server( TEST_DATA_PATH "/server-certs/self-signed.pem", TEST_DATA_PATH "/server-certs/self-signed-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); config.serverCertificate = RequestRouter::RequestsConfiguration::SelfSignedCertificate( QSslCertificate::fromPath(TEST_DATA_PATH "/server-certs/self-signed.pem", QSsl::Pem).first() ); mRouter.setConfiguration(std::move(config)); } const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->success, true); } void checkCertificateSignedWithCustomRootSuccess() { // Server returns signed certificate with its (self-signed) root TestHttpServer server( TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf.pem", TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); config.serverCertificate = RequestRouter::RequestsConfiguration::CustomRoot{ .rootCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/root.pem", QSsl::Pem ) .first(), .leafCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf.pem", QSsl::Pem ) .first() }; mRouter.setConfiguration(std::move(config)); } const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->success, true); } void checkCertificateSignedWithCustomRootAndServerReturnsWholeChainSuccess() { // Server returns signed certificate with its (self-signed) root TestHttpServer server( TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf-with-root.pem", TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); config.serverCertificate = RequestRouter::RequestsConfiguration::CustomRoot{ .rootCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/root.pem", QSsl::Pem ) .first(), .leafCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf.pem", QSsl::Pem ) .first() }; mRouter.setConfiguration(std::move(config)); } const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->success, true); } void checkCertificateSignedWithCustomRootAndIntermediateSuccess() { // Server returns signed certificate with its (self-signed) root TestHttpServer server( TEST_DATA_PATH "/server-certs/signed-with-custom-root/with-intermediate/leaf-with-intermediate.pem", TEST_DATA_PATH "/server-certs/signed-with-custom-root/with-intermediate/leaf-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); config.serverCertificate = RequestRouter::RequestsConfiguration::CustomRoot{ .rootCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/root.pem", QSsl::Pem ) .first(), .leafCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/with-intermediate/leaf.pem", QSsl::Pem ) .first() }; mRouter.setConfiguration(std::move(config)); } const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->success, true); } void checkCertificateSignedWithCustomRootAndIntermediateAndServerReturnsWholeChainSuccess() { // Server returns signed certificate with its (self-signed) root TestHttpServer server( TEST_DATA_PATH "/server-certs/signed-with-custom-root/with-intermediate/leaf-with-intermediate-and-root.pem", TEST_DATA_PATH "/server-certs/signed-with-custom-root/with-intermediate/leaf-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); config.serverCertificate = RequestRouter::RequestsConfiguration::CustomRoot{ .rootCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/root.pem", QSsl::Pem ) .first(), .leafCertificate = QSslCertificate::fromPath( TEST_DATA_PATH "/server-certs/signed-with-custom-root/with-intermediate/leaf.pem", QSsl::Pem ) .first() }; mRouter.setConfiguration(std::move(config)); } const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->success, true); } // schannel backend crashes if client certificate doesn't have common name: https://qt-project.atlassian.net/browse/QTBUG-142324 # ifndef Q_OS_WIN void checkClientCertificateError() { TestHttpServer server( TEST_DATA_PATH "/server-certs/self-signed.pem", TEST_DATA_PATH "/server-certs/self-signed-key.pem", TEST_DATA_PATH "/client-certs/client-certificate-and-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); config.serverCertificate = RequestRouter::RequestsConfiguration::SelfSignedCertificate( QSslCertificate::fromPath(TEST_DATA_PATH "/server-certs/self-signed.pem", QSsl::Pem).first() ); mRouter.setConfiguration(std::move(config)); } const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); if (error.value() == RpcError::TimedOut) { // Can happen with TLS 1.3 warning().log("Connecting to server requiring client certificate timed out"); } else { QCOMPARE(error.value(), RpcError::ConnectionError); } } void checkClientCertificateSuccess() { TestHttpServer server( TEST_DATA_PATH "/server-certs/self-signed.pem", TEST_DATA_PATH "/server-certs/self-signed-key.pem", TEST_DATA_PATH "/client-certs/client-certificate-and-key.pem" ); server.handle([&](const httplib::Request&, httplib::Response& res) { success(res); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setScheme("https"_L1); config.serverUrl.setPort(server.port); config.serverCertificate = RequestRouter::RequestsConfiguration::SelfSignedCertificate( QSslCertificate::fromPath(TEST_DATA_PATH "/server-certs/self-signed.pem", QSsl::Pem).first() ); config.clientCertificate = QSslCertificate::fromPath(TEST_DATA_PATH "/client-certs/client-certificate-and-key.pem", QSsl::Pem) .first(); { QFile file(TEST_DATA_PATH "/client-certs/client-certificate-and-key.pem"); openFile(file, QIODevice::ReadOnly); config.clientPrivateKey = QSslKey(&file, QSsl::Rsa); } mRouter.setConfiguration(std::move(config)); } const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->success, true); } # endif #endif void checkInvalidJsonIsHandled() { mServer.handle([&](const httplib::Request&, httplib::Response& res) { res.set_content(invalidJsonResponse, contentType); }); const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::ParseError); } void checkConflictErrorWithoutSessionIdIsHandled() { mServer.handle([&](const httplib::Request&, httplib::Response& res) { res.status = 409; }); const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::ConnectionError); } void checkPersistentConflictErrorWithSessionIdIsHandled() { const std::string sessionIdValue = "id"; mServer.handle([&](const httplib::Request&, httplib::Response& res) { res.status = 409; res.set_header(sessionIdHeader, sessionIdValue); }); const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::ConnectionError); } void checkConflictErrorWithSessionIdIsHandled() { const std::string sessionIdValue = "id"; mServer.handle([&](const httplib::Request& req, httplib::Response& res) { if (req.get_header_value(sessionIdHeader) == sessionIdValue) { success(res); } else { res.status = 409; } res.set_header(sessionIdHeader, sessionIdValue); }); const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->arguments, QJsonObject{}); QCOMPARE(response->success, true); } void checkConflictErrorWithChangingSessionIdIsHandled() { std::string sessionIdValue = "session id"; mServer.handle([&](const httplib::Request& req, httplib::Response& res) { if (req.get_header_value(sessionIdHeader) == sessionIdValue) { success(res); } else { res.status = 409; } res.set_header(sessionIdHeader, sessionIdValue); }); auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->arguments, QJsonObject{}); QCOMPARE(response->success, true); sessionIdValue = "session id 2"; response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->arguments, QJsonObject{}); QCOMPARE(response->success, true); } void checkThatAuthenticationWorks() { const QString user = "foo"_L1; const QString password = "bar"_L1; const auto header = httplib::make_basic_authentication_header(user.toStdString(), password.toStdString()); mServer.handle([&](const httplib::Request& req, httplib::Response& res) { checkAuthentication(req, res, user.toStdString(), password.toStdString()); }); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.authentication = true; config.username = user; config.password = password; mRouter.setConfiguration(std::move(config)); } const auto response = waitForResponse("foo"_L1, QByteArray{}); QCOMPARE(response.has_value(), true); QCOMPARE(response->arguments, QJsonObject{}); QCOMPARE(response->success, true); } void checkAuthenticationErrorIsHandled() { mServer.handle([&](const httplib::Request& req, httplib::Response& res) { checkAuthentication(req, res, "foo", "bar"); }); const auto error = waitForError("foo"_L1, QByteArray{}); QCOMPARE(error.has_value(), true); QCOMPARE(error.value(), RpcError::AuthenticationError); } void checkRequestAbort() { QTcpServer tcpServer{}; tcpServer.listen(QHostAddress::LocalHost); { RequestRouter::RequestsConfiguration config = mRouter.configuration().value(); config.serverUrl.setPort(tcpServer.serverPort()); mRouter.setConfiguration(std::move(config)); } std::variant responseOrError = std::monostate{}; CoroutineScope scope{}; const auto connection = connectToErrorSignal(responseOrError, scope); const auto connectionGuard = QScopeGuard([=] { QObject::disconnect(connection); }); scope.launch(waitForResponseCoroutine(mRouter.postRequest("foo"_L1, QByteArray{}), responseOrError)); QTest::qWait(100); mRouter.abortNetworkRequestsAndClearSessionId(); const bool ok = QTest::qWaitFor( [&] { return !std::holds_alternative(responseOrError); }, static_cast( duration_cast(testTimeout * (mRouter.configuration().value().retryAttempts + 1) + 1s) .count() ) ); if (!ok) { warning().log("Timed out when waiting for response"); } if (std::holds_alternative(responseOrError)) { QCOMPARE(std::get(responseOrError), RpcError::TimedOut); } else { QFAIL("Request must return error"); } } private: Coroutine<> waitForResponseCoroutine( Coroutine requestCoroutine, // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) std::variant& responseOrError ) { responseOrError = co_await requestCoroutine; } QMetaObject::Connection connectToErrorSignal( std::variant& responseOrError, CoroutineScope& scope ) { return QObject::connect( &mRouter, &RequestRouter::requestFailed, this, [&](RpcError error, [[maybe_unused]] const QString& errorMessage, [[maybe_unused]] const QString& detailedErrorMessage) { responseOrError = error; scope.cancelAll(); }, Qt::DirectConnection ); } template std::variant waitForResponseOrError(const Args&... args) { std::variant responseOrError = std::monostate{}; CoroutineScope scope{}; const auto connection = connectToErrorSignal(responseOrError, scope); const auto connectionGuard = QScopeGuard([=] { QObject::disconnect(connection); }); scope.launch(waitForResponseCoroutine(mRouter.postRequest(args...), responseOrError)); const bool ok = QTest::qWaitFor( [&] { return scope.coroutinesCount() == 0; }, static_cast( duration_cast(testTimeout * (mRouter.configuration().value().retryAttempts + 1) + 1s) .count() ) ); if (!ok) { warning().log("Timed out when waiting for response"); } return responseOrError; } template std::optional waitForResponse(const Args&... args) { const auto responseOrError = waitForResponseOrError(args...); if (std::holds_alternative(responseOrError)) { return std::get(responseOrError); } return {}; } template std::optional waitForError(const Args&... args) { const auto responseOrError = waitForResponseOrError(args...); if (std::holds_alternative(responseOrError)) { return std::get(responseOrError); } return {}; } template Coroutine<> detachRequest(const Args&... args) { co_await mRouter.postRequest(args...); } TestHttpServer mServer{}; QThreadPool mThreadPool{}; RequestRouter mRouter{nullptr, &mThreadPool}; }; } // NOLINTEND(bugprone-unchecked-optional-access) QTEST_GUILESS_MAIN(RequestRouterTest) #include "requestrouter_test.moc" tremotesf-2.10.1/src/rpc/rpc.cpp000066400000000000000000001157371523411710200164600ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "rpc.h" #include #include #include #include #include #include #include #include #include "addressutils.h" #include "coroutines/hostinfo.h" #include "coroutines/threadpool.h" #include "coroutines/timer.h" #include "coroutines/waitall.h" #include "fileutils.h" #include "jsonutils.h" #include "itemlistupdater.h" #include "log/log.h" #include "requestrouter.h" #include "serversettings.h" #include "serverstats.h" #include "torrent.h" using namespace Qt::StringLiterals; SPECIALIZE_FORMATTER_FOR_QDEBUG(QHostAddress) SPECIALIZE_FORMATTER_FOR_QDEBUG(QUrl) namespace tremotesf { using namespace impl; namespace { // Transmission 2.40+ constexpr int minimumRpcVersion = 14; constexpr auto torrentsKey = "torrents"_L1; constexpr auto torrentDuplicateKey = "torrent-duplicate"_L1; } using namespace impl; QString Rpc::Status::toString() const { switch (connectionState) { case ConnectionState::Disconnected: switch (error) { case Error::NoError: //: Server connection status return qApp->translate("tremotesf", "Disconnected"); case Error::TimedOut: //: Server connection status return qApp->translate("tremotesf", "Timed out"); case Error::ConnectionError: //: Server connection status return qApp->translate("tremotesf", "Connection error"); case Error::AuthenticationError: //: Server connection status return qApp->translate("tremotesf", "Authentication error"); case Error::ParseError: //: Server connection status return qApp->translate("tremotesf", "Parse error"); case Error::ServerIsTooNew: //: Server connection status return qApp->translate("tremotesf", "Server is too new"); case Error::ServerIsTooOld: //: Server connection status return qApp->translate("tremotesf", "Server is too old"); } break; case ConnectionState::Connecting: //: Server connection status return qApp->translate("tremotesf", "Connecting..."); case ConnectionState::Connected: //: Server connection status return qApp->translate("tremotesf", "Connected"); } return {}; } Rpc::Rpc(QObject* parent) : QObject(parent), mRequestRouter(new RequestRouter(this)), mServerSettings(new ServerSettings(this, this)), mServerStats(new ServerStats(this)) { QObject::connect(mRequestRouter, &RequestRouter::requestFailed, this, &Rpc::onRequestFailed); } Rpc::~Rpc() = default; ServerSettings* Rpc::serverSettings() const { return mServerSettings; } ServerStats* Rpc::serverStats() const { return mServerStats; } const std::vector>& Rpc::torrents() const { return mTorrents; } Torrent* Rpc::torrentByHash(const QString& hash) const { for (const std::unique_ptr& torrent : mTorrents) { if (torrent->data().hashString == hash) { return torrent.get(); } } return nullptr; } Torrent* Rpc::torrentById(int id) const { const auto found = std::ranges::find(mTorrents, id, [](const auto& torrent) { return torrent->data().id; }); return (found != mTorrents.end()) ? found->get() : nullptr; } bool Rpc::isConnected() const { return (mStatus.connectionState == ConnectionState::Connected); } const Rpc::Status& Rpc::status() const { return mStatus; } Rpc::ConnectionState Rpc::connectionState() const { return mStatus.connectionState; } Rpc::Error Rpc::error() const { return mStatus.error; } const QString& Rpc::errorMessage() const { return mStatus.errorMessage; } const QString& Rpc::detailedErrorMessage() const { return mStatus.detailedErrorMessage; } bool Rpc::isLocal() const { return mServerIsLocal.value_or(false); } int Rpc::torrentsCount() const { return static_cast(mTorrents.size()); } void Rpc::setConnectionConfiguration(const ConnectionConfiguration& configuration) { disconnect(); RequestRouter::RequestsConfiguration requestsConfig{}; if (configuration.https) { requestsConfig.serverUrl.setScheme("https"_L1); } else { requestsConfig.serverUrl.setScheme("http"_L1); } requestsConfig.serverUrl.setHost(configuration.address); if (auto error = requestsConfig.serverUrl.errorString(); !error.isEmpty()) { warning().log("Error setting URL hostname: {}", error); } requestsConfig.serverUrl.setPort(configuration.port); if (auto error = requestsConfig.serverUrl.errorString(); !error.isEmpty()) { warning().log("Error setting URL port: {}", error); } if (auto queryIndex = configuration.apiPath.indexOf('?'); queryIndex != -1) { requestsConfig.serverUrl.setPath(configuration.apiPath.sliced(0, queryIndex)); if (auto error = requestsConfig.serverUrl.errorString(); !error.isEmpty()) { warning().log("Error setting URL path: {}", error); } if ((queryIndex + 1) < configuration.apiPath.size()) { requestsConfig.serverUrl.setQuery(configuration.apiPath.sliced(queryIndex + 1)); if (auto error = requestsConfig.serverUrl.errorString(); !error.isEmpty()) { warning().log("Error setting URL query: {}", error); } } } else { requestsConfig.serverUrl.setPath(configuration.apiPath); if (auto error = requestsConfig.serverUrl.errorString(); !error.isEmpty()) { warning().log("Error setting URL path: {}", error); } } if (!requestsConfig.serverUrl.isValid()) { warning().log("URL {} is invalid", requestsConfig.serverUrl); } switch (configuration.proxyType) { case ConnectionConfiguration::ProxyType::Default: break; case ConnectionConfiguration::ProxyType::Http: requestsConfig.proxy = QNetworkProxy( QNetworkProxy::HttpProxy, configuration.proxyHostname, static_cast(configuration.proxyPort), configuration.proxyUser, configuration.proxyPassword ); break; case ConnectionConfiguration::ProxyType::Socks5: requestsConfig.proxy = QNetworkProxy( QNetworkProxy::Socks5Proxy, configuration.proxyHostname, static_cast(configuration.proxyPort), configuration.proxyUser, configuration.proxyPassword ); break; case ConnectionConfiguration::ProxyType::None: requestsConfig.proxy = QNetworkProxy(QNetworkProxy::NoProxy); break; } if (configuration.https) { switch (configuration.serverCertificateMode) { case ConnectionConfiguration::ServerCertificateMode::SelfSigned: requestsConfig.serverCertificate = RequestRouter::RequestsConfiguration::SelfSignedCertificate( QSslCertificate(configuration.serverRootCertificate, QSsl::Pem) ); break; case ConnectionConfiguration::ServerCertificateMode::CustomRoot: requestsConfig.serverCertificate = RequestRouter::RequestsConfiguration::CustomRoot{ .rootCertificate = QSslCertificate(configuration.serverRootCertificate, QSsl::Pem), .leafCertificate = QSslCertificate(configuration.serverLeafCertificate, QSsl::Pem) }; break; case ConnectionConfiguration::ServerCertificateMode::None: break; } } if (configuration.clientCertificateEnabled) { requestsConfig.clientCertificate = QSslCertificate(configuration.clientCertificate, QSsl::Pem); requestsConfig.clientPrivateKey = QSslKey(configuration.clientCertificate, QSsl::Rsa); } requestsConfig.authentication = configuration.authentication; requestsConfig.username = configuration.username; requestsConfig.password = configuration.password; requestsConfig.timeout = std::chrono::seconds(configuration.timeout); // server.timeout is in seconds mRequestRouter->setConfiguration(std::move(requestsConfig)); mUpdateInterval = std::chrono::seconds(configuration.updateInterval); mAutoReconnectEnabled = configuration.autoReconnectEnabled; mAutoReconnectInterval = std::chrono::seconds(configuration.autoReconnectInterval); } void Rpc::resetConnectionConfiguration() { disconnect(); mRequestRouter->resetConfiguration(); mAutoReconnectEnabled = false; mAutoReconnectCoroutineScope.cancelAll(); } void Rpc::connect() { if (connectionState() == ConnectionState::Disconnected && mRequestRouter->configuration().has_value()) { setStatus(Status{.connectionState = ConnectionState::Connecting}); mBackgroundRequestsCoroutineScope.launch(connectAndPerformDataUpdates()); } } void Rpc::disconnect() { setStatus(Status{.connectionState = ConnectionState::Disconnected}); } void Rpc::addTorrentFile( QString filePath, QString downloadDirectory, std::vector unwantedFiles, std::vector highPriorityFiles, std::vector lowPriorityFiles, std::map renamedFiles, TorrentData::Priority bandwidthPriority, bool start, DeleteFileMode deleteFileMode, std::vector labels ) { if (isConnected()) { mBackgroundRequestsCoroutineScope.launch(addTorrentFileImpl( std::move(filePath), std::move(downloadDirectory), std::move(unwantedFiles), std::move(highPriorityFiles), std::move(lowPriorityFiles), std::move(renamedFiles), bandwidthPriority, start, deleteFileMode, std::move(labels) )); } } namespace { std::optional makeAddTorrentFileRequestData( const QString& filePath, const QString& downloadDirectory, const std::vector& unwantedFiles, const std::vector& highPriorityFiles, const std::vector& lowPriorityFiles, TorrentData::Priority bandwidthPriority, bool start, const std::vector& labels ) { QString fileData{}; try { QFile file(filePath); openFile(file, QIODevice::ReadOnly); fileData = readFileAsBase64String(file); } catch (const QFileError& e) { warning().logWithException(e, "addTorrentFile: failed to read torrent file"); return std::nullopt; } QJsonObject arguments{ {u"metainfo"_s, fileData}, {u"download-dir"_s, downloadDirectory}, {u"bandwidthPriority"_s, TorrentData::priorityToInt(bandwidthPriority)}, {u"paused"_s, !start} }; if (!unwantedFiles.empty()) { arguments.insert("files-unwanted"_L1, toJsonArray(unwantedFiles)); } if (!highPriorityFiles.empty()) { arguments.insert("priority-high"_L1, toJsonArray(highPriorityFiles)); } if (!lowPriorityFiles.empty()) { arguments.insert("priority-low"_L1, toJsonArray(lowPriorityFiles)); } if (!labels.empty()) { arguments.insert("labels"_L1, toJsonArray(labels)); } return RequestRouter::makeRequestData("torrent-add"_L1, std::move(arguments)); } Coroutine<> deleteTorrentFile(QString filePath, bool moveToTrash) { co_await runOnThreadPool([moveToTrash, filePath = std::move(filePath)] { try { if (moveToTrash) { moveFileToTrashOrDelete(filePath); } else { deleteFile(filePath); } } catch (const QFileError& e) { warning().logWithException(e, "Failed to delete torrent file"); } }); } } Coroutine<> Rpc::addTorrentFileImpl( QString filePath, QString downloadDirectory, std::vector unwantedFiles, std::vector highPriorityFiles, std::vector lowPriorityFiles, std::map renamedFiles, TorrentData::Priority bandwidthPriority, bool start, DeleteFileMode deleteFileMode, std::vector labels ) { std::optional requestData = co_await runOnThreadPool( makeAddTorrentFileRequestData, filePath, downloadDirectory, std::move(unwantedFiles), std::move(highPriorityFiles), std::move(lowPriorityFiles), bandwidthPriority, start, std::move(labels) ); if (!requestData.has_value()) { emit torrentAddError(filePath); co_return; } if (deleteFileMode != DeleteFileMode::No) { mDeletingFilesCoroutineScope.launch( deleteTorrentFile(filePath, deleteFileMode == DeleteFileMode::MoveToTrash) ); } if (!isConnected()) co_return; const auto response = co_await mRequestRouter->postRequest("torrent-add"_L1, std::move(requestData).value()); if (response.arguments.contains(torrentDuplicateKey)) { emit torrentAddDuplicate(); } else if (response.success) { if (!renamedFiles.empty()) { const auto torrentJson = response.arguments.value("torrent-added"_L1).toObject(); const auto id = Torrent::idFromJson(torrentJson); if (id.has_value()) { for (const auto& [filePathToRename, newName] : renamedFiles) { renameTorrentFile(*id, filePathToRename, newName); } } } mBackgroundRequestsCoroutineScope.launch(updateData()); } else { emit torrentAddError(filePath); } } void Rpc::addTorrentLinks( QStringList links, QString downloadDirectory, TorrentData::Priority bandwidthPriority, bool start, std::vector labels ) { if (isConnected()) { mBackgroundRequestsCoroutineScope.launch(addTorrentLinksImpl( std::move(links), std::move(downloadDirectory), bandwidthPriority, start, std::move(labels) )); } } Coroutine<> Rpc::addTorrentLinksImpl( QStringList links, QString downloadDirectory, TorrentData::Priority bandwidthPriority, bool start, std::vector labels ) { const int priorityInt = TorrentData::priorityToInt(bandwidthPriority); co_await waitAll( links | std::views::transform([&](QString& link) { return addTorrentLinkImpl(std::move(link), downloadDirectory, priorityInt, start, labels); }) | std::ranges::to() ); mBackgroundRequestsCoroutineScope.launch(updateData()); } Coroutine<> Rpc::addTorrentLinkImpl( QString link, QString downloadDirectory, int bandwidthPriority, bool start, std::vector labels ) { QJsonObject arguments{ {u"filename"_s, link}, {u"download-dir"_s, downloadDirectory}, {u"bandwidthPriority"_s, bandwidthPriority}, {u"paused"_s, !start} }; if (!labels.empty()) { arguments.insert("labels"_L1, toJsonArray(labels)); } const auto response = co_await mRequestRouter->postRequest("torrent-add"_L1, std::move(arguments)); if (response.arguments.contains(torrentDuplicateKey)) { emit torrentAddDuplicate(); } else if (!response.success) { emit torrentAddError(link); } } void Rpc::startTorrents(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("torrent-start"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::startTorrentsNow(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("torrent-start-now"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::pauseTorrents(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("torrent-stop"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::removeTorrents(std::span ids, bool deleteFiles) { mBackgroundRequestsCoroutineScope.launch( postRequest("torrent-remove"_L1, {{u"ids"_s, toJsonArray(ids)}, {u"delete-local-data"_s, deleteFiles}}) ); } void Rpc::checkTorrents(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("torrent-verify"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::moveTorrentsToTop(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("queue-move-top"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::moveTorrentsUp(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("queue-move-up"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::moveTorrentsDown(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("queue-move-down"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::moveTorrentsToBottom(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("queue-move-bottom"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::reannounceTorrents(std::span ids) { mBackgroundRequestsCoroutineScope.launch(postRequest("torrent-reannounce"_L1, {{u"ids"_s, toJsonArray(ids)}})); } void Rpc::setSessionProperty(QString property, QJsonValue value) { setSessionProperties({{property, std::move(value)}}); } void Rpc::setSessionProperties(QJsonObject properties) { mBackgroundRequestsCoroutineScope.launch(postRequest("session-set"_L1, std::move(properties), false)); } void Rpc::setTorrentProperty(int id, QString property, QJsonValue value, bool updateIfSuccessful) { mBackgroundRequestsCoroutineScope.launch(postRequest( "torrent-set"_L1, {{u"ids"_s, QJsonArray{id}}, {std::move(property), std::move(value)}}, updateIfSuccessful )); } void Rpc::setTorrentsLocation(std::span ids, QString location, bool moveFiles) { mBackgroundRequestsCoroutineScope.launch(postRequest( "torrent-set-location"_L1, {{u"ids"_s, toJsonArray(ids)}, {u"location"_s, location}, {u"move"_s, moveFiles}} )); } void Rpc::setTorrentsLabels(std::span ids, std::span labels) { mBackgroundRequestsCoroutineScope.launch( postRequest("torrent-set"_L1, {{u"ids"_s, toJsonArray(ids)}, {u"labels"_s, toJsonArray(labels)}}) ); } void Rpc::getTorrentFiles(int torrentId) { if (isConnected()) { mBackgroundRequestsCoroutineScope.launch(getTorrentsFiles({torrentId})); } } Coroutine<> Rpc::getTorrentsFiles(QJsonArray ids) { QJsonObject arguments{ {u"fields"_s, QJsonArray{"id"_L1, "files"_L1, "fileStats"_L1}}, {u"ids"_s, std::move(ids)} }; const auto response = co_await mRequestRouter->postRequest("torrent-get"_L1, std::move(arguments)); if (!response.success) co_return; const QJsonArray torrents = response.arguments.value(torrentsKey).toArray(); for (const auto& torrentJson : torrents) { const auto object = torrentJson.toObject(); const auto torrentId = Torrent::idFromJson(object); if (torrentId.has_value()) { Torrent* torrent = torrentById(*torrentId); if (torrent && torrent->isFilesEnabled()) { torrent->updateFiles(object); } } } } void Rpc::getTorrentPeers(int torrentId) { if (isConnected()) { mBackgroundRequestsCoroutineScope.launch(getTorrentsPeers({torrentId})); } } Coroutine<> Rpc::getTorrentsPeers(QJsonArray ids) { QJsonObject arguments{{u"fields"_s, QJsonArray{u"id"_s, u"peers"_s}}, {u"ids"_s, std::move(ids)}}; const auto response = co_await mRequestRouter->postRequest("torrent-get"_L1, std::move(arguments)); if (!response.success) co_return; const QJsonArray torrents = response.arguments.value(torrentsKey).toArray(); for (const auto& torrentJson : torrents) { const auto object = torrentJson.toObject(); const auto torrentId = Torrent::idFromJson(object); if (torrentId.has_value()) { Torrent* torrent = torrentById(*torrentId); if (torrent && torrent->isPeersEnabled()) { torrent->updatePeers(object); } } } } void Rpc::renameTorrentFile(int torrentId, QString filePath, QString newName) { if (isConnected()) { mBackgroundRequestsCoroutineScope.launch( renameTorrentFileImpl(torrentId, std::move(filePath), std::move(newName)) ); } } Coroutine<> Rpc::renameTorrentFileImpl(int torrentId, QString filePath, QString newName) { QJsonObject arguments = {{u"ids"_s, QJsonArray{torrentId}}, {u"path"_s, filePath}, {u"name"_s, newName}}; const auto response = co_await mRequestRouter->postRequest("torrent-rename-path"_L1, std::move(arguments)); if (response.success) { Torrent* torrent = torrentById(torrentId); if (torrent) { const QString filePathFromReponse = response.arguments.value("path"_L1).toString(); const QString newNameFromReponse = response.arguments.value("name"_L1).toString(); emit torrent->fileRenamed(filePathFromReponse, newNameFromReponse); mBackgroundRequestsCoroutineScope.launch(updateData()); } } } Coroutine> Rpc::getDownloadDirFreeSpaceImpl() { if (mServerSettings->data().canShowFreeSpaceForPath()) { co_return co_await getFreeSpaceForPathImpl(mServerSettings->data().downloadDirectory); } co_return co_await getDownloadDirFreeSpaceLegacyMethod(); } Coroutine> Rpc::getDownloadDirFreeSpaceLegacyMethod() { const auto response = co_await mRequestRouter->postRequest( "download-dir-free-space"_L1, "{" "\"arguments\":{" "\"fields\":[" "\"download-dir-free-space\"" "]" "}," "\"method\":\"session-get\"" "}"_ba ); if (response.success) { co_return response.arguments.value("download-dir-free-space"_L1).toInteger(); } co_return std::nullopt; } Coroutine> Rpc::getFreeSpaceForPath(QString path) { if (!isConnected()) { co_return std::nullopt; } if (mServerSettings->data().canShowFreeSpaceForPath()) { co_return co_await getFreeSpaceForPathImpl(std::move(path)); } if (path == mServerSettings->data().downloadDirectory) { co_return co_await getDownloadDirFreeSpaceImpl(); } cancelCoroutine(); } Coroutine> Rpc::getFreeSpaceForPathImpl(QString path) { QJsonObject arguments{{u"path"_s, path}}; const auto response = co_await mRequestRouter->postRequest("free-space"_L1, std::move(arguments)); if (response.success) { co_return response.arguments.value("size-bytes"_L1).toInteger(); } co_return std::nullopt; } void Rpc::shutdownServer() { if (isConnected()) { mBackgroundRequestsCoroutineScope.launch(shutdownServerImpl()); } } Coroutine<> Rpc::shutdownServerImpl() { const auto response = co_await mRequestRouter->postRequest("session-close"_L1, QJsonObject{}); if (response.success) { info().log("Successfully sent shutdown request, disconnecting"); disconnect(); } } void Rpc::setStatus(Status&& status) { if (status == mStatus) { return; } const Status oldStatus = mStatus; const bool connectionStateChanged = status.connectionState != oldStatus.connectionState; if (connectionStateChanged && oldStatus.connectionState == ConnectionState::Connected) { emit aboutToDisconnect(); } mStatus = std::move(status); if (connectionStateChanged) { resetStateOnConnectionStateChanged(oldStatus.connectionState); } emit statusChanged(); if (connectionStateChanged) { emitSignalsOnConnectionStateChanged(oldStatus.connectionState); } } void Rpc::resetStateOnConnectionStateChanged(ConnectionState oldConnectionState) { switch (mStatus.connectionState) { case ConnectionState::Disconnected: { info().log("Disconnected"); mBackgroundRequestsCoroutineScope.cancelAll(); mRequestRouter->abortNetworkRequestsAndClearSessionId(); mServerIsLocal = std::nullopt; mGetTorrentsRequestData.clear(); if (!mTorrents.empty() && oldConnectionState == ConnectionState::Connected) { const auto removedTorrentsCount = mTorrents.size(); emit onAboutToRemoveTorrents(0, removedTorrentsCount); mTorrents.clear(); emit onRemovedTorrents(0, removedTorrentsCount); } if (error() != RpcError::NoError && mAutoReconnectEnabled) { mAutoReconnectCoroutineScope.launch(autoReconnect()); } break; } case ConnectionState::Connecting: info().log("Connecting"); mAutoReconnectCoroutineScope.cancelAll(); break; case ConnectionState::Connected: { info().log("Connected"); break; } } } void Rpc::emitSignalsOnConnectionStateChanged(Rpc::ConnectionState oldConnectionState) { switch (mStatus.connectionState) { case ConnectionState::Disconnected: { emit connectionStateChanged(); if (oldConnectionState == ConnectionState::Connected) { emit connectedChanged(); emit torrentsUpdated(); } break; } case ConnectionState::Connecting: emit connectionStateChanged(); break; case ConnectionState::Connected: { emit torrentsUpdated(); emit connectionStateChanged(); emit connectedChanged(); break; } } } Coroutine<> Rpc::postRequest(QLatin1String method, QJsonObject arguments, bool updateIfSuccessful) { if (isConnected()) { const auto response = co_await mRequestRouter->postRequest(method, std::move(arguments)); if (updateIfSuccessful && response.success) { mBackgroundRequestsCoroutineScope.launch(updateData()); } } } Coroutine<> Rpc::getServerSettings() { const auto response = co_await mRequestRouter->postRequest("session-get"_L1, R"({"method":"session-get"})"_ba); if (response.success) { mServerSettings->update(response.arguments); if (mServerSettings->data().hasTableMode()) { mGetTorrentsRequestData = RequestRouter::makeRequestData( "torrent-get"_L1, QJsonObject{{u"fields"_s, Torrent::updateFields(mServerSettings)}, {u"format"_s, "table"_L1}} ); } else { mGetTorrentsRequestData = RequestRouter::makeRequestData( "torrent-get"_L1, QJsonObject{{u"fields"_s, Torrent::updateFields(mServerSettings)}} ); } } } struct NewTorrent { int id{}; QJsonValue json{}; }; class TorrentsListUpdater final : public ItemListUpdater, std::vector> { public: inline explicit TorrentsListUpdater(Rpc& rpc) : mRpc(rpc) {} const std::vector>* keys{}; std::vector> removedIndexRanges{}; std::vector> changedIndexRanges{}; int addedCount{}; protected: std::vector::iterator findNewItemForItem(std::vector& newTorrents, const std::unique_ptr& torrent) override { return std::ranges::find(newTorrents, torrent->data().id, &NewTorrent::id); } void onAboutToRemoveItems(size_t first, size_t last) override { emit mRpc.onAboutToRemoveTorrents(first, last); }; void onRemovedItems(size_t first, size_t last) override { removedIndexRanges.emplace_back(static_cast(first), static_cast(last)); emit mRpc.onRemovedTorrents(first, last); } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) bool updateItem(std::unique_ptr& torrent, NewTorrent&& newTorrent) override { const bool wasFinished = torrent->data().isFinished(); const bool wasPaused = (torrent->data().status == TorrentData::Status::Paused); const auto oldSizeWhenDone = torrent->data().sizeWhenDone; bool changed{}; if (keys) { changed = torrent->update(*keys, newTorrent.json.toArray()); } else { changed = torrent->update(newTorrent.json.toObject()); } if (changed) { // Don't emit torrentFinished() if torrent's size became smaller // since there is high chance that it happened because user unselected some files // and torrent immediately became finished. We don't want notification in that case if (!wasFinished && torrent->data().isFinished() && !wasPaused && torrent->data().sizeWhenDone >= oldSizeWhenDone) { emit mRpc.torrentFinished(torrent.get()); } } return changed; } void onChangedItems(size_t first, size_t last) override { changedIndexRanges.emplace_back(static_cast(first), static_cast(last)); emit mRpc.onChangedTorrents(first, last); } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) std::unique_ptr createItemFromNewItem(NewTorrent&& newTorrent) override { std::unique_ptr torrent{}; if (keys) { torrent = std::make_unique(newTorrent.id, *keys, newTorrent.json.toArray(), &mRpc); } else { torrent = std::make_unique(newTorrent.id, newTorrent.json.toObject(), &mRpc); } if (mRpc.isConnected()) { emit mRpc.torrentAdded(torrent.get()); } return torrent; } void onAboutToAddItems(size_t count) override { emit mRpc.onAboutToAddTorrents(count); } void onAddedItems(size_t count) override { addedCount = static_cast(count); emit mRpc.onAddedTorrents(count); }; private: Rpc& mRpc; }; Coroutine<> Rpc::getTorrents() { const auto response = co_await mRequestRouter->postRequest("torrent-get"_L1, mGetTorrentsRequestData); if (!response.success) co_return; TorrentsListUpdater updater(*this); { const QJsonArray torrentsJsons = response.arguments.value("torrents"_L1).toArray(); std::vector newTorrents{}; if (mServerSettings->data().hasTableMode()) { if (!torrentsJsons.empty()) { const auto keys = Torrent::mapUpdateKeys(torrentsJsons.first().toArray()); const auto idKeyIndex = Torrent::idKeyIndex(keys); if (idKeyIndex.has_value()) { newTorrents.reserve(static_cast(torrentsJsons.size() - 1)); for (const auto& json : torrentsJsons | std::views::drop(1)) { const auto array = json.toArray(); if (static_cast(array.size()) == keys.size()) { newTorrents.push_back(NewTorrent{.id = array[*idKeyIndex].toInt(), .json = json}); } } updater.keys = &keys; updater.update(mTorrents, std::move(newTorrents)); } } } else { newTorrents.reserve(static_cast(torrentsJsons.size())); for (const auto& torrentJson : torrentsJsons) { const auto id = Torrent::idFromJson(torrentJson.toObject()); if (id.has_value()) { newTorrents.push_back(NewTorrent{.id = *id, .json = torrentJson}); } } updater.update(mTorrents, std::move(newTorrents)); } } std::vector> additionalRequests{}; QJsonArray getFilesIds{}; QJsonArray getPeersIds{}; for (const auto& torrent : mTorrents) { if (torrent->isFilesEnabled()) { getFilesIds.push_back(torrent->data().id); } if (torrent->isPeersEnabled()) { getPeersIds.push_back(torrent->data().id); } } if (!getFilesIds.empty()) { additionalRequests.push_back(getTorrentsFiles(getFilesIds)); } if (!getPeersIds.empty()) { additionalRequests.push_back(getTorrentsPeers(getPeersIds)); } if (!additionalRequests.empty()) { co_await waitAll(std::move(additionalRequests)); } const bool wasConnecting = connectionState() == ConnectionState::Connecting; if (!wasConnecting) { emit torrentsUpdated(); } } Coroutine<> Rpc::getServerStats() { RequestRouter::Response statsResponse{}; std::optional freeSpace{}; co_await waitAll( [&]() -> Coroutine<> { statsResponse = co_await mRequestRouter->postRequest("session-stats"_L1, R"({"method":"session-stats"})"_ba); }(), [&]() -> Coroutine<> { freeSpace = co_await getDownloadDirFreeSpaceImpl(); }() ); mServerStats->update( statsResponse.success ? std::optional(std::move(statsResponse.arguments)) : std::nullopt, freeSpace ); } Coroutine<> Rpc::connectAndPerformDataUpdates() { co_await getServerSettings(); if (mServerSettings->data().minimumRpcVersion > minimumRpcVersion) { setStatus(Status{.connectionState = ConnectionState::Disconnected, .error = Error::ServerIsTooNew}); co_return; } if (mServerSettings->data().rpcVersion < minimumRpcVersion) { setStatus(Status{.connectionState = ConnectionState::Disconnected, .error = Error::ServerIsTooOld}); co_return; } co_await waitAll(checkIfServerIsLocal(), getTorrents(), getServerStats()); setStatus(Status{.connectionState = ConnectionState::Connected}); while (true) { co_await waitFor(mUpdateInterval); co_await updateData(); } } Coroutine<> Rpc::updateData() { debug().log("Updating data"); co_await waitAll(getServerSettings(), getTorrents(), getServerStats()); debug().log("Finished updating data"); } Coroutine<> Rpc::checkIfServerIsLocal() { info().log("checkIfServerIsLocal() called"); if (mServerSettings->data().hasSessionIdFile() && !mRequestRouter->sessionId().isEmpty() && isTransmissionSessionIdFileExists(mRequestRouter->sessionId())) { mServerIsLocal = true; info().log("checkIfServerIsLocal: server is running locally: true"); co_return; } const auto configuration = mRequestRouter->configuration(); if (!configuration.has_value()) { co_return; } const auto host = configuration->serverUrl.host(); if (auto localIp = isLocalIpAddress(host); localIp.has_value()) { mServerIsLocal = localIp; info().log("checkIfServerIsLocal: server is running locally: {}", *mServerIsLocal); co_return; } info().log("checkIfServerIsLocal: resolving IP address for host name {}", host); const QHostInfo hostInfo = co_await lookupHost(host); info().log("checkIfServerIsLocal: resolved IP address for host name {}", host); const auto addresses = hostInfo.addresses(); if (!addresses.isEmpty()) { info().log("checkIfServerIsLocal: IP addresses:"); for (const auto& address : addresses) { info().log("checkIfServerIsLocal: - {}", address); } info().log("checkIfServerIsLocal: checking first address"); mServerIsLocal = isLocalIpAddress(addresses.first()); } else { mServerIsLocal = false; } info().log("checkIfServerIsLocal: server is running locally: {}", *mServerIsLocal); } void Rpc::onRequestFailed(RpcError error, const QString& errorMessage, const QString& detailedErrorMessage) { setStatus( {.connectionState = RpcConnectionState::Disconnected, .error = error, .errorMessage = errorMessage, .detailedErrorMessage = detailedErrorMessage} ); } Coroutine<> Rpc::autoReconnect() { info().log("Auto reconnecting in {} seconds", mAutoReconnectInterval.count()); co_await waitFor(mAutoReconnectInterval); info().log("Auto reconnection"); connect(); } } tremotesf-2.10.1/src/rpc/rpc.h000066400000000000000000000211371523411710200161130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_RPC_H #define TREMOTESF_RPC_RPC_H #include #include #include #include #include #include #include "coroutines/scope.h" #include "torrent.h" #ifdef Q_MOC_RUN # include "serversettings.h" # include "serverstats.h" #else namespace tremotesf { class ServerSettings; class ServerStats; } #endif namespace tremotesf { Q_NAMESPACE namespace impl { class RequestRouter; } struct ConnectionConfiguration { Q_GADGET public: enum class ProxyType { Default, Http, Socks5, None }; Q_ENUM(ProxyType) enum class ServerCertificateMode { None, SelfSigned, CustomRoot }; Q_ENUM(ServerCertificateMode) QString address{}; int port{}; QString apiPath{}; ProxyType proxyType{ProxyType::Default}; QString proxyHostname{}; int proxyPort{}; QString proxyUser{}; QString proxyPassword{}; bool https{}; ServerCertificateMode serverCertificateMode{}; QByteArray serverRootCertificate{}; QByteArray serverLeafCertificate{}; bool clientCertificateEnabled{}; QByteArray clientCertificate{}; bool authentication{}; QString username{}; QString password{}; int updateInterval{}; int timeout{}; bool autoReconnectEnabled{}; int autoReconnectInterval{}; bool operator==(const ConnectionConfiguration& rhs) const = default; }; enum class RpcConnectionState { Disconnected, Connecting, Connected }; Q_ENUM_NS(RpcConnectionState) enum class RpcError { NoError, TimedOut, ConnectionError, AuthenticationError, ParseError, ServerIsTooNew, ServerIsTooOld }; Q_ENUM_NS(RpcError) class Rpc : public QObject { Q_OBJECT public: using ConnectionState = RpcConnectionState; using Error = RpcError; explicit Rpc(QObject* parent = nullptr); ~Rpc() override; Q_DISABLE_COPY_MOVE(Rpc) ServerSettings* serverSettings() const; ServerStats* serverStats() const; const std::vector>& torrents() const; Torrent* torrentByHash(const QString& hash) const; Torrent* torrentById(int id) const; struct Status { ConnectionState connectionState{ConnectionState::Disconnected}; Error error{Error::NoError}; QString errorMessage{}; QString detailedErrorMessage{}; bool operator==(const Status& other) const = default; QString toString() const; }; bool isConnected() const; const Status& status() const; ConnectionState connectionState() const; Error error() const; const QString& errorMessage() const; const QString& detailedErrorMessage() const; bool isLocal() const; int torrentsCount() const; void setConnectionConfiguration(const ConnectionConfiguration& configuration); void resetConnectionConfiguration(); void connect(); void disconnect(); enum class DeleteFileMode { No, Delete, MoveToTrash }; void addTorrentFile( QString filePath, QString downloadDirectory, std::vector unwantedFiles, std::vector highPriorityFiles, std::vector lowPriorityFiles, std::map renamedFiles, TorrentData::Priority bandwidthPriority, bool start, DeleteFileMode deleteFileMode, std::vector labels ); void addTorrentLinks( QStringList links, QString downloadDirectory, TorrentData::Priority bandwidthPriority, bool start, std::vector labels ); void startTorrents(std::span ids); void startTorrentsNow(std::span ids); void pauseTorrents(std::span ids); void removeTorrents(std::span ids, bool deleteFiles); void checkTorrents(std::span ids); void moveTorrentsToTop(std::span ids); void moveTorrentsUp(std::span ids); void moveTorrentsDown(std::span ids); void moveTorrentsToBottom(std::span ids); void reannounceTorrents(std::span ids); void setSessionProperty(QString property, QJsonValue value); void setSessionProperties(QJsonObject properties); void setTorrentProperty(int id, QString property, QJsonValue value, bool updateIfSuccessful = false); void setTorrentsLocation(std::span ids, QString location, bool moveFiles); void setTorrentsLabels(std::span ids, std::span labels); void getTorrentFiles(int torrentId); void getTorrentPeers(int torrentId); void renameTorrentFile(int torrentId, QString filePath, QString newName); Coroutine> getFreeSpaceForPath(QString path); void shutdownServer(); private: void setStatus(Status&& status); void resetStateOnConnectionStateChanged(ConnectionState oldConnectionState); void emitSignalsOnConnectionStateChanged(ConnectionState oldConnectionState); Coroutine<> postRequest(QLatin1String method, QJsonObject arguments, bool updateIfSuccessful = true); Coroutine<> addTorrentFileImpl( QString filePath, QString downloadDirectory, std::vector unwantedFiles, std::vector highPriorityFiles, std::vector lowPriorityFiles, std::map renamedFiles, TorrentData::Priority bandwidthPriority, bool start, DeleteFileMode deleteFileMode, std::vector labels ); Coroutine<> addTorrentLinksImpl( QStringList links, QString downloadDirectory, TorrentData::Priority bandwidthPriority, bool start, std::vector labels ); Coroutine<> addTorrentLinkImpl( QString link, QString downloadDirectory, int bandwidthPriority, bool start, std::vector labels ); Coroutine<> getTorrentsFiles(QJsonArray ids); Coroutine<> getTorrentsPeers(QJsonArray ids); Coroutine<> renameTorrentFileImpl(int torrentId, QString filePath, QString newName); Coroutine> getDownloadDirFreeSpaceImpl(); Coroutine> getDownloadDirFreeSpaceLegacyMethod(); Coroutine> getFreeSpaceForPathImpl(QString path); Coroutine<> shutdownServerImpl(); Coroutine<> getServerSettings(); Coroutine<> getTorrents(); Coroutine<> getServerStats(); Coroutine<> connectAndPerformDataUpdates(); Coroutine<> updateData(); Coroutine<> checkIfServerIsLocal(); void onRequestFailed(RpcError error, const QString& errorMessage, const QString& detailedErrorMessage); Coroutine<> autoReconnect(); impl::RequestRouter* mRequestRouter{}; CoroutineScope mBackgroundRequestsCoroutineScope{}; CoroutineScope mAutoReconnectCoroutineScope{}; CoroutineScope mDeletingFilesCoroutineScope{}; bool mAutoReconnectEnabled{}; std::optional mServerIsLocal{}; QByteArray mGetTorrentsRequestData{}; std::chrono::seconds mUpdateInterval{}; std::chrono::seconds mAutoReconnectInterval{}; ServerSettings* mServerSettings{}; std::vector> mTorrents{}; ServerStats* mServerStats{}; Status mStatus{}; signals: void aboutToDisconnect(); void statusChanged(); void connectedChanged(); void connectionStateChanged(); void onAboutToRemoveTorrents(size_t first, size_t last); void onRemovedTorrents(size_t first, size_t last); void onChangedTorrents(size_t first, size_t last); void onAboutToAddTorrents(size_t count); void onAddedTorrents(size_t count); void torrentsUpdated(); void torrentAdded(tremotesf::Torrent* torrent); void torrentFinished(tremotesf::Torrent* torrent); void torrentAddDuplicate(); void torrentAddError(const QString& filePathOrUrl); }; } #endif // TREMOTESF_RPC_RPC_H tremotesf-2.10.1/src/rpc/servers.cpp000066400000000000000000000735271523411710200173650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "servers.h" #include #include #include #include #include #include "log/log.h" #include "rpc/pathutils.h" #include "rpc/qsslcertificateformatter.h" #include "rpc/serversettings.h" #include "target_os.h" using namespace Qt::StringLiterals; SPECIALIZE_FORMATTER_FOR_QDEBUG(QVariant) namespace tremotesf { namespace { constexpr QSettings::Format settingsFormat = [] { if constexpr (targetOs == TargetOs::Windows) { return QSettings::IniFormat; } else { return QSettings::NativeFormat; } }(); constexpr auto fileName = "servers"_L1; constexpr auto currentServerKey = "current"_L1; constexpr auto addressKey = "address"_L1; constexpr auto portKey = "port"_L1; constexpr auto apiPathKey = "apiPath"_L1; constexpr auto proxyTypeKey = "proxyType"_L1; constexpr auto proxyHostnameKey = "proxyHostname"_L1; constexpr auto proxyPortKey = "proxyPort"_L1; constexpr auto proxyUserKey = "proxyUser"_L1; constexpr auto proxyPasswordKey = "proxyPassword"_L1; constexpr auto httpsKey = "https"_L1; constexpr auto selfSignedCertificateEnabledKey = "selfSignedCertificateEnabled"_L1; constexpr auto selfSignedCertificateKey = "selfSignedCertificate"_L1; constexpr auto serverCertificateModeKey = "serverCertificateMode"_L1; constexpr auto serverCertificateModeNone = "none"_L1; constexpr auto serverCertificateModeSelfSigned = "selfSigned"_L1; constexpr auto serverCertificateModeCustomRoot = "customRoot"_L1; constexpr auto serverRootCertificateKey = "serverRootCertificate"_L1; constexpr auto serverLeafCertificateKey = "serverLeafCertificate"_L1; constexpr auto clientCertificateEnabledKey = "clientCertificateEnabled"_L1; constexpr auto clientCertificateKey = "clientCertificate"_L1; constexpr auto authenticationKey = "authentication"_L1; constexpr auto usernameKey = "username"_L1; constexpr auto passwordKey = "password"_L1; constexpr auto updateIntervalKey = "updateInterval"_L1; constexpr auto timeoutKey = "timeout"_L1; constexpr auto autoReconnectEnabledKey = "autoReconnectEnabled"_L1; constexpr auto autoReconnectIntervalKey = "autoReconnectInterval"_L1; constexpr auto mountedDirectoriesKey = "mountedDirectories"_L1; constexpr auto lastDownloadDirectoriesKey = "addTorrentDialogDirectories"_L1; constexpr auto lastDownloadDirectoryKey = "lastDownloadDirectory"_L1; constexpr auto lastTorrentsKey = "lastTorrents"_L1; constexpr auto lastTorrentsHashStringKey = "hashString"_L1; constexpr auto lastTorrentsFinishedKey = "finished"_L1; constexpr auto localCertificateKey = "localCertificate"_L1; constexpr auto proxyTypeDefault = "Default"_L1; constexpr auto proxyTypeHttp = "HTTP"_L1; constexpr auto proxyTypeSocks5 = "SOCKS5"_L1; constexpr auto proxyTypeNone = "None"_L1; ConnectionConfiguration::ProxyType proxyTypeFromSettings(const QString& value) { if (value.isEmpty() || value == proxyTypeDefault) { return ConnectionConfiguration::ProxyType::Default; } if (value == proxyTypeHttp) { return ConnectionConfiguration::ProxyType::Http; } if (value == proxyTypeSocks5) { return ConnectionConfiguration::ProxyType::Socks5; } if (value == proxyTypeNone) { return ConnectionConfiguration::ProxyType::None; } return ConnectionConfiguration::ProxyType::Default; } constexpr QLatin1String proxyTypeToSettings(ConnectionConfiguration::ProxyType type) { switch (type) { case ConnectionConfiguration::ProxyType::Default: return proxyTypeDefault; case ConnectionConfiguration::ProxyType::Http: return proxyTypeHttp; case ConnectionConfiguration::ProxyType::Socks5: return proxyTypeSocks5; case ConnectionConfiguration::ProxyType::None: return proxyTypeNone; } return proxyTypeDefault; } ConnectionConfiguration::ServerCertificateMode serverCertificateModeFromSettings(const QString& value) { if (value.isEmpty() || value == serverCertificateModeNone) { return ConnectionConfiguration::ServerCertificateMode::None; } if (value == serverCertificateModeSelfSigned) { return ConnectionConfiguration::ServerCertificateMode::SelfSigned; } if (value == serverCertificateModeCustomRoot) { return ConnectionConfiguration::ServerCertificateMode::CustomRoot; } return ConnectionConfiguration::ServerCertificateMode::None; } constexpr QLatin1String serverCertificateModeToSettings(ConnectionConfiguration::ServerCertificateMode mode) { switch (mode) { case ConnectionConfiguration::ServerCertificateMode::None: return serverCertificateModeNone; case ConnectionConfiguration::ServerCertificateMode::SelfSigned: return serverCertificateModeSelfSigned; case ConnectionConfiguration::ServerCertificateMode::CustomRoot: return serverCertificateModeCustomRoot; } return serverCertificateModeNone; } bool isPathUnderThisDirectory(QStringView path, QStringView directory) { // Path is not under parentDirectory if (!path.startsWith(directory)) { return false; } // Path is the same as parentDirectory - that's ok if (path.size() == directory.size()) { return true; } // Path ends with segment that's not actually the last segment of parentDirectory, just prefixed by it if (!directory.endsWith('/') && path[directory.size()] != '/') { return false; } return true; } } QVariant MountedDirectory::toVariant(std::span dirs) { QVariantMap map{}; for (const auto& dir : dirs) { map.insert(dir.localPath, dir.remotePath); } return map; } std::vector MountedDirectory::fromVariant(const QVariant& var) { const QVariantMap map = var.toMap(); std::vector dirs{}; dirs.reserve(static_cast(map.size())); for (const auto& [key, value] : map.asKeyValueRange()) { dirs.push_back({.localPath = normalizeLocalPathOrNetworkShareUrl(key), .remotePath = value.toString()}); } return dirs; } QVariant LastTorrents::toVariant() const { return torrents | std::views::transform([](const LastTorrents::Torrent& torrent) { return QVariant( QVariantMap{ {lastTorrentsHashStringKey, torrent.hashString}, {lastTorrentsFinishedKey, torrent.finished} } ); }) | std::ranges::to(); } LastTorrents LastTorrents::fromVariant(const QVariant& var) { if (!var.isValid() || var.typeId() != QMetaType::QVariantList) { return {}; } return { .saved = true, .torrents = var.toList() | std::views::transform([](const QVariant& torrentVar) { const QVariantMap map = torrentVar.toMap(); return LastTorrents::Torrent{ .hashString = map[lastTorrentsHashStringKey].toString(), .finished = map[lastTorrentsFinishedKey].toBool() }; }) | std::ranges::to() }; } Servers* Servers::instance() { static auto* const instance = new Servers(nullptr, qApp); return instance; } bool Servers::hasServers() const { return !mSettings->childGroups().isEmpty(); } std::vector Servers::servers() { std::vector list; const QStringList groups(mSettings->childGroups()); list.reserve(static_cast(groups.size())); for (const QString& group : groups) { list.push_back(getServer(group)); } return list; } Server Servers::currentServer() const { return getServer(currentServerName()); } QString Servers::currentServerName() const { return mSettings->value(currentServerKey).toString(); } QString Servers::currentServerAddress() { mSettings->beginGroup(currentServerName()); QString address(mSettings->value(addressKey).toString()); mSettings->endGroup(); return address; } void Servers::setCurrentServer(const QString& name) { if (mSettings->value(currentServerKey) != name) { mSettings->setValue(currentServerKey, name); updateMountedDirectories(); emit currentServerChanged(); } } bool Servers::currentServerHasMountedDirectories() const { return !mCurrentServerMountedDirectories.empty(); } QString Servers::fromLocalToRemoteDirectory(const QString& localPath, PathOs remotePathOs) { const auto localPathNormalized = normalizeLocalPathOrNetworkShareUrl(localPath); for (const auto& [localDirectory, remoteDirectory] : mCurrentServerMountedDirectories) { if (isPathUnderThisDirectory(localPathNormalized, localDirectory)) { const auto remoteDirectoryNormalized = normalizePath(remoteDirectory, remotePathOs); auto relativePathIndex = localDirectory.size(); if (localDirectory.endsWith('/')) { relativePathIndex -= 1; } return normalizePath( remoteDirectoryNormalized + localPathNormalized.mid(relativePathIndex), remotePathOs ); } } return {}; } QString Servers::fromLocalToRemoteDirectory(const QString& localPath, const ServerSettings* serverSettings) { return fromLocalToRemoteDirectory(localPath, serverSettings->data().pathOs); } QString Servers::fromRemoteToLocalDirectory(const QString& remotePath, PathOs remotePathOs) { const auto remotePathNormalized = normalizePath(remotePath, remotePathOs); for (const auto& [localDirectory, remoteDirectory] : mCurrentServerMountedDirectories) { const auto remoteDirectoryNormalized = normalizePath(remoteDirectory, remotePathOs); if (isPathUnderThisDirectory(remotePathNormalized, remoteDirectoryNormalized)) { auto relativePathIndex = remoteDirectoryNormalized.size(); if (remoteDirectoryNormalized.endsWith('/')) { relativePathIndex -= 1; } return normalizeLocalPathOrNetworkShareUrl( localDirectory + remotePathNormalized.mid(relativePathIndex) ); } } return {}; } QString Servers::fromRemoteToLocalDirectory(const QString& remotePath, const ServerSettings* serverSettings) { return fromRemoteToLocalDirectory(remotePath, serverSettings->data().pathOs); } LastTorrents Servers::currentServerLastTorrents() const { mSettings->beginGroup(currentServerName()); auto lastTorrents = LastTorrents::fromVariant(mSettings->value(lastTorrentsKey)); mSettings->endGroup(); return lastTorrents; } void Servers::saveCurrentServerLastTorrents(const Rpc* rpc) { if (!hasServers()) { return; } mSettings->beginGroup(currentServerName()); LastTorrents torrents{}; torrents.torrents = rpc->torrents() | std::views::transform([](const auto& torrent) { return LastTorrents::Torrent{ .hashString = torrent->data().hashString, .finished = torrent->data().isFinished() }; }) | std::ranges::to(); mSettings->setValue(lastTorrentsKey, torrents.toVariant()); mSettings->endGroup(); } QStringList Servers::currentServerLastDownloadDirectories(const ServerSettings* serverSettings) const { QStringList directories{}; mSettings->beginGroup(currentServerName()); directories = mSettings->value(lastDownloadDirectoriesKey).toStringList() | std::views::transform([serverSettings](const QString& dir) { return normalizePath(dir, serverSettings->data().pathOs); }) | std::ranges::to(); mSettings->endGroup(); return directories; } void Servers::setCurrentServerLastDownloadDirectories(const QStringList& directories) { mSettings->beginGroup(currentServerName()); mSettings->setValue(lastDownloadDirectoriesKey, directories); mSettings->endGroup(); } QString Servers::currentServerLastDownloadDirectory(const ServerSettings* serverSettings) const { QString directory{}; mSettings->beginGroup(currentServerName()); directory = normalizePath(mSettings->value(lastDownloadDirectoryKey).toString(), serverSettings->data().pathOs); mSettings->endGroup(); return directory; } void Servers::setCurrentServerLastDownloadDirectory(const QString& directory) { mSettings->beginGroup(currentServerName()); mSettings->setValue(lastDownloadDirectoryKey, directory); mSettings->endGroup(); } void Servers::setServer( const QString& oldName, const QString& name, const ConnectionConfiguration& connectionConfiguration, std::vector mountedDirectories ) { bool currentChanged = false; const QString current(currentServerName()); if (oldName == current) { if (name != oldName) { mSettings->setValue(currentServerKey, name); } currentChanged = true; } else if (name == current) { currentChanged = true; } QStringList lastDownloadDirectories{}; QString lastDownloadDirectory{}; if (!oldName.isEmpty() && name != oldName) { lastDownloadDirectories = mSettings->value(oldName % '/' % lastDownloadDirectoriesKey).toStringList(); lastDownloadDirectory = mSettings->value(oldName % '/' % lastDownloadDirectoryKey).toString(); mSettings->remove(oldName); } mSettings->beginGroup(name); mSettings->setValue(addressKey, connectionConfiguration.address); mSettings->setValue(portKey, connectionConfiguration.port); mSettings->setValue(apiPathKey, connectionConfiguration.apiPath); mSettings->setValue(proxyTypeKey, proxyTypeToSettings(connectionConfiguration.proxyType)); mSettings->setValue(proxyHostnameKey, connectionConfiguration.proxyHostname); mSettings->setValue(proxyPortKey, connectionConfiguration.proxyPort); mSettings->setValue(proxyUserKey, connectionConfiguration.proxyUser); mSettings->setValue(proxyPasswordKey, connectionConfiguration.proxyPassword); mSettings->setValue(httpsKey, connectionConfiguration.https); mSettings->setValue( serverCertificateModeKey, serverCertificateModeToSettings(connectionConfiguration.serverCertificateMode) ); mSettings->setValue(serverRootCertificateKey, connectionConfiguration.serverRootCertificate); mSettings->setValue(serverLeafCertificateKey, connectionConfiguration.serverLeafCertificate); mSettings->setValue(clientCertificateEnabledKey, connectionConfiguration.clientCertificateEnabled); mSettings->setValue(clientCertificateKey, connectionConfiguration.clientCertificate); mSettings->setValue(authenticationKey, connectionConfiguration.authentication); mSettings->setValue(usernameKey, connectionConfiguration.username); mSettings->setValue(passwordKey, connectionConfiguration.password); mSettings->setValue(updateIntervalKey, connectionConfiguration.updateInterval); mSettings->setValue(timeoutKey, connectionConfiguration.timeout); mSettings->setValue(autoReconnectEnabledKey, connectionConfiguration.autoReconnectEnabled); mSettings->setValue(autoReconnectEnabledKey, connectionConfiguration.autoReconnectInterval); mSettings->setValue(mountedDirectoriesKey, MountedDirectory::toVariant(mountedDirectories)); mSettings->setValue(lastDownloadDirectoriesKey, lastDownloadDirectories); mSettings->setValue(lastDownloadDirectoryKey, lastDownloadDirectory); mSettings->endGroup(); if (currentChanged) { mCurrentServerMountedDirectories = std::move(mountedDirectories); emit currentServerChanged(); } if (oldName.isEmpty() && mSettings->childGroups().size() == 1) { emit hasServersChanged(); } } void Servers::saveServers(const std::vector& servers, const QString& current) { const bool hadServers = hasServers(); mSettings->clear(); if (!current.isEmpty()) { mSettings->setValue(currentServerKey, current); } for (const Server& server : servers) { mSettings->beginGroup(server.name); mSettings->setValue(addressKey, server.connectionConfiguration.address); mSettings->setValue(portKey, server.connectionConfiguration.port); mSettings->setValue(apiPathKey, server.connectionConfiguration.apiPath); mSettings->setValue(proxyTypeKey, proxyTypeToSettings(server.connectionConfiguration.proxyType)); mSettings->setValue(proxyHostnameKey, server.connectionConfiguration.proxyHostname); mSettings->setValue(proxyPortKey, server.connectionConfiguration.proxyPort); mSettings->setValue(proxyUserKey, server.connectionConfiguration.proxyUser); mSettings->setValue(proxyPasswordKey, server.connectionConfiguration.proxyPassword); mSettings->setValue(httpsKey, server.connectionConfiguration.https); mSettings->setValue( serverCertificateModeKey, serverCertificateModeToSettings(server.connectionConfiguration.serverCertificateMode) ); mSettings->setValue(serverRootCertificateKey, server.connectionConfiguration.serverRootCertificate); mSettings->setValue(serverLeafCertificateKey, server.connectionConfiguration.serverLeafCertificate); mSettings->setValue(clientCertificateEnabledKey, server.connectionConfiguration.clientCertificateEnabled); mSettings->setValue(clientCertificateKey, server.connectionConfiguration.clientCertificate); mSettings->setValue(authenticationKey, server.connectionConfiguration.authentication); mSettings->setValue(usernameKey, server.connectionConfiguration.username); mSettings->setValue(passwordKey, server.connectionConfiguration.password); mSettings->setValue(updateIntervalKey, server.connectionConfiguration.updateInterval); mSettings->setValue(timeoutKey, server.connectionConfiguration.timeout); mSettings->setValue(autoReconnectEnabledKey, server.connectionConfiguration.autoReconnectEnabled); mSettings->setValue(autoReconnectIntervalKey, server.connectionConfiguration.autoReconnectInterval); mSettings->setValue(mountedDirectoriesKey, MountedDirectory::toVariant(server.mountedDirectories)); mSettings->setValue(lastTorrentsKey, server.lastTorrents.toVariant()); mSettings->setValue(lastDownloadDirectoriesKey, server.lastDownloadDirectories); mSettings->setValue(lastDownloadDirectoryKey, server.lastDownloadDirectory); mSettings->endGroup(); } updateMountedDirectories(); emit currentServerChanged(); if (hasServers() != hadServers) { emit hasServersChanged(); } } Servers::Servers(QSettings* settings, QObject* parent) : QObject(parent), mSettings( settings ? settings : new QSettings(settingsFormat, QSettings::UserScope, qApp->organizationName(), fileName, this) ) { mSettings->setFallbacksEnabled(false); if (hasServers()) { bool foundCurrent = false; const QString current(currentServerName()); if (!current.isEmpty()) { const QStringList groups(mSettings->childGroups()); for (const QString& group : groups) { if (group == current) { foundCurrent = true; break; } } } if (!foundCurrent) { mSettings->setValue(currentServerKey, mSettings->childGroups().constFirst()); } } else { mSettings->remove(currentServerKey); } const QStringList groups(mSettings->childGroups()); for (const QString& group : groups) { mSettings->beginGroup(group); migrateClientCertificateSettings(); migrateServerCertificateSettings(); mSettings->endGroup(); } updateMountedDirectories(); } Server Servers::getServer(const QString& name) const { mSettings->beginGroup(name); Server server{ .name = mSettings->group(), .connectionConfiguration = ConnectionConfiguration{ .address = mSettings->value(addressKey).toString(), .port = mSettings->value(portKey).toInt(), .apiPath = mSettings->value(apiPathKey).toString(), .proxyType = proxyTypeFromSettings(mSettings->value(proxyTypeKey).toString()), .proxyHostname = mSettings->value(proxyHostnameKey).toString(), .proxyPort = mSettings->value(proxyPortKey).toInt(), .proxyUser = mSettings->value(proxyUserKey).toString(), .proxyPassword = mSettings->value(proxyPasswordKey).toString(), .https = mSettings->value(httpsKey, false).toBool(), .serverCertificateMode = serverCertificateModeFromSettings(mSettings->value(serverCertificateModeKey).toString()), .serverRootCertificate = mSettings->value(serverRootCertificateKey).toByteArray(), .serverLeafCertificate = mSettings->value(serverLeafCertificateKey).toByteArray(), .clientCertificateEnabled = mSettings->value(clientCertificateEnabledKey, false).toBool(), .clientCertificate = mSettings->value(clientCertificateKey).toByteArray(), .authentication = mSettings->value(authenticationKey, false).toBool(), .username = mSettings->value(usernameKey).toString(), .password = mSettings->value(passwordKey).toString(), .updateInterval = mSettings->value(updateIntervalKey, 5).toInt(), .timeout = mSettings->value(timeoutKey, 30).toInt(), .autoReconnectEnabled = mSettings->value(autoReconnectEnabledKey, false).toBool(), .autoReconnectInterval = mSettings->value(autoReconnectIntervalKey, 30).toInt() }, .mountedDirectories = MountedDirectory::fromVariant(mSettings->value(mountedDirectoriesKey)), .lastTorrents = LastTorrents::fromVariant(mSettings->value(lastTorrentsKey)), .lastDownloadDirectories = mSettings->value(lastDownloadDirectoriesKey).toStringList(), .lastDownloadDirectory = mSettings->value(lastDownloadDirectoryKey).toString() }; mSettings->endGroup(); return server; } void Servers::updateMountedDirectories() { mSettings->beginGroup(currentServerName()); mCurrentServerMountedDirectories = MountedDirectory::fromVariant(mSettings->value(mountedDirectoriesKey)); mSettings->endGroup(); } void Servers::migrateClientCertificateSettings() { if (!mSettings->contains(localCertificateKey)) { return; } info().log("Migrating legacy client certificate settings for server {}", mSettings->group()); const auto localCertificateValue = mSettings->value(localCertificateKey); const auto localCertificate = localCertificateValue.toByteArray(); info().log("{} = {}", localCertificateKey, localCertificateValue); info().log("Parsed certificate:\n{}", QSslCertificate(localCertificate, QSsl::Pem)); if (!localCertificate.isEmpty()) { setValueVerbose(clientCertificateEnabledKey, true); setValueVerbose(clientCertificateKey, localCertificate); } else { warning().log("{} is empty", localCertificateKey); } mSettings->remove(localCertificateKey); } namespace { bool isCertificateIssuedBy(const QSslCertificate& cert, const QSslCertificate& potentialCa) { if (cert == potentialCa) return false; const auto issuerAttrs = cert.issuerInfoAttributes(); if (issuerAttrs.isEmpty()) return false; return std::ranges::all_of(issuerAttrs, [&](const QByteArray& attr) { return cert.issuerInfo(attr) == potentialCa.subjectInfo(attr); }); } std::optional findLeafCertificate(std::span chain) { // First, find first non self-signed certificate auto maybeLeaf = std::ranges::find_if(chain, [](const QSslCertificate& cert) { return !cert.isSelfSigned(); }); if (maybeLeaf == chain.end()) { return std::nullopt; } // Go up the chain to skip any intermediary certificates if they are present (we don't need them here but user can add them too) while (true) { auto issued = std::ranges::find_if(chain, [&](const QSslCertificate& cert) { return isCertificateIssuedBy(cert, *maybeLeaf); }); if (issued != chain.end()) { maybeLeaf = issued; } else { break; } } if (maybeLeaf == chain.end()) { return std::nullopt; } return *maybeLeaf; } } void Servers::migrateServerCertificateSettings() { if (!mSettings->contains(selfSignedCertificateEnabledKey)) { return; } info().log("Migrating legacy server certificate settings for server {}", mSettings->group()); const auto selfSignedCertificateValue = mSettings->value(selfSignedCertificateKey); info().log("{} = {}", selfSignedCertificateKey, selfSignedCertificateValue); const auto certs = QSslCertificate::fromData(selfSignedCertificateValue.toByteArray()); info().log("Parsed {} certificates from {}", certs.size(), selfSignedCertificateKey); for (const auto& cert : certs) { info().log(cert); } if (!certs.isEmpty()) { const bool enabled = mSettings->value(selfSignedCertificateEnabledKey, false).toBool(); if (!enabled) { setValueVerbose( serverCertificateModeKey, serverCertificateModeToSettings(ConnectionConfiguration::ServerCertificateMode::None) ); } if (certs.size() == 1) { if (enabled) { setValueVerbose( serverCertificateModeKey, serverCertificateModeToSettings(ConnectionConfiguration::ServerCertificateMode::SelfSigned) ); } setValueVerbose(serverRootCertificateKey, selfSignedCertificateValue); } else { if (enabled) { setValueVerbose( serverCertificateModeKey, serverCertificateModeToSettings(ConnectionConfiguration::ServerCertificateMode::CustomRoot) ); } // Find the root if (const auto root = std::ranges::find_if(certs, &QSslCertificate::isSelfSigned); root != certs.end()) { debug().log("Root certificate:\n{}", *root); setValueVerbose(serverRootCertificateKey, root->toPem()); } else { warning().log("Did not find a root CA certificate in connection configuration"); } if (const auto leaf = findLeafCertificate(certs); leaf.has_value()) { debug().log("Leaf certificate:\n{}", *leaf); setValueVerbose(serverLeafCertificateKey, leaf->toPem()); } } } mSettings->remove(selfSignedCertificateEnabledKey); mSettings->remove(selfSignedCertificateKey); } void Servers::setValueVerbose(QLatin1String key, const QVariant& value) { info().log("Setting {} to {}", key, value); mSettings->setValue(key, value); } void Servers::sync() { mSettings->sync(); } } tremotesf-2.10.1/src/rpc/servers.h000066400000000000000000000064261523411710200170240ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_SERVERS_H #define TREMOTESF_RPC_SERVERS_H #include #include #include #include "rpc.h" class QSettings; namespace tremotesf { enum class PathOs; struct MountedDirectory { QString localPath{}; QString remotePath{}; static QVariant toVariant(std::span dirs); static std::vector fromVariant(const QVariant& var); }; struct LastTorrents { struct Torrent { QString hashString{}; bool finished{}; }; bool saved{}; std::vector torrents{}; QVariant toVariant() const; static LastTorrents fromVariant(const QVariant& var); }; struct Server { QString name{}; ConnectionConfiguration connectionConfiguration{}; std::vector mountedDirectories{}; LastTorrents lastTorrents{}; QStringList lastDownloadDirectories{}; QString lastDownloadDirectory{}; }; class Servers final : public QObject { Q_OBJECT public: explicit Servers(QSettings* settings, QObject* parent); static Servers* instance(); bool hasServers() const; std::vector servers(); Server currentServer() const; QString currentServerName() const; QString currentServerAddress(); void setCurrentServer(const QString& name); bool currentServerHasMountedDirectories() const; QString fromLocalToRemoteDirectory(const QString& localPath, PathOs pathOs); QString fromLocalToRemoteDirectory(const QString& localPath, const ServerSettings* serverSettings); QString fromRemoteToLocalDirectory(const QString& remotePath, PathOs pathOs); QString fromRemoteToLocalDirectory(const QString& remotePath, const ServerSettings* serverSettings); LastTorrents currentServerLastTorrents() const; void saveCurrentServerLastTorrents(const Rpc* rpc); QStringList currentServerLastDownloadDirectories(const ServerSettings* serverSettings) const; void setCurrentServerLastDownloadDirectories(const QStringList& directories); QString currentServerLastDownloadDirectory(const ServerSettings* serverSettings) const; void setCurrentServerLastDownloadDirectory(const QString& directory); void setServer( const QString& oldName, const QString& name, const ConnectionConfiguration& connectionConfiguration, std::vector mountedDirectories ); void saveServers(const std::vector& servers, const QString& current); void sync(); private: Server getServer(const QString& name) const; void updateMountedDirectories(); void migrateClientCertificateSettings(); void migrateServerCertificateSettings(); void setValueVerbose(QLatin1String key, const QVariant& value); QSettings* mSettings{}; std::vector mCurrentServerMountedDirectories{}; signals: void currentServerChanged(); void hasServersChanged(); }; } #endif // TREMOTESF_RPC_SERVERS_H tremotesf-2.10.1/src/rpc/servers_migration_test.cpp000066400000000000000000000206571523411710200224710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "fileutils.h" #include "servers.h" using namespace Qt::StringLiterals; namespace tremotesf { class ServersMigrationTest final : public QObject { Q_OBJECT private slots: void ClientCertificateMigration() { const auto expectedCertificateValue = "lol"_ba; auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("localCertificate"_L1, expectedCertificateValue); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE(server.connectionConfiguration.clientCertificateEnabled, true); QCOMPARE(server.connectionConfiguration.clientCertificate, expectedCertificateValue); } void ClientCertificateMigrationMissingKey() { auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE(server.connectionConfiguration.clientCertificateEnabled, false); QCOMPARE(server.connectionConfiguration.clientCertificate.isEmpty(), true); } void ClientCertificateMigrationEmptyValue() { auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("localCertificate"_L1, QByteArray{}); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE(server.connectionConfiguration.clientCertificateEnabled, false); QCOMPARE(server.connectionConfiguration.clientCertificate.isEmpty(), true); } void ServerCertificateMigrationSelfSigned() { const auto expectedCertificateValue = readFile(TEST_DATA_PATH "/server-certs/self-signed.pem"); auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("selfSignedCertificateEnabled"_L1, true); settings.setValue("selfSignedCertificate"_L1, expectedCertificateValue); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE( server.connectionConfiguration.serverCertificateMode, ConnectionConfiguration::ServerCertificateMode::SelfSigned ); QCOMPARE(server.connectionConfiguration.serverRootCertificate, expectedCertificateValue); } void ServerCertificateMigrationSelfSignedDisabled() { const auto expectedCertificateValue = readFile(TEST_DATA_PATH "/server-certs/self-signed.pem"); auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("selfSignedCertificateEnabled"_L1, false); settings.setValue("selfSignedCertificate"_L1, expectedCertificateValue); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE( server.connectionConfiguration.serverCertificateMode, ConnectionConfiguration::ServerCertificateMode::None ); QCOMPARE(server.connectionConfiguration.serverRootCertificate, expectedCertificateValue); } void ServerCertificateMigrationCustomRoot() { const auto expectedRootCertificateValue = readFile(TEST_DATA_PATH "/server-certs/signed-with-custom-root/root.pem"); const auto expectedLeafCertificateValue = readFile(TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf.pem"); auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("selfSignedCertificateEnabled"_L1, true); settings.setValue( "selfSignedCertificate"_L1, expectedRootCertificateValue + "\n" + expectedLeafCertificateValue ); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE( server.connectionConfiguration.serverCertificateMode, ConnectionConfiguration::ServerCertificateMode::CustomRoot ); QCOMPARE(server.connectionConfiguration.serverRootCertificate, expectedRootCertificateValue); QCOMPARE(server.connectionConfiguration.serverLeafCertificate, expectedLeafCertificateValue); } void ServerCertificateMigrationCustomRootDisabled() { const auto expectedRootCertificateValue = readFile(TEST_DATA_PATH "/server-certs/signed-with-custom-root/root.pem"); const auto expectedLeafCertificateValue = readFile(TEST_DATA_PATH "/server-certs/signed-with-custom-root/without-intermediate/leaf.pem"); auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("selfSignedCertificateEnabled"_L1, false); settings.setValue( "selfSignedCertificate"_L1, expectedRootCertificateValue + "\n" + expectedLeafCertificateValue ); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE( server.connectionConfiguration.serverCertificateMode, ConnectionConfiguration::ServerCertificateMode::None ); QCOMPARE(server.connectionConfiguration.serverRootCertificate, expectedRootCertificateValue); QCOMPARE(server.connectionConfiguration.serverLeafCertificate, expectedLeafCertificateValue); } void ServerCertificateMigrationEmptyCertificateValue() { auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("selfSignedCertificateEnabled"_L1, true); settings.setValue("selfSignedCertificate"_L1, QByteArray{}); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE( server.connectionConfiguration.serverCertificateMode, ConnectionConfiguration::ServerCertificateMode::None ); QCOMPARE(server.connectionConfiguration.serverRootCertificate.isEmpty(), true); QCOMPARE(server.connectionConfiguration.serverLeafCertificate.isEmpty(), true); } void ServerCertificateMigrationCertificateCantBeParsed() { auto settings = QSettings(QString{}, QSettings::IniFormat); settings.beginGroup("server"_L1); settings.setValue("address", "nope"); settings.setValue("selfSignedCertificateEnabled"_L1, true); settings.setValue("selfSignedCertificate"_L1, "lol"_L1); settings.endGroup(); auto servers = Servers(&settings, nullptr); const auto server = servers.servers().at(0); QCOMPARE( server.connectionConfiguration.serverCertificateMode, ConnectionConfiguration::ServerCertificateMode::None ); QCOMPARE(server.connectionConfiguration.serverRootCertificate.isEmpty(), true); QCOMPARE(server.connectionConfiguration.serverLeafCertificate.isEmpty(), true); } }; } QTEST_GUILESS_MAIN(tremotesf::ServersMigrationTest) #include "servers_migration_test.moc" tremotesf-2.10.1/src/rpc/servers_test.cpp000066400000000000000000000353611523411710200204160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include "pathutils.h" #include "servers.h" #include "target_os.h" #include "log/log.h" using namespace Qt::StringLiterals; namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(tremotesf::PathOs pathOs, format_context& ctx) const { switch (pathOs) { case tremotesf::PathOs::Unix: return fmt::format_to(ctx.out(), "Unix"); case tremotesf::PathOs::Windows: return fmt::format_to(ctx.out(), "Windows"); break; } throw std::logic_error("Unknown PathOs value"); } }; } namespace tremotesf { namespace { std::vector createMountedDirectories(std::span localDirs, std::span remoteDirs) { std::vector dirs{}; dirs.reserve(localDirs.size()); for (const auto& [local, remote] : std::views::zip(localDirs, remoteDirs)) { dirs.push_back({.localPath = local, .remotePath = remote}); } return dirs; } } class ServersTest final : public QObject { Q_OBJECT private slots: void Case1() { check( {.mountedLocalDirectories = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return {"G:/downloads"_L1, "G:/downloads2"_L1}; } else { return {"/mnt/downloads"_L1, "/mnt/downloads2"_L1}; } }(), .mountedRemoteDirectoriesWhenServerIsUnix = {"/home/test/Downloads"_L1, "/home/test/Downloads2"_L1}, .mountedRemoteDirectoriesWhenServerIsWindows = {"C:/Users/test/Downloads"_L1, "C:/Users/test/Downloads2"_L1}, .localPathsToCheck = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return { {}, "G:/"_L1, "G:/nope"_L1, "G:/downloads"_L1, "G:/downloads/"_L1, "G:/downloads/hmm"_L1, "G:/downloads2"_L1, "G:/downloads2/"_L1, "G:/downloads2/hmm"_L1, }; } else { return { {}, "/"_L1, "/nope"_L1, "/mnt/downloads"_L1, "/mnt/downloads/"_L1, "/mnt/downloads/hmm"_L1, "/mnt/downloads2"_L1, "/mnt/downloads2/"_L1, "/mnt/downloads2/hmm"_L1, }; } }(), .expectedRemotePathsWhenServerIsUnix = { {}, {}, {}, "/home/test/Downloads"_L1, "/home/test/Downloads"_L1, "/home/test/Downloads/hmm"_L1, "/home/test/Downloads2"_L1, "/home/test/Downloads2"_L1, "/home/test/Downloads2/hmm"_L1, }, .expectedRemotePathsWhenServerIsWindows = { {}, {}, {}, "C:/Users/test/Downloads"_L1, "C:/Users/test/Downloads"_L1, "C:/Users/test/Downloads/hmm"_L1, "C:/Users/test/Downloads2"_L1, "C:/Users/test/Downloads2"_L1, "C:/Users/test/Downloads2/hmm"_L1, }, .remotePathsToCheckWhenServerIsUnix = { {}, "/"_L1, "/nope"_L1, "/home/test/Downloads"_L1, "/home/test/Downloads/"_L1, "/home/test/Downloads/hmm"_L1, "/home/test/Downloads2"_L1, "/home/test/Downloads2/"_L1, "/home/test/Downloads2/hmm"_L1, }, .remotePathsToCheckWhenServerIsWindows = { {}, "C:/"_L1, "C:/nope"_L1, "C:/Users/test/Downloads"_L1, "C:/Users/test/Downloads/"_L1, "C:/Users/test/Downloads/hmm"_L1, "C:/Users/test/Downloads2"_L1, "C:/Users/test/Downloads2/"_L1, "C:/Users/test/Downloads2/hmm"_L1, }, .expectedLocalPaths = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return { {}, {}, {}, "G:/downloads"_L1, "G:/downloads"_L1, "G:/downloads/hmm"_L1, "G:/downloads2"_L1, "G:/downloads2"_L1, "G:/downloads2/hmm"_L1, }; } else { return { {}, {}, {}, "/mnt/downloads"_L1, "/mnt/downloads"_L1, "/mnt/downloads/hmm"_L1, "/mnt/downloads2"_L1, "/mnt/downloads2"_L1, "/mnt/downloads2/hmm"_L1, }; } }()} ); } void Case2() { check( {.mountedLocalDirectories = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return {"G:/root"_L1}; } else { return {"/mnt/root"_L1}; } }(), .mountedRemoteDirectoriesWhenServerIsUnix = {"/"_L1}, .mountedRemoteDirectoriesWhenServerIsWindows = {"D:/"_L1}, .localPathsToCheck = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return {"G:/root"_L1, "G:/root/"_L1, "G:/root/hmm"_L1, "G:/root/hmm/"_L1}; } else { return {"/mnt/root"_L1, "/mnt/root/"_L1, "/mnt/root/hmm"_L1, "/mnt/root/hmm/"_L1}; } }(), .expectedRemotePathsWhenServerIsUnix = {"/"_L1, "/"_L1, "/hmm"_L1, "/hmm"_L1}, .expectedRemotePathsWhenServerIsWindows = {"D:/"_L1, "D:/"_L1, "D:/hmm"_L1, "D:/hmm"}, .remotePathsToCheckWhenServerIsUnix = {"/"_L1, "/hmm"_L1, "/hmm/"_L1}, .remotePathsToCheckWhenServerIsWindows = {"D:/"_L1, "D:/hmm"_L1, "D:/hmm/"_L1}, .expectedLocalPaths = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return {"G:/root"_L1, "G:/root/hmm"_L1, "G:/root/hmm"_L1}; } else { return {"/mnt/root"_L1, "/mnt/root/hmm"_L1, "/mnt/root/hmm"_L1}; } }()} ); } void Case3() { check( {.mountedLocalDirectories = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return {"//42.42.42.42/D"_L1}; } else { return {"/remote"_L1}; } }(), .mountedRemoteDirectoriesWhenServerIsUnix = {"/"_L1}, .mountedRemoteDirectoriesWhenServerIsWindows = {"D:"_L1}, .localPathsToCheck = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return { "//42.42.42.42/D"_L1, "//42.42.42.42/D/"_L1, "//42.42.42.42/D/hmm"_L1, "//42.42.42.42/D/hmm/"_L1, }; } else { return { "/remote"_L1, "/remote/"_L1, "/remote/hmm"_L1, "/remote/hmm/"_L1, }; } }(), .expectedRemotePathsWhenServerIsUnix = {"/"_L1, "/"_L1, "/hmm"_L1, "/hmm"_L1}, .expectedRemotePathsWhenServerIsWindows = {"D:/"_L1, "D:/"_L1, "D:/hmm"_L1, "D:/hmm"_L1}, .remotePathsToCheckWhenServerIsUnix = {"/"_L1, "/hmm"_L1, "/hmm/"_L1}, .remotePathsToCheckWhenServerIsWindows = {"D:/"_L1, "D:/hmm"_L1, "D:/hmm/"_L1}, .expectedLocalPaths = []() -> std::vector { if constexpr (targetOs == TargetOs::Windows) { return {"//42.42.42.42/D"_L1, "//42.42.42.42/D/hmm"_L1, "//42.42.42.42/D/hmm"_L1}; } else { return {"/remote"_L1, "/remote/hmm"_L1, "/remote/hmm"_L1}; } }()} ); } void Case4() { check( {.mountedLocalDirectories = {"smb://host/foo"}, .mountedRemoteDirectoriesWhenServerIsUnix = {"/foo"_L1}, .mountedRemoteDirectoriesWhenServerIsWindows = {"C:/foo"_L1}, .localPathsToCheck = { "smb://host/foo"_L1, "smb://host/foo/"_L1, "smb://host/foo/hmm"_L1, "smb://host/foo/hmm/"_L1, }, .expectedRemotePathsWhenServerIsUnix = {"/foo"_L1, "/foo"_L1, "/foo/hmm"_L1, "/foo/hmm"_L1}, .expectedRemotePathsWhenServerIsWindows = {"C:/foo"_L1, "C:/foo"_L1, "C:/foo/hmm"_L1, "C:/foo/hmm"_L1}, .remotePathsToCheckWhenServerIsUnix = {"/foo"_L1, "/foo/hmm"_L1, "/foo/hmm/"_L1}, .remotePathsToCheckWhenServerIsWindows = {"C:/foo"_L1, "C:/foo/hmm"_L1, "C:/foo/hmm/"_L1}, .expectedLocalPaths = { "smb://host/foo"_L1, "smb://host/foo/hmm"_L1, "smb://host/foo/hmm"_L1, }} ); } private: struct TestCase { std::vector mountedLocalDirectories; std::vector mountedRemoteDirectoriesWhenServerIsUnix; std::vector mountedRemoteDirectoriesWhenServerIsWindows; std::vector localPathsToCheck; std::vector expectedRemotePathsWhenServerIsUnix; std::vector expectedRemotePathsWhenServerIsWindows; std::vector remotePathsToCheckWhenServerIsUnix; std::vector remotePathsToCheckWhenServerIsWindows; std::vector expectedLocalPaths; }; void check(TestCase testCase) { mServers.saveServers( {{.name = "test", .mountedDirectories = createMountedDirectories( testCase.mountedLocalDirectories, testCase.mountedRemoteDirectoriesWhenServerIsUnix )}}, "test" ); checkFromLocalToRemoteDirectory( testCase.localPathsToCheck, PathOs::Unix, testCase.expectedRemotePathsWhenServerIsUnix ); if (QTest::currentTestFailed()) return; checkFromRemoteToLocalDirectory( testCase.remotePathsToCheckWhenServerIsUnix, PathOs::Unix, testCase.expectedLocalPaths ); if (QTest::currentTestFailed()) return; mServers.saveServers( {{.name = "test", .mountedDirectories = createMountedDirectories( testCase.mountedLocalDirectories, testCase.mountedRemoteDirectoriesWhenServerIsWindows )}}, "test" ); checkFromLocalToRemoteDirectory( testCase.localPathsToCheck, PathOs::Windows, testCase.expectedRemotePathsWhenServerIsWindows ); if (QTest::currentTestFailed()) return; checkFromRemoteToLocalDirectory( testCase.remotePathsToCheckWhenServerIsWindows, PathOs::Windows, testCase.expectedLocalPaths ); } void checkFromLocalToRemoteDirectory( std::span localPaths, PathOs remotePathOs, std::span expectedRemotePaths ) { for (const auto& [localPath, expectedRemotePath] : std::views::zip(localPaths, expectedRemotePaths)) { info().log("Converting {} to remote path when server is {}", localPath, remotePathOs); QCOMPARE(mServers.fromLocalToRemoteDirectory(localPath, remotePathOs), expectedRemotePath); } } void checkFromRemoteToLocalDirectory( std::span remotePaths, PathOs remotePathOs, std::span expectedLocalPaths ) { for (const auto& [remotePath, expectedLocalPath] : std::views::zip(remotePaths, expectedLocalPaths)) { info().log("Converting {} to local path when server is {}", remotePath, remotePathOs); QCOMPARE(mServers.fromRemoteToLocalDirectory(remotePath, remotePathOs), expectedLocalPath); } } Servers mServers{new QSettings(QString{}, QSettings::IniFormat), nullptr}; }; } QTEST_GUILESS_MAIN(tremotesf::ServersTest) #include "servers_test.moc" tremotesf-2.10.1/src/rpc/serversettings.cpp000066400000000000000000000555541523411710200207630ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "serversettings.h" #include #include "jsonutils.h" #include "pathutils.h" #include "rpc.h" #include "stdutils.h" using namespace Qt::StringLiterals; namespace tremotesf { using namespace impl; namespace { constexpr auto downloadDirectoryKey = "download-dir"_L1; constexpr auto trashTorrentFilesKey = "trash-original-torrent-files"_L1; constexpr auto startAddedTorrentsKey = "start-added-torrents"_L1; constexpr auto renameIncompleteFilesKey = "rename-partial-files"_L1; constexpr auto incompleteDirectoryEnabledKey = "incomplete-dir-enabled"_L1; constexpr auto incompleteDirectoryKey = "incomplete-dir"_L1; constexpr auto ratioLimitedKey = "seedRatioLimited"_L1; constexpr auto ratioLimitKey = "seedRatioLimit"_L1; constexpr auto idleSeedingLimitedKey = "idle-seeding-limit-enabled"_L1; constexpr auto idleSeedingLimitKey = "idle-seeding-limit"_L1; constexpr auto downloadQueueEnabledKey = "download-queue-enabled"_L1; constexpr auto downloadQueueSizeKey = "download-queue-size"_L1; constexpr auto seedQueueEnabledKey = "seed-queue-enabled"_L1; constexpr auto seedQueueSizeKey = "seed-queue-size"_L1; constexpr auto idleQueueLimitedKey = "queue-stalled-enabled"_L1; constexpr auto idleQueueLimitKey = "queue-stalled-minutes"_L1; constexpr auto downloadSpeedLimitedKey = "speed-limit-down-enabled"_L1; constexpr auto downloadSpeedLimitKey = "speed-limit-down"_L1; constexpr auto uploadSpeedLimitedKey = "speed-limit-up-enabled"_L1; constexpr auto uploadSpeedLimitKey = "speed-limit-up"_L1; constexpr auto alternativeSpeedLimitsEnabledKey = "alt-speed-enabled"_L1; constexpr auto alternativeDownloadSpeedLimitKey = "alt-speed-down"_L1; constexpr auto alternativeUploadSpeedLimitKey = "alt-speed-up"_L1; constexpr auto alternativeSpeedLimitsScheduledKey = "alt-speed-time-enabled"_L1; constexpr auto alternativeSpeedLimitsBeginTimeKey = "alt-speed-time-begin"_L1; constexpr auto alternativeSpeedLimitsEndTimeKey = "alt-speed-time-end"_L1; constexpr auto alternativeSpeedLimitsDaysKey = "alt-speed-time-day"_L1; constexpr auto peerPortKey = "peer-port"_L1; constexpr auto randomPortEnabledKey = "peer-port-random-on-start"_L1; constexpr auto portForwardingEnabledKey = "port-forwarding-enabled"_L1; constexpr auto encryptionModeKey = "encryption"_L1; constexpr auto utpEnabledKey = "utp-enabled"_L1; constexpr auto pexEnabledKey = "pex-enabled"_L1; constexpr auto dhtEnabledKey = "dht-enabled"_L1; constexpr auto lpdEnabledKey = "lpd-enabled"_L1; constexpr auto maximumPeersPerTorrentKey = "peer-limit-per-torrent"_L1; constexpr auto maximumPeersGloballyKey = "peer-limit-global"_L1; constexpr auto alternativeSpeedLimitsDaysMapper = EnumMapper( std::array{ // (1 << 0) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Sunday, 1), // (1 << 1) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Monday, 2), // (1 << 2) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Tuesday, 4), // (1 << 3) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Wednesday, 8), // (1 << 4) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Thursday, 16), // (1 << 5) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Friday, 32), // (1 << 6) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Saturday, 64), // (Monday | Tuesday | Wednesday | Thursday | Friday) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Weekdays, 62), // (Sunday | Saturday) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::Weekends, 65), // (Weekdays | Weekends) EnumMapping(ServerSettingsData::AlternativeSpeedLimitsDays::All, 127), } ); constexpr auto encryptionModeMapper = EnumMapper( std::array{ EnumMapping(ServerSettingsData::EncryptionMode::Allowed, "tolerated"_L1), EnumMapping(ServerSettingsData::EncryptionMode::Preferred, "preferred"_L1), EnumMapping(ServerSettingsData::EncryptionMode::Required, "required"_L1) } ); } bool ServerSettingsData::canRenameFiles() const { return (rpcVersion >= 15); } bool ServerSettingsData::canShowFreeSpaceForPath() const { return (rpcVersion >= 15); } bool ServerSettingsData::hasSessionIdFile() const { return (rpcVersion >= 16); } bool ServerSettingsData::hasTableMode() const { return (rpcVersion >= 16); } bool ServerSettingsData::hasTrackerListProperty() const { return (rpcVersion >= 17); } bool ServerSettingsData::hasLabelsProperty() const { return (rpcVersion >= 16); } ServerSettings::ServerSettings(Rpc* rpc, QObject* parent) : QObject(parent), mRpc(rpc), mSaveOnSet(true) {} void ServerSettings::setDownloadDirectory(const QString& directory) { if (directory != mData.downloadDirectory) { mData.downloadDirectory = directory; if (mSaveOnSet) { mRpc->setSessionProperty(downloadDirectoryKey, mData.downloadDirectory); } } } void ServerSettings::setStartAddedTorrents(bool start) { mData.startAddedTorrents = start; if (mSaveOnSet) { mRpc->setSessionProperty(startAddedTorrentsKey, mData.startAddedTorrents); } } void ServerSettings::setTrashTorrentFiles(bool trash) { mData.trashTorrentFiles = trash; if (mSaveOnSet) { mRpc->setSessionProperty(trashTorrentFilesKey, mData.trashTorrentFiles); } } void ServerSettings::setRenameIncompleteFiles(bool rename) { mData.renameIncompleteFiles = rename; if (mSaveOnSet) { mRpc->setSessionProperty(renameIncompleteFilesKey, mData.renameIncompleteFiles); } } void ServerSettings::setIncompleteDirectoryEnabled(bool enabled) { mData.incompleteDirectoryEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(incompleteDirectoryEnabledKey, mData.incompleteDirectoryEnabled); } } void ServerSettings::setIncompleteDirectory(const QString& directory) { if (directory != mData.incompleteDirectory) { mData.incompleteDirectory = directory; if (mSaveOnSet) { mRpc->setSessionProperty(incompleteDirectoryKey, mData.incompleteDirectory); } } } void ServerSettings::setRatioLimited(bool limited) { mData.ratioLimited = limited; if (mSaveOnSet) { mRpc->setSessionProperty(ratioLimitedKey, mData.ratioLimited); } } void ServerSettings::setRatioLimit(double limit) { mData.ratioLimit = limit; if (mSaveOnSet) { mRpc->setSessionProperty(ratioLimitKey, mData.ratioLimit); } } void ServerSettings::setIdleSeedingLimited(bool limited) { mData.idleSeedingLimited = limited; if (mSaveOnSet) { mRpc->setSessionProperty(idleSeedingLimitedKey, mData.idleSeedingLimited); } } void ServerSettings::setIdleSeedingLimit(int limit) { mData.idleSeedingLimit = limit; if (mSaveOnSet) { mRpc->setSessionProperty(idleSeedingLimitKey, mData.idleSeedingLimit); } } void ServerSettings::setDownloadQueueEnabled(bool enabled) { mData.downloadQueueEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(downloadQueueEnabledKey, mData.downloadQueueEnabled); } } void ServerSettings::setDownloadQueueSize(int size) { mData.downloadQueueSize = size; if (mSaveOnSet) { mRpc->setSessionProperty(downloadQueueSizeKey, mData.downloadQueueSize); } } void ServerSettings::setSeedQueueEnabled(bool enabled) { mData.seedQueueEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(seedQueueEnabledKey, mData.seedQueueEnabled); } } void ServerSettings::setSeedQueueSize(int size) { mData.seedQueueSize = size; if (mSaveOnSet) { mRpc->setSessionProperty(seedQueueSizeKey, mData.seedQueueSize); } } void ServerSettings::setIdleQueueLimited(bool limited) { mData.idleQueueLimited = limited; if (mSaveOnSet) { mRpc->setSessionProperty(idleQueueLimitedKey, mData.idleQueueLimited); } } void ServerSettings::setIdleQueueLimit(int limit) { mData.idleQueueLimit = limit; if (mSaveOnSet) { mRpc->setSessionProperty(idleQueueLimitKey, mData.idleQueueLimit); } } void ServerSettings::setDownloadSpeedLimited(bool limited) { mData.downloadSpeedLimited = limited; if (mSaveOnSet) { mRpc->setSessionProperty(downloadSpeedLimitedKey, mData.downloadSpeedLimited); } } void ServerSettings::setDownloadSpeedLimit(int limit) { mData.downloadSpeedLimit = limit; if (mSaveOnSet) { mRpc->setSessionProperty(downloadSpeedLimitKey, mData.downloadSpeedLimit); } } void ServerSettings::setUploadSpeedLimited(bool limited) { mData.uploadSpeedLimited = limited; if (mSaveOnSet) { mRpc->setSessionProperty(uploadSpeedLimitedKey, mData.uploadSpeedLimited); } } void ServerSettings::setUploadSpeedLimit(int limit) { mData.uploadSpeedLimit = limit; if (mSaveOnSet) { mRpc->setSessionProperty(uploadSpeedLimitKey, mData.uploadSpeedLimit); } } void ServerSettings::setAlternativeSpeedLimitsEnabled(bool enabled) { mData.alternativeSpeedLimitsEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(alternativeSpeedLimitsEnabledKey, mData.alternativeSpeedLimitsEnabled); } } void ServerSettings::setAlternativeDownloadSpeedLimit(int limit) { mData.alternativeDownloadSpeedLimit = limit; if (mSaveOnSet) { mRpc->setSessionProperty(alternativeDownloadSpeedLimitKey, mData.alternativeDownloadSpeedLimit); } } void ServerSettings::setAlternativeUploadSpeedLimit(int limit) { mData.alternativeUploadSpeedLimit = limit; if (mSaveOnSet) { mRpc->setSessionProperty(alternativeUploadSpeedLimitKey, mData.alternativeUploadSpeedLimit); } } void ServerSettings::setAlternativeSpeedLimitsScheduled(bool scheduled) { mData.alternativeSpeedLimitsScheduled = scheduled; if (mSaveOnSet) { mRpc->setSessionProperty(alternativeSpeedLimitsScheduledKey, mData.alternativeSpeedLimitsScheduled); } } void ServerSettings::setAlternativeSpeedLimitsBeginTime(QTime time) { mData.alternativeSpeedLimitsBeginTime = time; if (mSaveOnSet) { mRpc->setSessionProperty( alternativeSpeedLimitsBeginTimeKey, mData.alternativeSpeedLimitsBeginTime.msecsSinceStartOfDay() / 60000 ); } } void ServerSettings::setAlternativeSpeedLimitsEndTime(QTime time) { mData.alternativeSpeedLimitsEndTime = time; if (mSaveOnSet) { mRpc->setSessionProperty( alternativeSpeedLimitsEndTimeKey, mData.alternativeSpeedLimitsEndTime.msecsSinceStartOfDay() / 60000 ); } } void ServerSettings::setAlternativeSpeedLimitsDays(ServerSettingsData::AlternativeSpeedLimitsDays days) { if (days != mData.alternativeSpeedLimitsDays) { mData.alternativeSpeedLimitsDays = days; if (mSaveOnSet) { mRpc->setSessionProperty( alternativeSpeedLimitsDaysKey, alternativeSpeedLimitsDaysMapper.toJsonConstant(days) ); } } } void ServerSettings::setPeerPort(int port) { mData.peerPort = port; if (mSaveOnSet) { mRpc->setSessionProperty(peerPortKey, mData.peerPort); } } void ServerSettings::setRandomPortEnabled(bool enabled) { mData.randomPortEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(randomPortEnabledKey, mData.randomPortEnabled); } } void ServerSettings::setPortForwardingEnabled(bool enabled) { mData.portForwardingEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(portForwardingEnabledKey, mData.portForwardingEnabled); } } void ServerSettings::setEncryptionMode(ServerSettingsData::EncryptionMode mode) { mData.encryptionMode = mode; if (mSaveOnSet) { mRpc->setSessionProperty(encryptionModeKey, encryptionModeMapper.toJsonConstant(mode)); } } void ServerSettings::setUtpEnabled(bool enabled) { mData.utpEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(utpEnabledKey, mData.utpEnabled); } } void ServerSettings::setPexEnabled(bool enabled) { mData.pexEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(pexEnabledKey, mData.pexEnabled); } } void ServerSettings::setDhtEnabled(bool enabled) { mData.dhtEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(dhtEnabledKey, mData.dhtEnabled); } } void ServerSettings::setLpdEnabled(bool enabled) { mData.lpdEnabled = enabled; if (mSaveOnSet) { mRpc->setSessionProperty(lpdEnabledKey, mData.lpdEnabled); } } void ServerSettings::setMaximumPeersPerTorrent(int peers) { mData.maximumPeersPerTorrent = peers; if (mSaveOnSet) { mRpc->setSessionProperty(maximumPeersPerTorrentKey, mData.maximumPeersPerTorrent); } } void ServerSettings::setMaximumPeersGlobally(int peers) { mData.maximumPeersGlobally = peers; if (mSaveOnSet) { mRpc->setSessionProperty(maximumPeersGloballyKey, mData.maximumPeersGlobally); } } bool ServerSettings::saveOnSet() const { return mSaveOnSet; } void ServerSettings::setSaveOnSet(bool save) { mSaveOnSet = save; } void ServerSettings::update(const QJsonObject& serverSettings) { bool changed = false; mData.rpcVersion = serverSettings.value("rpc-version"_L1).toInt(); mData.minimumRpcVersion = serverSettings.value("rpc-version-minimum"_L1).toInt(); if (const auto newConfigDir = serverSettings.value("config-dir"_L1).toString(); newConfigDir != mData.configDirectory) { mData.configDirectory = newConfigDir; // Transmission's config directory is likely located under 'C:\Users' on Windows // If config-dir somehow is an UNC path then we are out of luck - we can't reliably distinguish // between Unix path and Windows UNC path unless we already know the OS. And that's what we are trying to determine here mData.pathOs = isAbsoluteWindowsDOSFilePath(mData.configDirectory) ? PathOs::Windows : PathOs::Unix; } setChanged( mData.downloadDirectory, normalizePath(serverSettings.value(downloadDirectoryKey).toString(), mData.pathOs), changed ); setChanged(mData.trashTorrentFiles, serverSettings.value(trashTorrentFilesKey).toBool(), changed); setChanged(mData.startAddedTorrents, serverSettings.value(startAddedTorrentsKey).toBool(), changed); setChanged(mData.renameIncompleteFiles, serverSettings.value(renameIncompleteFilesKey).toBool(), changed); setChanged( mData.incompleteDirectoryEnabled, serverSettings.value(incompleteDirectoryEnabledKey).toBool(), changed ); setChanged( mData.incompleteDirectory, normalizePath(serverSettings.value(incompleteDirectoryKey).toString(), mData.pathOs), changed ); setChanged(mData.ratioLimited, serverSettings.value(ratioLimitedKey).toBool(), changed); setChanged(mData.ratioLimit, serverSettings.value(ratioLimitKey).toDouble(), changed); setChanged(mData.idleSeedingLimited, serverSettings.value(idleSeedingLimitedKey).toBool(), changed); setChanged(mData.idleSeedingLimit, serverSettings.value(idleSeedingLimitKey).toInt(), changed); setChanged(mData.downloadQueueEnabled, serverSettings.value(downloadQueueEnabledKey).toBool(), changed); setChanged(mData.downloadQueueSize, serverSettings.value(downloadQueueSizeKey).toInt(), changed); setChanged(mData.seedQueueEnabled, serverSettings.value(seedQueueEnabledKey).toBool(), changed); setChanged(mData.seedQueueSize, serverSettings.value(seedQueueSizeKey).toInt(), changed); setChanged(mData.idleQueueLimited, serverSettings.value(idleQueueLimitedKey).toBool(), changed); setChanged(mData.idleQueueLimit, serverSettings.value(idleQueueLimitKey).toInt(), changed); setChanged(mData.downloadSpeedLimited, serverSettings.value(downloadSpeedLimitedKey).toBool(), changed); setChanged(mData.downloadSpeedLimit, serverSettings.value(downloadSpeedLimitKey).toInt(), changed); setChanged(mData.uploadSpeedLimited, serverSettings.value(uploadSpeedLimitedKey).toBool(), changed); setChanged(mData.uploadSpeedLimit, serverSettings.value(uploadSpeedLimitKey).toInt(), changed); setChanged( mData.alternativeSpeedLimitsEnabled, serverSettings.value(alternativeSpeedLimitsEnabledKey).toBool(), changed ); setChanged( mData.alternativeDownloadSpeedLimit, serverSettings.value(alternativeDownloadSpeedLimitKey).toInt(), changed ); setChanged( mData.alternativeUploadSpeedLimit, serverSettings.value(alternativeUploadSpeedLimitKey).toInt(), changed ); setChanged( mData.alternativeSpeedLimitsScheduled, serverSettings.value(alternativeSpeedLimitsScheduledKey).toBool(), changed ); setChanged( mData.alternativeSpeedLimitsBeginTime, QTime::fromMSecsSinceStartOfDay(serverSettings.value(alternativeSpeedLimitsBeginTimeKey).toInt() * 60000), changed ); setChanged( mData.alternativeSpeedLimitsEndTime, QTime::fromMSecsSinceStartOfDay(serverSettings.value(alternativeSpeedLimitsEndTimeKey).toInt() * 60000), changed ); setChanged( mData.alternativeSpeedLimitsDays, alternativeSpeedLimitsDaysMapper .fromJsonValue(serverSettings.value(alternativeSpeedLimitsDaysKey), alternativeDownloadSpeedLimitKey), changed ); setChanged(mData.peerPort, serverSettings.value(peerPortKey).toInt(), changed); setChanged(mData.randomPortEnabled, serverSettings.value(randomPortEnabledKey).toBool(), changed); setChanged(mData.portForwardingEnabled, serverSettings.value(portForwardingEnabledKey).toBool(), changed); setChanged( mData.encryptionMode, encryptionModeMapper.fromJsonValue(serverSettings.value(encryptionModeKey), encryptionModeKey), changed ); setChanged(mData.utpEnabled, serverSettings.value(utpEnabledKey).toBool(), changed); setChanged(mData.pexEnabled, serverSettings.value(pexEnabledKey).toBool(), changed); setChanged(mData.dhtEnabled, serverSettings.value(dhtEnabledKey).toBool(), changed); setChanged(mData.lpdEnabled, serverSettings.value(lpdEnabledKey).toBool(), changed); setChanged(mData.maximumPeersPerTorrent, serverSettings.value(maximumPeersPerTorrentKey).toInt(), changed); setChanged(mData.maximumPeersGlobally, serverSettings.value(maximumPeersGloballyKey).toInt(), changed); if (changed) { emit this->changed(); } } void ServerSettings::save() const { mRpc->setSessionProperties( {{downloadDirectoryKey, mData.downloadDirectory}, {trashTorrentFilesKey, mData.trashTorrentFiles}, {startAddedTorrentsKey, mData.startAddedTorrents}, {renameIncompleteFilesKey, mData.renameIncompleteFiles}, {incompleteDirectoryEnabledKey, mData.incompleteDirectoryEnabled}, {incompleteDirectoryKey, mData.incompleteDirectory}, {ratioLimitedKey, mData.ratioLimited}, {ratioLimitKey, mData.ratioLimit}, {idleSeedingLimitedKey, mData.idleSeedingLimit}, {idleSeedingLimitKey, mData.idleSeedingLimit}, {downloadQueueEnabledKey, mData.downloadQueueEnabled}, {downloadQueueSizeKey, mData.downloadQueueSize}, {seedQueueEnabledKey, mData.seedQueueEnabled}, {seedQueueSizeKey, mData.seedQueueSize}, {idleQueueLimitedKey, mData.idleQueueLimited}, {idleQueueLimitKey, mData.idleQueueLimit}, {downloadSpeedLimitedKey, mData.downloadSpeedLimited}, {downloadSpeedLimitKey, mData.downloadSpeedLimit}, {uploadSpeedLimitedKey, mData.uploadSpeedLimited}, {uploadSpeedLimitKey, mData.uploadSpeedLimit}, {alternativeSpeedLimitsEnabledKey, mData.alternativeSpeedLimitsEnabled}, {alternativeDownloadSpeedLimitKey, mData.alternativeDownloadSpeedLimit}, {alternativeUploadSpeedLimitKey, mData.alternativeUploadSpeedLimit}, {alternativeSpeedLimitsScheduledKey, mData.alternativeSpeedLimitsScheduled}, {alternativeSpeedLimitsBeginTimeKey, mData.alternativeSpeedLimitsBeginTime.msecsSinceStartOfDay() / 60000}, {alternativeSpeedLimitsEndTimeKey, mData.alternativeSpeedLimitsEndTime.msecsSinceStartOfDay() / 60000}, {alternativeSpeedLimitsDaysKey, alternativeSpeedLimitsDaysMapper.toJsonConstant(mData.alternativeSpeedLimitsDays)}, {peerPortKey, mData.peerPort}, {randomPortEnabledKey, mData.randomPortEnabled}, {portForwardingEnabledKey, mData.portForwardingEnabled}, {encryptionModeKey, encryptionModeMapper.toJsonConstant(mData.encryptionMode)}, {utpEnabledKey, mData.utpEnabled}, {pexEnabledKey, mData.pexEnabled}, {dhtEnabledKey, mData.dhtEnabled}, {lpdEnabledKey, mData.lpdEnabled}, {maximumPeersPerTorrentKey, mData.maximumPeersPerTorrent}, {maximumPeersGloballyKey, mData.maximumPeersGlobally}} ); } } tremotesf-2.10.1/src/rpc/serversettings.h000066400000000000000000000121741523411710200204170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_SERVERSETTINGS_H #define TREMOTESF_RPC_SERVERSETTINGS_H #include #include #include "pathutils.h" class QJsonObject; namespace tremotesf { class Rpc; struct ServerSettingsData { Q_GADGET public: enum class AlternativeSpeedLimitsDays { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Weekdays, Weekends, All }; Q_ENUM(AlternativeSpeedLimitsDays) enum class EncryptionMode { Allowed, Preferred, Required }; Q_ENUM(EncryptionMode) [[nodiscard]] bool canRenameFiles() const; [[nodiscard]] bool canShowFreeSpaceForPath() const; [[nodiscard]] bool hasSessionIdFile() const; [[nodiscard]] bool hasTableMode() const; [[nodiscard]] bool hasTrackerListProperty() const; [[nodiscard]] bool hasLabelsProperty() const; int rpcVersion = 0; int minimumRpcVersion = 0; QString configDirectory; PathOs pathOs = PathOs::Unix; QString downloadDirectory; bool startAddedTorrents = false; bool trashTorrentFiles = false; bool renameIncompleteFiles = false; bool incompleteDirectoryEnabled = false; QString incompleteDirectory; bool ratioLimited = false; double ratioLimit = 0.0; bool idleSeedingLimited = false; int idleSeedingLimit = 0; bool downloadQueueEnabled = false; int downloadQueueSize = 0; bool seedQueueEnabled = false; int seedQueueSize = 0; bool idleQueueLimited = false; int idleQueueLimit = 0; bool downloadSpeedLimited = false; int downloadSpeedLimit = 0; bool uploadSpeedLimited = false; int uploadSpeedLimit = 0; bool alternativeSpeedLimitsEnabled = false; int alternativeDownloadSpeedLimit = 0; int alternativeUploadSpeedLimit = 0; bool alternativeSpeedLimitsScheduled = false; QTime alternativeSpeedLimitsBeginTime; QTime alternativeSpeedLimitsEndTime; AlternativeSpeedLimitsDays alternativeSpeedLimitsDays{}; int peerPort = 0; bool randomPortEnabled = false; bool portForwardingEnabled = false; EncryptionMode encryptionMode{}; bool utpEnabled = false; bool pexEnabled = false; bool dhtEnabled = false; bool lpdEnabled = false; int maximumPeersPerTorrent = 0; int maximumPeersGlobally = 0; }; class ServerSettings final : public QObject { Q_OBJECT public: explicit ServerSettings(Rpc* rpc = nullptr, QObject* parent = nullptr); void setDownloadDirectory(const QString& directory); void setStartAddedTorrents(bool start); void setTrashTorrentFiles(bool trash); void setRenameIncompleteFiles(bool rename); void setIncompleteDirectoryEnabled(bool enabled); void setIncompleteDirectory(const QString& directory); void setRatioLimited(bool limited); void setRatioLimit(double limit); void setIdleSeedingLimited(bool limited); void setIdleSeedingLimit(int limit); void setDownloadQueueEnabled(bool enabled); void setDownloadQueueSize(int size); void setSeedQueueEnabled(bool enabled); void setSeedQueueSize(int size); void setIdleQueueLimited(bool limited); void setIdleQueueLimit(int limit); void setDownloadSpeedLimited(bool limited); void setDownloadSpeedLimit(int limit); void setUploadSpeedLimited(bool limited); void setUploadSpeedLimit(int limit); void setAlternativeSpeedLimitsEnabled(bool enabled); void setAlternativeDownloadSpeedLimit(int limit); // kB/s void setAlternativeUploadSpeedLimit(int limit); void setAlternativeSpeedLimitsScheduled(bool scheduled); void setAlternativeSpeedLimitsBeginTime(QTime time); void setAlternativeSpeedLimitsEndTime(QTime time); void setAlternativeSpeedLimitsDays(ServerSettingsData::AlternativeSpeedLimitsDays days); void setPeerPort(int port); void setRandomPortEnabled(bool enabled); void setPortForwardingEnabled(bool enabled); void setEncryptionMode(ServerSettingsData::EncryptionMode mode); void setUtpEnabled(bool enabled); void setPexEnabled(bool enabled); void setDhtEnabled(bool enabled); void setLpdEnabled(bool enabled); void setMaximumPeersPerTorrent(int peers); void setMaximumPeersGlobally(int peers); [[nodiscard]] bool saveOnSet() const; void setSaveOnSet(bool save); void update(const QJsonObject& serverSettings); void save() const; [[nodiscard]] const ServerSettingsData& data() const { return mData; }; private: Rpc* mRpc; ServerSettingsData mData; bool mSaveOnSet; signals: void changed(); }; } #endif // TREMOTESF_RPC_SERVERSETTINGS_H tremotesf-2.10.1/src/rpc/serverstats.cpp000066400000000000000000000023001523411710200202360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "serverstats.h" #include using namespace Qt::StringLiterals; namespace tremotesf { void SessionStats::update(const QJsonObject& stats) { mDownloaded = stats.value("downloadedBytes"_L1).toInteger(); mUploaded = stats.value("uploadedBytes"_L1).toInteger(); mDuration = stats.value("secondsActive"_L1).toInt(); mSessionCount = stats.value("sessionCount"_L1).toInt(); } void ServerStats::update(std::optional serverStats, std::optional freeSpace) { if (serverStats.has_value()) { mDownloadSpeed = serverStats->value("downloadSpeed"_L1).toInteger(); mUploadSpeed = serverStats->value("uploadSpeed"_L1).toInteger(); mCurrentSession.update(serverStats->value("current-stats"_L1).toObject()); mTotal.update(serverStats->value("cumulative-stats"_L1).toObject()); } if (freeSpace.has_value()) { mFreeSpace = *freeSpace; } emit updated(); } void ServerStats::setFreeSpace(const qint64 freeSpace) { mFreeSpace = freeSpace; } } tremotesf-2.10.1/src/rpc/serverstats.h000066400000000000000000000031451523411710200177130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_SERVERSTATS_H #define TREMOTESF_RPC_SERVERSTATS_H #include class QJsonObject; namespace tremotesf { class SessionStats { public: [[nodiscard]] qint64 downloaded() const { return mDownloaded; }; [[nodiscard]] qint64 uploaded() const { return mUploaded; }; [[nodiscard]] int duration() const { return mDuration; }; [[nodiscard]] int sessionCount() const { return mSessionCount; }; void update(const QJsonObject& stats); private: qint64 mDownloaded{}; qint64 mUploaded{}; int mDuration{}; int mSessionCount{}; }; class ServerStats final : public QObject { Q_OBJECT public: using QObject::QObject; [[nodiscard]] qint64 downloadSpeed() const { return mDownloadSpeed; }; [[nodiscard]] qint64 uploadSpeed() const { return mUploadSpeed; }; [[nodiscard]] qint64 freeSpace() const { return mFreeSpace; }; [[nodiscard]] SessionStats currentSession() const { return mCurrentSession; }; [[nodiscard]] SessionStats total() const { return mTotal; }; void update(std::optional serverStats, std::optional freeSpace); void setFreeSpace(const qint64 freeSpace); private: qint64 mDownloadSpeed{}; qint64 mUploadSpeed{}; qint64 mFreeSpace{}; SessionStats mCurrentSession{}; SessionStats mTotal{}; signals: void updated(); }; } #endif // TREMOTESF_RPC_SERVERSTATS_H tremotesf-2.10.1/src/rpc/test-data/000077500000000000000000000000001523411710200170405ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/client-certs/000077500000000000000000000000001523411710200214345ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/client-certs/client-certificate-and-key.pem000066400000000000000000000056011523411710200272250ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDYzCCAkugAwIBAgIUOUActRWAe1BWnwvxRlVsLQZwTokwDQYJKoZIhvcNAQEL BQAwQTELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEbMBkGA1UE CgwSQ2xpZW50IGNlcnRpZmljYXRlMB4XDTIyMTIxMTIyNTI1M1oXDTMyMTIwODIy NTI1M1owQTELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEbMBkG A1UECgwSQ2xpZW50IGNlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAs5P9AVu9kamTU4pDB2o6SSfTJXCSTCGJrA9ODXVbblc7Qk4DJziF +Cere4UHkhpCrIXqHc4IqA+HwZW0jl1USuqxNTrPTctwj2CDBsgFDi7r6jZvRu0H IkteLOsZLZwGKhLeuvREkBw6ul0YiTtQpC68hnfA3YKEmX30TNYMIEUnWHV1xp+e VKV9DxmY7+sinIH92npJjK86a0j0wic8v8UBGZjasDb4ew/usrsld2Bf3koyx13R UTUEeOL06mtpjmO2p99iNoS1Chl/PjbI2H/fMF5tvzS3FZMV6SkFPXdPPkK7387l 7hU5/Z2ria5XGWEzZzDKhgyuQ5y5y9FHJQIDAQABo1MwUTAdBgNVHQ4EFgQUnYxk 6PHtjZ1H9Xn1LgZO0KoGqaMwHwYDVR0jBBgwFoAUnYxk6PHtjZ1H9Xn1LgZO0KoG qaMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEArQqOTQNTmJM+ C2qfUX3fOaL23XKCeKbpGIKeaQRJT9s1Zzzdi7TcGgcaok7RGYflhDit50Bao2Hr 3JQIwLBD6YJwXG0LKDbarjzNNFJ1shbMF2dJz675//WtnET88ICYO1mmoYA3DLSF jdjiXqzlJSF/Ehy5VV/Il1TwNrpGeHgabmPZ611Qy6izZ1VzIGcLo81m8uwd5CEj TXFkImzrhryfI3Ax8bt4obYVf1cCWMIWh6VmCEn00SMmJepL1oZ4/sP9VAojzMxd ahoXCn2txFhGOXRcauAypJ0St6H9A5KRaBgryvnCRz5W16JAa0Ne2ZfTUQI0OdA0 ZCpT04u0ng== -----END CERTIFICATE----- -----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzk/0BW72RqZNT ikMHajpJJ9MlcJJMIYmsD04NdVtuVztCTgMnOIX4J6t7hQeSGkKsheodzgioD4fB lbSOXVRK6rE1Os9Ny3CPYIMGyAUOLuvqNm9G7QciS14s6xktnAYqEt669ESQHDq6 XRiJO1CkLryGd8DdgoSZffRM1gwgRSdYdXXGn55UpX0PGZjv6yKcgf3aekmMrzpr SPTCJzy/xQEZmNqwNvh7D+6yuyV3YF/eSjLHXdFRNQR44vTqa2mOY7an32I2hLUK GX8+NsjYf98wXm2/NLcVkxXpKQU9d08+QrvfzuXuFTn9nauJrlcZYTNnMMqGDK5D nLnL0UclAgMBAAECggEAOK0gUOlvbyWiBd/BP/na43PaRBq/UZ/UH6XE8KJ1dOG7 JjYQ8LP6NFPw308hEI+RM3ogZb+9I62jHwnsrnHuRKbFvxMMknT+1YGUWPOQBOXy Nz1u6WettLksw+h/TdHMcEL8YOzvJryCHId9UvKRhP/rKFVrXX3v87G5BPcZZKVm ZhPGkyfOltgi0p2t2P3o60DMXjCWmp/qd4etexT2uIsO55MEs0IJgVvVua9o8rEU TTSgpx/7Qaq/hwfvj6B8UxQSd+mPivoP/+2Vk9mVFWMiwQhRIvJfCGA1BG6HgK2k h2WJ3W+hdBvg2hXzWTfmOL4YrGDhERJ0FQD+rCbdEQKBgQDyVybCHquiR944yYug edY5sGdGflIxWMixZORWsyrq8RWjjOkoZzE6uib93Qc6e44odC3iBaFPAW8DkUFp 3spz2WzkYN+DTFdAG6MrKQAav3am1kaTzdFnLPm/f/PFwYa7hmMQxxb1WSqgG1oA WMYmiO+kKQ4BsJrbkPjk7TtvMwKBgQC9szVL89FO5D9/i/8jM77X4KWjH8vBIUE3 49w2p2+/TEMNHinG5sRVTTIfjOnltb5tGeXiaLcVU5w0eAyYK49irUkh1cblJV4v JtS4KwHLkS+8Fm3Etq3LpEn19rRIjb+AMx7GI9bL2dBAZo++NkNWueB8paKxPo27 Ck2k4hTQRwKBgQDaU7/8VStl6X+AA7vCWOGyWYXBkZ61DHrKrs20engo8AgBr7qD BuzoLrtgLNgNTTEWqwyHO3FHP1Bnk16uZeRZGMIswkW8AXP9sqh/AtIwRtw7lIJD OML2RCPA7iKNwDuFCJ6JiAPcCHgJhHrCIzhpkSbs63vN8/Cf7Wz+uee41wKBgQCw n1mhNQsNtDB29gcAZJ5s6yntXp5cXDUX75zKekzuRPgtD4eAPL5SWcSwYYgpK3V4 qWND0ZGdVrKam6fGStB+5K6xxRQhqBAwQKxQKSLLwYs7SXq8bAYXFAkU7LVg1DGY EIC3pQjJ1iwyugtd47IA3qHoDGQVORPHMUmnmiQc4wKBgQCULcOIT5Ib/TaJvZsL Jr6tQu8nWYk6ZxLvHikWSeOyQtaPTKt3eXw2zJfFvSsKxT59jgiikF3e30Gllwom QdtE4mGKHWycGga0I2KikX5hSFjRhdwH+E2dSnhwWbGZlE2W0IYKSjyFjI6YOl3/ S0JlK9MXpBd/2yhwmkQP0jqkcg== -----END PRIVATE KEY----- tremotesf-2.10.1/src/rpc/test-data/server-certs/000077500000000000000000000000001523411710200214645ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/self-signed-key.pem000066400000000000000000000032541523411710200251610ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCsoI8P37XzjZBB /ImB8A7kVJa6+2J5osJMqg4pYNhhuhIslAgZ87O5gxgb9cbX0jcM1dds0Nnr30/f iiahvXwuVwHtN15MfALPTJ5jObXvVnaNvjEhH7SjXRw5XqvcuZcPONTItEMAn9MX eoEdmPEl1+1zgHoGjOgYP1esaLKjWN0NoTsEOp06HZlQh63GP7ZBXUZ1CT9x6P4w /YTPtgFW7Bbcvcw0SXwee2j3hPfBjOQpUfIvdXWhnn8x5iRTMmoNI3eI475+5mPm WF3hz2DJW1JR8KBQ5MCBZbAqk3j/Xw//PYXI86UzZZ/5eMTeJHjCtn2WjN8uCIcd L0Uq9t2jAgMBAAECggEAAb0R9NjNxRR4zo+5HEXvfcsAstGWtxJtIP526y5dejBa pk2vOgDAOSAKgJ5iwaYm+OoWSkzl56AiXPdHdKgm2ckGecZ+vrsvRHWrcxgLqmVO 5RY4pKWXiBsg2yP9x5R46yoHD6aJ6QK7l1lPIcitgZiha4u9ZlcFr8b2eB16fTq2 JdG6gzvEXluLoRGpgDwcr0Qc0D7flF+ZzaBZQ40A3XOx3ZMmNkOTeRe62l3Uc3Kk Zyl70m8qAdOWr6d5lD7skkhhLmFhdqUBhnOTSdrQFBDW/taLvvgBODFMtt+sCWZe hDuL9wjRSTFKApdoQo9FtPntjzSzbsedhdUv/0XCwQKBgQDXe2Rqpe5nGOYSMb08 x+KCTJf1GDi5XEZLi8c9/71MNs19YRM3XjmoM4UIuD3QrUMpFYEdYFb1nbLjkvyo GuLxgEpvNbq8PLz1n8IZK8SX6Jzf+MpWO+QCP230QITjCSjeUmIR6H/edoyf3hPi 2Jp4H+jFzcjFlrFD+yzpvdg/QQKBgQDNFkYmI4Tgexqzuz5fCXOqVtk5EqyFSmep aq1GjdQPxcbfOBHVqw2Ju4Ne9KkUj6fez345NLfAx0yycvTz9P1hWoJ9QQk9NAef BTGXK9WfSCcvBkLNoBHM9WWZU9t5jjYffzHRIBtS12mrvgRF6IBywEsPZsT20hU4 xXL3h3wH4wKBgQCLLOZxv5SYwEsxazDXxh61m/T119wjVMcOWkyo7bn9NNRKa922 gERrMEN3m1BJrO7Y5UvMhoIECAQX2sMXbK4x33O0Gpbup6hAWRxLl6pVl2djfdRq GCqvjPperSFnZm4gEkPHX0zfieBQDWb4uIAkoC9/Oue3PxplVyDuI7E9QQKBgQCP TS4M/iqdtEcPKRnGzFLuxiqYb4zuwfO29FxI/VcdAkQYuxPzKceW4thDpE70mABj drarAEQZRuk3k43HXPsjaFvfa28qPc3JdpaVG8LzvjXq7MQG6/8kKKAqR+cGg7Eb xwOnLXkKJIyovWg89N2z0Nth0px0Ho1HkufnP9sRowKBgQDC/DVshUjH8ZYHI2FB 9z3Y+OQhg/FRYTJvecGWkemYCwKshAxrHZW1kpWJ7R/tsT2Kc+vYc28Uhty/XrUe W5ycThcMVc5l97bjXw6XYQVgy8yFgm5rOalAMj2v0DD048dNV9K9Snub+jmj9Xxf a3lKbnoDzr8gGHpSfxMTVt/qoA== -----END PRIVATE KEY----- tremotesf-2.10.1/src/rpc/test-data/server-certs/self-signed.pem000066400000000000000000000023311523411710200243660ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDZzCCAk+gAwIBAgIUR1PzQE/hJeUnr5x8Wfj4uT5Md9owDQYJKoZIhvcNAQEL BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLU2VsZiBzaWduZWQw IBcNMjUwNzEyMDExNzAwWhgPMjEyNTA2MTgwMTE3MDBaMFsxCzAJBgNVBAYTAkFV MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz IFB0eSBMdGQxFDASBgNVBAMMC1NlbGYgc2lnbmVkMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEArKCPD9+1842QQfyJgfAO5FSWuvtieaLCTKoOKWDYYboS LJQIGfOzuYMYG/XG19I3DNXXbNDZ699P34omob18LlcB7TdeTHwCz0yeYzm171Z2 jb4xIR+0o10cOV6r3LmXDzjUyLRDAJ/TF3qBHZjxJdftc4B6BozoGD9XrGiyo1jd DaE7BDqdOh2ZUIetxj+2QV1GdQk/cej+MP2Ez7YBVuwW3L3MNEl8Hnto94T3wYzk KVHyL3V1oZ5/MeYkUzJqDSN3iOO+fuZj5lhd4c9gyVtSUfCgUOTAgWWwKpN4/18P /z2FyPOlM2Wf+XjE3iR4wrZ9lozfLgiHHS9FKvbdowIDAQABoyEwHzAdBgNVHQ4E FgQUZegUu6uxrPDismrID+cuXFFqwG0wDQYJKoZIhvcNAQELBQADggEBAFCS/ZQV t+Fl7dWI0vWi9BstI1Z5DJfXa33b/uTzSkcLjwwEeteFV++deuBmd9d4E+7bwOtM PBqWWw77L2lFq9w1tlwMIjK2j2tEzmOSKn9bJfJkTVopDnt8PplMjCBApCLnCuz6 +z0zj9YvvCB5JyWbsprkM+aV29u6XRcFAZNXnwaTdTjJpEl4O5cQtY+Q1sm0nYwx pdE/0eAeoG+OstjDfGOa0cfJCPKOKiTUj0yyNqFdR1D1vCGvKmq1oJnfawr4DwUY b9HsWrYUfRrMTj8o8zznoIMuwkh9WzJfTHF5m7y99TGGLeqzLU4Jfz3sU9zy7aHq q7odkmPj1LI4FHc= -----END CERTIFICATE----- tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/000077500000000000000000000000001523411710200261775ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/root-key.pem000066400000000000000000000032441523411710200304560ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQC08XdgaZYOkT2l fQxB3xhRuB5Dg6tkKkkDyZczkJ4EC1pGF51a+bd8NlHymzxAVwB9rob8GBCSI8Hm BqTSqMtb08eZcX+79l1WuT/SA8hV3A+hpF8tE0dhwe9u0YlH+QONyJl+CQV/g8TV LsL9IQJu6t2KzsEACfeyhr5sXnSBahwclynA+mtM6WneDVWMAqfvYm5CAhZqzSrU cdABb8Dfy2aV2rZWTGbtYD1y13dF7fSBI1Zu7kj58Pn2cK1Vtip/YEZoz0keboPT /XXVmJER9syFWGmxM6Wsto0OOC4VFk54jS7FOgdTtVoL0ZW3kKaxI4DrdOqpJQs4 feS0vYLhAgMBAAECgf8PWJWzR94cwMFHZEjmGrlEhYnVLtYPJbgnn3+DIjeYlWBQ VByb4GqVLusIointXqJqC+WeHx+kMLPU7WFYcSRMH7ILAGfJLdFQ4qmNzZrcjltw CxYMinoVsNxDehbMMpXtEfIji1TNrZs/pRQegI4devzkTfqXf/lYqYhXRESp3J1B VINwdCiFucHEhAkMmW/711ggtuaqFCOImcThpBpOft7W1lRtO9Z6UYVTSmvMkFB4 X2j0/5GofSzK1HL6WLxtTCDJsfHcQxBS/9k+Rekz6/dQnVgADTtN4DvLGbUllGKO pKfrgDGjH5pFCDHeKgX5hL3BDrhxuyhukcphnsECgYEA7nQJzUA8XxCSQVRZ0dVX vNxFcy3dBwLflKWJfAIZ/PX3TyIA+Wtu0GL0V7Svo6737bpfsfkRD9Hapsu/Ir/S sfiGq2KhYlU3Ou4B2e7JU2cO49iwlSEUyI7Ilpapay7A12CPN2bw63Rb/Eh/N/iK IRf0qLYVWxLUGPJL+zE6q0ECgYEAwkIPELA+n3UX1qAWmHsAWihjVQocP+4cv5zc Qddfjj6KK32WrrtnxIRCv9ji8VnVSnxfZl03b1NLVXr14fAnCNr0vOVYAOwXg0eI jaZfP9p2643DiRbe07kBwEDBqCFP10TLS8ckBLRvkO+L0j00O/EERHyCd7YrrAdl JMYPD6ECgYBCXYlc1sP2sWYDSLa27+m7ZpLtu5YInYQcmvXozazt+ocaPxyGTqBI 30GiJ2e65reaMoTvw6I8BOwWAB7yTPEXF1Rj3s+LzqvQeu2I+iyOSeCbCXQcDVj7 eMHbJ5N/gUOqrfUuNjhXT8tKK+M8cLABBenSCttmvZbKWqVLBCiQAQKBgAEJlUcD ifIUEAKHbFd4ILJakN09ZpU40lJ7pfl8CviZgOdmjk10lsNH6YtYvy2Gy0rQizni uY8QpNBaDcIdJDg54yC3INcwa5e55BLNlqiipAvx/99Vje8Xh9jc/6vEMcb2iRdo gtq7k/T0Moz24raHPPyYpaG6CVWr3HBr1lzBAoGAQaow5Q6TL8ucFP8PsMpUzj3L y4ym++TQjxTu3Mcvgss33BZUE9s4pJzRxBg6kFK64Hv17zRvK7DAYsje9SIKnBG3 /MO4mmUDXKcW7pJiL1+KclNMeVJqxNc9j04h/UAv8lgAy2z8P16nnQ8I9jBWlcJr 4LsSucEOB/JHviHvIdk= -----END PRIVATE KEY----- tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/root.pem000066400000000000000000000023151523411710200276660ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgIUS7MCuQY+NU78pfE67ep0q5tWZSMwDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAeFw0yMjEyMTEyMjE1MTJaFw0zMjEyMDgyMjE1 MTJaMD8xCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxGTAXBgNV BAoMEFJvb3QgY2VydGlmaWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQC08XdgaZYOkT2lfQxB3xhRuB5Dg6tkKkkDyZczkJ4EC1pGF51a+bd8NlHy mzxAVwB9rob8GBCSI8HmBqTSqMtb08eZcX+79l1WuT/SA8hV3A+hpF8tE0dhwe9u 0YlH+QONyJl+CQV/g8TVLsL9IQJu6t2KzsEACfeyhr5sXnSBahwclynA+mtM6Wne DVWMAqfvYm5CAhZqzSrUcdABb8Dfy2aV2rZWTGbtYD1y13dF7fSBI1Zu7kj58Pn2 cK1Vtip/YEZoz0keboPT/XXVmJER9syFWGmxM6Wsto0OOC4VFk54jS7FOgdTtVoL 0ZW3kKaxI4DrdOqpJQs4feS0vYLhAgMBAAGjUzBRMB0GA1UdDgQWBBRbB9gkHAvb S7FtTeDPTDF9OGephjAfBgNVHSMEGDAWgBRbB9gkHAvbS7FtTeDPTDF9OGephjAP BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgtb0toT5Ww/e9ojH0 HxfXNLdCWMsEk7XiJc5buTe+HecSvpHnFHeZ8IE/plNJyExYRTHbF7UYTFjw1ENG XTGA427PrUdhPHm7T+5m87HQCxXmd8Js4h1Na8mL/8xNU6wcN+K15HJsInhAAb2/ SCuE9gK4H0KTIycYz0yMNympGnzmpKpM4fod3Gc7U/jmiPjc4VgWl/xq1oYSGjbE vfE6GiWNxOO9HMq7JennVevD6DIrnWM6MQZQJnrXEWBrWzxpNo3bKIDWbpIV/2Dh iAe91Y3pAXwoN96XzWGAEtAYmjFaxxjyTm+ZFwNzfNCUDdrsm6MbogOXNYp84d4Z stYN -----END CERTIFICATE----- tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/with-intermediate/000077500000000000000000000000001523411710200316225ustar00rootroot00000000000000intermediate-key.pem000066400000000000000000000032501523411710200355060ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/with-intermediate-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCdS64IBN1PfqUo 9dJlr8ez68iQgyjKMz0MIWGr3cGlBZifDQ209Wm8OouYK5YZ0J20MVGOiXBItA6z NefMaOAILJviGm/Y22aaoiYfe7ON/bkTCB9qjiO1f1pusUjo6up6yEV6i4VEBH92 QkKwpxJoixbYfAlrJmA1Ji+ap/4KLiJ7BGDTmy7GbEjr9bihPXp/BWO1AhiXuHPW mRnR97WzWtX8Es2iPDBI+3RY8DlgJYOLAGzMbU5uNOis6E+GAT0zsOGaMdp2YV9f OCj3HBwXDS9JHVa+8PIxxyp6vF40vvzDmg4xOQk9EqYnO5r5Zon5hJgOkXOFVxyK J1UCXauLAgMBAAECggEAJSoW7e0cTyaIpmNieUUthZy9kCt1LUUq9N1EQwPLt45Z b6b5aJqdh2w7xFH0DphkUolekDN39K2aVYx0fq7+09+aWzNM3uuD1CBgqoqjM3bR ziXaqmX4GO+EseewZtvuV+FOzTP+4kG/zfS85hkrd8j5NmAkJ41zKMujbsKnesbp qOwuzYEQsZtoVFZC0ErampDpcVRQHTtw3Pg24A9uK0HHgoUfsukq7Zs7qlPeNUMz yrpZoUSB0O3KFtfEKQdo3+bW5roY4RnD8sLMb3wqgIv6Quh0Xvvz62Pfowsh/+zv QZYOW9i0JZzMt/vIOKrFhC2uIrXK7IIdkwVly7NOWQKBgQDX80TWc8Iez9VZwoVg LDAgb5BgHubrZ8FJrqw5INFvOwbMrf55AOXXz4yQXKbR7sMxBA8qR0qF7KyQD6n2 NdilbQfLqeR4tSTrEcuI10nt2sBH7D9SrIqokx6DE2O2tusJNgJUZEbvl6Yvlrbh rcaQSM8Hp014047hqVvvEyCQGQKBgQC6d6Xi6XfMJLbLMObfvB+QciYHcVdwYjUT 8iByFX1MpnBPoUcfolszWp8z6WGOxZ6VQQxZ2H/isd01P6mtykaiYoTERkWq6JkY IeXjqe7l5tGQpH/shajwytiD+b8waf+DDmbZDqMfrwN1b4TtVTKK6fu/kIpfha49 Qa0nmtI9QwKBgQC960e+Jv4xJO1ozIeKRjJpRFTABE1n+M7n40O51l/6p/nZ5qfn eV+qhfEEQUfBV2+42u4Pa7VhxP0akXwTedW33wS9HARAGGeIiRTsmxJ8qiby2hXy uqYTjZeHBXVrisF4T1SLLcmpC0Dvl2ftYGxCFpZlD8E1ozGEyvJ8duDreQKBgEB6 eyxZ2wkGcQcWI9+d57RFJI1SThAYhY4eHcYVVDybGR8P/84akDrTzVVgEFIUV9/4 nFK9IIu9QWbWWtAzr/Ua6BoOyqxsvfOwVRRCgM+S30iK+YlKNr79BZ2tUMVnAenJ oxrPiuy70hBbvbVrzoxmmJ0HWDHrwfKAsWWOjcCNAoGAbMjRxFsWMFXYjhGT1gPg YVeoQDAb4aHXPe3dGeGu2wV/Qa+JhffbyQ1xFRxBCpn7xsD14seBPjPJ8/6q7Bd2 uvomhtiOU6tRSjzF4Wf6cl0YEeyWjQDc0a+69V9QC6ukiZG0qYInEGDvZr0gzErH bdDXz35GocEGm3+5WbjcdC0= -----END PRIVATE KEY----- intermediate.pem000066400000000000000000000023711523411710200347230ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/with-intermediate-----BEGIN CERTIFICATE----- MIIDfjCCAmagAwIBAgIUce9TKD/32uO/qbPHyxGSRc4AN9EwDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAgFw0yNTA3MTIwMjIyMjBaGA8yMTI1MDYxODAy MjIyMFowXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNV BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEVMBMGA1UEAwwMSW50ZXJtZWRp YXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnUuuCATdT36lKPXS Za/Hs+vIkIMoyjM9DCFhq93BpQWYnw0NtPVpvDqLmCuWGdCdtDFRjolwSLQOszXn zGjgCCyb4hpv2NtmmqImH3uzjf25Ewgfao4jtX9abrFI6OrqeshFeouFRAR/dkJC sKcSaIsW2HwJayZgNSYvmqf+Ci4iewRg05suxmxI6/W4oT16fwVjtQIYl7hz1pkZ 0fe1s1rV/BLNojwwSPt0WPA5YCWDiwBszG1ObjTorOhPhgE9M7DhmjHadmFfXzgo 9xwcFw0vSR1WvvDyMccqerxeNL78w5oOMTkJPRKmJzua+WaJ+YSYDpFzhVcciidV Al2riwIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTZoNsJWr26 I/Dii5d0VekeGcxGbDAfBgNVHSMEGDAWgBRbB9gkHAvbS7FtTeDPTDF9OGephjAN BgkqhkiG9w0BAQsFAAOCAQEAcyvMww7Unahi3tJlbNhyrPvXjbbr0amL0KMvAycf +pT7PFb9aHu+gwqv9wbhAhu79Wr0XQ+hO2emzydGAnqjvTRIC6Q2ODFiosmAPlRu qqAhQ1A7j2gwL9Ylqho6Sdr8ozCflQHZKG8bGxQMBZpI6E7a/CHzM1YUkhbxhOGg jr7zSjISr+gM+scTdA0SmqHnQuvjV0IsiyrRzixz0DPi8heZydH8YpOYahliT6IB slMCvWF+pdEWHo/p9kaTVy9pPhE4LQs3O8TVP2AvfM01YAQMSG+O34Wwk4+XHN4g KUe7CeADDKS4DwWgvnAfyqKuoPxZOsn6mE1H4HRR5not1w== -----END CERTIFICATE----- leaf-key.pem000066400000000000000000000032501523411710200337430ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/with-intermediate-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcVQ4rcZRpNlXM B6TW4atVuaZvG9e9djw1/rRiTd13XA5CTjv4pxeEVj1vEZljBczTmsoQKA/H4QGq Y5ClfCgvt552f5ndd/paouk6R7yxTI3pRAepEQ8wWTZE3Exz/3cBcSav0Oo3xAM/ OPEUgGsvgUPV47ZefiQXInnqJLETfoIry+bi34mTo0gat3+vSdncgTu+/3nTzV1Y ygkUHXap0uh236Ldq+h/Gg1lh0xR3uS9p87WkspaRo4RNTynrBUnpxB/or0ngbHS D/WnZAlmm1hKc5Kpi72bqgj3DKPCU3FdtpMQxK1Y24RCIXhArm6qH3baYxD47qIo tPQ+MDvxAgMBAAECggEAOR4tmbKMjEfjLwzOPUxsS8FgdY5Mu9dyXXcFkTRpVL9/ DM5WOqwqawqmS2xPDL4crsCCYHTaPnFEqsS740KP8mrSABstKp4nPybbelnNf1L+ uPmDWN9MxjZiXLCVez6/PZb1Nf/dMA623sVmre9qmfqTONga110xAvRTkZDofMpE NvqW6patTVsELQWcXBateUZn7eGvOko/NyTtJOAjC9ghcTD4h0Jrudb4edIeG5x3 G+qyx53u357exrsqGOZdABcCiNzIRtvsAPvapIyrtomNS7Svr2qemN2Ntqz2XZuw /q+OIH4Y4dJqU+xRTwLodYJlKJCjnRMQvQY8LkAbXwKBgQD4V/N8uBrX/lfEsYpB ciApJHT88Eg0iVrb+ReUTQcD6mxR4FCLbx1ToPK+vdDJyZc63EysMohRKTolXfqD lXcMMUw/P3vBBPkdNdTVdd45EDEjLo4dA4xKTHp+i3K1eywjR7b1mwdq1XawbA8y kjxxYqLOmMj9wWszb5vICcfkCwKBgQDjIAZ26XoKT7aMn9rKxr/X4bh2pUjMCbcI /wSQytXeDj33wAcbGuJn3tQiSkgVjHPkjMJGEZkbxLR3JDIwfhxTJLgf+djenzBt LauypkvGY7Ny2MSgtD18V8ehMysiWjLkD5YUqcYRq89EOWvAM/XxAetpu017J1Hr 5IR+CjpBcwKBgQCfPJXNdKcRs+5HGqfRywHAH7Y7sT47IY922MM7viAvbJLD9Rbw MPEW/G5py4AiwvYLMdKf307064BzEU1eLepFkR6Yt3X8j9nD1w5/SLKS2EUJkAJj rDC6O0uX9IY6w53LiWJRHXSmFfWirccY4BeNhHdsDR9jRjAcrQWwQEVBfQKBgGX3 BK/x1XY3ny62vVT3sTzL+zJYtbvUSLXjNzuBtE4CetN1BDuo8CfNo9LZIk96RLVA dB9c2y3aO2KZf2bB1xmEjJzeX/xh3y3EqSaE/dPNAsz2gB/dixT9m4PFIwJDGEHG tYwFRAS+60gntzYMncdbI0LOcdRuLJsIdmrXUJv9AoGBALmMzXw9ioiNcAAOkeGB ri5TqNWYzVajv7kUpUP0Hrl9HIPWOyr2wzfCGhM3H3JN38KN9APMEfkIlkXsUjCR GBY1HRk3sXD/5v0n4QXVShI+JsJ9aaLn+XTMbgbC3O9cllq5asdHwMGDLUmVB8xq WLiirZI31Z1G85jZw5kp/G4r -----END PRIVATE KEY----- leaf-with-intermediate-and-root.pem000066400000000000000000000073031523411710200403220ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/with-intermediate-----BEGIN CERTIFICATE----- MIIDgjCCAmqgAwIBAgIUNqw0dSZcydLEs9+lFxQG4LO06GcwDQYJKoZIhvcNAQEL BQAwXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEVMBMGA1UEAwwMSW50ZXJtZWRpYXRl MCAXDTI1MDcxMjAyMjMyNFoYDzIxMjUwNjE4MDIyMzI0WjBUMQswCQYDVQQGEwJB VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMQ0wCwYDVQQDDARMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEA3FUOK3GUaTZVzAek1uGrVbmmbxvXvXY8Nf60Yk3dd1wOQk47+KcX hFY9bxGZYwXM05rKECgPx+EBqmOQpXwoL7eedn+Z3Xf6WqLpOke8sUyN6UQHqREP MFk2RNxMc/93AXEmr9DqN8QDPzjxFIBrL4FD1eO2Xn4kFyJ56iSxE36CK8vm4t+J k6NIGrd/r0nZ3IE7vv95081dWMoJFB12qdLodt+i3avofxoNZYdMUd7kvafO1pLK WkaOETU8p6wVJ6cQf6K9J4Gx0g/1p2QJZptYSnOSqYu9m6oI9wyjwlNxXbaTEMSt WNuEQiF4QK5uqh922mMQ+O6iKLT0PjA78QIDAQABo0IwQDAdBgNVHQ4EFgQUf3i0 XgTSU0eHZSNPQS/+8KVMzKQwHwYDVR0jBBgwFoAU2aDbCVq9uiPw4ouXdFXpHhnM RmwwDQYJKoZIhvcNAQELBQADggEBAHKjiIw5tPN+4S3RjR/JAGaCYpKde/9hG8z7 d0dd4DzU//65Z50aN6VR5dkWzxy/KJzUedvKY2bwE9Oxx2k7vF24BI11B9h/mpRd wG844l44uZZq6u/J1UkNORGnWicl5r5Td10gjY4fvqrw5ZpvLWfIjoJmNwS5MPws B75130RihUOrlZU/TdLIMa8D/LRTSLdOGFR5Y60XaENqU+/IGkFblc1JNcjKis8C IZNs/TbUy+8s48FPXV0HJoMiV6ViTM3kEtSCZZE3ZccXCsoiwSjdIJpKsIg4deNT PdFrYlXN0zdxcZgd/WQYl36rzxDKwCAfP4BbDKYaw+biS1mAV44= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDfjCCAmagAwIBAgIUce9TKD/32uO/qbPHyxGSRc4AN9EwDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAgFw0yNTA3MTIwMjIyMjBaGA8yMTI1MDYxODAy MjIyMFowXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNV BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEVMBMGA1UEAwwMSW50ZXJtZWRp YXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnUuuCATdT36lKPXS Za/Hs+vIkIMoyjM9DCFhq93BpQWYnw0NtPVpvDqLmCuWGdCdtDFRjolwSLQOszXn zGjgCCyb4hpv2NtmmqImH3uzjf25Ewgfao4jtX9abrFI6OrqeshFeouFRAR/dkJC sKcSaIsW2HwJayZgNSYvmqf+Ci4iewRg05suxmxI6/W4oT16fwVjtQIYl7hz1pkZ 0fe1s1rV/BLNojwwSPt0WPA5YCWDiwBszG1ObjTorOhPhgE9M7DhmjHadmFfXzgo 9xwcFw0vSR1WvvDyMccqerxeNL78w5oOMTkJPRKmJzua+WaJ+YSYDpFzhVcciidV Al2riwIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTZoNsJWr26 I/Dii5d0VekeGcxGbDAfBgNVHSMEGDAWgBRbB9gkHAvbS7FtTeDPTDF9OGephjAN BgkqhkiG9w0BAQsFAAOCAQEAcyvMww7Unahi3tJlbNhyrPvXjbbr0amL0KMvAycf +pT7PFb9aHu+gwqv9wbhAhu79Wr0XQ+hO2emzydGAnqjvTRIC6Q2ODFiosmAPlRu qqAhQ1A7j2gwL9Ylqho6Sdr8ozCflQHZKG8bGxQMBZpI6E7a/CHzM1YUkhbxhOGg jr7zSjISr+gM+scTdA0SmqHnQuvjV0IsiyrRzixz0DPi8heZydH8YpOYahliT6IB slMCvWF+pdEWHo/p9kaTVy9pPhE4LQs3O8TVP2AvfM01YAQMSG+O34Wwk4+XHN4g KUe7CeADDKS4DwWgvnAfyqKuoPxZOsn6mE1H4HRR5not1w== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgIUS7MCuQY+NU78pfE67ep0q5tWZSMwDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAeFw0yMjEyMTEyMjE1MTJaFw0zMjEyMDgyMjE1 MTJaMD8xCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxGTAXBgNV BAoMEFJvb3QgY2VydGlmaWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQC08XdgaZYOkT2lfQxB3xhRuB5Dg6tkKkkDyZczkJ4EC1pGF51a+bd8NlHy mzxAVwB9rob8GBCSI8HmBqTSqMtb08eZcX+79l1WuT/SA8hV3A+hpF8tE0dhwe9u 0YlH+QONyJl+CQV/g8TVLsL9IQJu6t2KzsEACfeyhr5sXnSBahwclynA+mtM6Wne DVWMAqfvYm5CAhZqzSrUcdABb8Dfy2aV2rZWTGbtYD1y13dF7fSBI1Zu7kj58Pn2 cK1Vtip/YEZoz0keboPT/XXVmJER9syFWGmxM6Wsto0OOC4VFk54jS7FOgdTtVoL 0ZW3kKaxI4DrdOqpJQs4feS0vYLhAgMBAAGjUzBRMB0GA1UdDgQWBBRbB9gkHAvb S7FtTeDPTDF9OGephjAfBgNVHSMEGDAWgBRbB9gkHAvbS7FtTeDPTDF9OGephjAP BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgtb0toT5Ww/e9ojH0 HxfXNLdCWMsEk7XiJc5buTe+HecSvpHnFHeZ8IE/plNJyExYRTHbF7UYTFjw1ENG XTGA427PrUdhPHm7T+5m87HQCxXmd8Js4h1Na8mL/8xNU6wcN+K15HJsInhAAb2/ SCuE9gK4H0KTIycYz0yMNympGnzmpKpM4fod3Gc7U/jmiPjc4VgWl/xq1oYSGjbE vfE6GiWNxOO9HMq7JennVevD6DIrnWM6MQZQJnrXEWBrWzxpNo3bKIDWbpIV/2Dh iAe91Y3pAXwoN96XzWGAEtAYmjFaxxjyTm+ZFwNzfNCUDdrsm6MbogOXNYp84d4Z stYN -----END CERTIFICATE----- leaf-with-intermediate.pem000066400000000000000000000047661523411710200366130ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/with-intermediate-----BEGIN CERTIFICATE----- MIIDgjCCAmqgAwIBAgIUNqw0dSZcydLEs9+lFxQG4LO06GcwDQYJKoZIhvcNAQEL BQAwXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEVMBMGA1UEAwwMSW50ZXJtZWRpYXRl MCAXDTI1MDcxMjAyMjMyNFoYDzIxMjUwNjE4MDIyMzI0WjBUMQswCQYDVQQGEwJB VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMQ0wCwYDVQQDDARMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEA3FUOK3GUaTZVzAek1uGrVbmmbxvXvXY8Nf60Yk3dd1wOQk47+KcX hFY9bxGZYwXM05rKECgPx+EBqmOQpXwoL7eedn+Z3Xf6WqLpOke8sUyN6UQHqREP MFk2RNxMc/93AXEmr9DqN8QDPzjxFIBrL4FD1eO2Xn4kFyJ56iSxE36CK8vm4t+J k6NIGrd/r0nZ3IE7vv95081dWMoJFB12qdLodt+i3avofxoNZYdMUd7kvafO1pLK WkaOETU8p6wVJ6cQf6K9J4Gx0g/1p2QJZptYSnOSqYu9m6oI9wyjwlNxXbaTEMSt WNuEQiF4QK5uqh922mMQ+O6iKLT0PjA78QIDAQABo0IwQDAdBgNVHQ4EFgQUf3i0 XgTSU0eHZSNPQS/+8KVMzKQwHwYDVR0jBBgwFoAU2aDbCVq9uiPw4ouXdFXpHhnM RmwwDQYJKoZIhvcNAQELBQADggEBAHKjiIw5tPN+4S3RjR/JAGaCYpKde/9hG8z7 d0dd4DzU//65Z50aN6VR5dkWzxy/KJzUedvKY2bwE9Oxx2k7vF24BI11B9h/mpRd wG844l44uZZq6u/J1UkNORGnWicl5r5Td10gjY4fvqrw5ZpvLWfIjoJmNwS5MPws B75130RihUOrlZU/TdLIMa8D/LRTSLdOGFR5Y60XaENqU+/IGkFblc1JNcjKis8C IZNs/TbUy+8s48FPXV0HJoMiV6ViTM3kEtSCZZE3ZccXCsoiwSjdIJpKsIg4deNT PdFrYlXN0zdxcZgd/WQYl36rzxDKwCAfP4BbDKYaw+biS1mAV44= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDfjCCAmagAwIBAgIUce9TKD/32uO/qbPHyxGSRc4AN9EwDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAgFw0yNTA3MTIwMjIyMjBaGA8yMTI1MDYxODAy MjIyMFowXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNV BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEVMBMGA1UEAwwMSW50ZXJtZWRp YXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnUuuCATdT36lKPXS Za/Hs+vIkIMoyjM9DCFhq93BpQWYnw0NtPVpvDqLmCuWGdCdtDFRjolwSLQOszXn zGjgCCyb4hpv2NtmmqImH3uzjf25Ewgfao4jtX9abrFI6OrqeshFeouFRAR/dkJC sKcSaIsW2HwJayZgNSYvmqf+Ci4iewRg05suxmxI6/W4oT16fwVjtQIYl7hz1pkZ 0fe1s1rV/BLNojwwSPt0WPA5YCWDiwBszG1ObjTorOhPhgE9M7DhmjHadmFfXzgo 9xwcFw0vSR1WvvDyMccqerxeNL78w5oOMTkJPRKmJzua+WaJ+YSYDpFzhVcciidV Al2riwIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTZoNsJWr26 I/Dii5d0VekeGcxGbDAfBgNVHSMEGDAWgBRbB9gkHAvbS7FtTeDPTDF9OGephjAN BgkqhkiG9w0BAQsFAAOCAQEAcyvMww7Unahi3tJlbNhyrPvXjbbr0amL0KMvAycf +pT7PFb9aHu+gwqv9wbhAhu79Wr0XQ+hO2emzydGAnqjvTRIC6Q2ODFiosmAPlRu qqAhQ1A7j2gwL9Ylqho6Sdr8ozCflQHZKG8bGxQMBZpI6E7a/CHzM1YUkhbxhOGg jr7zSjISr+gM+scTdA0SmqHnQuvjV0IsiyrRzixz0DPi8heZydH8YpOYahliT6IB slMCvWF+pdEWHo/p9kaTVy9pPhE4LQs3O8TVP2AvfM01YAQMSG+O34Wwk4+XHN4g KUe7CeADDKS4DwWgvnAfyqKuoPxZOsn6mE1H4HRR5not1w== -----END CERTIFICATE----- tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/with-intermediate/leaf.pem000066400000000000000000000023751523411710200332430ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDgjCCAmqgAwIBAgIUNqw0dSZcydLEs9+lFxQG4LO06GcwDQYJKoZIhvcNAQEL BQAwXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEVMBMGA1UEAwwMSW50ZXJtZWRpYXRl MCAXDTI1MDcxMjAyMjMyNFoYDzIxMjUwNjE4MDIyMzI0WjBUMQswCQYDVQQGEwJB VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMQ0wCwYDVQQDDARMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEA3FUOK3GUaTZVzAek1uGrVbmmbxvXvXY8Nf60Yk3dd1wOQk47+KcX hFY9bxGZYwXM05rKECgPx+EBqmOQpXwoL7eedn+Z3Xf6WqLpOke8sUyN6UQHqREP MFk2RNxMc/93AXEmr9DqN8QDPzjxFIBrL4FD1eO2Xn4kFyJ56iSxE36CK8vm4t+J k6NIGrd/r0nZ3IE7vv95081dWMoJFB12qdLodt+i3avofxoNZYdMUd7kvafO1pLK WkaOETU8p6wVJ6cQf6K9J4Gx0g/1p2QJZptYSnOSqYu9m6oI9wyjwlNxXbaTEMSt WNuEQiF4QK5uqh922mMQ+O6iKLT0PjA78QIDAQABo0IwQDAdBgNVHQ4EFgQUf3i0 XgTSU0eHZSNPQS/+8KVMzKQwHwYDVR0jBBgwFoAU2aDbCVq9uiPw4ouXdFXpHhnM RmwwDQYJKoZIhvcNAQELBQADggEBAHKjiIw5tPN+4S3RjR/JAGaCYpKde/9hG8z7 d0dd4DzU//65Z50aN6VR5dkWzxy/KJzUedvKY2bwE9Oxx2k7vF24BI11B9h/mpRd wG844l44uZZq6u/J1UkNORGnWicl5r5Td10gjY4fvqrw5ZpvLWfIjoJmNwS5MPws B75130RihUOrlZU/TdLIMa8D/LRTSLdOGFR5Y60XaENqU+/IGkFblc1JNcjKis8C IZNs/TbUy+8s48FPXV0HJoMiV6ViTM3kEtSCZZE3ZccXCsoiwSjdIJpKsIg4deNT PdFrYlXN0zdxcZgd/WQYl36rzxDKwCAfP4BbDKYaw+biS1mAV44= -----END CERTIFICATE----- tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/without-intermediate/000077500000000000000000000000001523411710200323525ustar00rootroot00000000000000leaf-key.pem000066400000000000000000000032501523411710200344730ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/without-intermediate-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDL4WbASfef9Ixf MNXhSJxug05rL0L+K3r1RYiIPbWl1hC3eaIcPv9eaN7ug1WJUp4xf4WPwR6H4msF T4rAHvq3ITrM4kL7qtYfrI7XlCZUXVZ8EVVp0pxrQvkFqvQPG9PSV13pjLUpDEkA hzuBuLOo9dqxtSy1v8b7ejDK3qJhJb6ZiAWSLNt437ChiTaMhfufH3JJ72ym2oro jj0yGhfmnDf0Y35hV/EHbxTKgexdsn489z7W2Rwwh8wqU/H5zytL7BLIEEzwDwmc V/QrjnsQ2rJ8nzysvA0onKp2eO/HczzT9XbxWB9o2h3HPO2EFHT2Uo6tBEQjuEbi rBvYZyajAgMBAAECggEAF5sNbo4h4Z9K8WqSGPdzRUhmt/pM42jdX7ChUkTy1zSl 0th+cI7Kun+h8DO8n521Oz3slOxjQJBym7bN9digZruuNzKrVsA88oXkNAmvNm1o Kg1HumzsN28VrhZRE77BE8WR1gy4nPwTJ8CAn1BnXNS6cYDsoE6IF23aYGLOojir YdvqyA3RgtZYBUDhh+yaqOBpWdNAMaUE+h9UB8CVnrv8S6ahTqmXtUBkpZfH3toX D8zBtqW054UGn0T8vwiI7IOxL7CubTgG0FrVGCfFp9QwaQofFu0WCwZgIu3qcPac hNMTWJk0/mwchlU2Q+6tbkiQcbH6ngL7of47ZO2moQKBgQD8dl1esPh1RlnAxbsI Uq76njVLc2vVx8/bE/sVppXUC87hErocO7t2e3tXLmG981DuFEi/C8cS/Yvw/r3q 2gIWXY6ugv3TI/gRpXesTqJPm4enLyqa2qiTpLbokQOBM4yk19yt2KnelK9TeqEP r/5ABZ8BeprLPDpeAQgScxV0rQKBgQDOvMNkwVYVeaWNw3vm8fl2w9BNFphNsry7 dwtSfXYMjFePkp4D6IQblLhvAIOXDEE+2vTkWd313CVZNBnflUycYqu9fhfNZu3x VsUbXRiB1vdFJ5WON6/734OtVwRuwCD6HMS0ut8vs+B35bfqeaN0EHt0j0+NJaZW 1SmXHxwijwKBgHwcTEZyxZIRpanpCstKqo62HAkdktOjyQEwT20TWfDGZUusgyXC D6Rti/MN8CsLZKvF+QVCavlg/9zXvtGnnwMTt0L/Tnzv39H9AFoKxixQj12XCGDc QadjgC/QqmJnzypynCAytT+g5JgrXuZxZJJgsQxvE43568+uviMjxFZ5AoGASRZA uaEEDumHlruSVQ4clFkbjfU7+NQElv25+fqyvjLrTa2WwWj1M077ePqkisThWBhO VWBT8PRYG9Yog3ujrvHPJ1mpjosh+Z0kZ80cueJGXqNk4vda9t+JDMHjpke0INgE zqP1/UZJR/nlcDZ6i7C8BHQZupuh17baUjvEVY8CgYEA/EwtycnwJ2yHcvfrFwnZ T8osXumOgx+cgbel+5vPm4yh9OKUdJqQQd2ZeNNoDuTRklhd/GlxshlnV46ggPhf ju5Da//nfxUlpTrWiXycT74Xv+zZK9x0eU3NvdS/Qh8d0L4bQPp1/DTVSfSUHqvF taanVcfz6mzYRM6UXOALUH4= -----END PRIVATE KEY----- leaf-with-root.pem000066400000000000000000000046421523411710200356450ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/without-intermediate-----BEGIN CERTIFICATE----- MIIDZTCCAk2gAwIBAgIUFxezdbNV3ujmOrzcFLe0hokeDyowDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAgFw0yNTA3MTIwMTE5MjNaGA8yMTI1MDYxODAx MTkyM1owVDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNV BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDENMAsGA1UEAwwETGVhZjCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMvhZsBJ95/0jF8w1eFInG6DTmsv Qv4revVFiIg9taXWELd5ohw+/15o3u6DVYlSnjF/hY/BHofiawVPisAe+rchOszi Qvuq1h+sjteUJlRdVnwRVWnSnGtC+QWq9A8b09JXXemMtSkMSQCHO4G4s6j12rG1 LLW/xvt6MMreomElvpmIBZIs23jfsKGJNoyF+58fcknvbKbaiuiOPTIaF+acN/Rj fmFX8QdvFMqB7F2yfjz3PtbZHDCHzCpT8fnPK0vsEsgQTPAPCZxX9CuOexDasnyf PKy8DSicqnZ478dzPNP1dvFYH2jaHcc87YQUdPZSjq0ERCO4RuKsG9hnJqMCAwEA AaNCMEAwHQYDVR0OBBYEFFci2/86lMwscCDJtjnvLC4RHawEMB8GA1UdIwQYMBaA FFsH2CQcC9tLsW1N4M9MMX04Z6mGMA0GCSqGSIb3DQEBCwUAA4IBAQA40eilPuMJ t30CWLmVgSN1HVXZe/TqD+bARf/0T95YJPs+vraYdeuOpa4qxlWQttKiBBOJN/4g h1SCikD6U4fWh1sQ0MszDjzk6dO9qXDy/Z/AYZhtO4P0YXgkxUDCv2CK8TANQrXA lpdw5PDDij5dWLhqPW8t5qElPI90197Dfr7i0yTR5PExOvs6heBX99xApmTSXBJ7 BXb8riu2vGxLn5YdY6FInI2ZbRnVgFJHxoGLolJ9N87tS2NjA9mXOn9vQdiEFHsU xpVJBc+fARh09tmmqFLlT1HPhRbkBRucIfY3IWjlcvCGRdEYdjtVr3ae/40Txpq7 qXgnR8gtSQva -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgIUS7MCuQY+NU78pfE67ep0q5tWZSMwDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAeFw0yMjEyMTEyMjE1MTJaFw0zMjEyMDgyMjE1 MTJaMD8xCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxGTAXBgNV BAoMEFJvb3QgY2VydGlmaWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQC08XdgaZYOkT2lfQxB3xhRuB5Dg6tkKkkDyZczkJ4EC1pGF51a+bd8NlHy mzxAVwB9rob8GBCSI8HmBqTSqMtb08eZcX+79l1WuT/SA8hV3A+hpF8tE0dhwe9u 0YlH+QONyJl+CQV/g8TVLsL9IQJu6t2KzsEACfeyhr5sXnSBahwclynA+mtM6Wne DVWMAqfvYm5CAhZqzSrUcdABb8Dfy2aV2rZWTGbtYD1y13dF7fSBI1Zu7kj58Pn2 cK1Vtip/YEZoz0keboPT/XXVmJER9syFWGmxM6Wsto0OOC4VFk54jS7FOgdTtVoL 0ZW3kKaxI4DrdOqpJQs4feS0vYLhAgMBAAGjUzBRMB0GA1UdDgQWBBRbB9gkHAvb S7FtTeDPTDF9OGephjAfBgNVHSMEGDAWgBRbB9gkHAvbS7FtTeDPTDF9OGephjAP BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgtb0toT5Ww/e9ojH0 HxfXNLdCWMsEk7XiJc5buTe+HecSvpHnFHeZ8IE/plNJyExYRTHbF7UYTFjw1ENG XTGA427PrUdhPHm7T+5m87HQCxXmd8Js4h1Na8mL/8xNU6wcN+K15HJsInhAAb2/ SCuE9gK4H0KTIycYz0yMNympGnzmpKpM4fod3Gc7U/jmiPjc4VgWl/xq1oYSGjbE vfE6GiWNxOO9HMq7JennVevD6DIrnWM6MQZQJnrXEWBrWzxpNo3bKIDWbpIV/2Dh iAe91Y3pAXwoN96XzWGAEtAYmjFaxxjyTm+ZFwNzfNCUDdrsm6MbogOXNYp84d4Z stYN -----END CERTIFICATE----- leaf.pem000066400000000000000000000023251523411710200337070ustar00rootroot00000000000000tremotesf-2.10.1/src/rpc/test-data/server-certs/signed-with-custom-root/without-intermediate-----BEGIN CERTIFICATE----- MIIDZTCCAk2gAwIBAgIUFxezdbNV3ujmOrzcFLe0hokeDyowDQYJKoZIhvcNAQEL BQAwPzELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEZMBcGA1UE CgwQUm9vdCBjZXJ0aWZpY2F0ZTAgFw0yNTA3MTIwMTE5MjNaGA8yMTI1MDYxODAx MTkyM1owVDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNV BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDENMAsGA1UEAwwETGVhZjCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMvhZsBJ95/0jF8w1eFInG6DTmsv Qv4revVFiIg9taXWELd5ohw+/15o3u6DVYlSnjF/hY/BHofiawVPisAe+rchOszi Qvuq1h+sjteUJlRdVnwRVWnSnGtC+QWq9A8b09JXXemMtSkMSQCHO4G4s6j12rG1 LLW/xvt6MMreomElvpmIBZIs23jfsKGJNoyF+58fcknvbKbaiuiOPTIaF+acN/Rj fmFX8QdvFMqB7F2yfjz3PtbZHDCHzCpT8fnPK0vsEsgQTPAPCZxX9CuOexDasnyf PKy8DSicqnZ478dzPNP1dvFYH2jaHcc87YQUdPZSjq0ERCO4RuKsG9hnJqMCAwEA AaNCMEAwHQYDVR0OBBYEFFci2/86lMwscCDJtjnvLC4RHawEMB8GA1UdIwQYMBaA FFsH2CQcC9tLsW1N4M9MMX04Z6mGMA0GCSqGSIb3DQEBCwUAA4IBAQA40eilPuMJ t30CWLmVgSN1HVXZe/TqD+bARf/0T95YJPs+vraYdeuOpa4qxlWQttKiBBOJN/4g h1SCikD6U4fWh1sQ0MszDjzk6dO9qXDy/Z/AYZhtO4P0YXgkxUDCv2CK8TANQrXA lpdw5PDDij5dWLhqPW8t5qElPI90197Dfr7i0yTR5PExOvs6heBX99xApmTSXBJ7 BXb8riu2vGxLn5YdY6FInI2ZbRnVgFJHxoGLolJ9N87tS2NjA9mXOn9vQdiEFHsU xpVJBc+fARh09tmmqFLlT1HPhRbkBRucIfY3IWjlcvCGRdEYdjtVr3ae/40Txpq7 qXgnR8gtSQva -----END CERTIFICATE----- tremotesf-2.10.1/src/rpc/torrent.cpp000066400000000000000000001057641523411710200173700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include "rpc.h" #include "serversettings.h" #include "torrent.h" #include "log/log.h" #include "jsonutils.h" #include "itemlistupdater.h" #include "pathutils.h" #include "stdutils.h" using namespace Qt::StringLiterals; namespace tremotesf { using namespace impl; enum class TorrentData::UpdateKey { Id, HashString, AddedDate, Name, MagnetLink, QueuePosition, TotalSize, CompletedSize, LeftUntilDone, SizeWhenDone, PercentDone, RecheckProgress, Eta, MetadataPercentComplete, DownloadSpeed, UploadSpeed, DownloadSpeedLimited, DownloadSpeedLimit, UploadSpeedLimited, UploadSpeedLimit, TotalDownloaded, TotalUploaded, Ratio, RatioLimitMode, RatioLimit, PeersSendingToUsCount, PeersGettingFromUsCount, WebSeeders, WebSeedersSendingToUsCount, Status, Error, ErrorString, ActivityDate, DoneDate, PeersLimit, HonorSessionLimits, BandwidthPriority, IdleSeedingLimitMode, IdleSeedingLimit, DownloadDirectory, Creator, CreationDate, Comment, TrackerStats, Labels, Count }; namespace { constexpr auto allUpdateKeys = std::views::iota(0, static_cast(TorrentData::UpdateKey::Count)) | std::views::transform([](int key) { return static_cast(key); }); constexpr QLatin1String updateKeyString(TorrentData::UpdateKey key) { switch (key) { case TorrentData::UpdateKey::Id: return "id"_L1; case TorrentData::UpdateKey::HashString: return "hashString"_L1; case TorrentData::UpdateKey::AddedDate: return "addedDate"_L1; case TorrentData::UpdateKey::Name: return "name"_L1; case TorrentData::UpdateKey::MagnetLink: return "magnetLink"_L1; case TorrentData::UpdateKey::QueuePosition: return "queuePosition"_L1; case TorrentData::UpdateKey::TotalSize: return "totalSize"_L1; case TorrentData::UpdateKey::CompletedSize: return "haveValid"_L1; case TorrentData::UpdateKey::LeftUntilDone: return "leftUntilDone"_L1; case TorrentData::UpdateKey::SizeWhenDone: return "sizeWhenDone"_L1; case TorrentData::UpdateKey::PercentDone: return "percentDone"_L1; case TorrentData::UpdateKey::RecheckProgress: return "recheckProgress"_L1; case TorrentData::UpdateKey::Eta: return "eta"_L1; case TorrentData::UpdateKey::MetadataPercentComplete: return "metadataPercentComplete"_L1; case TorrentData::UpdateKey::DownloadSpeed: return "rateDownload"_L1; case TorrentData::UpdateKey::UploadSpeed: return "rateUpload"_L1; case TorrentData::UpdateKey::DownloadSpeedLimited: return "downloadLimited"_L1; case TorrentData::UpdateKey::DownloadSpeedLimit: return "downloadLimit"_L1; case TorrentData::UpdateKey::UploadSpeedLimited: return "uploadLimited"_L1; case TorrentData::UpdateKey::UploadSpeedLimit: return "uploadLimit"_L1; case TorrentData::UpdateKey::TotalDownloaded: return "downloadedEver"_L1; case TorrentData::UpdateKey::TotalUploaded: return "uploadedEver"_L1; case TorrentData::UpdateKey::Ratio: return "uploadRatio"_L1; case TorrentData::UpdateKey::RatioLimitMode: return "seedRatioMode"_L1; case TorrentData::UpdateKey::RatioLimit: return "seedRatioLimit"_L1; case TorrentData::UpdateKey::PeersSendingToUsCount: return "peersSendingToUs"_L1; case TorrentData::UpdateKey::PeersGettingFromUsCount: return "peersGettingFromUs"_L1; case TorrentData::UpdateKey::WebSeeders: return "webseeds"_L1; case TorrentData::UpdateKey::WebSeedersSendingToUsCount: return "webseedsSendingToUs"_L1; case TorrentData::UpdateKey::Status: return "status"_L1; case TorrentData::UpdateKey::Error: return "error"_L1; case TorrentData::UpdateKey::ErrorString: return "errorString"_L1; case TorrentData::UpdateKey::ActivityDate: return "activityDate"_L1; case TorrentData::UpdateKey::DoneDate: return "doneDate"_L1; case TorrentData::UpdateKey::PeersLimit: return "peer-limit"_L1; case TorrentData::UpdateKey::HonorSessionLimits: return "honorsSessionLimits"_L1; case TorrentData::UpdateKey::BandwidthPriority: return "bandwidthPriority"_L1; case TorrentData::UpdateKey::IdleSeedingLimitMode: return "seedIdleMode"_L1; case TorrentData::UpdateKey::IdleSeedingLimit: return "seedIdleLimit"_L1; case TorrentData::UpdateKey::DownloadDirectory: return "downloadDir"_L1; case TorrentData::UpdateKey::Creator: return "creator"_L1; case TorrentData::UpdateKey::CreationDate: return "dateCreated"_L1; case TorrentData::UpdateKey::Comment: return "comment"_L1; case TorrentData::UpdateKey::TrackerStats: return "trackerStats"_L1; case TorrentData::UpdateKey::Labels: return "labels"_L1; case TorrentData::UpdateKey::Count: return {}; } return {}; } std::optional mapUpdateKey(const QString& stringKey) { static const auto mapping = [] { std::map> map{}; for (auto key : allUpdateKeys) { map.emplace(updateKeyString(key), key); } return map; }(); const auto foundKey = mapping.find(stringKey); if (foundKey == mapping.end()) { warning().log("Unknown torrent field '{}'", stringKey); return {}; } return static_cast(foundKey->second); } constexpr auto wantedFilesKey = "files-wanted"_L1; constexpr auto unwantedFilesKey = "files-unwanted"_L1; constexpr auto lowPriorityKey = "priority-low"_L1; constexpr auto normalPriorityKey = "priority-normal"_L1; constexpr auto highPriorityKey = "priority-high"_L1; constexpr auto addTrackerKey = "trackerAdd"_L1; constexpr auto replaceTrackerKey = "trackerReplace"_L1; constexpr auto removeTrackerKey = "trackerRemove"_L1; constexpr auto trackerListKey = "trackerList"_L1; constexpr auto statusMapper = EnumMapper( std::array{ EnumMapping(TorrentData::Status::Paused, 0), EnumMapping(TorrentData::Status::QueuedForChecking, 1), EnumMapping(TorrentData::Status::Checking, 2), EnumMapping(TorrentData::Status::QueuedForDownloading, 3), EnumMapping(TorrentData::Status::Downloading, 4), EnumMapping(TorrentData::Status::QueuedForSeeding, 5), EnumMapping(TorrentData::Status::Seeding, 6) } ); constexpr auto errorMapper = EnumMapper( std::array{ EnumMapping(TorrentData::Error::None, 0), EnumMapping(TorrentData::Error::TrackerWarning, 1), EnumMapping(TorrentData::Error::TrackerError, 2), EnumMapping(TorrentData::Error::LocalError, 3) } ); constexpr auto priorityMapper = EnumMapper( std::array{ EnumMapping(TorrentData::Priority::Low, -1), EnumMapping(TorrentData::Priority::Normal, 0), EnumMapping(TorrentData::Priority::High, 1) } ); constexpr auto ratioLimitModeMapper = EnumMapper( std::array{ EnumMapping(TorrentData::RatioLimitMode::Global, 0), EnumMapping(TorrentData::RatioLimitMode::Single, 1), EnumMapping(TorrentData::RatioLimitMode::Unlimited, 2) } ); constexpr auto idleSeedingLimitModeMapper = EnumMapper( std::array{ EnumMapping(TorrentData::IdleSeedingLimitMode::Global, 0), EnumMapping(TorrentData::IdleSeedingLimitMode::Single, 1), EnumMapping(TorrentData::IdleSeedingLimitMode::Unlimited, 2) } ); } int TorrentData::priorityToInt(Priority value) { return priorityMapper.toJsonConstant(value); } bool TorrentData::update(const QJsonObject& object, bool firstTime, const Rpc* rpc) { bool changed = false; for (auto i = object.begin(), end = object.end(); i != end; ++i) { const auto key = mapUpdateKey(i.key()); if (key.has_value()) { updateProperty(*key, i.value(), changed, firstTime, rpc); } } applyTrackerErrorWorkaround(changed); return changed; } bool TorrentData::update( std::span> keys, const QJsonArray& values, bool firstTime, const Rpc* rpc ) { bool changed = false; for (const auto& [key, value] : std::views::zip(keys, values)) { if (key.has_value()) { updateProperty(*key, value, changed, firstTime, rpc); } } applyTrackerErrorWorkaround(changed); return changed; } void TorrentData::updateProperty( TorrentData::UpdateKey intKey, const QJsonValue& value, bool& changed, bool firstTime, const Rpc* rpc ) { const auto key = static_cast(intKey); switch (static_cast(key)) { case TorrentData::UpdateKey::Id: return; case TorrentData::UpdateKey::HashString: if (firstTime) { hashString = value.toString(); } return; case TorrentData::UpdateKey::AddedDate: updateDateTime(addedDate, value, changed); return; case TorrentData::UpdateKey::Name: setChanged(name, value.toString(), changed); return; case TorrentData::UpdateKey::MagnetLink: setChanged(magnetLink, value.toString(), changed); return; case TorrentData::UpdateKey::QueuePosition: setChanged(queuePosition, value.toInt(), changed); return; case TorrentData::UpdateKey::TotalSize: setChanged(totalSize, value.toInteger(), changed); return; case TorrentData::UpdateKey::CompletedSize: setChanged(completedSize, value.toInteger(), changed); return; case TorrentData::UpdateKey::LeftUntilDone: setChanged(leftUntilDone, value.toInteger(), changed); return; case TorrentData::UpdateKey::SizeWhenDone: setChanged(sizeWhenDone, value.toInteger(), changed); return; case TorrentData::UpdateKey::PercentDone: setChanged(percentDone, value.toDouble(), changed); return; case TorrentData::UpdateKey::RecheckProgress: setChanged(recheckProgress, value.toDouble(), changed); return; case TorrentData::UpdateKey::Eta: setChanged(eta, value.toInt(), changed); return; case TorrentData::UpdateKey::MetadataPercentComplete: setChanged(metadataComplete, value.toInt() == 1, changed); return; case TorrentData::UpdateKey::DownloadSpeed: setChanged(downloadSpeed, value.toInteger(), changed); return; case TorrentData::UpdateKey::UploadSpeed: setChanged(uploadSpeed, value.toInteger(), changed); return; case TorrentData::UpdateKey::DownloadSpeedLimited: setChanged(downloadSpeedLimited, value.toBool(), changed); return; case TorrentData::UpdateKey::DownloadSpeedLimit: setChanged(downloadSpeedLimit, value.toInt(), changed); return; case TorrentData::UpdateKey::UploadSpeedLimited: setChanged(uploadSpeedLimited, value.toBool(), changed); return; case TorrentData::UpdateKey::UploadSpeedLimit: setChanged(uploadSpeedLimit, value.toInt(), changed); return; case TorrentData::UpdateKey::TotalDownloaded: setChanged(totalDownloaded, value.toInteger(), changed); return; case TorrentData::UpdateKey::TotalUploaded: setChanged(totalUploaded, value.toInteger(), changed); return; case TorrentData::UpdateKey::Ratio: setChanged(ratio, value.toDouble(), changed); return; case TorrentData::UpdateKey::RatioLimitMode: setChanged(ratioLimitMode, ratioLimitModeMapper.fromJsonValue(value, updateKeyString(key)), changed); return; case TorrentData::UpdateKey::RatioLimit: setChanged(ratioLimit, value.toDouble(), changed); return; case TorrentData::UpdateKey::PeersSendingToUsCount: setChanged(peersSendingToUsCount, value.toInt(), changed); return; case TorrentData::UpdateKey::PeersGettingFromUsCount: setChanged(peersGettingFromUsCount, value.toInt(), changed); return; case TorrentData::UpdateKey::WebSeeders: { setChanged( webSeeders, value.toArray() | std::views::transform([](auto value) { return value.toString(); }) | std::ranges::to(), changed ); return; } case TorrentData::UpdateKey::WebSeedersSendingToUsCount: setChanged(webSeedersSendingToUsCount, value.toInt(), changed); return; case TorrentData::UpdateKey::Status: setChanged(status, statusMapper.fromJsonValue(value, updateKeyString(key)), changed); return; case TorrentData::UpdateKey::Error: setChanged(error, errorMapper.fromJsonValue(value, updateKeyString(key)), changed); return; case TorrentData::UpdateKey::ErrorString: setChanged(errorString, value.toString(), changed); return; case TorrentData::UpdateKey::ActivityDate: updateDateTime(activityDate, value, changed); return; case TorrentData::UpdateKey::DoneDate: updateDateTime(doneDate, value, changed); return; case TorrentData::UpdateKey::PeersLimit: setChanged(peersLimit, value.toInt(), changed); return; case TorrentData::UpdateKey::HonorSessionLimits: setChanged(honorSessionLimits, value.toBool(), changed); return; case TorrentData::UpdateKey::BandwidthPriority: setChanged(bandwidthPriority, priorityMapper.fromJsonValue(value, updateKeyString(key)), changed); return; case TorrentData::UpdateKey::IdleSeedingLimitMode: setChanged( idleSeedingLimitMode, idleSeedingLimitModeMapper.fromJsonValue(value, updateKeyString(key)), changed ); return; case TorrentData::UpdateKey::IdleSeedingLimit: setChanged(idleSeedingLimit, value.toInt(), changed); return; case TorrentData::UpdateKey::DownloadDirectory: setChanged( downloadDirectory, normalizePath(value.toString(), rpc->serverSettings()->data().pathOs), changed ); return; case TorrentData::UpdateKey::Creator: setChanged(creator, value.toString(), changed); return; case TorrentData::UpdateKey::CreationDate: updateDateTime(creationDate, value, changed); return; case TorrentData::UpdateKey::Comment: setChanged(comment, value.toString(), changed); return; case TorrentData::UpdateKey::TrackerStats: { std::vector newTrackers{}; const QJsonArray trackerJsons = value.toArray(); newTrackers.reserve(static_cast(trackerJsons.size())); int newTotalSeeders{}; int newTotalLeechers{}; for (const auto& i : trackerJsons) { const QJsonObject trackerMap = i.toObject(); const int trackerId = trackerMap.value("id"_L1).toInt(); const auto found = std::ranges::find(trackers, trackerId, &Tracker::id); if (found == trackers.end()) { newTrackers.emplace_back(trackerId, trackerMap); changed = true; } else { if (found->update(trackerMap)) { changed = true; } newTrackers.push_back(std::move(*found)); } newTotalSeeders += newTrackers.back().seeders(); newTotalLeechers += newTrackers.back().leechers(); } trackers = std::move(newTrackers); setChanged(totalSeedersFromTrackersCount, newTotalSeeders, changed); setChanged(totalLeechersFromTrackersCount, newTotalLeechers, changed); return; } case TorrentData::UpdateKey::Labels: { setChanged( labels, value.toArray() | std::views::transform([](auto value) { return value.toString(); }) | std::ranges::to(), changed ); return; } case TorrentData::UpdateKey::Count: throw std::logic_error("UpdateKey::Count should not be mapped"); } throw std::logic_error(fmt::format("Can't update key {}", static_cast(intKey))); } void TorrentData::applyTrackerErrorWorkaround(bool& changed) { // Sometimes Transmission doesn't propagate tracker error to the torrent's status if (error != Error::None) { return; } if (trackers.empty()) { return; } // Only set error if *all* trackers have an error if (std::ranges::any_of(trackers, [](const Tracker& tracker) { return tracker.errorMessage().isEmpty(); })) { return; } // Set error from first tracker error = Error::TrackerError; errorString = trackers.front().errorMessage(); changed = true; } Torrent::Torrent(int id, const QJsonObject& object, Rpc* rpc, QObject* parent) : QObject(parent), mRpc(rpc) { mData.id = id; [[maybe_unused]] const bool changed = mData.update(object, true, rpc); } Torrent::Torrent( int id, std::span> keys, const QJsonArray& values, Rpc* rpc, QObject* parent ) : QObject(parent), mRpc(rpc) { mData.id = id; [[maybe_unused]] const bool changed = mData.update(keys, values, true, rpc); } QJsonArray Torrent::updateFields(const ServerSettings* serverSettings) { QJsonArray fields{}; for (auto key : allUpdateKeys) { if (key == TorrentData::UpdateKey::Labels && !serverSettings->data().hasLabelsProperty()) { continue; } fields.push_back(updateKeyString(key)); } return fields; } std::optional Torrent::idFromJson(const QJsonObject& object) { const auto value = object.value(updateKeyString(TorrentData::UpdateKey::Id)); if (value.isDouble()) { return value.toInt(); } return {}; } std::optional Torrent::idKeyIndex(std::span> keys) { return indexOfCasted(keys, TorrentData::UpdateKey::Id); } std::vector> Torrent::mapUpdateKeys(const QJsonArray& stringKeys) { return stringKeys | std::views::transform([](auto value) { return mapUpdateKey(value.toString()); }) | std::ranges::to(); } void Torrent::setDownloadSpeedLimited(bool limited) { mData.downloadSpeedLimited = limited; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::DownloadSpeedLimited), limited); } void Torrent::setDownloadSpeedLimit(int limit) { mData.downloadSpeedLimit = limit; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::DownloadSpeedLimit), limit); } void Torrent::setUploadSpeedLimited(bool limited) { mData.uploadSpeedLimited = limited; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::UploadSpeedLimited), limited); } void Torrent::setUploadSpeedLimit(int limit) { mData.uploadSpeedLimit = limit; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::UploadSpeedLimit), limit); } void Torrent::setRatioLimitMode(TorrentData::RatioLimitMode mode) { mData.ratioLimitMode = mode; mRpc->setTorrentProperty( mData.id, updateKeyString(TorrentData::UpdateKey::RatioLimitMode), ratioLimitModeMapper.toJsonConstant(mode) ); } void Torrent::setRatioLimit(double limit) { mData.ratioLimit = limit; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::RatioLimit), limit); } void Torrent::setPeersLimit(int limit) { mData.peersLimit = limit; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::PeersLimit), limit); } void Torrent::setHonorSessionLimits(bool honor) { mData.honorSessionLimits = honor; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::HonorSessionLimits), honor); } void Torrent::setBandwidthPriority(TorrentData::Priority priority) { mData.bandwidthPriority = priority; mRpc->setTorrentProperty( mData.id, updateKeyString(TorrentData::UpdateKey::BandwidthPriority), priorityMapper.toJsonConstant(priority) ); } void Torrent::setIdleSeedingLimitMode(TorrentData::IdleSeedingLimitMode mode) { mData.idleSeedingLimitMode = mode; mRpc->setTorrentProperty( mData.id, updateKeyString(TorrentData::UpdateKey::IdleSeedingLimitMode), idleSeedingLimitModeMapper.toJsonConstant(mode) ); } void Torrent::setIdleSeedingLimit(int limit) { mData.idleSeedingLimit = limit; mRpc->setTorrentProperty(mData.id, updateKeyString(TorrentData::UpdateKey::IdleSeedingLimit), limit); } namespace { std::vector> toTieredAnnounceUrls(std::span trackers) { std::map> tiered{}; for (const auto& tracker : trackers) { tiered[tracker.id()].insert(tracker.announce()); } return std::views::values(tiered) | std::views::as_rvalue | std::ranges::to(); } QString toTrackerList(std::span> tieredAnnounceUrls) { QString trackerList{}; bool processedFirstTier{}; for (const auto& tier : tieredAnnounceUrls) { if (processedFirstTier) { trackerList += "\n\n"_L1; } for (const auto& announceUrl : tier) { trackerList += announceUrl; trackerList += '\n'; } processedFirstTier = true; } return trackerList; } bool isIntersect(const std::set& existingTier, const std::set& newTier) { return std::ranges::any_of(newTier, [&](const auto& announceUrl) { return existingTier.contains(announceUrl); }); } QJsonArray filterOutExistingTrackers( std::span> newTrackers, std::span existingTrackers ) { QJsonArray trackersToAdd{}; for (const auto& tier : newTrackers) { if (tier.empty()) continue; // Transmission adds each announce URL to each own tier when using trackerAdd property, so take first URL from each tier const auto& first = *tier.begin(); if (!std::ranges::contains(existingTrackers, first, &Tracker::announce)) { trackersToAdd.push_back(first); } } return trackersToAdd; } } namespace impl { std::vector> mergeTrackers( const std::vector>& existingTrackers, std::span> newTrackers ) { auto merged = existingTrackers; for (const auto& newTier : newTrackers) { if (newTier.empty()) continue; const auto existingTier = std::ranges::find_if(merged, [&](auto& tier) { return isIntersect(tier, newTier); }); if (existingTier != merged.end()) { existingTier->insert(newTier.begin(), newTier.end()); } else { merged.push_back(newTier); } } return merged; } } void Torrent::addTrackers(std::span> announceUrls) { if (mRpc->serverSettings()->data().hasTrackerListProperty()) { const auto existingTrackers = toTieredAnnounceUrls(mData.trackers); debug().log("Merging exisiting trackers {} with {}", existingTrackers, announceUrls); const auto merged = mergeTrackers(existingTrackers, announceUrls); const bool changed = merged != existingTrackers; debug().log("Result is {}, changed: {}", merged, changed); if (changed) { mRpc->setTorrentProperty(mData.id, trackerListKey, toTrackerList(merged), true); } } else { auto trackersToAdd = filterOutExistingTrackers(announceUrls, mData.trackers); if (!trackersToAdd.empty()) { mRpc->setTorrentProperty(mData.id, addTrackerKey, std::move(trackersToAdd), true); } } } void Torrent::setTracker(int trackerId, const QString& announce) { if (!mRpc->serverSettings()->data().hasTrackerListProperty()) { mRpc->setTorrentProperty(mData.id, replaceTrackerKey, QJsonArray{trackerId, announce}, true); return; } auto trackers = mData.trackers; const auto tracker = std::ranges::find(trackers, trackerId, &Tracker::id); if (tracker == trackers.end()) { warning().log("setTracker: did not find tracker with id {}", trackerId); return; } if (tracker->announce() == announce) { return; } tracker->replaceAnnounceUrl(announce); mRpc->setTorrentProperty(mData.id, trackerListKey, toTrackerList(toTieredAnnounceUrls(trackers)), true); } void Torrent::removeTrackers(std::span ids) { if (!mRpc->serverSettings()->data().hasTrackerListProperty()) { mRpc->setTorrentProperty(mData.id, removeTrackerKey, toJsonArray(ids), true); return; } auto trackers = mData.trackers; const auto erased = std::erase_if(trackers, [ids](const auto& tracker) { return std::ranges::contains(ids, tracker.id()); }); if (erased == 0) { return; } mRpc->setTorrentProperty(mData.id, trackerListKey, toTrackerList(toTieredAnnounceUrls(trackers)), true); } void Torrent::setFilesEnabled(bool enabled) { if (enabled != mFilesEnabled) { mFilesEnabled = enabled; if (mFilesEnabled) { mRpc->getTorrentFiles(mData.id); } else { mFiles.clear(); } } } void Torrent::setFilesWanted(std::span fileIds, bool wanted) { mRpc->setTorrentProperty(mData.id, wanted ? wantedFilesKey : unwantedFilesKey, toJsonArray(fileIds)); } void Torrent::setFilesPriority(std::span fileIds, TorrentFile::Priority priority) { QLatin1String propertyName; switch (priority) { case TorrentFile::Priority::Low: propertyName = lowPriorityKey; break; case TorrentFile::Priority::Normal: propertyName = normalPriorityKey; break; case TorrentFile::Priority::High: propertyName = highPriorityKey; break; } mRpc->setTorrentProperty(mData.id, propertyName, toJsonArray(fileIds)); } void Torrent::renameFile(const QString& path, const QString& newName) { mRpc->renameTorrentFile(mData.id, path, newName); } void Torrent::setPeersEnabled(bool enabled) { if (enabled != mPeersEnabled) { mPeersEnabled = enabled; if (mPeersEnabled) { mRpc->getTorrentPeers(mData.id); } else { mPeers.clear(); } } } bool Torrent::update(const QJsonObject& object) { const bool c = mData.update(object, false, mRpc); emit updated(); if (c) { emit changed(); } return c; } bool Torrent::update(std::span> keys, const QJsonArray& values) { const bool c = mData.update(keys, values, false, mRpc); emit updated(); if (c) { emit changed(); } return c; } void Torrent::updateFiles(const QJsonObject& torrentMap) { std::vector changed{}; const QJsonArray fileStats = torrentMap.value("fileStats"_L1).toArray(); if (!fileStats.isEmpty()) { if (mFiles.empty()) { const QJsonArray fileJsons = torrentMap.value("files"_L1).toArray(); if (fileJsons.size() == fileStats.size()) { const auto count = fileJsons.size(); mFiles.reserve(static_cast(count)); changed.reserve(static_cast(count)); for (QJsonArray::size_type i = 0; i < count; ++i) { mFiles.emplace_back(fileJsons[i].toObject(), fileStats[i].toObject()); changed.push_back(static_cast(i)); } } else { warning().log("fileStats and files arrays have different sizes for torrent {}", *this); } } else { if (static_cast(fileStats.size()) == mFiles.size()) { for (QJsonArray::size_type i = 0, max = fileStats.size(); i < max; ++i) { TorrentFile& file = mFiles[static_cast(i)]; if (file.update(fileStats[i].toObject())) { changed.push_back(static_cast(i)); } } } else { warning().log("fileStats array has different size than in previous update for torrent {}", *this); } } } emit filesUpdated(changed); } namespace { struct NewPeer { QJsonObject json; QString address; }; class PeersListUpdater final : public ItemListUpdater> { public: PeersListUpdater() = default; std::vector> removedIndexRanges{}; std::vector> changedIndexRanges{}; int addedCount{}; protected: std::vector::iterator findNewItemForItem(std::vector& newPeers, const Peer& peer) override { return std::ranges::find(newPeers, peer.address, &NewPeer::address); } void onAboutToRemoveItems(size_t, size_t) override {}; void onRemovedItems(size_t first, size_t last) override { removedIndexRanges.emplace_back(static_cast(first), static_cast(last)); } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) bool updateItem(Peer& peer, NewPeer&& newPeer) override { const auto& [json, address] = newPeer; return peer.update(json); } void onChangedItems(size_t first, size_t last) override { changedIndexRanges.emplace_back(static_cast(first), static_cast(last)); } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) Peer createItemFromNewItem(NewPeer&& newPeer) override { auto& [json, address] = newPeer; return Peer(std::move(address), json); } void onAboutToAddItems(size_t) override {} void onAddedItems(size_t count) override { addedCount = static_cast(count); }; }; } void Torrent::updatePeers(const QJsonObject& torrentMap) { std::vector newPeers; { const QJsonArray peers(torrentMap.value("peers"_L1).toArray()); newPeers.reserve(static_cast(peers.size())); for (const auto& i : peers) { QJsonObject json = i.toObject(); QString address(json.value(Peer::addressKey).toString()); newPeers.push_back(NewPeer{.json = std::move(json), .address = std::move(address)}); } } PeersListUpdater updater{}; updater.update(mPeers, std::move(newPeers)); emit peersUpdated(updater.removedIndexRanges, updater.changedIndexRanges, updater.addedCount); } } namespace fmt { format_context::iterator formatter::format(const tremotesf::Torrent& torrent, format_context& ctx) const { return fmt::format_to(ctx.out(), "Torrent(id={}, name={})", torrent.data().id, torrent.data().name); } } tremotesf-2.10.1/src/rpc/torrent.h000066400000000000000000000167311523411710200170300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_TORRENT_H #define TREMOTESF_RPC_TORRENT_H #include #include #include #include #include #include #include #include #include "log/formatters.h" #include "peer.h" #include "torrentfile.h" #include "tracker.h" class QJsonObject; namespace tremotesf { class Rpc; class ServerSettings; struct TorrentData { Q_GADGET public: enum class Status { Paused, QueuedForChecking, Checking, QueuedForDownloading, Downloading, QueuedForSeeding, Seeding }; Q_ENUM(Status) enum class Error { None, TrackerWarning, TrackerError, LocalError }; Q_ENUM(Error) enum class Priority { Low, Normal, High }; Q_ENUM(Priority) static int priorityToInt(Priority value); enum class RatioLimitMode { Global, Single, Unlimited }; Q_ENUM(RatioLimitMode) enum class IdleSeedingLimitMode { Global, Single, Unlimited }; Q_ENUM(IdleSeedingLimitMode) [[nodiscard]] bool update(const QJsonObject& object, bool firstTime, const Rpc* rpc); enum class UpdateKey; [[nodiscard]] bool update( std::span> keys, const QJsonArray& values, bool firstTime, const Rpc* rpc ); int id{}; QString hashString{}; QString name{}; QString magnetLink{}; Status status{}; Error error{}; QString errorString{}; int queuePosition{}; qint64 totalSize{}; qint64 completedSize{}; qint64 leftUntilDone{}; qint64 sizeWhenDone{}; double percentDone{}; double recheckProgress{}; int eta{}; bool metadataComplete{}; qint64 downloadSpeed{}; qint64 uploadSpeed{}; bool downloadSpeedLimited{}; int downloadSpeedLimit{}; // kB/s bool uploadSpeedLimited{}; int uploadSpeedLimit{}; // kB/s qint64 totalDownloaded{}; qint64 totalUploaded{}; double ratio{}; double ratioLimit{}; RatioLimitMode ratioLimitMode{}; int totalSeedersFromTrackersCount{}; int peersSendingToUsCount{}; std::vector webSeeders{}; int webSeedersSendingToUsCount{}; int totalLeechersFromTrackersCount{}; int peersGettingFromUsCount{}; int peersLimit{}; QDateTime addedDate{{}, {}, QTimeZone::utc()}; QDateTime activityDate{{}, {}, QTimeZone::utc()}; QDateTime doneDate{{}, {}, QTimeZone::utc()}; IdleSeedingLimitMode idleSeedingLimitMode{}; int idleSeedingLimit{}; QString downloadDirectory{}; QString comment{}; QString creator{}; QDateTime creationDate{{}, {}, QTimeZone::utc()}; Priority bandwidthPriority{}; bool honorSessionLimits; std::vector labels{}; std::vector trackers{}; [[nodiscard]] bool hasError() const { return error != Error::None; } [[nodiscard]] bool isFinished() const { return leftUntilDone == 0; } [[nodiscard]] bool isDownloadingStalled() const { return (peersSendingToUsCount == 0 && webSeedersSendingToUsCount == 0); } [[nodiscard]] bool isSeedingStalled() const { return peersGettingFromUsCount == 0; } private: void updateProperty( TorrentData::UpdateKey key, const QJsonValue& value, bool& changed, bool firstTime, const Rpc* rpc ); void applyTrackerErrorWorkaround(bool& changed); }; class Torrent final : public QObject { Q_OBJECT public: explicit Torrent(int id, const QJsonObject& object, Rpc* rpc, QObject* parent = nullptr); explicit Torrent( int id, std::span> keys, const QJsonArray& values, Rpc* rpc, QObject* parent = nullptr ); // For testing only explicit Torrent() = default; [[nodiscard]] static QJsonArray updateFields(const ServerSettings* serverSettings); [[nodiscard]] static std::optional idFromJson(const QJsonObject& object); [[nodiscard]] static std::optional idKeyIndex(std::span> keys); [[nodiscard]] static std::vector> mapUpdateKeys(const QJsonArray& stringKeys); void setDownloadSpeedLimited(bool limited); void setDownloadSpeedLimit(int limit); void setUploadSpeedLimited(bool limited); void setUploadSpeedLimit(int limit); void setRatioLimitMode(TorrentData::RatioLimitMode mode); void setRatioLimit(double limit); void setPeersLimit(int limit); void setHonorSessionLimits(bool honor); void setBandwidthPriority(TorrentData::Priority priority); void setIdleSeedingLimitMode(TorrentData::IdleSeedingLimitMode mode); void setIdleSeedingLimit(int limit); void addTrackers(std::span> announceUrls); void setTracker(int trackerId, const QString& announce); void removeTrackers(std::span trackerIds); [[nodiscard]] const TorrentData& data() const { return mData; }; [[nodiscard]] bool isFilesEnabled() const { return mFilesEnabled; }; void setFilesEnabled(bool enabled); [[nodiscard]] const std::vector& files() const { return mFiles; }; void setFilesWanted(std::span fileIds, bool wanted); void setFilesPriority(std::span fileIds, TorrentFile::Priority priority); void renameFile(const QString& path, const QString& newName); [[nodiscard]] bool isPeersEnabled() const { return mPeersEnabled; }; void setPeersEnabled(bool enabled); [[nodiscard]] const std::vector& peers() const { return mPeers; }; [[nodiscard]] bool update(const QJsonObject& object); [[nodiscard]] bool update(std::span> keys, const QJsonArray& values); void updateFiles(const QJsonObject& torrentMap); void updatePeers(const QJsonObject& torrentMap); private: Rpc* mRpc{}; TorrentData mData{}; std::vector mFiles{}; bool mFilesEnabled{}; std::vector mPeers{}; bool mPeersEnabled{}; signals: void updated(); void changed(); void filesUpdated(const std::vector& changedIndexes); void peersUpdated( const std::vector>& removedIndexRanges, const std::vector>& changedIndexRanges, int addedCount ); void fileRenamed(const QString& filePath, const QString& newName); }; namespace impl { std::vector> mergeTrackers( const std::vector>& existingTrackers, std::span> newTrackers ); } } namespace fmt { template<> struct formatter : tremotesf::SimpleFormatter { format_context::iterator format(const tremotesf::Torrent& torrent, format_context& ctx) const; }; } #endif // TREMOTESF_RPC_TORRENT_H tremotesf-2.10.1/src/rpc/torrentfile.cpp000066400000000000000000000024071523411710200202160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentfile.h" #include #include "jsonutils.h" #include "stdutils.h" using namespace Qt::StringLiterals; namespace tremotesf { using namespace impl; namespace { constexpr auto priorityMapper = EnumMapper( std::array{ EnumMapping(TorrentFile::Priority::Low, -1), EnumMapping(TorrentFile::Priority::Normal, 0), EnumMapping(TorrentFile::Priority::High, 1) } ); } TorrentFile::TorrentFile(const QJsonObject& fileMap, const QJsonObject& fileStatsMap) : path(fileMap.value("name"_L1).toString()), size(fileMap.value("length"_L1).toInteger()) { update(fileStatsMap); } bool TorrentFile::update(const QJsonObject& fileStatsMap) { bool changed = false; setChanged(completedSize, fileStatsMap.value("bytesCompleted"_L1).toInteger(), changed); constexpr auto priorityKey = "priority"_L1; setChanged(priority, priorityMapper.fromJsonValue(fileStatsMap.value(priorityKey), priorityKey), changed); setChanged(wanted, fileStatsMap.value("wanted"_L1).toBool(), changed); return changed; } } tremotesf-2.10.1/src/rpc/torrentfile.h000066400000000000000000000014321523411710200176600ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_TORRENTFILE_H #define TREMOTESF_RPC_TORRENTFILE_H #include #include class QJsonObject; namespace tremotesf { struct TorrentFile { Q_GADGET public: enum class Priority { Low, Normal, High }; Q_ENUM(Priority) explicit TorrentFile(const QJsonObject& fileMap, const QJsonObject& fileStatsMap); bool update(const QJsonObject& fileStatsMap); QString path{}; qint64 size{}; qint64 completedSize{}; Priority priority{}; bool wanted{}; auto pathParts() const { return path.tokenize(u'/', Qt::SkipEmptyParts); } }; } #endif // TREMOTESF_RPC_TORRENTFILE_H tremotesf-2.10.1/src/rpc/tracker.cpp000066400000000000000000000100411523411710200173050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "tracker.h" #include #include #include #ifndef TREMOTESF_REGISTRABLE_DOMAIN_QT # include #endif #include "jsonutils.h" #include "stdutils.h" using namespace Qt::StringLiterals; namespace tremotesf { using namespace impl; namespace { constexpr auto statusMapper = EnumMapper( std::array{ EnumMapping(Tracker::Status::Inactive, 0), EnumMapping(Tracker::Status::WaitingForUpdate, 1), EnumMapping(Tracker::Status::QueuedForUpdate, 2), EnumMapping(Tracker::Status::Updating, 3) } ); } Tracker::Tracker(int id, const QJsonObject& trackerMap) : mId(id) { update(trackerMap); } bool Tracker::update(const QJsonObject& trackerMap) { bool changed = false; QString announce(trackerMap.value("announce"_L1).toString()); if (announce != mAnnounce) { changed = true; mAnnounce = std::move(announce); mSite = registrableDomainFromUrl(QUrl(mAnnounce)); } setChanged(mTier, trackerMap.value("tier"_L1).toInt(), changed); const bool announceError = (!trackerMap.value("lastAnnounceSucceeded"_L1).toBool() && trackerMap.value("lastAnnounceTime"_L1).toInt() != 0); if (announceError) { setChanged(mErrorMessage, trackerMap.value("lastAnnounceResult"_L1).toString(), changed); } else { setChanged(mErrorMessage, {}, changed); } constexpr auto announceStateKey = "announceState"_L1; setChanged(mStatus, statusMapper.fromJsonValue(trackerMap.value(announceStateKey), announceStateKey), changed); setChanged(mPeers, trackerMap.value("lastAnnouncePeerCount"_L1).toInt(), changed); setChanged( mSeeders, [&] { if (auto seeders = trackerMap.value("seederCount"_L1).toInt(); seeders >= 0) { return seeders; } return 0; }(), changed ); setChanged( mLeechers, [&] { if (auto leechers = trackerMap.value("leecherCount"_L1).toInt(); leechers >= 0) { return leechers; } return 0; }(), changed ); updateDateTime(mNextUpdateTime, trackerMap.value("nextAnnounceTime"_L1), changed); return changed; } } #ifdef TREMOTESF_REGISTRABLE_DOMAIN_QT // Private Qt API bool qIsEffectiveTLD(QStringView domain); namespace { QString registrableDomainFromDomain(const QString& fullDomain, [[maybe_unused]] const QUrl& url) { QStringView domain = fullDomain; QStringView previousDomain = fullDomain; while (!domain.isEmpty()) { if (qIsEffectiveTLD(domain)) { return previousDomain.toString(); } const auto dotIndex = domain.indexOf('.'); if (dotIndex == -1) { break; } previousDomain = domain; domain = domain.sliced(dotIndex + 1); } return fullDomain; } } #else namespace { QString registrableDomainFromDomain(const QString& fullDomain, [[maybe_unused]] const QUrl& url) { const auto fullDomainUtf8 = fullDomain.toUtf8(); const auto psl = psl_builtin(); if (!psl) { return fullDomain; } const auto registrable = psl_registrable_domain(psl, fullDomainUtf8); return registrable ? QString(registrable) : fullDomain; } } #endif QString tremotesf::impl::registrableDomainFromUrl(const QUrl& url) { auto host = url.host().toLower().normalized(QString::NormalizationForm_KC); if (host.isEmpty()) { return {}; } if (const bool isIpAddress = !QHostAddress(host).isNull(); isIpAddress) { return host; } return registrableDomainFromDomain(host, url); } tremotesf-2.10.1/src/rpc/tracker.h000066400000000000000000000036561523411710200167700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RPC_TRACKER_H #define TREMOTESF_RPC_TRACKER_H #include #include #include #include class QJsonObject; class QUrl; namespace tremotesf { class Tracker { Q_GADGET public: enum class Status { // Tracker is inactive, possibly due to error Inactive, // Waiting for announce/scrape WaitingForUpdate, // Queued for immediate announce/scrape QueuedForUpdate, // We are announcing/scraping Updating, }; Q_ENUM(Status) explicit Tracker(int id, const QJsonObject& trackerMap); int id() const { return mId; }; const QString& announce() const { return mAnnounce; }; const QString& site() const { return mSite; }; int tier() const { return mTier; } Status status() const { return mStatus; }; const QString& errorMessage() const { return mErrorMessage; }; int peers() const { return mPeers; }; int seeders() const { return mSeeders; } int leechers() const { return mLeechers; } const QDateTime& nextUpdateTime() const { return mNextUpdateTime; }; bool update(const QJsonObject& trackerMap); void replaceAnnounceUrl(const QString& announceUrl) { mAnnounce = announceUrl; } bool operator==(const Tracker& other) const = default; private: QString mAnnounce{}; QString mSite{}; int mTier{}; Status mStatus{}; QString mErrorMessage{}; QDateTime mNextUpdateTime{{}, {}, QTimeZone::utc()}; int mPeers{}; int mSeeders{}; int mLeechers{}; int mId{}; }; namespace impl { QString registrableDomainFromUrl(const QUrl& url); } } #endif // TREMOTESF_RPC_TRACKER_H tremotesf-2.10.1/src/rpc/tracker_test.cpp000066400000000000000000000057671523411710200203670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "tracker.h" #include "torrent.h" using namespace Qt::StringLiterals; using namespace tremotesf::impl; class TrackerTest final : public QObject { Q_OBJECT private slots: void registrableDomainTest() { QCOMPARE(registrableDomainFromUrl(QUrl("https://doc.qt.io")), QString("qt.io")); QCOMPARE(registrableDomainFromUrl(QUrl("https://github.com")), QString("github.com")); QCOMPARE(registrableDomainFromUrl(QUrl("https://www.bbc.co.uk/")), QString("bbc.co.uk")); QCOMPARE(registrableDomainFromUrl(QUrl("https://en.wikipedia.org/wiki/Main_Page")), QString("wikipedia.org")); QCOMPARE(registrableDomainFromUrl(QUrl("https://forgot.his.name")), QString("forgot.his.name")); } void registrableDomainIpTest() { constexpr std::array ips{"127.0.0.1", "2001:0db8:0000:0000:0000:ff00:0042:8329"}; for (const auto& ip : ips) { const QHostAddress expectedIp(ip); QUrl url{}; url.setScheme("https"_L1); if (expectedIp.protocol() == QAbstractSocket::IPv6Protocol) { url.setHost(fmt::format("[{}]", ip).c_str()); } else { url.setHost(ip); } const auto actualIpString = registrableDomainFromUrl(url); const QHostAddress actualIp(actualIpString); QCOMPARE(actualIp, expectedIp); } } void registrableDomainUnknownTest() { QCOMPARE(registrableDomainFromUrl(QUrl("https://foobar")), QString("foobar")); QCOMPARE(registrableDomainFromUrl(QUrl("https://")), QString()); } void mergingTackersCompletelyNewTest() { const auto existingTrackers = std::vector{std::set{QString("foo")}, std::set{QString("bar")}}; const auto newTrackers = std::vector{std::set{QString("lol")}, std::set{QString("nope")}}; const auto expectedResult = std::vector{ std::set{QString("foo")}, std::set{QString("bar")}, std::set{QString("lol")}, std::set{QString("nope")} }; const auto merged = mergeTrackers(existingTrackers, newTrackers); QCOMPARE(merged, expectedResult); } void mergingTrackersAddingToExistingTierTest() { const auto existingTrackers = std::vector{std::set{QString("foo")}, std::set{QString("bar")}}; const auto newTrackers = std::vector{std::set{QString("foo"), QString("foo.alt1"), QString("foo.alt2")}, std::set{QString("nope")}}; const auto expectedResult = std::vector{ std::set{QString("foo"), QString("foo.alt1"), QString("foo.alt2")}, std::set{QString("bar")}, std::set{QString("nope")} }; const auto merged = mergeTrackers(existingTrackers, newTrackers); QCOMPARE(merged, expectedResult); } }; QTEST_GUILESS_MAIN(TrackerTest) #include "tracker_test.moc" tremotesf-2.10.1/src/settings.cpp000066400000000000000000000213171523411710200167360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // SPDX-FileCopyrightText: 2021 LuK1337 // SPDX-FileCopyrightText: 2022 Alex // // SPDX-License-Identifier: GPL-3.0-or-later #include "settings.h" #include #include #include #include "log/log.h" #include "target_os.h" using namespace Qt::StringLiterals; #define SETTINGS_PROPERTY_DEF(type, name, key, defaultValue) \ namespace { \ const QVariant& name##_defaultValue() { \ static const auto v = QVariant::fromValue(defaultValue); \ return v; \ } \ } \ type Settings::get_##name() const { return getValue(mSettings, key##_L1, name##_defaultValue()); } \ void Settings::set_##name(type value) { \ if (setValue(mSettings, key##_L1, std::move(value), name##_defaultValue())) { \ emit name##Changed(); \ } \ } namespace tremotesf { namespace { template T getValue(QSettings* settings, QLatin1String key, const QVariant& defaultValue) { T value = settings->value(key, defaultValue).value(); if constexpr (std::is_enum_v) { const auto meta = QMetaEnum::fromType(); const auto named = #if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) meta.valueToKey(static_cast(value)); #else meta.valueToKey(static_cast(value)); #endif if (!named) { warning().log("Settings: key {} has invalid value {}, returning default value", key, value); return defaultValue.value(); } } return value; } template bool setValue(QSettings* settings, QLatin1String key, T&& newValue, const QVariant& defaultValue) { const auto currentValue = getValue(settings, key, defaultValue); if (newValue != currentValue) { settings->setValue(key, QVariant::fromValue(std::forward(newValue))); return true; } return false; } } Settings* Settings::instance() { static auto* const instance = new Settings(qApp); return instance; } SETTINGS_PROPERTY_DEF(bool, connectOnStartup, "connectOnStartup", true) SETTINGS_PROPERTY_DEF(bool, notificationOnDisconnecting, "notificationOnDisconnecting", true) SETTINGS_PROPERTY_DEF(bool, notificationOnAddingTorrent, "notificationOnAddingTorrent", true) SETTINGS_PROPERTY_DEF(bool, notificationOfFinishedTorrents, "notificationOfFinishedTorrents", true) SETTINGS_PROPERTY_DEF( bool, notificationsOnAddedTorrentsSinceLastConnection, "notificationsOnAddedTorrentsSinceLastConnection", false ) SETTINGS_PROPERTY_DEF( bool, notificationsOnFinishedTorrentsSinceLastConnection, "notificationsOnFinishedTorrentsSinceLastConnection", false ) SETTINGS_PROPERTY_DEF(bool, rememberOpenTorrentDir, "rememberOpenTorrentTorrentDir", true) SETTINGS_PROPERTY_DEF(QString, lastOpenTorrentDirectory, "lastOpenTorrentDirectory", {}) SETTINGS_PROPERTY_DEF(bool, rememberAddTorrentParameters, "rememberAddTorrentParameters", true) SETTINGS_PROPERTY_DEF( TorrentData::Priority, lastAddTorrentPriority, "lastAddTorrentPriority", TorrentData::Priority::Normal ) SETTINGS_PROPERTY_DEF(bool, lastAddTorrentStartAfterAdding, "lastAddTorrentStartAfterAdding", true) SETTINGS_PROPERTY_DEF(bool, lastAddTorrentDeleteTorrentFile, "lastAddTorrentDeleteTorrentFile", false) SETTINGS_PROPERTY_DEF(bool, lastAddTorrentMoveTorrentFileToTrash, "lastAddTorrentMoveTorrentFileToTrash", true) SETTINGS_PROPERTY_DEF(bool, fillTorrentLinkFromClipboard, "fillTorrentLinkFromClipboard", false) SETTINGS_PROPERTY_DEF(bool, showMainWindowWhenAddingTorrent, "showMainWindowWhenAddingTorrent", true) SETTINGS_PROPERTY_DEF(bool, showAddTorrentDialog, "showAddTorrentDialog", true) SETTINGS_PROPERTY_DEF(bool, torrentsStatusFilterEnabled, "torrentsStatusFilterEnabled", true) SETTINGS_PROPERTY_DEF(bool, mergeTrackersWhenAddingExistingTorrent, "mergeTrackersWhenAddingExistingTorrent", false) SETTINGS_PROPERTY_DEF( bool, askForMergingTrackersWhenAddingExistingTorrent, "askForMergingTrackersWhenAddingExistingTorrent", true ) SETTINGS_PROPERTY_DEF( TorrentsProxyModel::StatusFilter, torrentsStatusFilter, "torrentsStatusFilter", TorrentsProxyModel::StatusFilter::All ) SETTINGS_PROPERTY_DEF(bool, torrentsLabelFilterEnabled, "torrentsLabelFilterEnabled", true) SETTINGS_PROPERTY_DEF(QString, torrentsLabelFilter, "torrentsLabelFilter", {}) SETTINGS_PROPERTY_DEF(bool, torrentsTrackerFilterEnabled, "torrentsTrackerFilterEnabled", true) SETTINGS_PROPERTY_DEF(QString, torrentsTrackerFilter, "torrentsTrackerFilter", {}) SETTINGS_PROPERTY_DEF(bool, torrentsDownloadDirectoryFilterEnabled, "torrentsDownloadDirectoryFilterEnabled", true) SETTINGS_PROPERTY_DEF(QString, torrentsDownloadDirectoryFilter, "torrentsDownloadDirectoryFilter", {}) SETTINGS_PROPERTY_DEF(bool, showTrayIcon, "showTrayIcon", true) SETTINGS_PROPERTY_DEF(Qt::ToolButtonStyle, toolButtonStyle, "toolButtonStyle", Qt::ToolButtonFollowStyle) SETTINGS_PROPERTY_DEF(bool, toolBarLocked, "toolBarLocked", true) SETTINGS_PROPERTY_DEF(bool, sideBarVisible, "sideBarVisible", true) SETTINGS_PROPERTY_DEF(bool, statusBarVisible, "statusBarVisible", true) SETTINGS_PROPERTY_DEF(bool, showTorrentPropertiesInMainWindow, "showTorrentPropertiesInMainWindow", false) SETTINGS_PROPERTY_DEF(QByteArray, mainWindowGeometry, "mainWindowGeometry", {}) SETTINGS_PROPERTY_DEF(QByteArray, mainWindowState, "mainWindowState", {}) SETTINGS_PROPERTY_DEF(QByteArray, horizontalSplitterState, "splitterState", {}) SETTINGS_PROPERTY_DEF(QByteArray, verticalSplitterState, "verticalSplitterState", {}) SETTINGS_PROPERTY_DEF(QByteArray, torrentsViewHeaderState, "torrentsViewHeaderState", {}) SETTINGS_PROPERTY_DEF(QByteArray, torrentPropertiesDialogGeometry, "torrentPropertiesDialogGeometry", {}) SETTINGS_PROPERTY_DEF(QByteArray, torrentFilesViewHeaderState, "torrentFilesViewHeaderState", {}) SETTINGS_PROPERTY_DEF(QByteArray, trackersViewHeaderState, "trackersViewHeaderState", {}) SETTINGS_PROPERTY_DEF(QByteArray, peersViewHeaderState, "peersViewHeaderState", {}) SETTINGS_PROPERTY_DEF(QByteArray, localTorrentFilesViewHeaderState, "localTorrentFilesViewHeaderState", {}) SETTINGS_PROPERTY_DEF( Settings::DarkThemeMode, darkThemeMode, "darkThemeMode", Settings::DarkThemeMode::FollowSystem ) SETTINGS_PROPERTY_DEF(bool, useSystemAccentColor, "useSystemAccentColor", true) SETTINGS_PROPERTY_DEF( Settings::TorrentDoubleClickAction, torrentDoubleClickAction, "torrentDoubleClickAction", Settings::TorrentDoubleClickAction::OpenPropertiesDialog ) SETTINGS_PROPERTY_DEF(bool, displayRelativeTime, "displayRelativeTime", false) SETTINGS_PROPERTY_DEF(bool, displayFullDownloadDirectoryPath, "displayFullDownloadDirectoryPath", true) Settings::Settings(QObject* parent) : QObject(parent) { if constexpr (targetOs == TargetOs::Windows) { mSettings = new QSettings( QSettings::IniFormat, QSettings::UserScope, qApp->organizationName(), qApp->applicationName(), this ); } else { mSettings = new QSettings(this); } mSettings->setFallbacksEnabled(false); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } void Settings::sync() { mSettings->sync(); } } tremotesf-2.10.1/src/settings.h000066400000000000000000000101071523411710200163760ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // SPDX-FileCopyrightText: 2021 LuK1337 // SPDX-FileCopyrightText: 2022 Alex // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SETTINGS_H #define TREMOTESF_SETTINGS_H #include #include "rpc/torrent.h" #include "ui/screens/mainwindow/torrentsproxymodel.h" class QSettings; #define SETTINGS_PROPERTY(type, name) \ public: \ type get_##name() const; \ void set_##name(type value); \ Q_SIGNALS: \ void name##Changed(); namespace tremotesf { class Settings final : public QObject { Q_OBJECT SETTINGS_PROPERTY(bool, connectOnStartup) SETTINGS_PROPERTY(bool, notificationOnDisconnecting) SETTINGS_PROPERTY(bool, notificationOnAddingTorrent) SETTINGS_PROPERTY(bool, notificationOfFinishedTorrents) SETTINGS_PROPERTY(bool, notificationsOnAddedTorrentsSinceLastConnection) SETTINGS_PROPERTY(bool, notificationsOnFinishedTorrentsSinceLastConnection) SETTINGS_PROPERTY(bool, rememberOpenTorrentDir) SETTINGS_PROPERTY(QString, lastOpenTorrentDirectory) SETTINGS_PROPERTY(bool, rememberAddTorrentParameters) SETTINGS_PROPERTY(TorrentData::Priority, lastAddTorrentPriority) SETTINGS_PROPERTY(bool, lastAddTorrentStartAfterAdding) SETTINGS_PROPERTY(bool, lastAddTorrentDeleteTorrentFile) SETTINGS_PROPERTY(bool, lastAddTorrentMoveTorrentFileToTrash) SETTINGS_PROPERTY(bool, fillTorrentLinkFromClipboard) SETTINGS_PROPERTY(bool, showMainWindowWhenAddingTorrent) SETTINGS_PROPERTY(bool, showAddTorrentDialog) SETTINGS_PROPERTY(bool, mergeTrackersWhenAddingExistingTorrent) SETTINGS_PROPERTY(bool, askForMergingTrackersWhenAddingExistingTorrent) SETTINGS_PROPERTY(bool, torrentsStatusFilterEnabled) SETTINGS_PROPERTY(TorrentsProxyModel::StatusFilter, torrentsStatusFilter) SETTINGS_PROPERTY(bool, torrentsLabelFilterEnabled) SETTINGS_PROPERTY(QString, torrentsLabelFilter) SETTINGS_PROPERTY(bool, torrentsTrackerFilterEnabled) SETTINGS_PROPERTY(QString, torrentsTrackerFilter) SETTINGS_PROPERTY(bool, torrentsDownloadDirectoryFilterEnabled) SETTINGS_PROPERTY(QString, torrentsDownloadDirectoryFilter) SETTINGS_PROPERTY(bool, showTrayIcon) SETTINGS_PROPERTY(Qt::ToolButtonStyle, toolButtonStyle) SETTINGS_PROPERTY(bool, toolBarLocked) SETTINGS_PROPERTY(bool, sideBarVisible) SETTINGS_PROPERTY(bool, statusBarVisible) SETTINGS_PROPERTY(bool, showTorrentPropertiesInMainWindow) SETTINGS_PROPERTY(QByteArray, mainWindowGeometry) SETTINGS_PROPERTY(QByteArray, mainWindowState) SETTINGS_PROPERTY(QByteArray, horizontalSplitterState) SETTINGS_PROPERTY(QByteArray, verticalSplitterState) SETTINGS_PROPERTY(QByteArray, torrentsViewHeaderState) SETTINGS_PROPERTY(QByteArray, torrentPropertiesDialogGeometry) SETTINGS_PROPERTY(QByteArray, torrentFilesViewHeaderState) SETTINGS_PROPERTY(QByteArray, trackersViewHeaderState) SETTINGS_PROPERTY(QByteArray, peersViewHeaderState) SETTINGS_PROPERTY(QByteArray, localTorrentFilesViewHeaderState) SETTINGS_PROPERTY(bool, displayRelativeTime) SETTINGS_PROPERTY(bool, displayFullDownloadDirectoryPath) public: enum class DarkThemeMode { FollowSystem, On, Off }; Q_ENUM(DarkThemeMode) enum class TorrentDoubleClickAction { OpenPropertiesDialog, OpenTorrentFile, OpenDownloadDirectory }; Q_ENUM(TorrentDoubleClickAction) SETTINGS_PROPERTY(Settings::DarkThemeMode, darkThemeMode) SETTINGS_PROPERTY(bool, useSystemAccentColor) SETTINGS_PROPERTY(Settings::TorrentDoubleClickAction, torrentDoubleClickAction) public: static Settings* instance(); void sync(); private: explicit Settings(QObject* parent = nullptr); QSettings* mSettings{}; }; } #endif // TREMOTESF_SETTINGS_H tremotesf-2.10.1/src/startup/000077500000000000000000000000001523411710200160705ustar00rootroot00000000000000tremotesf-2.10.1/src/startup/commandlineparser.cpp000066400000000000000000000067001523411710200223020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "commandlineparser.h" #include #include #include #include #include #include #include #define CXXOPTS_VECTOR_DELIMITER '\0' #include #include "log/log.h" #include "target_os.h" namespace tremotesf { namespace { std::optional substrAfterChar(std::string_view str, char ch) { const auto index = str.rfind(ch); if (index == std::string_view::npos) { return std::nullopt; } return str.substr(index + 1); } std::string_view executableFileName(std::string_view arg0) { if constexpr (targetOs == TargetOs::Windows) { if (const auto name = substrAfterChar(arg0, '\\'); name) { return *name; } } if (const auto name = substrAfterChar(arg0, '/'); name) { return *name; } return arg0; } void parsePositionals(std::span torrents, CommandLineArgs& args) { for (const std::string& arg : torrents) { if (!arg.empty()) { const auto argument(QString::fromStdString(arg)); const QFileInfo info(argument); if (info.isFile()) { args.files.push_back(info.absoluteFilePath()); } else { const QUrl url(argument); if (url.isLocalFile()) { const auto path = url.toLocalFile(); if (QFileInfo(path).isFile()) { args.files.push_back(path); } } else { args.urls.push_back(argument); } } } } } } CommandLineArgs parseCommandLine(int& argc, char**& argv) { CommandLineArgs args{}; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) const std::string_view appName = executableFileName(argv[0]); const auto versionString = fmt::format("{} {}", appName, TREMOTESF_VERSION); cxxopts::Options opts(std::string(appName), versionString); std::vector torrents; opts.add_options( )("v,version", "display version information", cxxopts::value()->default_value("false") )("h,help", "display this help", cxxopts::value()->default_value("false") )("m,minimized", "start minimized in notification area", cxxopts::value(args.minimized)->default_value("false") )("d,debug-logs", "enable debug logs", cxxopts::value(args.enableDebugLogs)->implicit_value("true") )("torrents", "", cxxopts::value(torrents)); opts.parse_positional("torrents"); opts.positional_help("torrents"); const auto result(opts.parse(argc, argv)); if (result["help"].as()) { printlnStdout(opts.help()); args.exit = true; return args; } if (result["version"].as()) { printlnStdout(versionString); args.exit = true; return args; } parsePositionals(torrents, args); return args; } } tremotesf-2.10.1/src/startup/commandlineparser.h000066400000000000000000000010321523411710200217400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_COMMANDLINEPARSER_H #define TREMOTESF_COMMANDLINEPARSER_H #include #include namespace tremotesf { struct CommandLineArgs { QStringList files{}; QStringList urls{}; bool minimized{}; std::optional enableDebugLogs{}; bool exit{}; }; CommandLineArgs parseCommandLine(int& argc, char**& argv); } #endif // TREMOTESF_COMMANDLINEPARSER_H tremotesf-2.10.1/src/startup/main.cpp000066400000000000000000000143541523411710200175270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include "commandlineparser.h" #include "signalhandler.h" #include "ipc/ipcclient.h" #include "log/log.h" #include "ui/iconthemesetup.h" #include "ui/savewindowstatedispatcher.h" #include "ui/screens/mainwindow/mainwindow.h" #ifdef Q_OS_WIN # include "main_windows.h" # include "windowsfatalerrorhandlers.h" #endif #ifdef Q_OS_MACOS # include # include # include "ipc/fileopeneventhandler.h" using namespace std::chrono_literals; #endif #ifdef TREMOTESF_USE_BUNDLED_QT_TRANSLATIONS # include "fileutils.h" #endif SPECIALIZE_FORMATTER_FOR_QDEBUG(QLocale) using namespace Qt::StringLiterals; using namespace tremotesf; namespace { #ifdef Q_OS_MACOS std::pair receiveFileOpenEvents() { std::pair filesAndUrls{}; info().log("Waiting for file open events"); const FileOpenEventHandler handler{}; QObject::connect( &handler, &FileOpenEventHandler::filesOpeningRequested, qApp, [&](const auto& files, const auto& urls) { filesAndUrls = {files, urls}; QCoreApplication::quit(); } ); QTimer::singleShot(500ms, qApp, [] { info().log("Did not receive file open events"); QCoreApplication::quit(); }); QCoreApplication::exec(); return filesAndUrls; } #endif bool shouldExitBecauseAnotherInstanceIsRunning(const CommandLineArgs& args) { const auto client = IpcClient::createInstance(); if (!client->isConnected()) { return false; } info().log("Only one instance of Tremotesf can be run at the same time"); const auto activateOtherInstance = [&client](const QStringList& files, const QStringList& urls) { if (files.isEmpty() && urls.isEmpty()) { info().log("Activating other instance"); client->activateWindow(); } else { info().log("Activating other instance and requesting torrent adding"); info().log("files = {}", files); info().log("urls = {}", urls); client->addTorrents(files, urls); } }; #ifdef Q_OS_MACOS if (args.files.isEmpty() && args.urls.isEmpty()) { const auto [files, urls] = receiveFileOpenEvents(); activateOtherInstance(files, urls); return true; } #endif activateOtherInstance(args.files, args.urls); return true; } void logLocaleInfo() { if (!tremotesfLoggingCategory().isDebugEnabled()) { return; } const QLocale locale{}; debug().log("Current locale is: {}", locale.name()); debug().log("Language: {}", locale.language()); debug().log("Script: {}", locale.script()); debug().log("Territory: {}", locale.territory()); debug().log("UI languages: {}", locale.uiLanguages()); } } int main(int argc, char** argv) { // This does not need QApplication instance, and we need it in windowsInitPrelude() QCoreApplication::setOrganizationName(TREMOTESF_EXECUTABLE_NAME ""_L1); QCoreApplication::setApplicationName(QCoreApplication::organizationName()); QCoreApplication::setApplicationVersion(TREMOTESF_VERSION ""_L1); // // Command line parsing // CommandLineArgs args{}; try { args = parseCommandLine(argc, argv); if (args.exit) { return EXIT_SUCCESS; } } catch (const std::exception& e) { warning().logWithException(e, "Failed to parse command line arguments"); return EXIT_FAILURE; } if (args.enableDebugLogs.has_value()) { overrideDebugLogs(*args.enableDebugLogs); } #ifdef Q_OS_WIN windowsSetUpFatalErrorHandlers(); const WindowsLogger logger{}; #endif if (tremotesfLoggingCategory().isDebugEnabled()) { debug().log("Debug logging is enabled"); } // Setup handler for UNIX signals or Windows console handler const SignalHandler signalHandler{}; #ifdef Q_OS_WIN const WinrtApartment apartment{}; #endif // // QApplication initialization // const QApplication app(argc, argv); if (shouldExitBecauseAnotherInstanceIsRunning(args)) { return EXIT_SUCCESS; } QGuiApplication::setQuitOnLastWindowClosed(false); #ifdef Q_OS_WIN windowsInitApplication(); #endif setupIconTheme(); QGuiApplication::setDesktopFileName(TREMOTESF_APP_ID ""_L1); QGuiApplication::setWindowIcon(QIcon::fromTheme(TREMOTESF_APP_ID ""_L1)); // // End of QApplication initialization // logLocaleInfo(); QTranslator qtTranslator; { const QString qtTranslationsPath = #ifdef TREMOTESF_USE_BUNDLED_QT_TRANSLATIONS resolveExternalBundledResourcesPath("qt-translations"_L1); #else QLibraryInfo::path(QLibraryInfo::TranslationsPath); #endif if (qtTranslator.load(QLocale{}, "qt"_L1, "_"_L1, qtTranslationsPath)) { info().log("Loaded Qt translation {}", qtTranslator.filePath()); qApp->installTranslator(&qtTranslator); } else { warning().log("Failed to load Qt translation for {} from {}", QLocale(), qtTranslationsPath); } } QTranslator appTranslator; if (appTranslator.load(QLocale{}, {}, {}, ":/translations/"_L1)) { info().log("Loaded Tremotesf translation {}", appTranslator.filePath()); qApp->installTranslator(&appTranslator); } else { warning().log("Failed to load Tremotesf translation for {}", QLocale{}); } const SaveWindowStateDispatcher saveStateDispatcher{}; MainWindow window(std::move(args.files), std::move(args.urls)); window.initialShow(args.minimized); if (signalHandler.isExitRequested()) { return EXIT_SUCCESS; } const int exitStatus = QCoreApplication::exec(); debug().log("Returning from main with exit status {}", exitStatus); return exitStatus; } tremotesf-2.10.1/src/startup/main_windows.cpp000066400000000000000000000023641523411710200212770ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "log/log.h" #include "startup/windowsmessagehandler.h" #include "ui/darkthemeapplier_windows.h" #include "ui/systemcolorsprovider.h" #include "main_windows.h" #include "windowshelpers.h" namespace tremotesf { WindowsLogger::WindowsLogger() { initWindowsMessageHandler(); } WindowsLogger::~WindowsLogger() { deinitWindowsMessageHandler(); } WinrtApartment::WinrtApartment() { try { winrt::init_apartment(winrt::apartment_type::single_threaded); } catch (const winrt::hresult_error& e) { warning().log("winrt::init_apartment failed: {}", e); } } WinrtApartment::~WinrtApartment() { winrt::uninit_apartment(); } void windowsInitApplication() { try { checkWin32Bool(AllowSetForegroundWindow(ASFW_ANY), "AllowSetForegroundWindow"); } catch (const std::system_error& e) { warning().log(e); } const auto systemColorsProvider = SystemColorsProvider::createInstance(QCoreApplication::instance()); applyDarkThemeToPalette(systemColorsProvider); } } tremotesf-2.10.1/src/startup/main_windows.h000066400000000000000000000007271523411710200207450ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTEST_MAIN_WINDOWS_H #define TREMOTEST_MAIN_WINDOWS_H namespace tremotesf { class WindowsLogger final { public: WindowsLogger(); ~WindowsLogger(); }; class WinrtApartment final { public: WinrtApartment(); ~WinrtApartment(); }; void windowsInitApplication(); } #endif // TREMOTEST_MAIN_WINDOWS_H tremotesf-2.10.1/src/startup/recoloringsvgiconengineplugin.cpp000066400000000000000000000052321523411710200247370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "recoloringsvgiconengineplugin.h" #include "target_os.h" #include "ui/recoloringsvgiconengine.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) thread_local bool drawingSelectedMenuItem = false; constexpr auto overrideStyleEnvVariable = "QT_STYLE_OVERRIDE"; QString defaultStyle() { if (qEnvironmentVariableIsSet(overrideStyleEnvVariable)) { return qEnvironmentVariable(overrideStyleEnvVariable); } if constexpr (targetOs == TargetOs::UnixMacOS) { return "macOS"_L1; } return "fusion"_L1; } } RecoloringSvgIconStyle::RecoloringSvgIconStyle(QObject* parent) : QProxyStyle(defaultStyle()) { setParent(parent); } void RecoloringSvgIconStyle::drawControl( QStyle::ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget ) const { if (const auto mi = qstyleoption_cast(option)) { if (mi->state & State_Selected) { drawingSelectedMenuItem = true; } } QProxyStyle::drawControl(element, option, painter, widget); if (drawingSelectedMenuItem) { drawingSelectedMenuItem = false; } } class EngineForStyle final : public RecoloringSvgIconEngine { public: QPixmap scaledPixmap(const QSize& size, QIcon::Mode mode, QIcon::State state, qreal scale) override { // QFusionStyle passes QIcon::Active for selected menu items, but RecoloringSvgIconEngine/KIconEngine expects QIcon::Selected if (drawingSelectedMenuItem) { mode = QIcon::Selected; } return RecoloringSvgIconEngine::scaledPixmap(size, mode, state, scale); } }; class RecoloringSvgIconEnginePlugin final : public QIconEnginePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QIconEngineFactoryInterface" FILE "recoloringsvgiconengineplugin.json") public: QIconEngine* create(const QString& file) override { auto engine = new EngineForStyle{}; if (!file.isNull()) { engine->addFile(file, QSize(), QIcon::Normal, QIcon::Off); } return engine; } }; } Q_IMPORT_PLUGIN(RecoloringSvgIconEnginePlugin) #include "recoloringsvgiconengineplugin.moc" tremotesf-2.10.1/src/startup/recoloringsvgiconengineplugin.h000066400000000000000000000011401523411710200243760ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_RECOLORINGSVGICONENGINEPLUGIN_H #define TREMOTESF_RECOLORINGSVGICONENGINEPLUGIN_H #include namespace tremotesf { class RecoloringSvgIconStyle : public QProxyStyle { Q_OBJECT public: explicit RecoloringSvgIconStyle(QObject* parent); void drawControl( ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget ) const override; }; } #endif //TREMOTESF_RECOLORINGSVGICONENGINEPLUGIN_H tremotesf-2.10.1/src/startup/recoloringsvgiconengineplugin.json000066400000000000000000000000451523411710200251230ustar00rootroot00000000000000{"Keys": [ "svg", "svgz", "svg.gz" ]}tremotesf-2.10.1/src/startup/signalhandler.h000066400000000000000000000010301523411710200210460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SIGNALHANDLER_H #define TREMOTESF_SIGNALHANDLER_H #include #include namespace tremotesf { class SignalHandler final { public: SignalHandler(); ~SignalHandler(); Q_DISABLE_COPY_MOVE(SignalHandler) bool isExitRequested() const; private: class Impl; std::unique_ptr mImpl; }; } #endif // TREMOTESF_SIGNALHANDLER_H tremotesf-2.10.1/src/startup/signalhandler_unix.cpp000066400000000000000000000144531523411710200224610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "signalhandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "log/log.h" #include "unixhelpers.h" using namespace std::string_view_literals; namespace tremotesf { namespace { constexpr std::array expectedSignals{ std::pair{SIGINT, "SIGINT"sv}, std::pair{SIGTERM, "SIGTERM"sv}, std::pair{SIGHUP, "SIGHUP"sv}, std::pair{SIGQUIT, "SIGQUIT"sv} }; std::optional signalName(int signal) { for (auto [expectedSignal, name] : expectedSignals) { if (signal == expectedSignal) { return name; } } return std::nullopt; } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) int writeSocket{}; constexpr int notReceivedSignal = std::numeric_limits::min(); // Not using std::atomic> because Clang might require linking to libatomic // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) std::atomic_int receivedSignal{notReceivedSignal}; static_assert(std::atomic_int::is_always_lock_free, "std::atomic_int must be lock-free"); void signalHandler(int signal) { int expected = notReceivedSignal; if (!receivedSignal.compare_exchange_strong(expected, signal)) { // Already requested exit return; } while (true) { const char byte{}; const auto bytes = write(writeSocket, &byte, 1); if (bytes == -1 && errno == EINTR) { continue; } break; } } } class SignalHandler::Impl { public: Impl() { try { std::array sockets{}; checkPosixError(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets.data()), "socketpair"); writeSocket = sockets[0]; const int readSocket = sockets[1]; struct sigaction action{}; action.sa_handler = signalHandler; action.sa_flags |= SA_RESTART; for (auto [signal, _] : expectedSignals) { checkPosixError(sigaction(signal, &action, nullptr), "sigaction"); } debug().log("signalhandler: created socket pair and set up signal handlers"); try { debug().log("signalhandler: starting read socket thread"); mThread = std::thread(&Impl::readFromSocket, this, readSocket); } catch (const std::system_error& e) { warning().logWithException(e, "signalhandler: failed to start thread"); } } catch (const std::system_error& e) { warning().logWithException(e, "Failed to setup signal handlers"); return; } } ~Impl() { debug().log("signalhandler: closing write socket"); try { checkPosixError(close(writeSocket), "close"); } catch (const std::system_error& e) { warning().logWithException(e, "signalhandler: failed to close write socket"); } debug().log("signalhandler: joining read socket thread"); mThread.join(); debug().log("signalhandler: joined read socket thread"); } Q_DISABLE_COPY_MOVE(Impl) private: void readFromSocket(int readSocket) const { debug().log("signalhandler: started read socket thread"); auto finishGuard = QScopeGuard([readSocket] { debug().log("signalhandler: closing read socket"); try { checkPosixError(close(readSocket), "close"); } catch (const std::system_error& e) { warning().logWithException(e, "signalhandler: failed to close read socket"); } debug().log("signalhandler: finished read socket thread"); }); while (true) { char byte{}; try { const ssize_t bytes = checkPosixError(read(readSocket, &byte, 1), "read"); if (bytes == 0) { debug().log("signalhandler: write socket was closed, end thread"); return; } } catch (const std::system_error& e) { if (e.code() == std::errc::interrupted) { warning().log("signalhandler: read interrupted, continue"); continue; } warning().logWithException(e, "signalhandler: failed to read from socket, end thread"); return; } break; } if (const int signal = receivedSignal; signal != notReceivedSignal) { if (const auto name = signalName(signal); name.has_value()) { info().log("signalhandler: received signal {}", *name); } else { info().log("signalhandler: received signal {}", signal); } } else { warning().log("signalhandler: read from socket but signal was not received"); return; } const auto app = QCoreApplication::instance(); if (app) { info().log("signalhandler: post QCoreApplication::quit() to event loop"); QMetaObject::invokeMethod(app, &QCoreApplication::quit, Qt::QueuedConnection); } else { warning().log("signalhandler: QApplication is not created yet"); } } std::thread mThread{}; }; SignalHandler::SignalHandler() : mImpl(std::make_unique()) {} SignalHandler::~SignalHandler() = default; bool SignalHandler::isExitRequested() const { return receivedSignal.load() != notReceivedSignal; } } tremotesf-2.10.1/src/startup/signalhandler_windows.cpp000066400000000000000000000051441523411710200231650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "signalhandler.h" #include #include #include #include "log/log.h" #include "windowshelpers.h" namespace tremotesf { namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) std::atomic_bool exitRequested{}; BOOL WINAPI consoleHandler(DWORD dwCtrlType) { exitRequested = true; std::string_view type{}; switch (dwCtrlType) { case CTRL_C_EVENT: type = "CTRL_C_EVENT"; break; case CTRL_BREAK_EVENT: type = "CTRL_BREAK_EVENT"; break; case CTRL_CLOSE_EVENT: type = "CTRL_CLOSE_EVENT"; break; case CTRL_LOGOFF_EVENT: type = "CTRL_LOGOFF_EVENT"; break; case CTRL_SHUTDOWN_EVENT: type = "CTRL_SHUTDOWN_EVENT"; break; default: break; } if (!type.empty()) { info().log("Received signal with type = {}", type); } else { info().log("Received signal with type = {}", dwCtrlType); } const auto app = QCoreApplication::instance(); if (app) { info().log("signalhandler: post QCoreApplication::quit() to event loop"); QMetaObject::invokeMethod(app, &QCoreApplication::quit, Qt::QueuedConnection); } else { warning().log("signalhandler: QApplication is not created yet"); } return TRUE; } } class SignalHandler::Impl {}; SignalHandler::SignalHandler() : mImpl{} { try { checkWin32Bool(SetConsoleCtrlHandler(&consoleHandler, TRUE), "SetConsoleCtrlHandler"); debug().log("signalhandler: added console signal handler"); } catch (const std::system_error& e) { warning().logWithException(e, "signalhandler: failed to add console signal handler"); } } SignalHandler::~SignalHandler() { try { checkWin32Bool(SetConsoleCtrlHandler(&consoleHandler, FALSE), "SetConsoleCtrlHandler"); debug().log("signalhandler: removed console signal handler"); } catch (const std::system_error& e) { warning().logWithException(e, "signalhandler: failed to remove console signal handler"); } } bool SignalHandler::isExitRequested() const { return exitRequested; } } tremotesf-2.10.1/src/startup/windowsfatalerrorhandlers.cpp000066400000000000000000000214301523411710200240710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #ifdef __cpp_lib_stacktrace # include #endif #include #include #include #include #include "windowsfatalerrorhandlers.h" #include "windowshelpers.h" #include "log/log.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) bool showedReport{}; #ifdef __cpp_lib_stacktrace constexpr size_t maxPathLength = 65535; constexpr auto maxPathLengthDword = static_cast(maxPathLength); # ifdef _MSC_VER QString getExecutablePath() { std::wstring executablePath(maxPathLength, L'\0'); const auto length = GetModuleFileNameW(nullptr, executablePath.data(), maxPathLengthDword); if (length == 0 || (length == maxPathLengthDword && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { return {}; } executablePath.resize(static_cast(length)); return QString::fromStdWString(executablePath); } # endif void appendStackTraceToReport(std::string& report) { # ifdef _MSC_VER const auto executablePath = getExecutablePath(); if (executablePath.isEmpty()) { report += "\n\nFailed to determine path to executable, not reporting stack trace\n"; return; } const QFileInfo executable(executablePath); const auto executableDir = executable.path(); const QString pdbPath = executableDir % '/' % executable.completeBaseName() % ".pdb"_L1; if (!QFileInfo::exists(pdbPath)) { fmt::format_to( std::back_insert_iterator(report), "\n\nPDB file does not exist at expected path {}, not reporting stack trace\n", pdbPath ); return; } SetEnvironmentVariableW(L"_NT_SYMBOL_PATH", getCWString(executableDir)); # endif const auto trace = std::stacktrace::current(); report += "\n\nStack trace:\n"; report += std::to_string(trace); } #endif void showReport(std::string report) { showedReport = true; #ifdef __cpp_lib_stacktrace appendStackTraceToReport(report); #endif warning().log(report); showFatalErrorReportInDialog(std::move(report)); } void onTerminate() { std::string report = "FATAL ERROR: std::terminate called"; if (const auto exception_ptr = std::current_exception(); exception_ptr) { report += "\n\nUnhandled C++ exception:\n"; try { std::rethrow_exception(exception_ptr); } catch (const std::exception& e) { fmt::format_to( std::back_insert_iterator(report), singleArgumentFormatString, formatExceptionRecursively(e) ); } catch (const winrt::hresult_error& e) { fmt::format_to( std::back_insert_iterator(report), singleArgumentFormatString, formatExceptionRecursively(e) ); } catch (...) { report += "Type of exception is unknown"; } } showReport(std::move(report)); std::abort(); } std::string_view sehExceptionCodeName(DWORD exceptionCode) { switch (exceptionCode) { case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION"; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT"; case EXCEPTION_DATATYPE_MISALIGNMENT: return "EXCEPTION_DATATYPE_MISALIGNMENT"; case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND"; case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT"; case EXCEPTION_FLT_INVALID_OPERATION: return "EXCEPTION_FLT_INVALID_OPERATION"; case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW"; case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK"; case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW"; case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION"; case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR"; case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO"; case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW"; case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION"; case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION"; case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP"; case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW"; default: break; } return "UNKNOWN EXCEPTION"; } constexpr DWORD cppExceptionCode = 0xe06d7363; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) LPTOP_LEVEL_EXCEPTION_FILTER defaultSehExceptionFilter{}; LONG WINAPI sehExceptionFilter(LPEXCEPTION_POINTERS exceptionInfo) { if (exceptionInfo->ExceptionRecord->ExceptionCode == cppExceptionCode) { // C++ exception, delegate to onTerminate if (defaultSehExceptionFilter) { defaultSehExceptionFilter(exceptionInfo); } return EXCEPTION_CONTINUE_SEARCH; } std::string report{}; fmt::format_to( std::back_insert_iterator(report), "FATAL ERROR: Unhandled SEH exception 0x{:x} {}", exceptionInfo->ExceptionRecord->ExceptionCode, sehExceptionCodeName(exceptionInfo->ExceptionRecord->ExceptionCode) ); { PEXCEPTION_RECORD nested = exceptionInfo->ExceptionRecord->ExceptionRecord; while (nested) { fmt::format_to( std::back_insert_iterator(report), "\nCaused by: 0x{:x} {}", nested->ExceptionCode, sehExceptionCodeName(nested->ExceptionCode) ); nested = nested->ExceptionRecord; } } showReport(std::move(report)); return EXCEPTION_CONTINUE_SEARCH; } void abortHandler(int) { if (!showedReport) { showReport("FATAL ERROR: std::abort called"); } } } void windowsSetUpFatalErrorHandlers() { /** * std::set_terminate is thread-local * SetUnhandledExceptionFilter and std::signal are global */ std::set_terminate(&onTerminate); defaultSehExceptionFilter = SetUnhandledExceptionFilter(&sehExceptionFilter); _set_abort_behavior(0, _WRITE_ABORT_MSG); std::signal(SIGABRT, &abortHandler); } void windowsSetUpFatalErrorHandlersInThread() { static thread_local bool set = false; if (!set) { std::set_terminate(&onTerminate); set = true; } } std::string makeFatalErrorReportFromLogMessage(const QString& message, const QMessageLogContext& context) { std::string report = fmt::format( "FATAL ERROR: {}\nFunction: {}\nSource file: {}:{}", message, context.function, context.file, context.line ); #ifdef __cpp_lib_stacktrace appendStackTraceToReport(report); #endif return report; } void showFatalErrorReportInDialog(std::string report) { showedReport = true; report.insert(0, "Press Ctrl+C to copy this report\n\n"); MessageBoxA(nullptr, report.c_str(), "Fatal error", MB_OK | MB_ICONERROR); } } tremotesf-2.10.1/src/startup/windowsfatalerrorhandlers.h000066400000000000000000000011201523411710200235300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_WINDOWSFATALERRORHANDLERS_H #define TREMOTESF_WINDOWSFATALERRORHANDLERS_H #include class QMessageLogContext; class QString; namespace tremotesf { void windowsSetUpFatalErrorHandlers(); void windowsSetUpFatalErrorHandlersInThread(); std::string makeFatalErrorReportFromLogMessage(const QString& message, const QMessageLogContext& context); void showFatalErrorReportInDialog(std::string report); } #endif // TREMOTESF_WINDOWSFATALERRORHANDLERS_H tremotesf-2.10.1/src/startup/windowsmessagehandler.cpp000066400000000000000000000222361523411710200231760ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "windowsmessagehandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fileutils.h" #include "log/log.h" #include "windowshelpers.h" #include "windowsfatalerrorhandlers.h" namespace fs = std::filesystem; using namespace Qt::StringLiterals; namespace tremotesf { namespace { class [[maybe_unused]] MessageQueue final { public: void pushEvicting(QString&& message) { { const std::lock_guard lock(mMutex); if (mNewMessagesCancelled) return; if (mQueue.size() == maximumSize) { mQueue.pop_front(); } mQueue.push_back(std::move(message)); } mCv.notify_one(); } std::optional popBlocking() { std::unique_lock lock(mMutex); mCv.wait(lock, [&] { return !mQueue.empty() || mNewMessagesCancelled; }); if (mQueue.empty()) return {}; QString message = std::move(mQueue.front()); mQueue.pop_front(); return message; } void cancelNewMessages() { { const std::lock_guard lock(mMutex); mNewMessagesCancelled = true; } mCv.notify_one(); } private: std::deque mQueue{}; std::mutex mMutex{}; std::condition_variable mCv{}; bool mNewMessagesCancelled{}; static constexpr size_t maximumSize = 10000; }; class [[maybe_unused]] FileLogger final { public: void logMessage(QString&& message) { mQueue.pushEvicting(std::move(message)); } // We are not doing this in destructor because we need // thread to be able to call logMessage() while we are joining it void finishWriting() { info().log("FileLogger: finishing logging"); debug().log("FileLogger: wait until thread started writing or finished with error"); { std::unique_lock lock(mMutex); mCv.wait(lock, [&] { return mStartedWriting || mFinishedWriting; }); } debug().log("FileLogger: cancelling new messages"); mQueue.cancelNewMessages(); debug().log("FileLogger: joining write thread"); mWriteThread.join(); debug().log("FileLogger: joined write thread"); } private: void writeMessagesToFile() { windowsSetUpFatalErrorHandlersInThread(); debug().log("FileLogger: started write thread"); auto finishGuard = QScopeGuard([this] { debug().log("FileLogger: finished write thread"); mQueue.cancelNewMessages(); { const std::lock_guard lock(mMutex); mFinishedWriting = true; } mCv.notify_one(); }); const auto dirPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); debug().log("FileLogger: creating logs directory {}", QDir::toNativeSeparators(dirPath)); try { fs::create_directories(fs::path(getCWString(dirPath))); } catch (const fs::filesystem_error& e) { warning().logWithException(e, "FileLogger: failed to create logs directory"); return; } debug().log("FileLogger: created logs directory"); auto filePath = QString::fromStdString( fmt::format("{}/{}.log", dirPath, QDateTime::currentDateTime().toString(u"yyyy-MM-dd_hh-mm-ss.zzz")) ); debug().log("FileLogger: creating log file {}", QDir::toNativeSeparators(filePath)); QFile file(filePath); try { openFile(file, QIODevice::WriteOnly | QIODevice::NewOnly | QIODevice::Text | QIODevice::Unbuffered); } catch (const QFileError& e) { warning().logWithException(e, "FileLogger: failed to create log file"); return; } debug().log("FileLogger: created log file"); { const std::lock_guard lock(mMutex); mStartedWriting = true; } mCv.notify_one(); while (true) { const auto message = mQueue.popBlocking(); if (!message.has_value()) { return; } writeMessageToFile(*message, file); } } void writeMessageToFile(const QString& message, QFile& file) { try { writeBytes(file, message.toUtf8()); static constexpr std::array lineTerminator{'\n'}; writeBytes(file, lineTerminator); } catch ([[maybe_unused]] const QFileError& e) {} // NOLINT(bugprone-empty-catch) } MessageQueue mQueue{}; std::mutex mMutex{}; std::condition_variable mCv{}; bool mStartedWriting{}; bool mFinishedWriting{}; std::thread mWriteThread{&FileLogger::writeMessagesToFile, this}; }; void writeToDebugger(const wchar_t* message) { if (IsDebuggerPresent()) { OutputDebugStringW(message); OutputDebugStringW(L"\r\n"); } } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) std::unique_ptr globalFileLogger{}; [[maybe_unused]] void releaseMessageHandler(QString message) { writeToDebugger(getCWString(message)); if (globalFileLogger) { globalFileLogger->logMessage(std::move(message)); } } [[maybe_unused]] void debugMessageHandler(const QString& message) { const auto wstr = getCWString(message); writeToDebugger(wstr); static const auto stderrHandle = GetStdHandle(STD_ERROR_HANDLE); static const bool stderrIsConsole = [] { DWORD mode{}; return GetConsoleMode(stderrHandle, &mode) != FALSE; }(); if (stderrIsConsole) { WriteConsoleW(stderrHandle, wstr, static_cast(message.size()), nullptr, nullptr); WriteConsoleW(stderrHandle, L"\n", 1, nullptr, nullptr); } else { // stderr is redirected to pipe or a file, write UTF-8 const auto utf8 = message.toUtf8(); fwrite(utf8.data(), 1, static_cast(utf8.size()), stderr); fputc('\n', stderr); } } void callReleaseOrDebugHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { const QString formatted = qFormatLogMessage(type, context, message); #ifdef QT_DEBUG debugMessageHandler(formatted); #else releaseMessageHandler(formatted); #endif } void windowsMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { if (type == QtFatalMsg) { std::string report = makeFatalErrorReportFromLogMessage(message, context); callReleaseOrDebugHandler(type, context, QString::fromStdString(report)); showFatalErrorReportInDialog(std::move(report)); std::abort(); } callReleaseOrDebugHandler(type, context, message); } } void initWindowsMessageHandler() { qInstallMessageHandler(windowsMessageHandler); qSetMessagePattern( "[%{time yyyy.MM.dd h:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{message}"_L1 ); #ifndef QT_DEBUG // Make sure that Qt remembers current thread as "main" thread. // If logger's background thread is started before QApplication is created further in "main()" // then it will be remembered as "main" thread through QFile constructor, and Bad Things Will Happen // See https://qt-project.atlassian.net/browse/QTBUG-146190 QThread::currentThread(); globalFileLogger = std::make_unique(); debug().log("FileLogger: created, starting write thread"); #endif } void deinitWindowsMessageHandler() { #ifndef QT_DEBUG if (globalFileLogger) { globalFileLogger->finishWriting(); } #endif } } tremotesf-2.10.1/src/startup/windowsmessagehandler.h000066400000000000000000000004471523411710200226430ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_WINDOWSMESSAGEHANDLER_H #define TREMOTESF_WINDOWSMESSAGEHANDLER_H namespace tremotesf { void initWindowsMessageHandler(); void deinitWindowsMessageHandler(); } #endif tremotesf-2.10.1/src/stdutils.h000066400000000000000000000042601523411710200164140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_STDUTILS_H #define TREMOTESF_STDUTILS_H #include #include #include #include #include #include namespace tremotesf { template inline constexpr std::optional> indexOf(const Range& range, std::ranges::range_value_t value) { namespace r = std::ranges; const auto found = r::find(range, value); if (found == r::end(range)) { return std::nullopt; }; return static_cast>(r::distance(r::begin(range), found)); } template inline constexpr std::optional indexOfCasted(const Range& range, std::ranges::range_value_t value) { const auto index = indexOf(range, value); if (!index.has_value()) return std::nullopt; return static_cast(*index); } template inline constexpr auto slice(const Range& range, Index first, Index last) { const auto begin = std::ranges::begin(range); return std::ranges::subrange( begin + static_cast>(first), begin + static_cast>(last) ); } /** * T1 is always deduced as value type, T2 may be a reference */ template requires(!std::floating_point && std::same_as, std::remove_cvref_t>) inline void setChanged(T1& value, T2&& newValue, bool& changed) { if (newValue != value) { value = std::forward(newValue); changed = true; } } template inline void setChanged(T& value, T newValue, bool& changed) { if (!qFuzzyCompare(newValue, value)) { value = newValue; changed = true; } } } #endif // TREMOTESF_STDUTILS_H tremotesf-2.10.1/src/target_os.h000066400000000000000000000012151523411710200165250ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TARGET_OS_H #define TREMOTESF_TARGET_OS_H #include namespace tremotesf { enum class TargetOs { UnixFreedesktop, UnixMacOS, Windows }; inline constexpr TargetOs targetOs = #ifdef TREMOTESF_UNIX_FREEDESKTOP TargetOs::UnixFreedesktop; #elifdef Q_OS_MACOS TargetOs::UnixMacOS; #elifdef Q_OS_WIN TargetOs::Windows; #else // We shouldn't even get here since we will fail at CMake configuration step # error "Unsupported target platform" #endif } #endif // TREMOTESF_TARGET_OS_H tremotesf-2.10.1/src/test-torrents/000077500000000000000000000000001523411710200172235ustar00rootroot00000000000000tremotesf-2.10.1/src/test-torrents/Fedora-Workstation-Live-x86_64-34.torrent000066400000000000000000004536201523411710200264530ustar00rootroot00000000000000d8:announce46:http://torrent.fedoraproject.org:6969/announce13:creation datei1619460779e4:infod5:filesld6:lengthi1062e4:pathl41:Fedora-Workstation-34-1.2-x86_64-CHECKSUMeed6:lengthi2007367680e4:pathl41:Fedora-Workstation-Live-x86_64-34-1.2.isoeee4:name33:Fedora-Workstation-Live-x86_64-3412:piece lengthi262144e6:pieces153160:ÈÃv!ÔDë[€®';8òøø;.Ch½ ˺R¾sþ Ð4è+ŽŽ]aÜxJÇ¢Õ%„ y™­ˆß¿€úm–Šeo1ŽÇTÚÄ?5ùe5»¯@Ëq®Àwz#¸�NI¡Ïã” hÂóRJ˜IR”ê4²¼ðE i³¾ì=áЯV ˜…Ègˆ‹i¬é�o\æÓ¤_V5u-å$Wk’Q-'^þ~ó]1»€ÂèeV\0oxIw¶ú˜9qu¨KŽˆBãÏ×]ïAduüoƒÇÄÙ"^µl�µ­•Ó3Ÿ_¡—ÐU,cV:ŠŽ6M¹ãM�Ó¸ÈéÓt`š«à7ÀácZ*GÀTÓ�|+‰£¦fÐ Ùº¢R'hþ@c"R˜²Ú2w•\•Á=©²E¦jô%æïÉVjƒç>·û̵å¨â \}Ø?Y€½PBF A•–Û,{n¿$h¿8™¡ˆ5ªù—ÜzOÿ22¿Î½Ñ .èq'†óNk‹Ê­®„ûWœm]Ôù‹Øðä�ø¨5†³ ÚŒuÕDVýí¤óSžKDüì1�êÉ·žvP¯tÏîö¡½Íy`öè¼0„€§€ËUëž ˜£Ù²Å†óIr�É0ã”f!q�Aœïré莇¤‰e�}ö<˜Ó]؇T¦”ùŒÂÏzü 6„¼+¼ª©ßDL…vhÕˆ)Z�A{29ǪOËç.H:Ž>¯¬móîa  |~8"`ç�ɪk·M£í@ãó™Á<Áú aaqÂ4\°\–7[íÌ£HäÕPoØO»Ø¹s¹øèªãÉp,I™36%ñ°¼¸¢ÈÖ„$<6 €c† ƒ/�17ô¯ÚÑû¢l!ᵡ;¹ê24\i´¨¡ Ö—Â&»ºðåNþXþÊr’µüÜÅS ÈÐzT©²^ëÝ…{�2¥È÷+@v$u®8‹†ê^ósÆ4@Nð«þ?>çõâ,¡l–I��É<@œbB­LZôùôç}’7Ãä_p>ݹ‹½.§èWYÇôÂTÔÙÃ>ô�äY§÷¼ÛYÏ3èä0!–H|Unâ¶\‡5Õt Yo=cÇ_P?ùcº™^U!.OV;XEReIý>�`p•ùúزChÇõà6.NëbDÎ<Ð�ST‡K—4”k‰‡³)P¬—VÙÜëcÀY¦»÷ÍKnBÒ¢Q±Ü_ÝÜ–‚‡7Õ�¹ .5)ÁhV;òëF‰t”žÃ²¤Ýœ\ ?–Åoï î¿s”YpdôõÚÖLļÊÛÙž@�T¢ÙÖˆXïøÊ/MS»hó£/˜›Q~ÌÀÌõãž*ˆã‰Ô«›ùg4æÄ!Åå4ið(ˆ±ïºu”±Óæ¶‹Èžõ£©“@_ƒªEÏÈ’[FSõÒ»Lâ9�çý÷}º‚‚¥æz­’=¿·û@G0÷-¬•9q“gôÚí¢<¦%fÃaLS¬`_Óup³€Â Ž‘p8ûe/ì„_,Ú]c"ãÛä8‚²?úŠ„£V·µ#Íd˜˜ÿ‘ò(cÅq€É`žmÖIf4íþr|–ÁV`¿+þÖ颯âÿO¹ëýe4ôVj”=&làfü¾…¡wú"�ìõßX!â³$}NIQ²Îe)cY×Irn±íß6�u”–ŽÒ*æ�ãË üP­›ª bŸûsüÍM­¼~eçÖ1 P#†ø|li%3ŽbmùâgKªˆ¡ºÉUt.ùî숆Œë¡ówÐ97ÉÈ#wD Ãt%3xW‡ú]Ò´–®:ïŽÛ“˜ ¢�„‰=Ÿ‹ƒ–…ÿ‚¢/ÝÇ—ÙüjtùnjÆÿQaðS|Š>ËÃ¥ç+ÒöHœ`UtíUÄ´(¸G°—_n¨âó(¦—wѧIC=ˆÍÐ4ÔZÏÉ– Xo¸úŽù®œ�À0Øf_•ú@Tâ;Ý ™íºëk€ÆñßlŽ@Œ½™î Æ-ÂðÃùAAqLLVïÇ’.DO‡‹÷÷vàh|ûÌæ¶RÊ&£ž&š$ï�ÎØ´q-ëw¤g˜áh9±>¸d„Ì£ î‹;áÒ‡Y ÚžÐÁ^r�?è µ�¸DÝ2~zåÀÅÎE{Èfã4^¢ÊJUmÄ>Õo=Ô2døù¥JÝxÇÄEB?n-­Ý5'@E­«4»šª‚­vnYþH&sñ0íì#/ ´ºÖ=ž¸G£dSïAQ­ÒpÍ$ÂÌ,JAçÿ°Ñë¦âª½Œ—"P—»ÓÓÎÀf7ÿ§\Š>Ó‡õ7EæÓ.µ†ßHëæ7‰°å‹ˆ°ïŸðP�Œæ¢žßÞÆq<¾òÝMçŸ^™�àF�ñ.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§¬pˆÙ—–Cà –Ü�V¥‚8Ðm•òé?mDÖÞ7�‚¨Yb‰×a“¹÷°1¢Àæ}™wjÛzƒ,q»ë�øÏomÃ?Áp#FÂC¿;–ÈVžƒ‚´ƒH¬á$ãÿêf9Œ +÷¤­œ1[¥Ûæб’ÑTNYpæ3ý;èa øÄ4 K.ñJ²AÁ‚‡¿22R-¢÷8èZ´nQ7û‡f@$‰(…euÉ$‰2ãÁÔ÷¬~Q$õƒúÕ!°÷ÈÔ�^ßößHvȨ/½º¶9beþè[½HÔè¶+>Ÿ‰ZÄkÝ%jöáä¨?izâ8%�ß!bıÀcËâ0Yß$ 9)"PCÌÃPECn*UHؽ}åEŽ/Íj�Y”ìºù™(Ýh³#gsâµ6;Wy”h$†µk°:CÚÊ(�CN¶í–*2=1n„òV¦‚û Áô.áíÔíCÖu§¥ªó¹ó£Ar„a þÓL_?}=qZjc §íI©Ý¬¡þÖ°Fc%‚ÅÒ†%¸0ëýˆ–¾#ìGn7xÐÈý+¯‚—Ф�×j¤5™Š¿S ·dWfvgÔŽ^Ça1»—ËÇ~oð‹ÿ¹åôÙZ>J�¹oC�®Öšƒò­|m"ÛÿéãUû”&" O 6@lÃà=K4³zíö‹™ð UÈθ¹Jw°Áo«i'p¢Öwb0CìÍÎ-o¤íå;„`‡ ÞTO±úW=D¾Ã¹”ÙŽ!‰½Cu»{Âb)À#�A3QApN]?ú·Q#Ñ Ë“†É{®ûŸ±xˆåòŽåñÜ¡ðßâÒHy1¯>®LMÎu3Jv…²Ð|÷7�öäÚˆ·9oýŠäl¦EÑÀàÔá�¾–‚*vWQç{òìMf@º¨�£nÆ¥)[=¯h¹vçß»£o2¨P¯T«¥-Öd¿ØË<¹|Ñ®ÚÙø�¼Ri |)]Ì':ü²ÇJð˜Ðn¤õ[‡U©S€õκ—ñöðE?¡ñ<ü-WßçK8¶ê)ÂÔ2Yb­“N�èNÁQ* âúd!Ý©Oå]…A?éÀd¹¹¢RGÇîXò,¬ÒlÈj³�¡¨ Qñ3´wz_B›/CëòRN³ÇÝ;gcOØT`cˆãyÿʼnWS.“94{÷A+äê/žZup YÑ=d´(œKp¡›3üƒr�)„ªÿu°eËûß*XÒcmˆ|uÙ8´Ž)§nÔ·w%�Î ',ƒzA£=Ó™šz|ÃÂ.7ù£º•ðéàWÞ—½x#c×À�D,N¡ÿéŽÓ—…ñï •æìÓ¬úA/Nø(5¡‹^\•{-S–k%´@w' d#(U§ÀµƒZûЦÀ„#ŠgÆš*ýÙ,8]Žèêìí æ2~¥$Ëy. :ìáh›ë ΦööKiNÿVËUvÝÖÑ[JÝO2Õv oÞ¦‰ÏU¬ú˘õ�<²•Ú:‰à/»|^艡Z²LµÃâ¾¹†$"uÄÆÜ÷¦oZoZÆûÕ[Š ½´#füÆ�ê¤pùà$­üš~q<ì®�ÓY“Ê p<^b's2•¹›Z²Öùömâ$A`GåÝ_¥%�VË)h³f3ã|wÁÁ]/ÉÖŸaÐèŽRÉGÖÈ 8¤ôBS²]ÿ“½¹oCÒ”q«ó05F¸’öâ¼p‘z#Û 0ö“15ÔLî¶+(_ ¡r´WŸqáSÀVOð~£>+e”�'h$*-ÎþwÓ7Äþv—, ¹YŽÕv‘qNé¨û1Ϙæò³ôÿáiU�ݤþeƒŸgÚ÷U§�—¦æUsVóƒÜЇž4S{á|›nÅ­l—$bM�36rBëfâÖ=­ßi9ãÌ¿«¤$B/¨�î@!H;„,CÞ�„”šè’ÔFo,xüz{D‡¼ {o–ì$Ã"”‘…ïÌΰψC±j´Ð ˆQªÿÀ~øcµÍ=úŠ.œDdÄ©¿Œ <þuÓ‚u²ö!Þ‰b¸UD—ÿ0©¶üëðÁ4mû=2>B4`[Cu@ÏvmGÖKC8üŠâ¸©#Z†Ä=FI«]Ǿµ0ÎÞd k >É5i|)¶Œ7n 8Ê…Üå=nïP²ß‡ ¶BEès L õ\ÅûGZñ‚ …ëÍLºŸ ™põ!ÁaÀ@ÒÁÐzݳ¨å%¿Išÿ’µÇ>¬¢�ÜG·(:úºïv jN µÞÔ”ý½ç-<ÜPÎg3ðª‚OŒÐ`XcžR…½Ë²Ût¥M‘½À­§–Þ€k•ö´ÝÂSZz¸‘…®¿òùëT\(Q¼]P1úññ¯Ú«´9ô…4™r÷G—éWŒè‰DŸ…õ=º¦ý5´ø¡€-¦4Xß²Í�ζ¡[‡6%5™£®ëN¶ŽEF¬9¡yNƒÀßXXKE§;0ß0–â‰zÑ;(»gñv6õ�ˆ¡‘ÀA–ÕpÄXÆfÐÈ›èèÚë ¨í ÚÝâ‘kí¦ q¨ÏZï™5jü‹Óƽ„;oŒÃÊÚhÔ3àñ?}Fp¿ðÀ~Xš~¥KÑ*#·FÍ«¼�Ðè‘áÈK<ßòãØUà¥G–¡§5+ØLù¼Óú7 pº©åb¨™Næ1ÞBÙ D}Ÿ�ÈÊú`oÕ^Íf¹¨;¡Xüu VŒß=‘,<�÷Wbœ_á?üà°•–LÁýr:EXôiŠ—ÈU`¯Žê¬s¦(œ‚ÂóQQ¢D/„ªûï÷?ïâÄË ÓgŠ�‘Yû`i,xÓC˜ê\ƒ›2Š[Î'aÐS ­ Þ³oâMÜ·ÙW9›}âïäÕár? ¤xê"1Uí³;KJ8÷íiÊl0eÙƒ¯[ñëI*Ï_ Èýøìï4ëž°¹Í—_#Ÿ�ȇ:„èÅ•O?9ÏWå¸(ʋǬñ‘Îh1MˆÓ3© ¹æÜ}—¢Ö”h¥¯ÈÛïet“{Òp?Dg™Ï0)³Aº¦côÎm )Õ--o4Ô&LË‹ÓW¥Â }�MaëJD§öVœ0¾Ü¾W ÆË{½ðüܼ`÷r̸ƒõë? p}+-bàmp™Àöt�U‡ÿ8|ˆ»Û9Îk�kà5Rµå"‰HÈU²O ÿ2 ΀ÅE~ÂY »Áz½âˆVQ˜(η{YÅœ !“´¿,\öÌÀÕ_J§¦b$'(xÞâÜòR,0ìΣ·?6}~“²<�R`®óø9›¹>öñ¼kwzî‹nª<1ù6Pg0âš^‘i‹øŽºCÆïÒ&�?KbKðƒéžHDT`µq'enCô.0»ÖúŒ i§iŒ3ïðqâѳ0½þnßxtþ]t‹ŸÞŽ1…Š£Ñ×'�¾L’¨-ЧAú&ŠWPiaÍInP8ÑñœŒüMfa„ÿ@ê®uiж­ð�$…uÖ¥SP×#‹¢T*ãè» áYv™!£ºÀ=ä©ð*Tê Ñ;¾'„˜†–é _…�mꛄ¦xäMvÌŒ6 ë üêu/mÞrzçš¶î¿J�VÚ6ÞµZ,5b;FƒKçžuéãY�Yly$žƒZbSx–þ¼–¯¡‰-C„¦Á‰¡HÊ ^*4òR Ü„áê‰UOµ4(^ILûX�»gKý…N÷1ïµR d=J½ZáÅjIÇ�š£þ؉fýOãØ ˜ë1Ëß±Ï uÞràÿ†°�­SËñˆK{[ ¾›vd>—œ~ÏHÇuS·sÐÚ+í-Óí.ké±ÀŠA0$N•‡K�´‡H�éqP׊%)Oêà+4‹úK»–?m}¯9Õ:×¹÷ãy¤qB¶åCÒV›8c{à&#ÔNߊ|íûÀê玽m|u~ñÙÞpæ-ËLû7#„ñÎù°­´ë¿}°�†Ö,I% ‹ôê\þ’ÀªÇnqp-c—ª"kc9tÝp`Û°­Äªn�˜»zÕ?í¯Vœð¾ ÃkiÚí÷µ»”‰`%€V¤�ÝòD/*´ ôwgºÍ»ƒ(g¶(sͱîog8`}à¡]uBZæÛÎÚ¬¦`DPÏÔ†½ö˜‚Rc<õõý„¡$¨¶¬Wï(0Ö6 )�#rÃIeÉÍIJÐ6T2Ê_¾ó@ Å8p­|v7ä±°Èn¸-B á ðeäˆ5T¸‡Ž:§–iòšéú1Ã÷H¡\Ê1:Üá„°1ÑøÙ¯§ÅdÃ;ëöOòС»+R˜h.twaϾÏC´ÃMkµx×Û݉¥¡ª‹¤ìD£oQøº ßS€KÍKž%ÜšÖ5±¨‘uú©ªW™8Þwm>„ Ø9Üé<Íó<ïiÏäMI¡›ß–×¶gø¤9Ú yÅ‘=¼Ï#b3q¶šÞ#‡@րě#6‹ºŸê,Üù\Œ�󮇀¹ Ðâága ÞB¥‚³,äÌÛ9êXÞ4¦�‚unÆoMÒ‡è®î ‰ÇK­m·?%˜î½ùn ·¬_HÚLyé^ÞpÕ¶Hb3g5@üLlQ]â†ï–'‰�v„­¥\Â(ju «æ7N2»>!¯J¼"ÿUÇ(ÛéÍMÁµ=U�týYNF(ù¦Ãº4´¨CQ™o›EU’ôK”4–€ßsÄ:·Àô¤¼mI#RûÝ  +¦•ôX«—¹Ë¤ãykŒ%ˆYV®Þ ªaO'F +«��YÐcQÁ®›¾fj-ƒ1‹¬†²gTé\wSh¾wGÑiûÎ8Þå(ÜŸ!ѧ7úOÎÍÓmŒ¸¯f(Ÿß±¡“}¢u ÷M‰aØÃäžÕô¸Ö¥-'J�O·˜âu–t |á_õ�1áSO}µƒK‘ÿHD`x¿ )깤Ï(æÈàÿÛG¾µë]“MUÿ!•Ê ].:ÞÛxœ‘z �¥AYÎ΂«ª¶2‰¾Ø�É>ëò/~í*¼Á"(ÑA»I±°=[UÞBVÒìHeÅ!ý�ýÜ«××(rÈ{|w Qrs} >¸üá+YR%u°iþühkú,Éí 5Ý™^0ŽóÜB)M™@°ûßõ!$ öú ¨"ÒDó„E6ÖÇêñ%.…ÿðÁ‘–¯_òhXAÀ³H ˘ðI„íü.c8 ¼ðú Š3 ç5æÞ£N‚JÔàÚ±‰¿¶¿õö[pçG¿à?úü²f”�µš 1”—Þº¸å,Ìèd2sÉyF™¬jüª­ŒV[·×>Ó¢M\½&å3“Å{?­ÇÜvÔ�´-—©7(žŒ8n&áO3†ŽÉ"Êÿ7«+jݲ\Zï²§n²±¾K°ËPt ]`E:Út÷e‚3Ák‘”‹@Ði7寓�oÙ§a^ŽXð�b³§åC¹ÂT ŒHò”Xù`Bp�Uy“š$�Ƽ,�™‡³bZx/jω&½USO¢D´‹}æmu[ªÀRC±³8JÖ5¤ ºÖ÷/b6Q‹uJ’öê]޶~r‚l`pðÌ"¹6à!� ”ʼW�ôV�ý'kîr>:¸ý¢¥ Ù£•ájXVÆÞdA-?®²ÒÛ”™ÊhR( x}6>ÞpA+p1CNº² Š[‘( ŸÊ"aõë@ê&© 5Ìžá³èvµ¥¾Eõ~–öKŠy¹Tï+Ý:’p¹¡<†u â oɈÈXl µÏÙ!xwç­«VÚ︢g·•�ÏÁçr#ǹÿd™»bÐbEOD@‰Ç8ް»l/[‘5¶­!¹">±Æ6Î�Ž Ö¦Ööré±È@>}]ø,‹TU25ÂÉ=2_ƒx°“ ³gVÒݺ¶õ¤»×´kJíér=Öì. KÛÓŒœû×ü�¸ùµŒ'ƒÌÌvà}BähÕánkµZ¯EÃ÷¶ˆXk¢ËIçM _´ÛEbäu÷��™‚I[9!_Ê�†qðî�Ø ÔêêÛá³ 1"¬AÏ´€�ÅÎ$é!µG�ÿYÛ%å'!]/öa5 ŽOÛz¾OWÍ»7ÚšOùª”é6è>©ÛCgÎÞ´”‡›ãb8\ƒ·‰ÐáLÞæ �ju¶­‰&äd53ê€Wreí ©´Ó ç„<)Jjý›‚8¶â.ËM¶f´@#ÂH@ Õåi·}¶“;¥l'}�FN?»à¾†Äü¯�½¦­ùôÙO1DzÞ�âj«ïÄöyú¶�ºx³¬R.¿-Fà1«YrœèÌ<™‰sšD³ìÑžp-Ôª­ùîŠçP†I ”ÌûZ+šq; ÀO{ý‡1s…䔋–%;<âK d‰;WŠv¡:²{Ÿ÷Yý¾]`ôýþ&y?ê1¶‚¯ìÔÔ XAvzG`;t¼*áéI¡ZzÙ³ÑkÜùj†LÕë^Ñå÷\a 4&µá[Ÿˆé5»úÈþp[Jé[ÄïUÕ!ÆS¡ÚÌ¢$~Þ€Ú�NßuOgH&´R�ÕµG©8cÏË翆¯°’­é¨�v\µš#yB·óœ•l†9s®-8�§ÚŠUEN®Qhù,8Ï^mK¨$¢…'ÄòʽD§æŽ˜->6³N…ŒðjÞ 0�ã!Ïz*‡«~·�Y<[}'÷½»v# äb (Ôg±¼ZΔ4=;Øê›Òà´ùò—ΩÇ0°=™ì8\p¦Ü7N(ÖéÇá_%"tÎe¸Q©&ÌíŒkþE'99êgõ�AP «~ãämR§Êz¡³å’xXÏ ñ¹™ò0Pÿkq:Ò€ ¾y¨íN6ä«Ar`ß0g’ààßTD¾kgBªËÅû£êiš¡\¦S-»ô|0Öƒã ÿúpm|³#´*r =nö£ß²øCý°ÀßÉ,'«ÆüP’Óf=):cp0š›ÒZ¿hGa�îꢳò`N%#|F…Ò¡´7¾°,ã´²5|Ýÿ$+Jb<õ(@ˆŒdG}pÒ5È›;¬°J«TßݹwoŒ¿®8�/¯R ~uJ» ŒwË€.®‘—ïr µÞ;.ZöÒäjSÙág1„ƒÚy—n R:Üd�®œ(ßèi"’Yá0 ¨,ݰ <8¿»°ù>ߌrއë͵%“©ˆÜUükK]Ä&‡¼å Koõë•@ÚÁ¤¶—Î8�g9Z³§®×=Ÿ9ÕñD–]´y¬ÁT¡Óº‚yá®n[t,ó@6J%ð^�op  …¡(¢gÈ}v±£ú¾Dì K²¾+LÒÒ} N… qý"Š[¿A¨kèôé¼¶‘¤ \$‡†ëÔ˜ŸuÐJ$`MÓ¸àq¨4±ôÈNáÍòN ¨cb]}ÒñÇ"’QöE¯¢ÃÇ'VåŒP Ú%ô8b%'Õì‚™ÙýyÚkí>?.jˆá²ðô~ k 8‹J’Òѵqµ¡¹[P{±Íø»G aïÕàÏýV›O·.:«2Fín¹€ÿ™…ƒùGFë’˜ª€x±‘øh»èQÝiQh)ü¢H«ø¿¹üJ“@ ÐÖãi±µí`�‚Ì'ø?âèàw¤’ˆïL{áø ¯Ä1œ›6{ÃþWÐU¼íݥߛï [sÔ ú–Ûé*•hq`ÇcʼnáBW'w™ ¥Nof.õlPö…}‚�¡¦bÂ`Ò…gâ3+ëGÅ‚#zEF6ÒØ<-lñ ÌxîZè²îFì‡+‚ºSë—êgÄœõ0³G?¿Ê¿™p7é¥KRš1 8ApêpMƒÌ|�ÎS§è]ÈxÞ×ãí¬S;��ž„b“×s^·x„=<ãFTÆ‘ëK ?Û¨ãÇoÏ€´Ýí«•ÅI‘ÅŸ;Úéx-Xëb›÷áR@�#Ñþé…PoØ\ ØY\L«û9"ÛÌïU}·˜\_éÿu®ã#uQ¢T€1GGeª¼Þ‰ªø"øå›§ÊôzŽÔœ…ÿ$Ú5r ‡#&Ûû~d7y~­™äN¯¦¬ö3(á1ó `¦–sŒ Šv£²Ø¦ðà-î�º5Ô]ÉHÑø’òƒ¥ADø¶ë 3@òxˆ D;¸×›s±ty¾¸š²åÒâ+té"¥HT·f9Â^ÄÞþÎÆvÐUº _=²$h²yEªäØæE#‚Yëî¨Õ× £?E™›î÷E?¸¥ê`_jý}lO†qh3ïIY—VóP®6¯È|Szªõ�©hlã®§.3ÓãÁC‰tuÞt= Ì|øTk¥&¿["Œ(9éÓ¦¡&Ø�ã^‡BæLW펲¿/íg­FEGd8è‰ÜÀYYÜ^¢©ÕZØ}T6í;¾]/¬I9 óK`™›7ê¡Ûeú"­¡žMýÑwtA€J^:½Øë+,…jihì1Áýñ¬7 šÓ°3ù¡õ0ÓŽ~€¸ ¿×—[ª”8$Ú Øµš¼³âŠQ›–$Ç–×PF´Hæ_°v/ÌyýØGá�†Â¡­›|vºv˜ÎV |ÖXT#3}= %õÂ˱¸$Cí§—%æãÃ3ƒô`Œ|â´/ý¾nÌåu¸™Çíû~úæFë†PäÍ=ígÆ�H|íÖãP7ìì}Lªÿt¢zqW�¤εøB­X¡ÌLZÐÃ_MÐY®{äN`&OØ÷ö­¬ëæÇ1A–è~…ˆ¹4¾ ¹á¸4!¯{Y¹Oƒ¼Æ(¦ÏÉ}\_W’aäm=¨Ö[\qv¨PGJx�¾ï+*øQ‘_f qª½KuOœû-ˆ C&ó¾K ”Ù-TÆ*@E:qöêê%YýÞ~è§ÔJu±;«$)�S1�nîùx!§,3@ù¡Ý/wÞ¯cüÐQ)p¦ƒwƒ˜¾2æ3"½¬2¦‰�‚Z;±Ì‹ëe0.]'Sþ­glô ”‚„«JT!pBsõ�T‹`ÉJ ´ ×+‚‹*l5Ú$ +C�98Ç_G«Üã�â;㳿c¨U;M»e…'º²Ü�ÄóÛr]VøkFëŠkëâtŸÜÑaþÄ(m<ØÄ3÷šÒÖ=Ù: ¹‡m¯Òhf‡VÀt&óáKr¢ÑÂE7z .Žç\±Ú‚ÿC´¯�ôpæ`) ¾’Áó&Ûõ ‘Zx 7ð‘žÎ×<^n«•.] �kQßø8ÑÑ/ä}HŠïºÞ¬å<®Šý2’ ‡ßF@£ôÏ=R?Óáé m† É)gÁbÙK¬ŒÙâµjK�2Ž,˜¦&NBÃyÃTî>/ȤèÕ¤�ÃŽ±®Ôï7D²9>RØó1L ØÜA˜_‚ˈ0CœSßQ4'-RAº“Å3ÔIJw„ÁãâzúÁ²ýCÝ7¬žùó‰­ëSëuoš�4Ù\H<}ýý¥ŸÛ_^8VKn¤ûd…^YÈ!³MÏöLFe¨:µ”_Ñw$c°õÐEnDe_^{B fÉ“w 1Š˜—cÕÆóô(»”ƒ7q¤O�ÇP4ŽQÒŸ"xhôKÌ¥ã§mûYt­6vÄ=’Ä5;¸34 dð‰anÄ3Mðb_ zŸ‡¹ùMêóÎÓúøòšd;Û9✠„ÔÂo�tÓ¨´jŒ…_%Áñ-À5M¹ê¶|˜7sþÿë™oÃS€RWqeƒÂrªtôsãÉ4rÞïÑTÔ§e’Õ—k�z¸ ]7&Òv:¶ˆ«éBá;ÈQœsîw)|œÚõÉp á Æûà-âÖ”Odï|+’pÜÍ•’ó ѵ¡\.ÐÓÞ®±¥â ÇÍ Z KM¯îçÐÔÒ�x9Ð¥';§«�R%?ÖãT6cŒ/‰7ãðµo›²Ä§ÕsÝ«¦Éc %ÊœZ¾�Y1'"*‰/ Ë6fåô}Q€Té0TÐ>5Éçvt}Œó¿¬Ä6¡Ý”ñ€Äo17¼Œõ«úCæ¨J�ú¹$›'8Eb ª:ÐD´3fÏôÅ•-+Z‹Ë6?ê" £'A”usœŸ¯æ„Yì²âÇÇjSÀ0èù”“ð'AŒKp—;iºa—kÓM°3Äz1ÊÅo úQ¤–ì”_ú;Àãô‚E‰b c¿”=ÂØ`½ßxv÷Gf–Èçd-—OZ�"î%©ª/.ÿ]ù›ÔŽ~¤f’G“ùðlÌÝÞUÊæ¹†õÞHqbÑçæ".Ï–N"žNëg ´«7J5ˆ§… ²)n�ßQ&S³·ƒ>êDâóã=]ȧW§„zßLILB€ó¤ôÜÚÜ ÉŸ�”)cwZ<æt“€­ŒÞ¹.öAV Á®.ÿö{~ÂprÄ€qBî¹"›€<À²‘FÅvÖßáyÿµðc­Ðá½ÝÞ–KÀPÊ¡C¼ ÿZû™æÈes2ÐøÀ…'‘Iú†L§c¾®7Œjíck0Ã/4/ñ°)ÀœŽ%‡&ð.¹­‰�ñ@¤h:ဌ9:,àt±øq}Îg¤bcèH¶^¨jTlï=-u5ANïcÙápÇvüwm]ÐÒ &Ìoàôãp “ã= Eê¨ða—®íÍ‹jôégXãï^”ÐR4¾-q³ßÅš>¨uˆc’1úÞ1àçÚ ¨ÃbÃ:Eþ¡2ô^âðÄ¸Ï O�®Ÿ£yÒTNg~„‘]Æ�TÀží£W1/4SørÁ”^ßj‹vFÔå@\È%œi/ë—ÄäÊ<óé[5Àw²ó…µHºç²gMänÃ>¯ª£u¥^?éé&ãS™Ì‰bòûè'Ò^�¿ ²üCVÜ=.&×™Æh{˜èóŠIUÇõ÷l6NïkÍjP\|x©‰QUDÏžP£˜>ÓcQÑ„¢n±`€!… ?Çï@¦HNÔŒÙzЬýÚ=uøÀøù†HþѤœ”}Ï` Sèíw¶útìT;rkuížÞ Æ2gÉ.kµe<“"°Gä²LTvÑK2. %¦eúº§¹c3�ôï @Í=o† &oP˜Ü.÷ÏEû§‚„�ç\ŠÛò)—æDu@°1"ñe¹ ]âäg•\ƒ¼½QÂðßH×"ibIX½ó¨=¾mßùŠ4�¤-ÃÒð'=‘ÙÀ>â—Ê7\?BÁ³å•1$ô+¯º¿oX“ǤރW½É·“„J…Ú>_ò�%ìX¾Ýšß…O­¿â…Rä|÷ƒ0ös”ËÄ�:@¤j‰n[ŽM ‘yD�;½7É!eÖNmèª ™I5je}”¨l�A¶$Þî+v˘)w? öÜ8?´N}5|vŸäp;ÈÃSi,6Áé¼²a€»ŠT3Rd¾ðL®ÝÕ oÐý)Ši¿øÏG, ÐIвÆcRkÑ·O†÷IÞ"¿ŽÍµzg£_éÃq¸i*Sdÿ€/²‹÷Cû³ïmÌ•Lâ½6Ž@æ7ö¹˜GÀ7³Ú„Szòo…ø©gΰï\»è|¢[Îe~79ÔŸâòüŒ8·Ž3}h©Í‰Ì’˜3…Rÿ[^åc>eǯee¯c5˜IÄ›cG}Ú"x¤‡æ,€bð§qrì‘Ø…AÍühb°H„ Õü)wÞ2!ªÛ Xai;WÿkWQV ;Óž= Ï�6oYú„ �Ea÷,t#ÞÀeA¨(”ÖüKM ž'és«0%–—Wì†&Ûäz�Œ�¯ æebÉ wôýžƒmïŸ&ΨLXyÌÍÖ�–¸EXôá=_ 1ÇÒ$ÚÙ=ÆñMBK¬B&Çc~ý1„]JäŸ*±Ü$KKO$ qHš÷öhù¾9\»ô7å/Æt"ÞŽÛ&Zljá:[soW·A`¿e™h+ƒÝ%*ë+ã—ê-â´R-hy¸óYv.-`Cµ˜¸ŽB­Ã�¹ûÌåWþRwû,ËÖ÷‚fB,à¦Iö!ì@‚ðvs;…–h ±ýÚ¬“Ê!"e3§–yüð)Z¸Ef”|Ú³¢íVyg ½©IÀ!’ëö¤�mTšÇfÌp|¢hg¶8y-šöÞ¼��úo{cfmÁ[ß´Ç4šÖ °]h…-5Áeœ²ªF½Õs(vÈýÎ1ÒDD$øÛpÖV­r m#‘¤"r27ø…4ÌDG³kp‡4Y�ÇëTîÅ=îC¿ô¶0[�lÀrÓ´3÷â»�âÍ£�?AdN (þšZfbüÖP•¤ñç6C÷e2/ Y¡`x}�ÓQ܈wY&5vFÍWO)j²ßÜ ]Ò_Ój‡µ$íæÆ†UK,jךÑÇU$hv˜kä„ dp±Ö Kd͉י£50ªMµøÓÖ+¡RHÑa'Û�«¡&d'¢N‹êYOýK�2V>oàD¨ÛLò€7™V0ŠÞ&jüRPÐË¿Y¸·q8°©ÊªüFùéÛÓÉ&&'ýÕ‚pHg q Gî—>×ïY�ÍwCÔ‰)ù[}Ø(fúén÷dšYôW€þ\Ñvþµn8ó£–¿tød'Œ®|àv$·ÌÜ->Ë�ÏÅñoÿ´ ®Ð 9Ù€)?äIo§ ŸÂ5pJn]{ô…ÂûÑ=ªiôú�ex ó?\"ày¤úÇ1NŒMÓ*X‰UÕÎþû³$wõìÅNÃç\ž2гVËÊ«âg:M å Ë�úÚ º£̦7Ž@¯í k͞ǽ§4“j,¶ˆ4R}*è†n²=v’"ê�Þøh™ €S®Ì ðæz–ãTcÀS75òn€hn�‹+îÃOxµwŒ\ƒmgfã�,Z¦â€÷î¾_ êÖ£™„wá�k0Æÿ³C&êñ¸Û:X¹] ¡³Ú@4t–®ÜKµÕL£ÍÌÇ|¹ÀÁºï–�—a,ä"RlIë 2–ÝX¶¦g90WÆJ+5£%o¬q~( ÑJsZê¨<ö�¸ŸªåÐíàÈ'.>Ý–×y§Ë5–Èщ쭩AV‡¼é„Ð4sD£+¶~ºæ· K}¡Ç³p(�Ev!í›/ó¤rÔàyF—é�eÝך¾ËëÇ“­pÏŽGùµ}b¯óæ ø‚<ÖÚ�j�64Ÿß2—@cä–þ{u›âl{’—'0c:k?|T¨Ù²ØŠÚ\jlçùׯm=�2 aøffú(ü?5Ôó ½±ÙMp³@öîz#¢ðÅKŸš½ÌŒ€V7Î+º;ÏD­ËEßMÍDÏpªºý|öŠF ;-j'µmS~÷—Ú\ùòÿA~!"àôbp€dúE •âE—Ü2¢ÅsVd…>Ç•p ¼˜kzb¹wå¨�­La“îÖŒ¡¸Š�wkØ·Þzó·;‘ÿ(í�#2IÎ>}—5•dWtí�a4 ‹æ$ð vr–ô èr¶¯èâ"o"’Ç®î¦{Æ“~ª�ÁÔ3Ð| yÌípoW¿I4ufâúݱtmÍÞ›ª  |ΞEÜýpÚ4Žž�S è6E÷*º‡›Áæ=OD]˜�³F»]ùñX³ÍvIŸýÖùpKÿs»¥ú|4Z�<›¿`s틊À0®Y—QÝÀâs`K…B7Zy„Ó‘83z�a¼ì;d]…µibHo‘j$ðgnS³q…dKïNtÍ;¬”àÜãmªGŒ.r‚!36À±QÎiÚ�5aÏãÄNc!±�X·\ž‰‰çÿ©&>Zwt .%®íŒ/ VzÖ¢9†¿l½{ÎvŽÀ{§‰âé,Iœ‹H"N3N …u(¯äž{–k)>´ÆI§Û»:huäÖ*`šäiÒ}{±¼r{!êg»_ V)ñtTéxΰôæ“gÁp_¡{††@B<yôØ3ú|jº‰XñĶÈ�A‹“Ù´ü¯~(ânéç!Ž+"—yŠYn<ñÍy’èϘ;[#�ZT2gø>BËoàrCµ0ébfw—«æƒ�clè„4ê!´žžl¢v=èØÔ‰ö—$"ò×®y1ÿɇLo?®�µ–ÓÏ µ<‚y#ø€ŽÚŠ$O¹üG5�«Êº¨qw6=@)é;±Ú ÊÂϬ\®Lm«FNQ8áèÁé�TÙÈìT*K¬FŸ78‘‡DY"DÈ튦ØíEO»É1g,d³({D¿ƒ^žJ�è€=Šo ú8×0ܧ߰«ýVêÑþÁNó…ì²±ë¦ ¿~G¿ãx¢ ¡«-:ÏòÏÁ\E¦ç1çùÑ‚PpsHeSˆŸN‘Åcûù'‹Ïˆ©i\:Ïñ•êß�PØ ‡I¤ÑV;°SQÜóߨG¨†�kcÕrPY,ûÁ?z�Ük].½¹+$ࢧ¯€g›–—�Πë@)Ó@m6:p²Åg~u�Ñ×ä‡Ù8ÊÔ^j”+ ßgTCQq4e2]€µë2ô„–—ÙÀH![#b( ÞҸРíE®íÔgŽÃ–¿@2Tö{ó%Ù.+5{è¤Zyèn~°'6Íä঺D^îÏõ¡€ÊÞgcÌ!o{ TÐ hL¤zÉÙÙ`ƒ€bó?T©™´xlC "ãÀÿNîM‰ mm)è,¤ü‘ H:ñÑË�ž ¶¹µúª±(Ì“pCKø†hFîY¡ËùI�Aå�/ m"øÀbÿ (uµÌ†ei�»é(€^½º)«C¦³=@AÜÁ»ž¨÷Ë) oÇFØÌq5È ö«z\?Š‚f6­m¹#j66Ú¶x‹Ö`ן"í—a�­+ ­'ÁסɨÅ÷>–’hC2ÏÖPlÃrF{ò�îAjK =èD¢$»Yدf!¥RéÓ-DpHUëÇnW¿­‘ ìK±¡Ø)Û !>¯¦"Úü.`;ìÍæÌ€E’IUÉo�D/R3ÝL¨RXn+Ö-uÚ¦%ñ§@3Ç+akóä°¶\T÷{äæ×n*Ž\K¨3õÅxfÑ ÒÝ=t΂iS¬³šQæè¨sUêvbvŸ{âxÞuPRÔ @ó°µÏ>gC‡�³Dï�‚¹Æ|6F,·BäÞ„ØT3ûª;?`7 ŒÁW<&pÕvXžºƒtZãGvÁÒ¢ Œåçù-šéÿ, ÁÝJŸ`†©?ûÁÄ»^puΟl,ú¸íÐõÑAÃ%$c·däüï6¢×¥ÅûѾ¬Vlɱ\¹e)ëšUÈå� ÑA8-ƒaä߯m÷ͺîܧŸírB´•Ñ]Í|Ð ñÈ¢Èkñ˜Þϻո̓~‡+Ý×…¹7ÂWº)Z°ßã[p³›É<®Áe¬Yrs˜ú‹®M€ä�°xçÇÂ÷Ãìñ˜ý�òŠ7»4Ç=9f1â8O‰a«(Û/ÙÁvtÂue£m9_–s„›´³ò>™ wƒÈ\BÃ?-çAÔ%u‘a~‚«ó‚BQOÑ©LšzÎjf?!�ˆè«…åíы隮öÿwÀs�ûFª®F"}‘:æoí‘|›~€Wì]ýÔ]J`‹žŽ{tÇT®‰¨é‹.Hö[6ÔåÒ]6Ø<²ºTß‘ µ vuôÏr�ä¯ëz%4qÙ{o3&ªù‚+qN ^8çdw„ŒÓÇQÒ7Fã7¶¹�×8iðÙ�¥)œî`Ô7Pë!(Ši¼¶J b�-Åq¨ì³ —5vÙ­î-"…“Zâœf‘çÛ%Úÿß{˜ÚÖÍù9HÙpؤNÕYd!ÄôP¡µü4JYŒ,/‡¶¹3â�+±(\YÈF´èîá¶3còa*½ºpw½�C:3pj¤s¯€®ƒ¨ÿÛ \æ_í˜<|Ö¼h\·J6‡¨–7Ü5»º“�HSÂ1òª0ÿÌOž€+ý�+D< Òg4LUÔ g©Ô»jŸÎXŠÓ\¿Ù&Þ»Õû÷&`%•SŠ£�â�=§žZ™,C1EV†Ãö¹”×yG̃AÑñ6}Ñ‚æ2«¶ÕbQ;œQÜêÖB .Üïà—_õ# L[.�½²ŒÚ×ÌñÔÜ‹ÿT£@K¯ék}J°ôŒc&“à0ÚZ£©É0W‘{i÷h€OÌÉEáo†·¬–æ |œ�ù3lÀÿüô€�¨¸M¾yµ´9»EÂZ¾;ÖÖ•HMuQwYDÕ‰Õ¾_¾‚Q�äº0át#�)µpÙ¿ ñaâ Ä!†·Fòçj˜¾8qÕø«;8Éé¹þû®öŽ>S1 ¨úcPûUeñ-ÕøÐ'g×è9Ù/´èŒºÚ¹=Ç·j¢÷Óô:Ô˜þU�ô©«#Ý8äypÍñ½í¶)$<ߢ³»«äëAw·âWØýc„Á�ž[ïdF>·¸àr °‘¢5䬲:gœFEßM{ìªd£yW€Œ\DCó§qŽå§ìL…Þ?Ù–’ó WÞv 0 蘴RÙšrÞ%œKøò @@ú¥ž>@ 2:GéˆÂ3tÅ.£ÚxŽcóðZ„Ü{°&Š gÆ*ÃÏžUõúÙ Rm=ÂP‹6Þ¼“tA`Ìyº®½VÌ¢O}ÁØ ^°LR4%©…nÑ¥õ2´å�Ò#/“�-©Ñ¤ZÝT@YÇni}¦âÔûŠÍ›á&¾GÕ‘EG7á-ä1 ôšáµ€¤«H¥èØî×Ky¯ÙcJ¶Ç»åd…éƒ$ôÁ “ðûHÓÚ%ù]àŘ/–ê"7A [Ôs&=ðÊYÉѨ7Kµ»ç×ÓØ‰: ¥×#ˆb/ÇùK6õçI‡2;¤†;ç,§ªŠ°d¢ËÙgÌGdO äx«7šK­¼i¥`üA]çÅ/‘@·a¢���®[ìÂÕõAù"C¼éù¸r þÕèÜnûf‚×ÂÈ«Yf©K #2Ä|”~nÔ…û]D€š;ý”©~(ê€M»0æ•b‡ŽÝ©â,ÙÒy „Ûe¯lJh2é󒨟á m•0þ�í¦0LççF·Jï»%¬–š .šHf’&†ëö…Í…~‡ÎÍgv•Îë{˜LK& g‚E¥†P$Ø]ÍŽÀ: ɯ•0ÀˆbDÚ‘eånjÆÖCKÀk„,'‡›Ó¬RëYçâg°m�€æ¾D@F�G1#wŽ–²<2ìüom:±sw•î§|==öP¾”n¦_áè˜úÌ’d´¨J5Ÿþœâ|Xu“ö½éoT& áC¯µ`¤Áo7)[*ÔáEÜ-ÃôËL�U/"NèÍ2T�¯>ž>¡ŒÍórKQoŒ©MO½¼½µ€í’¨mN‰‰Ÿìp°hiSÙ•×– ñX®“²Jè®VŽƒŸU¡a_'w\ŒpîT¶ägÚe�§™Ôœô;)lçîAÇ·´Ÿ:[`¿ÞݵØ®ëBâºÃÒéìùwŸÕ‚�åP|—$ñœ2íËAÃâayu-b³1V�ž$m.„²óO_¿Š¡�âjˆ!3N¶Wªg©ô`ûïײøì+Kn[ÆJjÁ´o±ý( °*3¤�D²èÝ(L-ë˜\sËß#›Yò�¥Îþ6 #©j—AH*1Òú¸ƒ-@Þ@" ·�ˆqÍ;@¥î¡–/ž¢î¿Î—5òõX$q^ESáXH´ Kß1Camèì9BÜŽ°#Ékpü›Ò¢ñ'Há �4ô²&ê"ŒfjÏK,HÖ;z ¤‹Öìš¶™È»É·D”òÀ09y¬Ó’ xàÃ7½Š< j!1X¦9úݨù-;sûµÝ›Þ-Ç�¢ÛLð]®u¨4l1.ÆÈ:é2>A%'E¦ßÿßb½'¤/Ñ(#÷ž¥ˆ‡p³Å9°Î·cvßJuºžE�“%©„†Æˆïü|:Âø+Ž8­AŒƒTqCÓ¾m¢ÜW¼›‚]xuƒh7r…S‡Äª e Ÿà½»m"žÿ2‹ bá T¸QTž�ðÁÆò“a‰C(3ÂÇÂî iÎhZæ•qû)€])�ÂDÅáçXíäcà‘ø�HÍgÑôfNši{¼/Ú«Hò»tÞ_“à¶9$Ì× j:ˆg·Àþ—K 鵯ûvnœ|åä {±z„³�Ò¢±VÄ3¼wÝÚ�"n�߆„+¸Àn,IUWp³Ïhÿ÷m÷‘‚ó„§FÝfð0}èhý:²ëöÇDƒw¦a­Èï$«|$ø¢íìÒ_“…ÿŠ]†¡¡d�IM&Vò3ôý¿kÐÆ/lOtSÈ,Dø@N@ëß°ÄTs>�Ü%!ÙI ´õï^Ù¿)Ø7^‚Ò]»”ïo =ô#ÿ\JÀí}B³gPÕ†çÖz6o¢–Ù÷ŠD‰ s4Uõ•´ßXªF[r}V²ýÑ5N½U[¾ì8¥Ñåêäô{�3“ž?[„'ˆàçºWïgVsDÞ§�5› ÕG°\©ü`‚rf¯6T×ݦƒŸ¿1’¼)ÀUûË^`%efÝ"W3L—wAL¤°'½¡_fe-q.$ik2!-s¬Â#$}¯þ¨s=¡Ä¤GƒyBÈþ+u8¦öƒ¯gpSúg#½Íi©O`¤h)[ôƒîb›ùÝ!¥ä)24_mÓV·{¾z@Í?±„âbÏcÍ�”Üý@õŽá{Ü2qÞ(Á5õò6&ÎÀ0î;ïc‘êTÃIVÑŒyNë g ÿ ñ+ðš€pl³€_ÇOj77´€6‘„HÄ“Ìx~ç�7Kvês| Jº=–l~ 9Ư|µ”Àn� «?Æ8€ªžcÊv¬�}© vD ›e,-O5ï°*k&÷MuÕ¨`ÆýB»%�&+°ÿ㵡—£0‹�J …ÑÃïœ2%K�ðùû¹ ¶Ì»ýÓ 0÷å”xÛ©×íà£0é%n#šá•Z.ÉPê÷óÒú{í©`)‹„*å%… –Ÿ“e.’�ƒ#õæ$/TjOOkò ·7õ¦L.ÃŒÅûa°ÐÃ;ð�И,�ÐR3J5�*_5¹>eˆD²ú+Áü€MXòñ å¡öš©/" 0|Ê(ü~�BªüæÃ(áØâsÀÃ}çBe½\Ó»œ.8 è¯Íl¤•6^"“zø[»Ó‰� ç,ˆd28—Ù˜^J̃o™HHÌDGfœwÏhÞùÐ-b3FÅC7¯[´ƒ’S<2g”}…Aà7„†-¾†‹˜TþŸ@b<·ãSöÙpšcÍñ}7Ñ%è¼ój×—ä�Òcü¦"RçJð_”Å¥Š�R «&ž—Iü¾AüÓ;áŽ0 *Ͳ™œpµµÕd áIâéïíÆ3 ç~ñr¶PBr�‡lJ„óV3Âì5¸â%1·¹;‘ïÚ¥Ê)Ç÷Ñk=@ùÕß9É<Ó‘ûñJülÒEFe(›„ìy!|Vå_å¿¢jȬ�¨è–¾Šµ7�K:‰d39íïÛ»1È”ïä€ûz«)Dò¤?\l“I“nE!Cœ%u)2�·{ðôíé4Á“ÉÁߨ’áø:û«#oZ½á%p{•7€.)s¬.y©%bâ1ó”¼Úd*‰¾<}î–:–výò=ýR͉Ø`võD}jª6 ha ‚“UK×dƒˆèåq`:E»�Î Œ¡/^ ãxIŒˆë+ûž:/4Ý–¸;¸Î¼(ŠÍ¬-Õ&H4-1V-_œHu§U¯{-Mß$UŪÊéÏ #>N¸û¡·D½¦šÌø©£ƒì¬~ÔÛlá 6�M¿N?\à%åÄj±ëéôoK^&ÑËöN!«~ê“uÇ–Æ$ûC»•„C{)ØÅ\9àή¦k÷b7iv· a U�Êç3ªÍe,ÇO�á'5òYĵ¯å¹Ÿµ^c�Œ5�0 ›’"Μ8¡äpY‡c…ÖMV–}ÐÊf•�}ÏÿQs~XDÄÛÕÕÌ@{› „\d‹^ò2V¿õ2>r›ªG9›Êj “6ðÁM¯“1ØÝIBó0V›S}ïå<³(ðÄñG’93¢} ŽÿÚPªéåµéØÌö¸œ@µS4/å=JY<´}Q;м^~ìù¯zn¢QQSÁè hTÿ®ˆ )G3ý”½´ùt•n…vºã››©õ�lÓ>éXźà!Ôê~ÆæŠ‘Ë`›Ø%5K›L¥Óë¶î‘Ÿ_$Yõpv¬Rðƒ¹ r>Ñ]qúÄhG‚ù›’-·<Þ6ºøc‹²}Æ žzíè1bÖ#MlËÆ¤<.=5&ãhH{ÇÄç¨dZp‰ä`­:yDsU”Y~1ëäIÂïìüK]au]hX¨ämͱŒÚXÕ¿�c¿¬P\ýi·è<¾cQiw™ÉèôÌ>³ý·NhëÙœÝâùyfÑOÍý�u"Þ¶R±}Ý aË«ÝÅ—q©ôš3Ù@^ ¡K¡Ÿ™ Å#ÉC‚¥ïÎ ½?Òí*¶�µ}”…1G†›õ/qJ<áì®ëEf#mººŒ‰|» µüóæÇÂ� ÒÓ\%ÆÝ; ßȽíøüãëªVÕšþÚ2|’ul¨ì5�ûÉV‰M^厌"p8l{ó÷á( Fˆëáà2k犚¾©%xu \;ôþf¨�9 G…˜, Ι- ®ˆI5 �‹äE€ñòõFxfÑh5^8-�Ø1 DÑR?jYcá»Ë‡U¢�U¶ÙçZö#&L­›rÁÂÒòŽ»Ø—Ò+r¤•9ΜßPæ²úƒ¸Éî2Ÿa°%árTÕê,Jß�1—IÇ�ezå>û£GÓdðo¯<ß©s´ú¯¡Ó~{pµý»a~lúú�Ê Zap¼#ã‰ò¥ÙúܰSÏ#6�%—ÓR!È3ÕGY�*M–DDR®^b‡°ÆWX´jCNc�•°!$jq­¶·g…"ƒ#𿦗ÐP%ÝϋݕiÏül/{Î�X»ð¯¡ò²ì˜jŠ^\I ] ;çxÏO§¦gý·Ä‰ Uò~7Ñ®MPÚRj¹„€Vw/¸Ì7 ižø(N ˆ(¬›~£øJ­�A¥Ä¬ôG«8È¿]vÙ¾êR ʺwaÿç³)árFD§1ß?Ƚ(�Ð:ñ€B›¹/š r0 Î�ÀafeuÏȺ*Ég›óÜb…RÆ’½.å"z,#@ ½©”òˆ·è¹×}†Â/X� ôo¨½ Läß®�™Œx[irk û…­I"ÀC¯@t˜‚”<Õx‹Âžv²§ïÖ=´˜Fn#d‰ÒIq†¥…hÂÐC(kðŒ¿ÖD��;Ýp¬ÁyhCïÖpš;¥ÒíÍ2†Ìë¯öóÎ�ä»Å~/T“‘ÌÏ—=.¡éËX\.Ç#p¯5ðª;ãÆp6‚'®gù{ÇRºêá+^�2–mHj=N+Å/ÖsÝh�V¨N÷´Çà@ˆL/8h�pÉ_9Xù£r"¿R ± Ï)Ñ<¤b�Pu‰£Ÿ!0¼ç�ì¥Ó¾lð”' +ÃûÑŸ2vfÖÔžð ­RôÛòàAh‚‹ÀÀ„`x¶Fø1>7 öß9sM•M‰» Ô×Õ܆ EB¸9|\*ÙúŠF‚òæ1Ööýp‘ýˆþZXì³;Ì äm!e›ÃHïODâàKÏ1×_�¥›#í«÷°ÑuÞ…¤.kŒ}ï¬GÐvhÿëêÌ Ý 3´šœ˜—á¾ÄDÉTh¸‘ô“�‚gDÂæŸÙ–�éÞhuB€˜ËØDüà^vôf]PE5–êØQ)*»ÉÆL¿çE_Ù4¢ÄàÕ5Á·z¸¯£Q�—üäÙw!Y˜WŽ`-¹ó¥UtK¿µÂR^7 >°kô sÄ~çvü¹ÈðçÚ‚=6ÞžcËwÊùׯ}+ïúÝ�äá Ý¡Ê\tehô®0'œª�ÄI” %/loJ,¼I6t‰Ä¶›3ecü yYJ»îE6˜Óvï"ÅÒâK:u7m{¨Ÿš &Áá9ÚtÇ-ÄÙ }1NËàÏêÞÇsh¾¹étWTeû©(úÑÍ‹\éD¨ÌIäTkWœî°ßŠ¨Ì‹2ÛÞÙ(Ts&þ[RץϿ_>ªÒ0�ÔÒº¹U\¬ÃËo†óÛöcü<ÚÀç§tù3aü§¬Æ“ûIï üÉH¨Qì©ÿ‘›Ê�:ö T-Ò�—¬ÕùŽÉGmºØ”?%¤D” üÑL8LBuix�nœ'RÆÙG,—q’é”ÒUù7²¾ Ûò€(‘Çœ 1_ è‘“óiJY.ÿÂÜùFˆw‰ ÀÆ”=Èj+å˜ËD�°i´T3"ظg”mÁ’¥ôahÄ+y‰· |@‹áJ§úÁ®•䎸-_Mƒ1“EnmGK'˜º/&®óÀÊÐ SØ (<<ÌžïE ÕëQŸUy»+mu òue©›'žA±ËQr·©¢”h‘ »@n눛\è_E[¢\Íõ©ZP_%öaÊãàÔ‚xdYvÕ,iCAq¸n#PÇ@æß›ì#Œ‰!¹BÞá)0gÛ¹ 0Ìç»$J6�nv?‰&þp2³1f£6Vƒ™åánÜüƒõ¥!Ó‘Æc3Tibö‘�Vê?5­òÌ…þã9meõƒR\´ª¯ø¢ÎÔß_¤Ì}l_ôÌ5ÚeöÚ/'µ4sY†¿Ÿò%Z¦“Õm’†tFȉ¡™ƒÙV­fî¾(pœ2ƒ8¦të‘ZX:½Ç4Áq1Âìꀳ™®è!«‘Qs ZhÙôÒÍÊæ*�f¹Q}1­x2ªõ0ú»†Çž�/­Ý2�©¦6™eñeǨaš�ˆóožÄ9)�c�‚›tšÅí5ï¬%x´ÑÝ7ºC­vþØ^N÷”³ýUBa ̽ÔÄ›qp}ÎòȤ…år“�‹ï¶™‰Œ™s•¥Z!Ô››tÒ2ÓùþNrã‹«Ãï1Fn#ç)Dç zlFØ6Î0_QÝð7Èâä^ß/FôLŒôζë Á8Blgß_bñ:ǯtÅÿ'Àf³öåªêø9çÈ›Ûy%—ª«"xÇp œ¾×Ì›=³´bÛ§r.æ.‡°)ùà‹Nj™¼}=´go)ÿ&‡:~fç¡»�´–=.”J‹§”XqÉõè<%i–T[yÂnÓÍi,Ó„Ò°Ñ Sü‘jË#' ƒåƉšª0p­Â+ó>3 =ZÃ¥¼þ7N/8ʧ^�œ-•U¯3t(ÎÈ7øKõ>í=Œ[‰èáÃ}âÏ= ÔXΉ®Kx}~蛯›~*J¿ RMw‹YÁ,·¬#dí?AÛ|Ç PÈþv«’ËzΈíáݾèÉ<þ½û¨lO± Öúí>Fcó8…îÄpPyãr|h£‰.Nµ¬Ö¹%è³WÏú´]ï«À¸¼r`™1¦KWboCñ¥ße��Kûý× ‚Aß `ÉòÏ1±¥Ô}°²eáÝ ™ÔÀØ•i“¿ÜŽžá9M 6eR=§W5³¿ÇO©ÿ"�¡ ÒÝfæ†%šNçžNCŠŒè`;ÌX—B5Ý}ò‹åƒ\iµz B_WNÖ¤¹åï/Ëø0ľÊó»J• çob Úšuí?y#xSrøÛèkE-œ�_É p+¦ãÄË«�ÿ¬›c_M#.GÚh¶µ†û®Q9m¸nÁf-�/‡”Õ8�ò)eZF¥Ÿ�—`®™þ�¥”î�fÙ—ÝÚЛ &¡,yvÒ@)ùïÇXÂ’ñ‚˜ùFtòkµ…ä£3*ò�%¿Š@VùZ!±SµU.5˜édSU@"ÛùÓä¬NÝ IäÃ2܈`8ªPé~°iæ˜Á“X1ì€7.õNèÒ³«’öTÒÿ×çЊ6§Ç“v$'G¦º!Vì´•ؾ⛉A ¶ÕµÜQ'ñ¯èZ ÝôŒ‹N „®Šì¡Þéù?�÷㋎9»„¤å¶ÌÁ˜žB?m®Z!Á˜â“˜ ΊV®´£¨b Ëk½Ã.§0pz#Ó¦áU¬¸tÓ2œŠ3D—×ò”ÈeÑ”øÃiZëqZ}¬Bõ!ã9ÿÊ>U!­èœQ—º ý•·•Æ2á Vűu/2_Xá¥Z;# “ ÒG¦ôág5·Ë$¡ƒ3Œ&[õ[î)OÔ·G}ßUÔÄM?%ô¸ÆÛ»s|äô‡¼;‘ŸÁª^€á­áîo ½")‰רšJ>”8ƒK±·F˜JiÓ¥˜‘Î~9›#Ë@ÓÇè+ÔsM7F¬Bû$2b% $-eÆ¿6oïU¾Ø*ÿKÿ&Œ�•í<…}~Ôàxg�÷ÏåQáÒ"fžVE·%Ú%ü¿ …Å·hel%…f¦[~d}iŒYP¯u& ôa–÷J{S¬ÛDBDÂÄlKCM5£žÈö�¨p›CûO~AGtrÛDñ,ý |aûDDjs©Ÿ'Ó± æ�¼™”æ»Õ•�[j?ÉVÒøÇö/àSmÉ÷Áú×À£î"/A»D_·ÎŠì4³q艥;}[“ ±Þ:©Ø†y0¾UÏã�‡e…ëê—‹�™ˆiÄ-­*ÇÑï7‰Üºí^vŠÊX+ Ô/ƒÕ˜@(ûà ‹öNlK.¡ÕNd2‘_Ü‘öÉ(!;ÃÚ+�|&ÃGl(_‚ЈÀ8gééé gúŹ>¿éðm…›%yzIˆMå-ÿ2;m'¶å¶®oëR{89q§8E�yÜhaÜZ¯b`çÿ{ íý¤3ÄY,nEƒ÷V ²R�&\,^Á™ŒÏ”°Ì.`$½>¼‚slÆh'róºHýž!Q�½Ð#�™CúÝ1µä˜W€ª[ÃW †e±¶V‘ÏüŒ•Pï¥#pz„°»ÜÐ[12$‘ºªŒgçMãÑÕ1Ñ1ê-¤r‡èÑNB³ gÇ×ôì—•²V ÍsÒ0^ê´k:¬µù›ù6æØ+‡8²™³Y ¸=BuWüÂéeîÂ{<áºSœöþB\ÙP–”�:VÅY$ÊS1 ÷©2¨áË~‰9]˜òŠò Ÿ�1±¸V[›¹CEQ�WêTT|7Õɾ�9÷dçoÚß× µi†ÝûràeNl2hX0æi/½RÝ ?Æ~ª½ Ô'ÄÇcŒhméÈ¢ÕH� ¶,ûl‹Df QÇk>N=°gà T''DëYO¤~)t2e›`:%1¨ÌOS_z9l5LŸ^„ƒ|‚¸V¢åu8O©Õ÷ÉIvh"”Þ¤»÷±«^Œj4ÕWìÆ¸òÑÓ£Æû[3^CB_‰Ì$ÎÓzI†Bòf~Y.·a*ªPŒþ\ž€åQ‹“széœx.,€Ó�9Ãû=Ç)Œý#&LX©§™¬ýöÛ€ªëxŠ%ƒE®e0 w‚Ô$vGU¯~ò-£Ô0>;ºóÕrx´õä"ØÙ6’!x>Z›¬&;SäR„Ï<Ǩî`ÀÆZ»?¬iNG=××j¶èó0ó�(Ü且½ú¦Ø»r Òý­ÁDá[òûç"Ó”Z¬¡)ŦxZƒ”ë�Ž­Ý ˆ5£Ðàum;4'ìAØ2@縕ëÍ5l6 ûû,/ÉfüÊL©Èœ�ŽÕ �öSRñ Á6 i³¸U¢8fW âÑF¼Š‰ªÛ©» ¸[&Êã [iÚÔ)Ú ”¡-û§""·P¢3­-o#–¦+d#£¡~-k¿ñþMO™=íñ¹Ãqø·1Ô�çqÏ Ã /¦©íé s¯(ÎØÅ¶óvÕ"å‡Ymg-‡Ö÷nUBó�§T³B6wòšE âìÊÀ Ûø¿º½ZëûŠg(ù Jóà_“¿IÛ%o*Á�;<ÚA_9q©.¿æ:˜# ú®Yå@<�¡o ›Ò„Ueü8ÓµÊâáÝåX�uzNu˜Ëö)_ ðN[äK¬‘:|\›2�„ìéÃ5§fU@%q„q~Œ¥¡=¡˜4߈ >1N€ß¹a üÿ:ô…KN�L}¯Ž¬Cˆ8×{P¼£æÃøË¿²,ûìúÍgÌ©çÖ½¢á“§,Ï¥eBÙy˜Ä¸_ÔI—œFIz@ÉQ€º¤q>ò€Gê5ñªx!SßMÈ|x¾²_NyKÀ`¡ âÈD1)ÆM¼âyg˜*sæD€2Ù!€ ×“Ñ h‚ó—(ñô\¤Rp�B+û‰Y«šæ—?$!â?½=ß=¤I¸kr6�…Á¼—ܵ¶ÀÌæHµ˜Êž&>þßéœ.þ½Ð_ÜÖ‰;ë—/Kä�§4ÈG[V1CÏÁt#¼ HüõÛ¥juQ)Ðî„¡N�{¥¶äòõU¾XÂ#*nјg‚ÏËì,‰"„ú .:ÜT†4ès� œÌ ÕŒ�…F… µ-M Þ{ÙY/ Þ|øŽ�†p%ŸELjzyÈ`g;lˆÛ{±ùc¨õqõÝÁLŒX~?ó‘_>MÕ_œ‘i´v?HEÐð¥ÛÀRX*; ÕY39‘ËtÇ¡7LDÒ#Ê‘ÉvDºòÒòm”Ý{€¦ „‚Ž •*7aö-p ‘\ ‚úŸæ¥\oÒŽÂY¬ìYúk‘ÇÛ$—Z€^ÒA;^$Y5Âk“Í 1ã/\ ‰D Š�ë'äÍg\µŒ?d¾Á1]NHª™¥ÛO™¹ŸN8Ì‹Z�º ã yFŠ3……àŸZJí`�OÔª‚FNk¨ ÈuºTÌ”‚ˆZ�lÐϬ‹ ­ƒhjÌý«@¾*|Ï#úôl»ÀÖXy5D¸G¡\x×®;:A“�“(<šfc8¦ôö¯‘-ˆ@V§��Kó©È¶ÄƒJ¹¨·ÈD0Ëé[4î KtÀëŒü©GpååuÉÛ=ɪÜSézöÙQ¿k˜i¹»þsïÊžxpC‡ãA}hÄ*ì›P™ÇµÑtôBP ç÷µŸp'8ò•LèåìÿbùɆmÖÀ]™ÃßáI`Á™Ç(¨rãC²@(4ºˆW5z Ð�±üV�ÊQ×ÛJ©akºVœô…7ûÄÇé¢V—�´Ÿ^Š[±z¥7‹ô‡é8r\[um§ÏFyú‰O^°[¢D¦çÁž·†b¶‹ö$ÈÚ0BÍ4wÌQ†Ž… �ZõR®Uüíߌ°g qŸÅÖ fN\”|GGÜtc¼ÎhÄ>Ǻow�âÈ=¶Í·å¤Ê r0G†çZ«¬‚”;·yÒÖµÊuÇ�CñÀNPr¾Óš—‰ÒíÝÄCá~e þÓ÷úÿS@¶§ ñ©ÐjLú1wì2a ÒKy­ØûYd ˆÙH²×úèëôar¥[¤Î}‡T—^¢°…Œëµgª„üZ±Š”¶ ‹mNÝ3xe­ÊÁЩÒoèŸÔZ;O�;)¶Š2¸7«]™jµP ô:Ńù ¦»¸3*N³oÎAШR'„–¢¥ÙlHŒÎÌà#¦=�Zsÿ¹½Ôm>:˜®}€wȃ \Y(Ì•tãBœ…-êeK­Ò•†¡üßDŸ…äR1‹ÑvTØ6| ˆ�Ý—ßß-%Ó!A†CËW ±ª uÇÓîå>ü.‰i>¸B�9ÚHU8¨q!¸Œp“}šŸ¼¥f¡G]8ÝF‰(¢c*WÈåscöÒ{5@×áÔ �±!�οí>²F�2Ô‡®»rýõS3/}uPâÕÚNk¾šÎÀ�¬óÑÖìG§Íá­¿6ô˜Ç8EWå™M)P±Çvt»Ã(]¹á�Ä€YNX!³Ô·²ÛÐùõ¶ÚÛZy~¥wˆ¡âÞ)»éý¾Z¤¨†ß2P¢] ¦6I›#‡Š µ-¶µo”7ÉúÆd¤õ umÏJP—êÎ0©�ç•™ ¡øsi¨a‚¡ƒ)@>v| ÑC�:ë$Õ£÷k‘œ_VFõ‹æ³EÚ¥rå8~V�&Mý5 ÆÓZ‹RÎ席—ÙaÓ#W…XÅ6¼”™Áwþ°”9†Ç*]¸5“à­›ªJ€¤ŸÐÉ$«ûaξìw°Ð¿CuÑ-YFЬÏpѶHìx vÎD®(…©ßˆ—‚R!Yö 0roaÜ£á¼�hä± &ìaõŸÌÙeä ˱>ã`@-ýÆï{Õ�˜@vGQ-}ІŽÁ ¥‘(Fà·q3“ýiŽ Ï²”ãg™\ŠÓô`r�C{šÀ3|!ð;²ƒL‡©®�͆Œè�¤³Ôt½šæ—êîä7ÃÝÌŒl[ƒ"MƒåžÎ KJžäãøIS<�Ê+á£Ç1mUp!^2Å¥¨+ëÓ„Ÿ>‰åtF¡¡9lM# Ò0¹³6hvÖxqÄ91[<<ûñ¶h`«™-‚zôÕ‰Oˆ^ÎÉyGß]=ç™ôÚ¿Ëö¸e_àŒñX0þýÛ4[ ~ê]ÙUjÓ©ù9Mõ¯ÑSçñ:v€‰ý+oï¯<¼A÷A?Zþj´˜ ˜•uâ,Î ¼›hµJqžÛjC̶ž~ vw¦2† œÕoèž°D�´"2̸gh… gRÃÈæáÀ¡0L†Hœ ˜VÜíA” Š–c\òk¾EK +±Qky³• EAk&reð2s¾–¢²adº¼"“ÖSä <@W 2~À˜¼l~Œ>™£þµüð3XzP/hëBû5°æ>Á^¬c¦äG$k몺W~˜2rüqOºŽ“õ Âl‰_óYda:Ú˜lÅ6n=þf𣅲7¢yD’Õö%y¨à'„…›-Y#T ®ÞjòÁ0R„u�± ñ3òØüú†Š%¥ÀÎׄîGA÷ì‚üIl{.º¨å Q£mÆv~$¬£¸Av‹Q}£#0ö/Sˆd±Køö-zZê]ìgÚ`·U¡!(G“ÌJ�KRÎHz¯Ý lB©ÖþªõgI�åü³Ç´MÒDUæR ­êõ©Ù¿V/1zBÿS)Žn0[­ÊŒHnM~pö Âb¶A.]?fCãî1¾ñ íõ£©>E,ËFöÁYhÃ<{¥ýó B•¦ç£©û˜s©¥»¾:•_ý¼è1eô[ÿÃWЄ’űƒšã_³ü¡�_ÌŸ†�€–³ºœ^²l¢C*ò9Y@¬F,]§=:ÉÊzµ¥-r˜-Ö,–h©ßžnÊ› ~ŠsK¹U&­Lºª!ðÔ„û!ÿ{Âc6Þ�â\aþð$/b¬E†ªî6Ö†”šÌ/ÅĤÜr㾺Ó?–Óª B½p;Æï÷Nï�Ó²çô»çÙ‡:`ûÙaCc°!Êt›5%Ó"æyF!öÊ´ê]À3TIgw ÍH¬¥ª¡dHÐ]·µ�aÉÞG­¥Rã )‡ -C÷Þ‰r¥F¸„ßæqo6«„ü&M²EpÌ/‡¥ Àr�QQÑꬬt¿·ž½â¾WŠ 0éu  õ' zËt%¶ü<ðŒŠY’ˆvGÆ„„ð M�E�¨`ø2öÞXÒªGîÀpf|¢º®Ø¿raú‹{i½fÔ¾€�E�X´¬$Ë ”XOÆ8 ¯`?¨¦êeø‚ç­6‡n—TφЗy>�¬™ÐØÔÞËý±x~�roå½C�ÇaŽHX–›‘»^70YgÁ!`-RhìÐm^6;Ç"®Sc Gê税Q`·PPâÉ`R­<àñí¾QŠ€ÍŸ³ol”’t¡Î…w ÅA½ uõ,§^�ˆ¿ëH@ÎÃ�‚îOû}u1[|–zäœ�ÔA vã JÝe�¸8fs•ï§3þ»Í `=p¬Ž?¿ºd9IÃÇQ³²›ü�1FŠV?ß%PF'%¦ícƒ�àÎå&/U£YžÏÙNæŠ|uo“‘Ñ DqZ‚¬Û~Ë~ –ÚϰpOA!c7J›Áa¨³QyðOec¦ÓË[á*-ÔCû ɤÙ±²î:ã#ö".(ã�ùÙ^e°ô* § c9^n_П˞²“ycDZdÀÕ‡š_òŽÀGU�«U ù'¢8#ØëÒ@>…Sš¶³Ü"ļƕ­I9Ù•ë‡<¹´á¹Mäí͂߾Üt1Z(†èmŒÏAÌútÆ÷æzb»ùú¡4{·WX²”g­Í-�9‘.X.ñàNÕüšÚ4…xîy"æì†›y@7:Õ¡ú!–˜b@‰ �^[dlj٠=ZÎ)a3ŒV³©²?Z(¨ ÝÆ ä²›r#=ãÕ|¨SÞKÏ'º¬GÖ«(qà·¤JmìÏh×ÁeÀø'�WƒÜ˧‘K<ˆðf@Pš»Ë'¯Xˆ\rÀëH¯—ac ]ˆ‘#?·ã!çÑÏííH; ªŒ‘â©¡g{)êµý®Œ–Hb¬v(‰ #.`èfՃђmÛõ�VÉ�ñˆªy´oÃCÙ±O ¼­ É—Q¾E¢§Ow¿î£íUšÎ]�Ãj·èþ×ýud(ÆêgðWp.! �Èó{Çô²ÿq— ñwFAû4�`¾Ø$õ‡ó¤¡{/¢-æê»Ã£ÒßCræ }ü«/åöë¼ÇTÑB0�œ³¤×i(á‰Û‘à»Yf1Q”šgø´*ÀžÜ¤Æ¯šRî�úØFÔ™Ž5ù¨:Ýx êŸe;ÿèúè‘øß@ê£M›}æ‚!hºnÇ œg*¯‡€¶�Lé>v$p'š·bR >”�šŒ_âÿ †€Ì›€údNôÝß••œf´ø£çEÏjÒ‚–•@•R5`D_3­¡±ãÝL®•I›É”¹´ \uõÂæ$¶ý~º{™Zý÷Òt òc½S�l·ŠÔ:l]x÷?¸ˆ Ýç|Êó¶|}¯‚a Ö)À£ŒÖ1€¼çâñdñçžþ»€ËLð“Î,âõtÏVužt$ßè¼íå%Z®àŽEû·iEy|Æ€ÙŸ Õ¥%"yâoWÀíÛÊG/J‚P!JµUg4 W}ä£pÿ½�àîêïþgú?|ý/LJå€&‡Íï‡ͅDî�IñD£•—‡àW6‰mScmÉ¢Ù,›XeãÝ©èZ¤„ ¿; #ºõC•Â)!´)Äo8[–b€•_TØ;¿ä›ßH“–ýÀáðYèRqNë·eþùâ+²±ÓŸÔ°mFV}Z‘–. JÁ÷?_´Æò(µcƒ{ß0]‰N»˜u¡5ÖÇHïœÂ†y™ bz@õ;ÞaéP y°ky_ªúSô[ƒëì–5£Lƒò[:0úËoU6ž¿µiy±[“ÑmDãdJ,qáÉá@j76^½oä½Ä"\Tÿ9µ\[S|6–¶d½°ŠÃ{NQÝdë3Ý‚1(tV¤Þég…£Û�ù÷rl§Må.Ûš°O'1¤Wõ”ê@ø {Šè…\ÉE|ɾu]ó^ÆGUrY…6ã`Knx|›Ó°C:šƒ 'ÕU~aqÿ 5°Qc•úí’Ì_íë}Uq÷U²¬¤è9‚Ê[|Á1ïÄñ¼}C´q)´ë‘<Æ,®0s+LÌkâŒNDÄØ8„þ¯KT ÷xô© §QM6.äW�ºše²S'œ¹� �o& Ëö>ip±ÛŠšj[ç8瘖ÚrJƒ�ÆHÝÅcÿ^HÀU/ewØÙb>ªÅõ0)þk»¬�üY¨ 6¢5y%ÁÌ-§+ÍŒÊ ·ÈöázȘ•€™€´Ü_öž»EÈîw«Ï�$¤›.Èç¨2âC„ ü¯eàAû4ÀaEªqþ·Æ6ÒÄ¿klø2—TiyoÙ¼˜s_ f5w¨žksJØÝàâ*8ƒw•‹b`Ó¼ ¨RÇ3hd§º¼æ ½$Ú78T}ýt·‡n6wÿئn7®5æß&–¨Qóì@¾8c²MøHŒtM?hÆW“üqó¡O§B�jqõˆ8$0özåå:o8ÉL t…ÌœÛlê¸Òñ7OÑ@졟“„Ñ'&q92@ö¤ŽV$,“üÄÎuÏS>>‡‚#øÌô±¥=Ûíò¾¢Ó|îN'õ¸ª=>ƒ|C™·ƒú“8Á¢ÿ ÃÌr™æÀ¨‚ˆg’… Î`øNýðœúór¾ÉËV’ø;”–/ÛÕ0è¶ö(ô;ÏY” õr�ws䊈 XlÇ>î nïUþß=�ý×?-ºÓ¾:S}æ‹ÓŨW½g¢–X.M<~}™L‡ÜÏåJh@ £QL�õ½¾?ÄUñ~Në©MB~|—Ûúè´kæ–(NÈ•VàoÎ<߫֯~|4£AÒÉ&ï!¤Ã€iúÕ�X¼4…‹÷\ÐfäôÏœWjòÏj,¥ |ðd õ2""ÖzSb1œIn.ú‡ÿ®6_õ´œe¤õ£Áx�¤NáÃ�Þ`'W£Ž ÿ¾ ÄB�VV—¿ÓFß/{³Fám±²EAw !Ë!5ײQ ¼ï²¢È§Àü"�‰Eâ)) Ò òè%ÊÅMŽ� 4pø`ÜžsÐýàca�ÒîM~”Lrgs±”ŽTëƒ|2sýÜÝïüm¬ÞÏ$+!*Õ#wìϯ èÞ†·˜ŒÏÆoÓ¡&]'@’° ¬Õ)X\„üL׺†Ö9‘5KÛ<ÝñÄ0¡Ü ³"E€ƒm‘V˜E•±tñ^ŽÐ#IÙ¨«æˆÙXNý�W,_eôU§»ù7­ó`ÚÉI‘4ñk _�t ñ%'q�Õî|gvBÜj ÄÆ½iyYu¸ÖÎ °^õM$u•ÇlH“ ×)>ƒ†aÄê‚PcJ[ŨDÞUûºL’ü³ÌÖ—ÍP §³U!àºáçË5?R™²‹!.l@»³ð2ª5uSTã»MÁDÌöqÜZ³ �Q3(4ÎLI:åxPgÜ ÄNš¯mn|³s™" ‚×8÷f…ùtyÍït°F¿ñV!þh{Þ0K ôEö ×¼ä]cW„:À\Úº�òÈ?‹ãÞÖpÀuñœÂlÎèèÔ±³4†Wùwî%°}±*œQú }ž>ÓN«¬è ò( ý�*y²³~>Ô¶Uø®##’XOõmõê•ÉièÅOLE‚§þˆW3ÝKo‹ü°Ÿ$ÆH;=”Jv†CE/\e?“l‹kW†“ª�V†› iIEg6‰®­æsD¡2õ´¸`îò¾ܤÛ“çK])!lZ µAæ·d.ƒŽ+¹!¨ENR¤‚ˆxñmN¬U¥Iñ@�&ëðµßýö+¹3Xl§á×Ë…ømÄ%'7ŽìÛ 6M_0’&8®‡úy%'»Ä/À÷ÊAr𬧅²Ï=4ôï1†;G¤?…œNé+�ìF5Ϲ�Þº•løý)sMáÆNe9\BP|Ø5[úTWÌ!}`«Î}]ãh>yýuúc /Í:j•ê¦2zNgÅ"×öFø0.Àu�Bovä_-ÅßÀ¼<›üw·k`¿�Ùl+?ʈY4&ƒT„ o�žšNª§^[Õ©ÜïÊ-5Œ™UúîšÿSíøìrËÚ¥‰¹«/ì¸ ’·ë|Ï„u0“|û)ZZ¢Õ̼2²­)@^\”ö?²¯ÏÙÃRÝÐ!d hñ3 ð’Ïܘƒâ¶O“œ6b¹äz²{³ðZy–ï܇ 9µofïª�¯hàþ< þ‰ÖýÖO€ÄáƒÅQ>&¶ñµÃëgmçÉ=pà˜¹ªé~ªy~Öª?>ÓS8j¢ŽÌ¹OË…ðÑͨõ‘UËÀzzÙ!G›1nªÁ-“ö¦b¯:Ä »(jœ;wÇýŒ's™MŸ�Òr7sê0E‘)ð ¹ 'ψÀ‹<è¥U•�Ƭqãxn�ìP™ðœÒ—(^à–2á)åÈíG·ú»×FLxx¸¸ôzU¯œáb ]ØÆ‚ <$`õHuwaï@ÓûU-�LR$ká*8QRŠ)Ò_C*Ê »Ë£6XÌÔŽ” lØ ÝLùÉêhhïfßšpÀ w‰&C¹Â?Ì’¥[¸4€�#€~Â{7#§ÁÊñß_<'ÎîZÆ-jñ^]8h’MþLÀ³-O}ø}#«!ÑÃ'OÌEŽš2§1Ì*»¥”å˧¢H`wÃ>í>Ïzè)§eën,¦»ÙúhCaRôT¨±¤©�y¼N±×e3{ËMI<7Šsùp×…=´oL,áöz‰™ë²çR¦›æÎ{ËÚ³C©àý¨ÿQc‹gðNPœpBi�(ã>U�Ea‡Þ¨í$#åÈÆ-‰ÓìÉàD/7Õ“=}›ÝRðþÄ(z b6:8�y¡ØYj²#]•1öƳ3OÊê®}U¹ò ÔŽCùö Ú8³µsµ'Û&m{.„‘“˜†×ƒŸ²U¶ASfN¸güÈáeëãýJŒŸ8�_ø�4-¤¬ô¥ÍYÝ“1‘-±Úr Ë[Â1{úÕo-ú4•—–è±ÁP‚Ý'>%’Éå=`7âýÐÊßlQ4GèÇH”ÜunNÙ&ÄŠ ç#gi�aëO#Ze#”“´rŒ‘A?h8±Ô˜úÞ :HÅ«“ÊÕ]rä>6*Á¾�i[‹cšÚQ•ê8²òuo± cÅ&˜êhþ¡5ÇpÚÛ"G¥Î E³@òS'Å+¥Q¢`JWÀ¼Ë}+ÞâŽÇ/n ‹kybøhvKEž˜^|(QW.®ò>Ï;¼¬‚~^*‹f…ŸI¢ôÜq¿!®èïß±/º·Î˜¨ ‚€ìÄÐ?]”w.5VìŠ^™R�û™î·¬N§“ëX¢LÒkU)’&Œßàk “4/×~�)â/Ê ¾šŒú·ˆxƒ™ü’Ï“XÈI&·ÓŠ-Qe ºip¤Mv)ð¡Ç´NKC£÷¤/óŸ§JæóÒ|wÍLq ó«½ï¯H�IÖÂɾ¿¢ä'eT¯Ɖ°£‹M=’Öy¾]1ׄ ~6UYû6ùdé'û/@‘÷U8÷‚©©‹ hªéÔ“ŠÄW•~áøŒB´]`NI¶·^—·,˜(kÓ»°œh€…F èpÒ¡í�~´H…Äj0BïtmO "GøÕ‹É[ŸQ¾áè¸3ânJï²í¥ºÂH¿_Ø·„k¡lÒ ”¿G™A<*°B¦ç8â˜U®‡OÊ Â,©»Ô�C‰ÞAvxŸØ“ÆÃ¹úèàB­Ív‹Š“³Øn%�ðŒcÁÚ=ØëS´¬ÇøÄ4ÌÄ=Ší@S†uÚOLésЃᖴº‘ž"ß©¤°.¯¼D0{¬Ä²Ó«oU��Ð]œß%A`T Š=—•V½—£amE �Œ{úãL¡lË|«²ÁUÅ6}Š ±^ÑR²Ëœ� ãb$ÆÈÁa“¸æ9Él5­Råˆg£ |*ÃxF,ícùß:òóe2*kè 2ô‚žâ]µ™Á¤Q¢vo³[ÚðÖj,ÕpïïåÜaî¿ÞH®‰[B‹Œ2 Z÷4ÔÕb·#ôÖ+û©#MÓÀÆ$xwë˜K£ ^¤LW•òVê,$Qß‚!U-ƒm“„èøÔWÝöd²w¯È(Å0Ó�>;OiõÂÂY&1)9mé ¥Ôìñ.³u|anÝ�LÐ,‚æšØ"ÂÊ2è†Æ°2;Üùýÿî¾w¢¿ÓÐf]¦+ÓaH͆Rè)º[*�‚’îç�(¾5>Ž—ù•íŸbqb‹5Q.ÓziçùšR‚-=Õ«¥è´;[Ô·=Œ±„/‹Í¹‘›ò³û<§&ɽRßQ!Jt<ضÄ%´®u#GV¨U/Å!íèg+ÌϳnK ­d¶Ë¥RÄh‹‰À¼Xéß`]Õ<´ðûDVG”8'ÄÀqS“wËú&ºnyÂ&~œ��±½Ò&ÛÂòêzÏH›£àX¹ã•€†˜ïä:O–µý×�õhà=>#k)o™?˜ÀV‹Ó>–€êïeN ¡½Î.ëCê‚�:bV×Ú¿ÑEré&„i®‚#MÈS9òòžÀøw]öÝäÂÅ>aÛ<’maÀO¢ïôÝ0¿Y‡Ú×TƒO¥—ZQÖÍ’s1Š­®@L»|!až VˆÅÐŒ:8-ÖQ~(»”NæCyxjÁ–ï4 {YB’ÒM¸ÇNnIGÉ¡É5ˆOÃ#4oϧÊ5on‡ÿ7\iÝ·7|m‹íù[¤+´�¥Å5}€ÕP`ß[:‹\ÂpSÍb)HÅÅÙ¶ý{ZZ“Þåe¿²¦µN…¤ ר+Æðá}‘c©Úk«‘‘4‚¬³Ê®2m5ÄæS·�þ¥>ï¯Dö³3ö\Ψ~Ç¥�íD‹a²@ ŒöºüM;W]UV\Ù7_CõyïiaPGè£ê¹n”÷Â_NÜâÚaìo!ìÃIΘ¥Â³}à2ŸÁ5±KĈÄÃéL ?,Oeèo* ©¥ÈÇ¢íTÁ‰>*Ø-ûüj6g¼ dÎ56›…~ ‘sçÔxÏqœð"r`ã8ø·�-õ„v�nøB(ÄËv¢y霚t»�×Þ‚ÎAgcÊ­!€2ÌaV’Lns詜ϸ3mŒdP(m¦¶¢ýº_2ɘ9ïŠ߈ètÂÿ;¾²Ê*¼ÃúY�T±iï»úY™Ž9Õu‰ÒÄ ¬îN²ñ>�YŸ(Dg,‡«¼�°%‰¾IÈ^Úº{ðÞ4‡Ö…iaxG¯C[]ìÇu˦”ŽÉS¯tÅ�;¸£ù©ÏÉ`7ûú¾›ÍmDxlæ›ÍR…e‘ŽÜ§Ÿ]f}B=WXö°ÆWàVÛbxmµÑeê…õ޹™~=¿š`‰<ì¸æåü¸øõ€TÔÚJêºöåq¡?‘Ìv¶8ï{ùÐÊæ#Ã1gÂÿÞu#e6•Ö‘2Qz{Ght©÷ûž”Q-Pw´íBLlC*°ZÖ=�Íyw©šâBÔß®a›àÜTî[ÈíÑ ®:#1 oq]eß"¤Bx/V¼´T3$í<-ã§~TßI†â8h‹b•€÷ÂZw!ëÍÈmŸL¼Žëäž SkŒÜnexÓçá\OœX1~Ûß&{Í`H7pË _ã«] 5Z¢È^Y5‚ýfß$ ~ øì‰<Š8ÒX¬n`L È ¿—¶ÑæË¥³bºÿ_Sñ�z-„J<‹xÙÀ8K–%>ñW�ÿ )—„¢öÊ|IÿãÅ’šOË’c–ÆI ýG¤¥„èIJ•òS4"×Ë–�_¡tƒô45[¹¬‡ UëT!ËõH�ŒHü‚ký³O®ºÏÿwäj“r à߆a0à”™6…u5�|ÑN&$6 MåµRÏð+Àâ_¹“ôÉ&Þ�3ýé›:;ÖŠeLçKûÏ·Gf%ug(ÃüœP³Ôˆ–Ä“v¨˜©9¿$þT®Ñ!ßú6ÿ¯Gó �º”tµü™ÛÕç±ÈŒpúŽET“ÆiC‰UÜga6¥Ý%ëÌÆ%ôí…a¡ƒS®×:Òî?š+3†å�¿×1ScmRÛù"`zS…÷u?šK"#}~Z3¤K¬@�q'†ƒ~Ûo6 NRX:”ùqQ³�Ð¥<äï Yòób�)™ÛAðGU bvè �Ðþd£*,pÈŸÝáÛS€m^Ž‚� Þ.½uë7Kº”ŒÊþ%$Ú÷v÷@È”VÑ'ÐciÌyW'ñsy f(‚&õ 'Jå¸[ÓÉ5è8€Ó!D­é�—ßqÂf.HQÿŒ3‚wax½6Ÿ;¾c+„�ŸÕgf¢wY¬ëüa¡!ÎÔ¸ï&@‹»x‘íVž«<‘yjÎ’[TÂn!€YŸ´AÐLÕRéŒm¦D^䘬ú�Ã;ÓÚº#›s†ïn/�=Ä(œëxµ› 63õPû&H2Ì•,ý$AC­Á,”8BT‹Ýw5 *²¤ÝyèL†ŽÅ+H^(s«‡›2Z˜ëÒK¥âk¦ÒpʾÛâ ‡˜ZÜç4�1©eÑIž$ŸÂc—Öß›³ 6YÅ­«ÔÇ•(wLŇš+yDIk‡æRä5æÄðóŒýîœv8ÀM\Ý||”Œ¦É97?‘Cú.W=’Mf¡¢ÑPÇ�*òÜ ÖA9j™/bñ‰þÞüJã§Ôùì])–XɹòA�`§J<·Q y”£/Þ%hc ’ß"¦zÃÙþÛ“W±e H²×Hõ¥!ˆÓMÊ™¯S”Mm«GŽp/ýZÕ@lÐèý¡~„ËŒ$˜àdës¹R ó3¹<‡ÍùÔ·_-|¤mÊÌp±�ñYãö{"!H˜—~;É ìbµÀ¦c¡ŠT h:ìÐy¿5ÿ™Äm<UöØ-’$„  pPÓw±h‰•EAñ­OÉû˜�CÞ×O¶�œtôa«ã¨•ÚoàZÄ!ŸMã=ªÈyCn¬%ô0ÇÜtÅÃ~Jfù’„¨Jƒo ?Û[|lT1 ^ù£^Ÿ×X7P­Õõ¬þGñÍxÖ©ÛýÓ†GDÒ¡x¯«º¥Íwu=©hÀæ�×<ÐíØ„DÊ�*ø |ð5¥º¹¡[5¼¥už:Aœ“O¡GÏrwšv¬ÍnØŠB+Ô ÜKæÇëöoÕ‡áÀ8vB¯e“°™ ._GµzÕÕÚ dÓÄ‘dbeªcG5’—hÉ×–¾¦éÃ.Ôêp�$.¢)"y<1zd¦ëžfaÙgv{ð«ò°e(!ÖÙ¢ ëX$>–­Go‡›;M#û×{iõ/ÖúüRC;ü2yâ‚@â°}6Ƨ?œö¥Ê žÒºÇ…´Ífäö¸˜¥H‰èZ¦U¢SY¤5ì—fÀ_¡ý4ü¥E°O À¡ºVLS‰{Zgö­§­"Hóþ`\+{Óì‡ZµDæfªè—&œ�ûRè!FðÂ*­Ä6§ãëŠKDh œÓìí3£(ÝžS{�BXïÀq‹†�j…-–z~JÕ‰I•h÷ú"#³pR(§³M:1‰ª¿Ùy¥µy{Ñ1£ú(RÚð`K3U�U²ÖPKI<{6È!˜íñê�S¼­;†é‡\J_Ð‹Úæ7Þ6™]E©¯¬\ ¿ž�cÚ+7Qq4^¡‡1´ïò¦B{sÈ ¹[X�|vï­BéIAz}ŽÊ�;t‡Ù›‰Hlž¤ççüQ ;Dsù¬þ¯Î@W\?í/d–"/ Ka~¯pNN²`­Á P.Ä—‰Íò€ñ2¦4TDÔÚXJÓÒêrhص0–'2}DÕç¶½±Øe³ë1¯À9|–ʹô"�DR”檌,ºš;V´—‚½¹w $#ÙM3† žú«8,ýw´;Šž»ñùhÆbÄúPÎ{·B�¯z¡¯øï·–jç¼É 3€ü€j¤¯_OÒèø²Ö�Èò\2vøÄ2t>º"»›CÓ-ÞcÈìâB€ƒÎán%þ&,¬™ ¨P�2ñ]ØÌ]Yeÿ·°ÔÉ 4ŽJ|m0KœñBÜE–½7M¢Q�èÙ9°?]è–ÅaŸÕ—·ïÕ¶B‘k]åvª¾ÂÈ‹K1Â>h½àÕON ˆ�3eˆç¤U‹úÊ�*²_UyÓði•ëvÜåýo¼dDNÙüìtº4 5Á–.@ðpOÂ4ÀÞ6aÄ=Ò´ :ÁͲt48*ïâÚ Çc©˜F›wpon,e+1G´\ÿUl{Î#wÚrÜ„„’kË&i9-ÝåR¦È|ØÙüM샯ÿ*%¤¥`ìɹ\äVÅ·ù RÝÄ7MÑÏc†U®Œ‰iC�È¡�íšÎbnõž{ã5^)æ~> IñÚ]”‘»# )AŠBtÞ d¶QAA 7Ô€1YZFw}BFÊ'ÃÓ~Ÿ3Í·�=õ›€z‡˜iGæ1kHPv.SöCkD—¡jŠ êÛ„�ÝäcÜþÄ‘õLÎ7Þ4=¡–½Û·­i=Ã[ iáù¶P8lÎbÖC“¶Õ¬¸Þ }Ø€¢¯šŸ‘xɼ²ÀX1:º¹nŠaFB7“‰³‚µv¯ÌŒÌÕ§`kÿÊ ç)à4+»Bâ[W‹‹ÃHH&ÆÊé®ëé§´Â)µõY¤Ù^œ®S0Bõ”žA)qÚŸKˆ�Àë[úËÛ£UfµpS˜Ñr¸tÖ�ÙO€RBÀ.Và2o NÌ5âf¤[€¢øgq[ˆ�·±\:Y}ôßš+fRQ«qHAmò²ˆqïWMTdŒ"ÎQ¸kÆÈù%2¥%8—N½ìÖÂ9<|{ŸsŠ­£«i:ó;Ñ€¨°í'ÓZäð3Ÿ t“]æ¸"1;"Áºú ÙúZÈëÌÌC•G#m`m/æŒ@Íb\æãýÏN2ÍcbÐà‰òNÉ+̵ɬ¶Ï6‚ƒOKdZ±ErÍ_YB œù®�Љnó#ØÏÞšAã§ÍÓ*èÚÔ Õ”—ú97•ü ú‡ÃH´Üp�í{‹3IIXëÈf3�Ãð„/ì§.ÏžþÙÿU¦=ÇKñ¤ZŠåí™9Â2>8øðpŠÅñFÞéàä"¸`ÜC„§Å¨à‡¾d¥w‰h**�Ñ–ˆîNHíɺô ÿf(?3c�H[ÇhùOk­ž‰6'§ø¤¥žß:é•yˆhÍ&ÚÝý@½¶™°dµ¿ Ï!i¦ãÀ§”1ÃC¶&)Øù¼Ç?EÙØÖø°¨NqY •;ëEjßÌ<¼š[9iÝüð.µŸXh[T탡r>rY±ó.í¬²%ׯt0š�¶—Ý/;#•¯–+¨žÚ_y€E%(7y•q1ÞÏ׆õÀ´4êkÏÔÆuA¼c®G_@� XË :žø)`¦ .™Ï¸89BDŒ€Éo‡7á�ÝΨÂu‡_¤Y‹Üe“__êüú«UÐN‡G¢úÛ|+]mZEå(7Pü½ÿx" |þs …ׂ°6Óã aî’ý^€M…àšc¥üƒ¬ùE(ãY ç?~ñ},iìÈò%²=K±%‡I–Ú¢WŒ"¢7x`Õ!»´+Á2wä‚úÇÛ�Uþð)w¤†áŠûU ^Œ2•"LT•òõ…ä]à”ð›ÖÉd:4Ž’ŸŸæä†[E;^­O‹Ü’léQ¥`qÌaÞ^ù”aĉ1rNw"¶ª‰Ä6½å…bø. ¦e1F§EEƒw†3‚ì b]eýÆnÉw@À!ºe8Z!QêÀó`E¥¾ÔÖY2. ,Ò¥£ß îŒÔ5D�»P¤ä5d¦ )ŸG }Q?9Ý‚x/©Ô9a,&¬Þ¸û?1Òᢑ•W’e£âÃ(Ög¤ŒªäjÔ0­0¤A ³¾Bl»ZñSÌraNb£gmã! R#p‚¿þ¯‡;©)°° H‹@Z­k#`¹h# À,‡TÔÌuªØìK æëª€tÆyï1«P\Â^¢,Äm@]Àž”uDÆô¿£4tY-(–O?ÿ\"¸ýóó#f`ræÀ{c7wιž�Û“Tâ)ÚQØ–r/pçØK3üõäíU¼à% Š³7Ù‹O8žFi;K®“õ*ŸNÕ‡`ÑATø…ŸR»750¥2·õÿ®ü§ÿ­g–æd4‰æ0E}“ï6ãÏûºå=4Ó"Hñí™”xØVÁ)ðq5Fm�TÞä˜9«¨°>¹i±¬®îýØD/tÛC’ô`/¸°/]hQܽ0y/v�w•/×GwßÐÎ0´�oF,T•!ƒ%U§Ë†ø3×±\)¼\_˜cIÕD¼èL'ž\I”»' Ÿ¦ò -�Jõ½µ¡!=ª¢vµšÔz…yÄ÷ó . ŽeŒzjX͘ÌDˆÕ´ñgxË'„°aä,©9Ó¹ÇÇ—�•Ÿi^÷&Û¿ ±ŸçòÓZ¿ís9]Øž4ãR›ùñþ³IV=)@ /é•Hs17�Å”Y±ÔÈ?ºFo¸Y�sól¿Apg ³n÷aÍÓ´u½püßX!¥äô°oø –`ÊÀ3Áäa¯k##ÿ{¤�h‘Jk˜›O"�¾ÊðÞ-2õ¨"_ón¨‘~'¹UŠá¿ë—@D"�-�\<}Ä1à <û� H ð‡J0ê(<œÒ%Ÿºnj¯`=fI™æ³öbp�Ò:¯Š&ÑÛ3 Ưù�á€úNW“s!Zì·-Ó�y|MÔT»î‡ü¦hŽN¸Þ;&‡»_¸÷Í¡ÓlóÇ!—WOPic,TÈðµMÝz@ŽÚµ„ñg™³45aAû8}H¨Ès—ѬA�­ùßoa„'í ™©vkCá±H U®à„M¼| Z±·…Ú¯W¤=äÏΤy:9½·;Ïö~õâ´3rдìÂ@e¬µ êîϧs²Ìo(æíÃ@$_ʦf‘H­É–¸dUŽ”¸RÁ´ ˜¢û¶‘ýU”õS£|«á" ám�³Ÿ®‹Qy×¼&¬¥·/[¡”>{º°†Ã3£Šáð¤StàŠêAYè¾`§såY…,UnÀeõC ‹Ã# ­Qh xV ò ?‰3Çš§ Å)Рت3òtqFÆõO¨ ù•Ä_½íd¢�‹IžïC>w~Àf¹zEo/ãÝ\3ÄÉ'\CŸc§Ö  Á©ÉhŒ†…z(†ÕD¨g?À EA,ÒA}ÃH2*ºþKãj*c¼ëMuÜ=ЮºD—cÿ��Å’±�›ÇŠD¤ tm™ç41»³Ó/×ø]à‰Z.d£�½öZbñ2 Uí�1ÑÈ `ÅA5º{ƒUhwpâvñÞ!m¬Ý ŸÏ¥-ÈN¢Ä?uŽ'\¹: Q–¿¯tÚ¬éÓ`Fj=¨Bä8‘ÿñxÈ&IK„!KÞAm·én½üR¨»ÊƒF.æô;Uª„^;íñÒk �¨L¸òVé\: 棬‚?4Á´Ä·Øûã‰Y…bUJ®Lç GüSW6‰|Mç4Ôµ"•rs Ý,)v¿ä,fÞþ5@µ ´gu\ ÆæI3ÙS=Ë#þB`ÄuBIPæïz'1š¦ oR…ŽúmÅÔX8X(Ü>¬‹î; œZøýÙZ¥opÛ›¨ºa*^ÄHQœÿ-¨üåÞà[ gA7}2Ê!²!eΆu‰/ÓV“Ǩ €S5_pæ�$Ò¼Þú䮟x.w¥ø©¹bÛ8¶r33Ú[¬Ôu¬rŠ6Yø®4.ù`© ßÆþ}ð�`Af1íî\ÍÄ|ì"-VE)ÔŧáþPi‹dÙd!p N‡ÊÇØÂ\-ÍØ�œÝF”oýIùB”B¡C¤3o,ôÑøùâW;ØÁ’¤w#—$ÓfQx|OÍ^~Êðg”Ë�/&zoõV/J;£J˜±>Õ¥X½Í‚ pÃ$Ôaëü½r/LÄ�ÎÒy9×A‹°¸•ˆ¹_ÈÙðIŒ3…ØÃÙ‹P�Õc›Yžh�™‰÷뇰ùȦãAËèFþ¿—7#cýTe�®NF”dqG2ˆ—ál™d”–ÄO#¬ÓsPD†(®–BcÝ3åë D+Ù?'¡èi’Ù“k`Øð?Ï ­Ôˆt–qŸrm½TP8ÀKÐ~Ev«QJ)t‚­&…þ‚*Ñ«kUê“•1ŽÎGv¥lÛ(Hˆ�Ò}{>¤pÕ8iƒ†`ÅÝî ²Ÿv÷IôBuªÓ�.Î�Ä v ëCDé%Зô€6Ž>ÆÛ§K†Ö#Ýúä@²¸Î¿?“Ù;ã«HÐ~_ýN �‚z¬î¿|:ŒþH!weK¾ZB ãÚ^(K6>¶±7d„Vy¢xqš^ù¶D¢£FµAžOò]ð¿z²{@VMŠñ@%!àÁ·¤ã½Ñ¸À�çO<^> aÍK!/ó¤…V+ØA¹¥†©–o81&ÖÙ¹�áw5û{CÊ"ìûâõa_¬± ·0n3ÙÁP4u®p¦t/=£¤ÌS:Æð¤)£ÄšOeúJêaçA<¹~âjÁøW<î¶q€Žs(ôbð»LØø˜oÐt YLXI•œý%”h»¶‡™öÚ³ƒ¦¥ôLÊ£Ü2`Q ¶¼eùO"dgV*Ù¦B~œØf FIÄ?t$É„­DjgWP ôWv�(ž}ôÍKŸSä2ÛÀ»§Jé5Q‹:Å”P’½Å\0øÕ)-ðJ°kb%ZãòcÛ/m­]^¹á—G�bB²¤Fï)æáf{G­]å“sŒ�¶ßd¸u­œÐÀNe&’¹B®âžg`ðX¿R¯ �~� ÛU(G}”äTh!!-F‡*2¤_¶¿ú‡Lñ'Ïâœ;Ëôàgèµ»B¶m§eÿ{ªýÐÓÄŽƒMƒ‘éT·íÀQ0o‡ûdsÔ4ÅK.ýŒÄI~‘þ¢NìšÎ³Ê´1ƒëï×kª$@½³z§³YÙšŠÌ3å*H7’u¯:ý i%»)¼¸(!ú”~‡ÓQkpÔî%›´º•ÊÖ‚?�Ôš¥«þDSûMß¶)ï÷(Ìñ#@Ê(—°<|ryô0bÙ1ã{`ù#úªšõ œÜÀ½þömØoViÛÒ‰A¡ä\N;¸³oì]šË•vàxodltáŒ�Ž9ñ5Élòl—F÷]oa’q _–¬o3± ãUáB¤v£ªí 쿇¾�1Œ‡5{£‚D,ÑÇax$m„«8m‡7¼3!ë·¾sM7/a>o¶þO ë}ˆJT‡œÅˆÈ%Ìæ×*u‘¥m/ÂiRþðœ â‹Fñ î8ôTÊhJÏGxG9ã /åJ„Õè½ë-Ù-—rÜO�àL_[ògÈVÙE)@: ³/îÙ~õ²š5ÍÒ3€9]ãŒË)PÍŽñ7em˜߻k§Aã»h_Ú€\Ý}3Ø’]CAq—Ãx#,啬ހKÝeözÐÚ³oè½|@üP-ñ§�pÔ+᯻¹cçÖ"øŒî|h¦ïlï¬^Q1Í–gß�ß98Ðþ9KÉSn”ŠÚ¼a¼¡Yï›�œ`¥^߯¿¬È4rev’휒›xKí‘…ÞôWá+”œ)¯¾Ñá ïÈT›ji¥{âLèàCzPö>…ocêxÓ 5N~D»ä¦1v\w©Á†šÙ\0~éYÛÕ cß­Šp«w:ÙŸ~‘§^m5ÎB¦‘!áêÁp,»ˆ )( K˜Òo™xE¯¹jgµ<Îwòô kø­5fæÌ�ÛfB�ÿå *Mxò�h²ébjm:tyáOm\Tíd'rëÃ"mo;Š’®EvÓdQ_Ùyþr=´ê4±-*ß´V‹{½cOª2>É©¢°r½Ò¦b�k.uµáŽô@}m'ÈŒv«,´¿µÃ�ŸÂ4T½ð]Ñ™<"u?Oj€—ÉÑnºØ…œƒøhx‚òh¯¥‡}ý³²¡ ŸorqIZÚq 7Ÿ$'Ë]¢Ø²¦Åbµ�¯ÀŸŸÐM!tB@úô8`x'm%`# ¾£­7›§½ûßâÑ·Jê£s#Ù•–œšT úp�Ët›¼)2¾Þ œ‘×VZã+š)úË]ö¢øÒÈ ÷ìñ�åtíqŽwö÷I­µ9Ö„­>ŸHôc£®�Òƒ6i†ckŽ2bcøäßú* Ìèoúg/$è ©óM"ƒ0º–žs¡�3·RXðSÏ•{Û“W½‚µ÷‰­ô3pl¶!e¢<Í*…®‹E¡: z[üþ–°¬;àS«äÈáÂŒP6K(c×()°eÌGÅP®–¥«6h ûø�èoØ4„F$6ã¶,-'N>�Ê&VŸþuÿ÷Îêp‘– bnúÔþŒ‚~(ö†@Ñ ½2[Øñƒ_–ÙN¸ð Ÿ:ªÃiFW½WîuåO¶c:ËT¥aªËyåÎeZ¸ýœŸ‘ +m«/ [¬œúMT’5Ä¢/´˜Ñìub¾¦«|žGÕ™`£MÕ,±˜¦‹y_ ™O&í`T­²�6 7¸‰–Ý­óÅÜù( ˆ‹ÄcI ‡Õ§ (‘€¦ý³±Úm<¬¡ÿ²z®aW ~™·œ—¸(!òµ„À09¶tÎJç&ÉÌòª- HÈ÷�Z²éÒxõûñ–²#ëYsU³~5"ô(ÉÊVˆ.í­ Î… Â$î�­yu§Þˆà”9^$Ö¤)åêIVoš�ÿz(8�^EҨصƒ¢‚ ´ >F#|Ò¡Ñd`©Q¥ÄÕÌqø÷§EÑhè»Ù."Ô¬$%Ò”úKñárï–èà© –¡dí’TÚDéÁc;)CcÚ|Îõý¼–gŠ^ÃÖËÃF …¹ÅïŠîj¨Ö<¢¸ã xö×Q¬ +qÕÍ�.ºŸ²ÁISź« õoê|©EÈÀÅ.=w�ŽWùÜÙÖ5|ñûnºß2U „è|¥·t–f‰ ÃQØ‹´ƒAÝjV,Ý‹#ËÀ™hEchL—ta¦È"TWêÊä®0jæØØéN±ó¸ecJf¶Ãy)a†-$ù(¯kϵ§G�rCøØ–ó²'U³e*æ’N6!è¶EÍüY�E2eæFPN7ä0-c»ÂôV𰯊©ökùßÁJÛÀÄ8”ø­ÐŽ©’Ì-šéL QŒyh�GÐÉÏbñ~qÛ—•즃úN©sz^–ì¸ä� Fó#RÛj€ÑËŒ‰*©ãÿ>È/ÕX<á¢Óme»¾¨§ê=ëØs�zÂûPÉ ¤ÚQ9§bfF^œö:³£‹a™ïf@Ëß41Ñ|R>ûP‘Ó†Fe0´šFÐ’fŸn&–©cJ½YEîÇ¿ ›Ÿåúv‹=@Có£V¹ñá[ƒòYïãG^5x¦oèX[Åäéf,°r&:»×¦þø½G°cÒ’Ê„¾^(IªžÑØL!¦&s}2ü ñÍÚ.‡;›=E^F„0´I­/Á«pè¸Ú"ZÐâÏ%ÓƒŒ•t2ì¬öÚa›A±¶Ä]ó +ÖÓdBáôÀû@“§Mž¶L{çEAº8$kã*ÚÓnq‚·@gÜj‹«"iÌû€FŠ÷"©Ý´�F0¨†Q’–û?xËJÒ0ºôà!sר·zq©«€1!£Ê{J%N#èú�¦z-+›cÄÒ¯’öð·…ö(/§I€dkyUð ÃÒ ‰×sVC¿¡ §H€Ê˜=ºï Õ'xÓ‘ß-´k°Vð}ä¿ë¿ùÊ›;ßÏ{!;,L�bÜÆ¤‚çÇfƳWÍÞç.ÂÊf\ý¦Ïãh>F÷A¨�ù¬×_]™äQ‰,¤ €¶·ok÷¥X$ ‹@9p¢4(B°Öèá45‹Ük‡E³vìÕHÌÛoNŒñä+÷r�Xª4ëô™�yྂm÷Q´A •Š$™5µn%Ò6daœts”ë•“¼4@Yß7ΛÿwwÇŒ?_yû‘éÝ>"Q~‰ È‰ê<£˜Ó6£T®Kà]©¨lƒ22<˜<ïÖX†–^ñ“ ˜ôØ*®}�Ä‹íbïZ+Î}2ý6ú!ÂcQÈ$Gɼé×­˜ ökÿ‰X¬ßNÉŸÞ_•õèo“éãf�»Ÿ«Ž"7éøM�¬¢º'®°PÔþè{´:(æÈ¯´z < FhC�¢ñ¬â¼Žˆ!íÉ‚íhš‡+ŽìT Œ½Ì@k,QÑ|LÒÎhÜãóðhø„Áô{q†x·, “2ÃQ§÷�#‰I–\Þ|Þø.ÿh¦`àfÝ%ÿ¼'_^IÄÜ +¸0´êY²n]«í¯´BbVès ]Y)ݯîç@7ê»Ü©�íØà#rw™céõÏGüÓ–Þ—¡h¥œ© � É€FÍGù=Ocà“G¹…Ûqß>b¿4æŽ;§jÔjÜ£¦@È÷s·2©$á×€çØPX,1‚G¬h”xT Æ^(É“×�>¹2›ýót}/>ÙÌ ó'Èw)�ÄsÉ ˜ܹ˜; ½*túÂHÏ>ƒ·m>cSF«šWž¦øÑ�n¦ñ£ÀDd ó¶·ˆÀë¦ÅXZC¾¨:þ`èÙG—í`”¥iÊÖÝâ#ÝÙÀx�9"‘WNs³{FƒËF@c§÷û«cäI¦TžêSCkÂ9ˆbÔˆ¹;ãÒÖ¦W\ÙÒ¤JÉAÕ�{´òÇ,I×ûÔÏwE¹-¼ù&ž­ßg`¿¡á·z…Øg.ñͶ8¾ÈµkÐ_›U ,*êËý)ή›¶`‘#‰š�0¡×ÚGvY�ůÁšì$Z;¨X1–ÝçƒA0äÀ�xMÚX“.]ؽ…†©\ᆲóV$ƒy;³ßb:Xä‘-1ZË»ÙèDvâ¨ÂÛ”V¢êÌ5­×"‘Ý£\¡iÐFHw–ò•Ø>·7ªhv¯ÃEÔD2kñ'žêG�,ýÔHz³'‹É?õÓö«ý5V«M÷. ]¾œ"i¼ñi)Á"¼}Šþ5`ðŠÁÕ™zsNÄzŸ™DØÍœ™u囇òg=õ¦S\ŸMäN1óxTó‡ù\‡ÿ�ÄzìG'f"äA.mÄÙ|ÇŽYÎ7…êã…V*›‘˹„48xfD¹J !:6œv…Z-ïæxÌÐÔÜ«9?~EÖWôЦu`ÑÙ¾;›“žUÙ6lüÆéÀ3këË]«·¢yÆ«v4pÎUiCì“P�µÜ�R}5äçÞÔ½v‘C`+QÞÃf@A2Ä:a—åX,Gœ)ϰT¨™bà!pÉîrŸcÔ<Ø21_5imsãÂw|ò&sµÀ°0Ëbÿžõ!‹ó?wñ£�ó¯ÝÄÇ¥ÞŸ¨ Ø“Änb@Õ8! Ñ0{;w•Ñ,×ë+¨|SåËÆnüE¤’tÓuÞyþžÓãϾsùVê„‹'!ô*¡ÙÒž1¬¹%O«»^,Š��…”Ы,ñ…,ºá®ÛFAB7{7,ߊÇÙJ.ÿ.U{»É“¥'ë–ÇOÙ‡Öå� Å}âóøš´5*`ñ7–ž?ùGhöF]^«€nHÖ¤Aù’=¾œÕçè.èFØî™di…”ã~5�kÈß»¶Ì^ÕäJ[$ï«äG b–2Ó8YÚ|:¦�^Yƒußö4‰]ruŸâ9¸ÊSžû‚£®S£ÿÙ[ªO2"ÄjÑ`=}ù³j#´°Ksq»©´�§x\1,\[î4@V^„{“MúÀrHR¿éN½1&B¤»]›ÿû/¾Bhá ì›ÁPC§»P«�A›¼àŒX"mÊÛ&¤+¦öî°Cb²Áö÷”YCÁ1#›#o(¹+CÓÞÆ5'þ'ׯ‹‹ëúZ×0Ð-Û½6ÀpÞÇnÜWfJïÄp½ÔeÐÇýKŒã‡oãÎ>š‚<ȃöð÷ý¥éØG©NVŠC Z­…è|¶ƒÄ¹.a2|\ Rƒš‰ª ùh’ü(F{µ o6HŒPõý×ù?à&´ê~¦ÊíûŒ‹…8 ‚뉎øÞE`4âÖé’€"„ïÄ™sßÛÏ%�öÜ+NÂZ<š¬Êç&ÇOOýL²/‰¤QE %Ŷ1Üo±b*oâ² ‚h`Ÿ;p:QuûÍ‚ýkgw>ßÎfèð¶o÷P�°©áaŠù•^^ŸÊ>4š Þ?2~²'ËÛt_I 10– ^É\4­ SJtð^b0™wùºÛfæÊ½ -�Ÿ¶ ³ÏÚ16Û¹Kn`cUÐá9}¸"-s«ú õç¥gÿý(Áp˽±»b*Ú D„¸ ª—˜í*$÷�Ñ¢Æy iq?;¿+ ÍÑÌh§fΦ'ƒ÷åe A‡B—Â#ÍÓ…þ- ÊŸøŽÒ ¨ƒ–]Ý÷ÞœÇñ–\ˆY�4/r6á[ôÛ! ÙÐ!8t•蹞ô#�”ä ;áx±¤R¸&ùˆ_Îì±¥;Fuœ ¿•Ä(ݨ¥øÃÕF™Èeý�þu`À\‚(Á‰ñ£UÓx›Ùþ_Ü@¶Ï¹7NÊóè‘–H?LŠ $Ó�ÚD¼÷¡4?iûáñ »ÏÜ¢<íq²–Øóô�×\Òxj½¦@/}í(‹€!Í�޾°ž/”óº¶QÎÍÌÀ\(Ý6ÚByÊ&O€úb³ì.ñ‹ÿN¸àÈà†™vïaRyÊ*Ø‹¥-ؘm”Ï’ö�öAѯètatjÒcíÌøŸ]¬=0´�™ºs(g½ê9ÒtÃãH¦�j!$ê! @qì6Š×Ÿ‚ÚPãU¦G‡*}ØieU5¾Ì±ù»ºD˜âkúP]9†Ó¤ÞÛj¹Á¸€Ël¹T¬œÿ˜Ñ[//�þIÎM‘u˜´g§#Ì!²´ô‘§×O<\[_\_‡øtÚãþ$ë÷£ å0ÈéûMÆ—’©MóæPñwXC ÌÔ©-žÐ²Ð¿B¸D³GÀ­iÏH*lQÙp;)»`�Õ@ô§(ù7•Óáf¼˜ÑÚ÷)>ßVX'bG4Ž5ë´z F®Ýˆ^¦>@°äƒz™c# O½ŠYÈš�ïmt‹dBâ�ØY9ëY_�†‹¶Øý§“ÅSƒÑIíkÈI6:EÐýX@aG0ÚéXÐã!–ʃxBlPØdUK—Â?Ê‚�ŒåôWª¨{|²IŒeXbÞø4æª\à�#æ�™Âž*dK'�4W}ÿ€"¬V»Ç.Z\µL4Ê´ ŒgîÑ“KÎç© øaSrhÌ1g� Ž—øJ3 ¥ÇäEe¾¢¾Ãå%ý½A‘nM%ÏŒh²,^m: @1,l¬.Qø>ûöë1@jꈼ—…·žK«|S3‘x9ýkGZu7¢{¡YáHH!Ã5Þx -¶œŒ×îìë(Û÷•'Ý–$@Õç^±!A ç¼üWšß¹*¸-o/’Ö hny“ˆ‘·y·ß2iF:Qê�5µÑ¶ç×5¶w•÷1M;ÿã¤ÄuÐÀCUc^n¥À½®‘7ò“å¢#Yö™M£+à¶Ýx½ØçR÷ÙU„¸4Hó�+?S£ßÛ.ZèR�ÏM_¦õ¾¯ç¤fìôáÊžæ"ãÇ8ªË€{Mݽ»—‡üvóbÈÎÞn™ ΊŒ-½P3÷H$+Õwôj‚|]aäóäEuB”t'”;¦16KÞéyÞú}j¥ Ø{‰âðúÛ,Ãc^Ðw73ÕWÓ��£‚vº Vw Æ`Œ“�˜€…ã'Ó„ëh±â¨À`øã¿8ïö¯ß «>pÝìŠ0ÄÒšÃâ¾ÿ·3gOÆÝÅcP@EC—+G„ ý0¨ãnyö6ô´w½%bg¤P µy8ŽHmÑ(Ê |Ö(Vs¸Vf))¿Ï Åö÷а­—� Ån`j\$—Str6. Nü0 ª#ƒíI@ˆ"Oö�]¨õõýUfÏõØ;Ks²ç«-ò#±'YÇ’ŽVfåÚv”Œ]äšÀ# !~2µ´u|êºØdCÙÑõŸ»rË$cPCœØ€Ae=‡À ÷zÀ&C¿‰…G½‚Ca±Ìù·¾râ¹Xž”cC;$1?\—åôxóZL»nµ0òµšé¡a™°`7?ilT á…G7?øg8ž½L r<)d¡Awžâœ-�Eâêþ£ˆ™²¢V]´Ž¶-v#•Ð2y|o8(Œd6ál?ÔÚ…Ny¨±½ÅùO¾j½�ŽÐUªH¤¾Í‹À{Žmƒ�GèÛ-ªoöumaráMFHˆ$è/lÏH¤¦>Ä { ’å1·¡Îâ:?RúâÍ<ÑIJ¹µI�m–¹¬éŒou!Ã1×ÒÛ@-L&¹äpv*"tøß¦ãã‘%ågÚ=kê?bm0?Ô$îd‹…÷y˱Ö4`®{;³¢ ‹ÓÁá@ 33#ºÞ¨5�!d³„=sðšbbâ6Ê$h¤“¾Ô'íÎfÎà¹,ªž'.…ƒÒolhj¥Š:‹É¡°çŒW#HMÆîâÐׂÐoûê1Ö$V9é©Y»prR(1�yÁ� M¬ÌàשqúP+22óvgsõ£à"qG`dY{�RY’|ñ&&[Ì-‰~›‘ GneÇ=;Éú:µodÕW˜°q¨&U��Ç3v¡ñèF{�½–È9ÁW ×MbY÷¶;6�o…ÞŸ™ÀiSè¼¢xsÞU*´†:þ¶Ë¼{ À '¯Jÿc0‚—„¨Æîö;±,sÌ­èda#6Ü9î)íàsuâ²{~�mCrÏYZXº²}Ó>ãŽ6c�YßzÏÉjZ'6Ý8Rûp*tãö†KU3ªêÇÛêŠí»—ÕÓ£nMoVå5ÈT-ÓÅ4øÝzëj! {xÊÁ%à•Í@Ÿsæ?üJÃ!_í€'µ–×>@×™¡ˆÔhWOõ÷³„{åÌ"ÓÆÖ¸+ OHQÀ6#eeõ®ç%[6 G‰Ayüà¿dð+#.®L6$Ñi�5ÐslÇúÇqâ³âÀ�ØÓ’�§x­7º&n×îÙÚBä‰x ái¸uú5±û^¥wY~ŒsB§ Û2ïÖþïÿuÒ�]«ÕoðùCH9:¤€£)ꃴ‹#H¿¤€* {aʲ Y˜|©›ä…ƒp»sâBõ·¾cØËÒ¶7$ë‰Nê§³Ýiqz—È.4jÚ—qø%,ßäöz�¥É¾~RÐ;Þ„W0 R�^ŸØ�ˆ—lg ç´�{5ŸSÛzíÓ…1 °›&ËÐ ŽE`ÀïL£ço‘Xª ¦]¡_”Õ¡HGùƒ–è ïM§KÉ÷”R½(eÌ¢“»!ªF=µÓÑ={· ÎæÇ~»ŽÙ±à,¾ÉÒ�B;ð¶DRF Ýz_£¯Ê÷4yv„ 7:èué¿ê“[3¹.ž¢:sÅ-îõƒàÅ*ÄfÎÆ@5ˆ+¡ýQ�r§],ÂðÊn”vïT�ï-¡d˜2ÎÕ袴àà�î+섪*íJúÑ bÜê°?ä¦Gظû:zpSÔµÊã ?¾lEÂCbìàË‹Í]`Z³—ЖçÒ_‡¢/ˆã"Á¥9îî჈ə`#!ZŽ·à˜Ã}3³P[¹–»ÆÂ`0ï<9ñ¤sƒª@^ÅÙK!A &i½ÈöiøÃœÀL–í–¢ˆ8‡YÏBU�FúÄ.=‰ï™Å4—iá×P`˜Z0l¼ �÷Ãüñž¤}mµ�1öRðà‡!°lBVwmØ‹}J¾€Ž–ò6N}J�ÚP%àÌ¿eWco^P¶RÛ³ºEC 5 Ÿ­Ø€ „âZ²{XöÙ™?g¦M]½Š&æ„þ�ˆ¯~F–ï‡U…93êêÒ>¬‹FvÕäD$T-Y¥£eJá-{c\Ñ&ÿûa•…É ¸–¯®!.Íù;0Ž05�ƨ¬Ø*Üï”Ê+ƒkuNýr¤OfÕ ÿUÙºÈ[KÔÿÏ�¶(@ölx «/‹¼RÔ»ò9>‡¯¦jÚ?© $D£u}^ä T+ÐÿIÛ6D…âHø¢HjT…EÌÊäÊb­Ê¨¡ŒØÞ燞eM9á�kxŽË5Í8�”Óeõúy>¾$kë·`^½|`�¿¨ðŒ„d–ýjˆ< *_éò¸‹ÒW:Wî‡äÏþÙÚl‚ã)òÒÞ„Ô‘bf@½Y瘟uVätÏ„_B©N Ÿ…«ÇNOmN ¸(´^‘3ðdcoEÔN‘M€æVÅO:j®Ö¯ˆõë Í«¼ZD0¤”A�BÇ5GI¡AŃ|y®²×²y{¶† B9Á÷%†¦Vc'b5¸}“9\ØdŽÕ²³ë¯lÔŒ€~«erÉjÈdšc9ñ„-ÌbcísœïœlªØU&%¾Jü(6£Ú¼?2¬ã7‡ÅÔü,ü_r… hUßapÛ ŸÕùäg½Æë%¶h&ýRSÇôW‹if ìKÞ)ÉDЦÆ"-ZC0‰55|ÄÕ=kvÀ²¤•PΙGW‚× ‘¤9°¥‘¤—c{Àý%ý‡_@†ëßx‡SКŠü!ÉÜÕ’$$…d‰Äª¹˜ r™ÎüO4ìxk²-‚ù“æŒÐ8÷~½ Ð:Œ ƽžÔCOg{¯†‘Fùþü±XMì¾ l?ùq×Ã&t¿T ‹+ö½G�õØ`ô”AãŠ"z¡]ÂjFkˆ fä30Ç´’ùm*÷ú~®ýùå·—£—±?KµÅÊ(%ÞuZZ耙}á&-k€¨A©-Ô?iÒ¢¼ÎÏö=ýñEÊé›w­ÚŸÓƒ'‰-ç°Âr•ì­êÃ�e²TO„E4Ÿ¡gšô�fÐãºÓS€8ÏìN§·Èj‘�Ï?�ìêU£º{–Ñ­ûVŠLÚ%< ;ºÄt”³¾Äš‚@a.×ÍóJ�õsû‚j´}UáÝô�HƒÑ¢ú(Ô°Èôqå­¶(Þ²¯Óˆ|(:ÐjO�cÁ�‚Çæ¾æ¼»=HºoHx&ÔSù)á@Dkø±7[£ð¦->ÞI*áñ®G5abøStô– ¡Y[ª­ 9‹&ŸLÆKãË”+z¼N¡ÍúÃ÷eˆc˪§J·O¢­Ó>q ­ÑFIžó*Ö›ª¦ æ{-c@ì3IS·Ê„C¤H•xö/›°BÝŽ£<…IÌ9]� ÷¹m}ùŠ……Øô�A €æÌ ˜â¢“LMOf×類b^Ìyèdg:éæä<³”‘[,FÝ.â›R(ž ¼¦˜VÊP¼é¢'S{�34ÂNNýÿœÊÖQQÅq5øÈd2È”Ñ ËØ¯é%ìïã!U´±ÏÙ�èý×n‹ìéÀþÂü’A�tnÁÏ}.Uj¥q™¬ÈÔ!ٳ髓¼Î¸G�5ÓUì¹”)ó”×g?Ì·öˆ·YM ÚÑÈXML£Õ�™áÝk‚¸�”|’‘Ñ¥¨~Ž6Omæ{ô¬U[ÒMg%iãlx(ö&óâ'£<6À-[N€ÚêAis>LðK³|%¤?>ÍaUÏÈçß6X}ãpðÌŠslªIBÐ=ì'¬†s›v6EL÷ù¬üæÕLøK\´µ1?OÄÂûÃÿZ`7”³óëkд±=E÷ËPÿpzÒ!x¬³é³M×Í‡ðƒ ³hœ;5j5ùözê/ð²)UǤn¼BQv8UQÖQ <òò¥qí&Ó‰MC®m•e`Éò¦ur ®õ¨ŠGÿÙš´Òƒ&ÕËĺ+¹™¡õv64¿ÑˆõiaOEÆ�ÿ€ÒÉG8�urØ¢Á¥^^œ›ayåÌœEuI‘'ƺ�+«ˆá2à0C•¶3ùÉ€¥î±äáËû«Ý0@3Ña”ÅÄÞé G0]éçµýñuåï’ªˆþ`‘ˈ܇�:OR¬ºO«0/Ø!s\"æP ú0Ï÷]œ’È¥RTú;D=T_‹„SÐ 3R)¼ÐrX<ù¶þ°+‘´ ÀÑ›§ý_gýháu‚œë¯lRÔ½^j(ý#T„ª÷Z§O øÌTÂIܲµÐE—\d|uEœ³€þ< n×BñÝİAêêMT^kã¸{mKÑ€õV¯‰!! µ{—rš=×nIŒÍ\Ú]�Û–Øým.ÊŽ:tˆ9dÊ“ì¯$>å>àŠÌþ¥ ×á_* w±à mWe¤AmŠ‘{Ùñ-î`6NZcí?úTG©‡ø¨s@7°™y¬gGSŸ“›ð0ù~¢O°�‡ž†�orlwM>»R›RxËd@DvèŠË¯Þ�3͉&Ô>,Ô¥Ón�›¥¼`pW£¶w”VhL�ùà�À "ë·Kwæ =kXå8Ÿà†D§wÍWޙѭ³ƒ†3Kž·X ó,O—ÞÛÔ/™ÜP´,íúí1þOÍnIhn)Ç©Á&Ü\eºw5èÀiÅgwrí¼Që‹ËýKúÈTŽÔÊÔŽJ‚£´L¤Wn>!þËpê¹Û6EA!]Ÿ Ôò[›g/¶4�äY^ VöšC.…xÓcK|©Qƒ Ï.ä¹èáfçH„7@Û™u=$�ö8Š«užë<áËãquXÝ�Ð?m€ &À8úöOØÂ1&‘é"âzº¶œïæË)f„�ÍÅ‹â†}GÂA‚wš’‚É@‹pW W˜xärL^ŸšÛI6yh‰ŒÇü¨ÚÄBŸ5°ž=+X<©ªDhïÛ\äRDóä<è ýˇ%ò5¶Ñ7UŒÜsb'Ͷ/w:é’ïm bu¼(ÙÇRö*(ÅÒ@kx²=i·}«7À¶¤Š~GM*푲>³Æþ©+!ô„"ï_‚“`˜Œ†pEÛCØýˆT}Â�z[×SŸ}qédªêHþJ9¶už7Î K¨ ÓàËgɈ§Žé™ö�OÉ{ ßGé*U_ó™÷†¡<œ…/ÐBÙG'¦p˜ò¼èjš<€A22�dÓ :"ÇÃÑ0ƒµnçÁ›;�½‹û !ѺþŒ­·µÁÒl´$›ØH:ƒZê ,þ¤å•yHÙ°DÊX1zr¾ý»‡<¯bÖ/"÷ŠÀÖ0f<=¬‘7ëÙ®¶W™SóPý"Qu6ícAÙSç)Î×­~í¡•MT\î)I"�80,nÛŸ‘‹‰GƒßA¡C‡é)e—^O›F*oHÏ“Ô÷Ì7ÌômmOª"fnìXÛƒç²Ñ+8÷"YÝTvS$ˆ”¾¬Ô©ò­“Œ'-WpL©M„»Ç3f ½¿ #`··©ô $¤î°!lW.¨kkðéœßLÃþæ"•I–YpÙ†ÜÐŒm1›t$2�?åĉ){ÿÇ?Vèr”äô2gÎåtŸ\e�ÅQTéÕà'#xZU›³à«¢²}‰Á4‘µG�™ÔÃbÞçâcfžÁËU[ó†�ާkÙPBâ[g}HüÕHÈ}&ðšo6™¹ž– ˆÞC­BqÂo”ØÌÓ«h-/Ùq˜ê>ú(â‰Ù*jiÇÔ‚ž¨GOÞ”ef Ìæ³î„ƒa~|G¨ÕH¥rxëtnôJÁž:�%È%,�…ÈÝúgosaEL¸©°m"�Eã¹/¤n¸xóÍSÜs’šçæ¥óKsa8nŠæ‘Á“6êP…\¦ ‹c�HBȹò }e…�ÊnëæÀæ&ô¯2ø�Ð’›•*àÍëØñ­?k�Z¢ $ö2[v P¾tÃ'_¾K´8l_ñ ž>·Ìûl¢Á„�7ÉR¤)�S[ý¯©gRü�I±H¶¹1¥Á[«*Â"è N2Á‘š©8t™d9IãU%g_ÐUòÙËãËÖè‰Üƒ]‡Ùêí\Š .�­pG™9[ÉP¯ònœ)+Ø:¢â ŒÂ—±‡Ç€Û*¦G°)v¦V-•I_­JT[Å��‘¼’çúŠZ®lµ“×¥¦êèÅÃú\´ý€‰£:­ËŸ�Îr9ƒ‘ú|‘d¥„"CÚÓ¹� òÈ6‰Þ² X‰k˜¼ÃdµóæJc: �!×î.9z"OÑò[ö�+"ä²½ø0”0Cä·¦Ÿsw eê ÓWûNþíÝ’(C„áW¨Tj‹…5¢krçe !_‡ @Í8)aGý÷�´}Õ ÉG¤òùªÅIø‹�Ç�V±‰öµ  ]^©Ö"Þ¹ÁM¼£¤ÂŸ¶é 5Ú»ˆ\6’ìÓ�ñŽ^ÎTƒºÁ¸ƒ Åþ;SçöOMrƵj�b …|ÇPc›ÏYO©¸½îf¶ÞzEæö½ö?ÖÊrîãå­¤!Uú#®õÍA%tK¿TP¥¸8´Û0Cl`G&[õVƒD�¡’�€=?¼=Òç¨/í«ñ9 ËñhÙIˆB"hT ëG¤hWZE§¬«éåN¢ž6�ç|ã+XgœÄ†k3ËÙxÏF¨·C„ í5t‘æ²�"¤]l“²��K‡r5j­QüCo ×o4ê„ -z;rØP±†½ˆ@oEo/•x¶\¤×3y—�Sûñ«ð8Ú×nÿ=½ Ä*ç8#jÇ(Rô+£(½äÛ4�'`\ ½U‘Ûœ¥-½jæ/�ùŽør› „r ×W(ÈUûQÀQ­¼F^À×nZì ÛÒ Ç(¶l;¿}'oRÝØ&‰ºÝ¤ƒ»MG÷ÔJDÌ’C=·ÄK'Èßï×wlóþZ|ÉIN­¡/ ^$o Mã "£Ç¨HÜé0øÖ'Þœ˜Ë7úEKÒ-\¤•Œ ŽMjäKæM;Lɻâ"÷`¤®Ø~•Záf¿!•Æ ~‚L9$¹H,<™a͉:2ëTš‘ojÀ>¨ Xø÷î#`àÈjV?¢F† ò»��¿§È­­4ú;•ÿ*.­ydY¡2\WÝLæñ`¼è<»ŠÒ ­JàEh¦N³s…o^e5ÜC€èXð¼+¶ÕgQ‡„�æ@° IñÉ€öëp©”¸d@â*šëÀ—É‘‹ŽvöºùЭƤí/½Çµ6Ø1‰¶žüµÇû6*¿ß»&ŽUŠç)K°»nK±8w�rg1tãî?•ãâ¢À²Ê™¿rmè’ë½_°˜¢ñ—±êþÉ)Ê®éø=ü%wd•§îËç.LOF[�l—JÆ>´r)áí¯¶÷)ÅÁÝ€y91€S‡ÿ±29_û’YŒ ~|Ìé-Š Ò«Ñ&!CzðV¼¹‘DQƸáÚèâ{™ Ìô´Ç=M­1*R‚caàRquŽ £¬°WZ#½Øh�žI¢Ð&_±E&…¼Lÿ�ÞØ™çÚÙvkc¨è–<$Ã4¿`‚`wnªžFm0.3ù2É$Š}.*V‚2ƒ×„óê+4*ájhˆ$ïò"ˆ�÷ëàª[±»ó}ÕŸìD&[(x©­Ý}M§ø¨µ Ì"…ºôî€kj:Ú:#-‘}�ê•»Ž`HšÒŠÑ �K�—§3m¡÷â¸•íª `Ó“+J‹ ÜåÞšË?òÇ:Pt3�×Çí*Šnў܎€átžPˆ=ì�Ï@’©;ÌÛ«À­×å³¥[D5~bÌ}æÖhšQE¦3ƒ»TÌ©�¡jÑêæo2áÂó»1›Ú4v±fó%6Ôù'ø¢�*žT³L¯›7Z3±ƒ‘>�j…Gˆc Þd*¤ò!T"\-RYræ³]Y��>½(kËŠþÊQcU�½©™’¼ÍΞæVéC`ø ¶O•A¡C%ï¶äs˜<:îj¤¬‡¢ Wæ91­±|co\°.¬š d!6ÐÉ|9Ùé&$Z¡˜Ì‹ 72½§/³=2UUãs�§º¤iíòçí);ZS#\Ë‹jÉŠŒÍkšV÷¸z+�/&Ye˜„pÞ>–¯H6¶Çû“K>Œ29¶IT¥B©×¤·®:½SÝ’"�ô äy 1þTðº÷ºf¦HóËýQ—W°NñÊÂͽ,�§Þ~%‹€1µÿuª-U+³µ‡š]%P °rê «Ã1¹ Î'Þn€# fú8l³#„>Æb;—üðùnƒÒìAúu4˜øþ©HΕìäËÎ�jŠ*2¹ŠqNæQ¢J ¢›ä™UÈÙHÊF¶P— ÉôŠã†1–µ=½ØlöޤZ›yo½Zû$XöV»£LsdÖ3|?•drtgòË< y¿¹¥;nÒR?ïÃ¥7 R×ñŠN_»$'0>ã»1Ù¥9É•Ptf ¡ ûÔç!…zW$Äj³l‰¯K>q þ}í0NO-w\Ç…ŽÁ€™sXhóØa¬ £õ?·+pÄ'1DÝäÔ Ù½÷|ÓíZ)_Óm *†9.ÏxÒ¸ -˜-D†¸%½F’Æîè2n µ‹8/³¬ˆ¿êÒ8RñÓ#6ø””È^NmþH mÛï„.1iÔBPÙÀë± Þ¦%ਠ’Û‡"°ÛßÚíÀA}µS _ØýúÕIæäØÀ` DÙðáõÆ`ª1^4Oì½qËÚÚ;ÙŒîB°YqË\Ë|æý ­vˆzc�j‘�ñ¼¯2¬Hu=®Kís3šÂf,íQ�ôo¨«Ÿ$Z‰.¦c¸åÂ~UrãI '�ò¯'T/;t±]ãôõmžÆbÕA…±9€Åû¬[2ŸòrÔ¼ÕÓO€/UtÒ¿ÎßYÙ3£.(ÝŒŽÝ&›·íðÞÆ‘›mÎöe@öË-»yãóô ía@‚>9.¢rñ`Ç^ª2‘P^R8êU¨‹Õ0¨�?v݈w³4§lwµüžÕÏz£¢#_{k¸ƒ†¥ôÆt3™6œ óŽùSs. yªîAeቕýÙðɹÉ;Û¢ç!s° %)Ý|ðÌ=æøâTàÿ%¤2)¸†³€ÄÁdA�fôªß*ä~äé3Ò,K¦ÙhÁG ð{"ìk¼år<ðû'ÂêwªºnïJÙCÆÇð虓Ÿ*‡BBP}a¬SRø—îù-¢Zýܸ£â5 yT)ÝB´upŸ¡<)¤¹Ê«"³:dþ ÿîìªHˆ™A= �ø üÔkG¯tžXÕÀÇnŠ1RQ=¸XT’•1¥ïb›I¥þÌc=éO}Ñî§–ö†“ÌÕ’üAÆF˜Œ¥$½&ß6!%"·Šêߨt <›ŽÏOn\ê± °üÖPR‰Î|%Fnš—I09È%‘óŒÛù~Bw¾”äxœQêI•Þº2¡” ôæ¼ÔcµØÕ2_Ú@üzª÷ەܧ??øµá¨ªÙ Ÿ3Š×‚1cÉ`biZE”èÖçÒz—×C OÆ’]ì¦N|³Õ— É9>t¼·¡¨Ñô‘Ó á[‡Ô1ê­6B^A5à<òIÌ8w‡ f™XjLó¶žÐä:6…³ÂÆ1ÄÆì¥N�àOêŠå#™ ëæÜ¼0ü5Øœ8-iuyZ0oÆ”%]î û»à5{W4A›¶ª‡»ùÛôa£BBÅïÁî.ý®'vP�5׊Z¥%5’Í­þË>™ÕË™ÀDΞ^*Do¤îuî"x'©ð«ÉÉys§á ü<@tu‰ñôåºd�ãÖ&+û y´‰¦¨ö/[Gâµ)ç^š©a¼î¤"›Å™«&[H$ÜŒÙóûAâùrao[ â‘S0‘©öj´öÒ}ÿ EX¿+ÅÐ}ûr£l|Íe^�drR$¥©ü§"\âÚŠ¥neÛdÝÊ1¦„¶æ� ˜¹+”@ûx\¦û¤®5H‹èíÙ" wB|ËqnA¨ÔNYâ³ÉÄç[¬jx§  ]nlöñI±'¢¿“êoü§2œÜ^´¦7ÃOJèd^Vȶ8iWq_æ&· Z‘ú]$†€Ú!ºAÄ?˜oc ìíÖh¬,‹ÈacÐå�ï ,óq�H' \Xº@©éäJ°]5 ™/÷Â[qÊUŸ?‹“•§I—á7Ja':¾«àc»+à—(êô© ç™êÀ·‹½¶Ƕ-˜…të„JPM�ºïœµ’ç9´gJ Á¦9{âw²˜d_]ƒ>¤ ;üS€¡/]öN«siÈL6wç€ Åü_»SSÒ•4‡I!}�Äwø<¯5N""PZs±ª3ùLÁcþ—ÇåVSq´6ÛéµüÓölT„¸ê!õD>mÚ‹ØÀ÷IP´Ï,íK=ü;®U°’¸=d%y�p-yí{,1*LDZI,†ÀÒ·dÍ>–4éžP¾Ø¦x¶‚<Ѓ5 D�ÿ[Ž+É$D~ØI¶=KÇvË·œ‰ªÜÎK|Ò0².Êì¼:·?ýD)—¤A1Ù+`Èm±†Q^,U��öToMßÄOYôo=WVh€RhßùeûŒÚNMæß×Bõ{Iny��—&Š’UïæŸmm�r€ØW:!ØŽ¤íoä [•�‰;}7S“3S Ef± ½N$Bë&y’òÞúg;Ü•"ŸR2ƒªßÏVü…³ Ù=‘›¢¨â5”-ð²pOÛëÌ’é_F{Þz?Ž>�Þ³–nVöàˆj­úJÏMõXìƒ çËh+mXßÕ’C±eý‹(¥ª¬&�’Ú»¶á´?„?ù¿Ë]ÍO‰�Ó©º®ãóO#—‹ aðOd·ùaÎÆ¢M•eRåö }öY3�ª(p’?ÚÕ¶|{vþõÿA ò¨ï£ž;Ì” Û›á§ð­¹~‹E�#¹pñBW WObÖø°®À(-ÑqÙ¬�¬ï«=Ö ]u³Ò¿vëÙÞ€R~ab9Á6ùØ2Mˆ£Áv¯¡SUüÞÙ«kÞ´“²ç·D wÐA/%B¯j.øÝQµšýuW®¤¤ÆÛ‡-‰+yµcõÖ½‹‹*#–ø³T·�\ʤák<›)¦ˆð3@nš]2ÆŽŸ�#‰9–¹¥ó»b»¼;Ü TîþOÜÎŽC+>Õ^¦¿M‡Id†V‹I0¹·Ï1ësG‹�~;ìF¦g‹/^Ô�œ}]‚à?lg—!ü ÊiÀvš£xÑVOY9K%ò‘;Þ’lô»¦¬M-*.è¤�f –�㯺¦—I ž—Ô¥jíýSÎŽqA÷ím¹‰¥š~.§“þ–ù?JCÒ¦ÖŸE½ñà”R;ËÍßñI Ð�¢gFǪñ¢Ê÷l/§„xb»ß¾‹üÕ_®âþƒÓ ្jYÑ ÏR”©0y‡Y¡å2mÞhNSaÝöiw.�„4ÿ{ôÈñc²‹^.Ý­'cÒ\†­À™FW$ºë*ºo†O .L7~�¶•&Õ\mˆI £Ì@véÁ!q° á«6·©p\ ûäˆtTôŸ°uŒ¼Œ €%ôÆfkŸÀffä ,T9&åÆë™{¶æXBäžµãÉá÷!ÙÏà\ÖâÃ.ôFT4#À’åÅ T¼�/\„sÛ‹\Y…ÉL�8-ÉðÜu]ÿ÷‰æ{i¾–ñ¢&f²’ÚEßJàÎÐÈC:¥x°"UvNêQžÚUÀmÇ9å€/)«’ÿë¶“™§ê ¹Ü)ïÛwS%ïëkÁ�cHŒ× ¼£<ç— X|;?‚ñ2‰®R6ÓJ›vÆ€sç—Fæ­³³\?L)zrk…½Iö²Åõ€ºK&ŒîºÈ8£Š›áǪvsŸ“ß^_ûA=ù¸˜”×o½T"¬ -®Žå‘–B #£ã—[&Ì ãÔÚaèú¦7�PÊÌ«º¦|Xà;”w0¨ž <ëî%Ású&©`só^'У½‘ѲCˆ�ê4°Ks./I•M µ¡®�@’ÓIåÞWóúGÂz)_+Ѱ•»íp¬›ôH§:K¹0Ÿø[š …,ü<û£r–Ó"˜~l³Žpe¯i ‘½3©rlØüA1bŸûOmæ×j"s.vTÄy‰åÕ¾fÇ#bƒCqÏ=t²âo3ÐYš6Ë#Ÿ1Ãÿša»Æ�¨¦¬#¼Rð?pÂy”Šzª–/¸ÊÝ%;ƛǜc1·Sm§šE×Äæ†¼]qž4w¼b™ä+Ð$_ŠçÍ„õu“ ‰vldiö´¥2&W}‡œÞ‹´ŒgÒÕè.‹C@¥´]äñ`7—ë© +å)2¼ÐUnüB‹û1÷±ËùQy–ñy¹šÚ_Fl ¦ÊûÜæÁsôî`RC«~Òê­'ÔÍ+ ä>|Š‹ƒžòSy2ØäØ’tPŒïÓX Yá€oLHý¬{!`•Ø�×''D¾Óg 9ô „žQ™šçÛzað<­èî,Ñ+EîÌ=I¿<›œ:úظ`âËVPÒXÊs8l}8bNä.*vmÎ+Ââ–m¶# >©@`Uuf*­fjö§S;v&1—õãÖ,°ôÄa¾nÂÿ÷´,’“c]Jdhöê ýØÂšöcp~Sn¥TØ%CÎú<‡È°ºè 5nï*¤PC×_¦“§Žß˜êw‹ènؽVdÝÕ•Õ6á8^2l–á ûg!H-Z×U/hü"{8ž ídä*Þ”o›Ï ð�¸…ãÞsL ‚„gF‚‰ÅɳÁD¯DÝ<œ])ëIÓ.˹2û•ƒþlò¤lÔj¦æÚ ?‹ñqxf¦¿?Ðõêð:.FÇÙèõðçÀpñôß=ø¦XÄàæK?~A˜Aô‚þÇë›vf—äq%0ÎT³åc=“ ÊÐ�&o oO—ûù‘mI .†ßNzdÐÅ­ôsRÚªë¶`©ʽ�±Ž´ÀW©T ¹�x½�iºú“‘Zò=ÄÒ8.e…Ã2,'K°ÊF¤d ù¢¢ÃÛ«wF§æb†Iš ¦ôí‰"GÄšÂK'c)¿¨Š›O†§Í3Ò϶ëô×â:Ÿ0ü+\ÍT&å~Š~ˆ D‚†*šF×’iK-f×à™¿BdS"!4^ƒ VÏ‘3·§ÛI��gæÛhktê¼Èå[[}lljT:•&Wz—¶>U0ʬO™œ ¡Žm’[z‚m‰Tœ}Êî÷â»ÉÅx‘ÅD—¹Œ(ÌAE»[pœ#“Èýyf7™ZL½`¥¦®˜x"Yê•X®³ã]Å’w|¨Ì¤GEiÍ]�•iãœÄÊ�%¶8RMKs�@‹ì¿YÅä1<Ú®¾Ô?,Üeº¯‡ÿ$[¨\.ª^&ÉÆ+€øÍo5\^ºàå劜u/È5IGÝëë=L:-³Ë�$zŸ«^ÚÉ>¥Îÿê�“* ‡oH2 6ðÔªÙ&�-à«fK.o2e«�Ç?ÓòJo2­`h·MU!Îó®ÂËvÓn~Spà«rÒx=»§<-ušçì™Ó„‡x7_;h ÖPí=t-ŽÈymwüu?ŠÍ´n8ssIOf%”ícÒèun^Shh¯ÿT#À5_6ìšc`5 n�“E9Ïu6W� Çœ3ÂÉ./d�ðúv2Z ç´¾©—ØÐ¡„�à�- è Ø>MÝ!6ÏshÌØæ%Ý\úAÞ$ˆ´¸¢V.8˜îÈ)±‹©MP”ü�?Ve¥ ï"¤±Ú-Odð6 š™¦8‰Õøìëj™Ë´›80,RÙ6(•¤£Sc[ÈX»qs3ú6õo€¿­äì*Àê¹-@eên7úµÌ# dH·Ÿ.ž·ñpk`áÑŸ.-ýfFâoÚë®s£ÍmÖ¡ó›À�š¢Þzú:ñx¯yL ¯¹Ÿê^Ñ¥_s €Ÿ,¹ëƒyݽÂ/¦3Íw'hìe”Ï×éEúT]ºJ,æBg♟xµ™.ÉÛ†TàA‹üˆS™à—³ ÇÛÃNfÿï“Þ_‘«&®€¿ùì†If5y=Ýbmlîz”(Æ„]\“’¥K¤¤/Î7-ÛW0X}W7{R÷ܰÿMסӜá_]¹¬¿z–¨ÙMÇl¢a|GÔåæ1¨¨IŠÖyÎX˜£ËNòµ›Š/o»¯{í:ŸÈ,Ñm¡ÖN€wÇþ÷vÞÌl‘ÛŸæÃ6�bÌé¯åâÉÒ¾ðX¸L«Gœ¨.Aàä¶Ú¼(Õ\¹¿™Õáµ™”“È +ÀjÉî§þ­Æà¢£þj?Â{,¿P½_'¶7®¶$ ŽtµÐ…K‡Ém…¢þŸA»Ìã-G{"H×3¼éË»ûEe`N£v¬ïdÑùÝm&`g­ÓL¶Ü½p4fñå�Réº äðì­‡qͳZÿo‘«Õ3AÔÂsõad\PÏ%Òüô1š|'BHQU�õK®G Œ/„[n;÷R’PN’²@ô¦vÃ7ª>kÊ/C“¡L‘2WW2 b[ã¼VW&t‘MÒš¥ZÏÕÂ�º6R¡¸W§¹ÛT$N§•,ÕðO' P�ýËa/N7­Ðý8Gb�ÖßaÞ°Óä9iÖσ›^âjF20y�¹'ôgXï]5ÑÆ'#ÖFO5\ôÑHú©m;Í ¶€Ð™ñçÿ* Oio°12?°¯ã…´å6TÄò9q—í©q;M"ËŒ×Di ¥ÑÑ…‹ŒkßÝ`ܼãF‘vZÑyl'vÁO¿Š¢›¡_p^K{èsñ—H|L+ã8õã›ÅB$ùè3n¶Þ%Ò%D�+¦íóŸ6ª]ø>¥VùKv"ç§uc¾�'˜•<µ³ÇF¶Ž³Jw ½W/™¬l†hi`”ŒŒTþÎ]~ë1³üfü°ÇÉã/AY?ij€gŽë»*^u”V¤YàèÖ1ç!Þº3‚âXÓ,3Iêǧb›nÙ»Xª, g^…0ãÌ2:ÈCa[$ŧ8“Ó4÷Q^zΠ1r^ÎòíÞ*%§SÀª;žnñÐeØĤ8¤Ïoéa¿îøòUÍH R“>¶˜e‹fäZ¢Û\í}`;u;xB¡Mïö-º|j›9A¸%‘'WâÏÅ3_¢°Lh¶¡öðv/x]ÊÀ`Á#çvƒ(x›*yº¡kf,šm"ϰìê¶‚­%¸n/–•+¡ ˜>úër‰eÝ“ÇV<Œ̳mb`™È� sB|mYI ta«1H8-ŽÕ=ä<¡1ó§úšîYÇÁÄ‘åݬf³U¶Y/{‹`|šÞEÛUƒõ‡½„›Œ¬lxòÐ!矶æOÊñcÕY?þ¥ ­ŸBñ Œë!…àˆ—1'™ºQøqtV?h“+ÍøÛÌå·!qÿ° ƒ�a?¼öqZ�ò‹@­eSÄÛ©vB–<+‰PjXÍÂa£~Õ“¾pi‰À ò�)¾Dh0ÛÞã¾Îô@�ìv=IµÖükUR¼B81¬ÀÖ-[à…*Š¥PH�êëcÈy5b¢?{´<¸§¯¨ó‡ŠÊ¹´+ôæ²V.#+q<Ö�„>�h×5¡éÓÜ)áAÖ6s{,GJ§cKÒ‰¡-â(FïÏ­¡{®ýŽûCŸ¦ö�ª€ŠÍ�ÌžæÞgÉMï£ ‰”þÙT¸æÒWâæŽä-{AÞÉäÓ�Õ´AaÍÒÀ¥‡Žõä�飯¾ßuÅHDr¿$½ªÜÀ¤ä&€¯ÕOJ!bOÓ ,G¹´·þÓÈîˆÅØßíÂzjuœ ÍSg�™´:aŠ}m³´àøŸh� ïÀ¨GÒÄ~ðCKvp½ÔaẌŽBìyüüP‡Úp*øäù©?ç,m�MQÚFõz¤© 6Õ˜æ—Ö%pNIÁ£¹]�ŠG1,«O5¨�·Ùåìùë Šƒ™„�S�”7Ýwþ ;ƒÆ_‘w/eÀ ‚r›šx¸�ætÇ.ÉÂZ 8äUz+µ ‰2ƒK5餌¤‘ *ˤ쾊R½ ðí›Wôí¿:ÄäÌráݸÊ49‹s^á¦Aèͤ=Þ«¢Xf�šp¶g]lRf—‡¯ƒ)Z�¨ûÊ%/¤\$µYÌ€#u€ÄBf÷¯ù/† XNsA¬ Õ?É]çZÍ�ƒc‘ň¾–Å:ÀH¨Ic¹¶!åRWÄåŠ|#Ö À�cƒUšòÕ&•Ú�wÛ¾/µ2Äa6e§è#vO7¤üx¯�ƒ¥ÖÞ%pd«Ë+¦] ïsá8r@XÃ�dHØŸ›I~kdøÂ:óÁ±Ï$€îúµº ‘¤3£ê{ïõÉëéväèŽÚ‘T3³ <}ä&»¬i$˜ˆ¥+L”c²ƒÓ3‰Èˆ°ÇÞ³þ±Ä®ÀvÑ"0ÇŒ…×n­]…úr?�úıHÂÔhÒiÝaÁ*±¯E—�+¢¼Ù¢©¿[uNOya�HX!ð‡ŽÈ-áL%#®%¸ ‚Í/kÚ\«¨ýlÆî¬o¬qzÕä"MôNýÙ  ©:¥lÙ,kL4t}à(ý-Ëõ7†Í±ç¶`†×-Ì ‚9á—…ð•óÏæ»†|«vé›»¢ýâ� K_)¬Á_ŠƒŸºë·Þñ+‚�!ÒŽF¤ýÔ×µc ¢ßÈHè�ÖÅøÉò`õjž±½HOØ^;fœD\WøäñúùÀçè)sÕÉñÖa� Ðsôö�Æ«Gr�öðìa×L–@Ž7Jì9²4%º¥Gû›êÒ† ÙËl(µtÓzË\§>ô0#²•Ëè±sð®ÄH_ÆÚ7OÅ”õvŒ›):’*Æö3V¦gó`ž¿¬}3::IwôûZD¯oª?$Éš9÷¿Ò*p˜‰UÿÛ×8%ÜÞ#ÓÒϬРl[ÈÛ�%z­Z™®‡Y©|6ªv¥$±>!»‹Ó·ì€=;TÍ�@ç2°ÊË5“.v}xÓdÁ<ðÖM,Ð^“û:S„–#A)õ�¾�ðX·lXY¸Yý˜º¼-¡.oí©Õ ûÛ1«~ì$©½Ã@ò›dÙ¿k®¢QL?N_t’i¾BXýÁI$u�¿l@†LŒÀ­2]éY¢Š§äRÌ™Áxë'L*þš™lüL‹  Ü—¡7ѤFš©Ø:}ËÔsN½þ~�mïj¡ëÙñ°ÝΟ¢Á´ÏfÑy~†#² �”©2íAò{­ÝË™†¤5‹H£Ø‚p€q 4Ÿ[Liâ1‘sߨNðÞCÄW¶¤œ|‹‹`�JàÞÒêJÔ½cV‰}¢Ü!ÐgTÕ¿ —f_1 ïÚFžúÊc1ºã±½e¨ÉµÓп’€ã˜°çåê?Ið·ÕW)­è¢`@æ¹l¾;ttì“–b¹OÞ$»dUL£¥ºÏ^J@’àpReTN]l(¾·ÊŸ<ý7Ä~\å\³n¿§âŸxVf5·3»e òÂ/ËŽJÍ�ö?"˜§+WÍ2^´û^¥ãjû;#iDruJÆìSL­�—+øè ozòòÝôœ ¶æ$M.(›_®Ú�8¾)KDí‹›Îò£ɦßK5Ó�ñ§®LŒö:Šjýgì¶¢ˆžVc<¶AjÚI_ýÙS¹¢!bðjܻٚ‰º¡ÜT3À˜/{Äþs$-¥t€@øÙÿÚuøÍ7×ìNl ¸¤ŸÙeLÅÀ|b”qþ;Cƒg‹£Šã�ÊÓH«ð©ç_<2½›‰}‘RhS_:H¹Q5Ðr)�ó¹=¤hÝmâMÓ~ŸÞ¤Ä‰«ïÏ>Åïý²hðÒ¥>™ww· Ô€[Ä6ø ©S2‘Œ%ÇÏæ¥»¼9ƒÙ:¬�8—°®)ö:àcÛ'̉š!£Ã²Ö¹NÙ#Á°Q‚Ÿ›W\É\Mç뮥ˆiÝÆÕY¨�"u<.Úî¢dÒÄ>´&y‡oÙ©*¶&�-uˆ±F�§Ùö(cô)o�È©x…,¦m阽sH݇~Y1• íÒ<Á½«Òøzòú½Å¦1c…Œós>À½.V„Á°Th«h ?;á¨dºÄÀ´R7|ÿ»Îtjìi’à˜,9c�ïÎoZKÅÑNÕ“¹Õã ~W‘À@©ÊóôiŒ<̺ýñ>N¾ûoAuôžkJ†áíR¶–í|JÞl£·Oÿèþ+¢ˆ”Êyƒ)TKgkÔÏá¯Á5$NF\ôs+=ùl—^9�(x^2ð§Â¸¤4í×ìTV}`X(‚¨³c›ŠX| L”ã 0÷ëÚ£ÏCÕù^ÿêh…eÃôÊÛ°LÏ €§³õu?ÿô¥SæF˜Ý™–‹sŒ¢è¾D ^SŸ…ÁÙÈ»êúóHmÚ$~I¸uåÑ7ó¬hÚ†k.±K@UQù¶žËRvztyh¨g�üœC üÝY ² P½ÚXŽª}g^k¼�ê¾ò¬î½ßµ½ÔËõ¾Ò0Úâ úyTç…|þÆ:“ðG±»zâ«êŸ°`É� ¸_oŠU¹å›L,¤é¸’Þ%5×kYa¬¨Ý«rMw!i…­EaCÇSK§¯#$Hx ±ž™--©€0~kú[ì0Ç¡È�p|@µ• 07Aáߎ·ˆ$2îgøù‰Û«w&MØ–g{¼sÝLû¦‹' †K{ãÝÂ\<Ýb+ZkXJ ’ö›ßy¿…þ3²Ó€r§ÕˆôÆ7R¨Æ«{Ø7BõÁéªyicÜ‹s IÌ“e.xfgû$ßÕ‰~¯ ö–X”R»£ÇÜfd]‡íÂ’Ëé�e­TRÀÙc° þ¯×”2*Ngëi Ú§‰R!†¿8¶\®» |I�ÍÁ¥ >ïÝ—¦²ù—Z¿ï¹Úp²è}’ìó¯�¹cÀ 7ür‘é�</ .9OÏôÞRÙãDj¾�šæNB£®WL�Ú­OJvÃVú‡DÅ¥©u+;x·&íÖqÄbç¾ò›‡xêݨ—â°¹°Kβs|y²“h–@Øû—ô�AÙ5t¹T÷Ô™Vüú¦BÙ28¢ßߣëBµç’5v‚²Ó 7e2~ûˆaR`qŒðø¦V:GÈG¥§û# ÷x @Wd›˜[~Ó¾3�¤,�ÅiÑ%ihݘ¡bùϱ€µO±ó€T¼æ�dï¨;Òœ§¢#ÓóÕÂ"Çépub®O1ŸJª†ÏP¥Â{ûôiäÈ‚M5|˜Üa—¯•Cʬl…áŒÑ†›Žøëk‚2ûifÏ:Fé›.¼üOeˆ�Q€#åk02#»ÞB”? ©£·ä`i$/äBŠ1’¬Rþâ—[¨¢¬òÞ7õ’ðmÙ…§ ›69±%¹Hÿ²ÊVßdâ;Ÿ…Åt$Å=ù‡ð«/)ì˜èª¤+µÄ¢éßa&'ú®99‚ÆéÀ� SÈ¥Æ/œßnpª…ä\ê4ä²£$fPß ZÌ?¿µõÄðÁL¬VI�’ýÂ˜Ã¾Š™SžR�3¼ØõècÓ˜ÑÚ©¾cîNí_ &Ð@Ó”“Â)Á ×~—hŽkÿÛVeûÊ}õm7t½šËÔ–ëÄ–yÂUÖ.'¼N�é‰Kÿ˜àæç´ �:tßåÒ¼]DŠUŒ@l�QTwŒ¸Ñ™ºN#óBüLQ}¯—ˆÉOx!£V6ôÓñL9%@žߞ6`òÃã·Öˆ=�ðºn¼¹&0È9pÏíWG°:¿â[¾Y³ÛÑäúÂvyfLêÂP½—CÛõcöûJ6˜‡äЕdšâRWÆ©®oÂÖü,t?¬ME`Hôh\|ç/¹Œ‹‰)ÓSe+K])¬„rWdMDD±rÔ†ð¦ß£R•;Ñ1ä&.l LSVÛv­žÈörÿ²[ÆR Ó4ì5?¼ B¹�3¡Ï§É’ï’ªÙ0ib?f[ñš)7š_:­wü§ŒµQ+ªµ\ ¥hQqxdäÇ]$¸‡›•€%—=°·=-ŠÅ·pœGo¢>ž×»" Æ© èF.Ph.¡³�™Ÿí,ø9Åpl³�Î7Vg«äùŠI<Å¡•g¢¼±ŠÛÞó*ØF’hÏxÖs±>¬V}º¸1Ƽ ¤)kM糋ô†ý…\÷(Ö‹ŽU±b´Å·L&Ÿ,`(Œ»)D ¨"or¿q/›‹]�Å<òŠnÈ­‘Уp %z�—,ì~2P†½ùzÚ›mù;½ õ»s U§à~—'ƒ¢yµû49´9£%<\¾Ô¥�H­r $Ñ/´ï9š,lV,pС[²a¬¶3<ðwÈeÐHÕ¦ŽZÆ5:úh®\!•>¦ÚžžîØ‹‹$õo>R…O�î„‘HëH\$eSk2¸ËæAÃh“ÛN˵uÄDœõCžJŸVw›ÌˆÖO³ó¤lRµ4\ 'íPwã`o‘0Q”ÑÍ`¼ƒZ¨"Ȇ¢Ó4m?P7åZó׌1p/¬Ü›•NËçaw¸‘Æóbµ�½™¤#eéê‘Ýüg’'q„Ø+à{ý7Œ¸ÑŠh4\O×Xì2¤Í[©Ï8JîÔÛ¼g|Z˜Î·Gz•ÜÇ=ã»8«sêGЦVlf3oÙ+C®Õùsõ’KåHÀ«g 5«¬³™GKÁ·ÑQ°ø[5c�ˆó#s¢[‹Q¬]Ê·¥§šCû™Ñ¡˜õöÐÁfË�…à`Æ7奜ÐvhéU]†—¹KÕÕ"8ä¾Ë&ªwT¶j¡©BÑ=J¡@ÞWY( Öá0=ædÖ2ýÚZG+µZ¡$4€ž=þ¥äù8 _âÔòê&êžw¢›‹BÙ2f=‡M­´›Ëߢ»h„ˆF[ƒè�«Ýój�µŠUXÝh5Â#†�_E§mßìšÅ[”ýÙÈD°d9‚Ž¢€¾?\ö¥ä)§'IåÇQuƒ’²ÖqV~-Ud¿-*ñV<ñ›A†Ë„¿Y‘Š(8GÕ¢ò}gh#(�§ä1R7¥W{t”�2f:Ëbk�¥dK ±»OÙ.U»w”è"÷)âݵl[2ƒœ›v6hÄñ›•41nelþg0a¤ô-éÕº—«ˆ¬Ýz²9ÿÝã6UÙ}L0 ,ý¯ëK´óIqÍÔMÈ5 ¨ ­#ååÛèæm–´ µ§$ôi„o›Óêq‰@ej‚¢ÙïõeÆ7Ðä7œ&„âÒšª<­ãEMÝ�Mã€N‰Ýš´;<¨Zr6h†,ÁÊöf4ï>[<åø"Æ€aaƒ*H·Á¾Ñë‡É8æÛÀΖÁéFÆ^ýØM¦¹âçGÉ¥ÁlZöé’¡ó5$Þ�Ò6=]G„@è�åº�9¦èõ Âlõk�R°ïpöUDnŸ*qýÓQvód@ð~º þæ‰ìöóÛäyš9—�j–æÛú¥Ö6Ö�Þó{v\ƒå¬Bw¦ý¼ aóÕG"­ c/D Šuý\vLÚ³Uì@cªÜ�•½÷è�ãÎM§3úh€A¬ñõç]Àý|žÓNKÇ)sôðdægÆÔÅþ¶2JëzŒ|�u¦Óp;¯Ì5Wí"Ãôöï»k]Åk _*F!]ŠV�¿í;ªè1Ÿñ1ÐRÇ£A´Uf¶õÐE�²L"2c`=yuzéL¾ö ¤_ &ÛI`ëimËt 7fíÇÐ]kñ»zëÊŸÏ5~Ô7ïwM“q¨vÿ(ynmû˜²¥}()àž}³�ÖöîúŽºªˆÓ¿UxDOÞ„ËEW2Ót‡0-µqdÌlÚ2k±=Jš˜¤á“Ÿþn™nï|¨UZ­‡–ä´ *Å®°Y/èå¤pq¹#XÚW¢øÂ_°èÚÀðTYh”1›VíÖíÖçŽ�P˜Ðtòá½x“ÇÔùR]¶à²‰=O&©ç>Ð21FºA.ëGà"=|£Ê›”˱s¨ºÞé„iŽuÞ§óÈÐeM‡wòa‡R#^¾Ó\Zž‘ùå1ŠjžNøî¾ì;t‚paæØ ‘e™åâ fž·Øž·FC©~¼ËÂüÀÉt¥VÆ _[Þ'±t8ÜYøä —qG-Žó‹b˜Gª¢õ{ r /…ÓÒŒ¢2¦HZÑ'¯]{n= †UW3p¾#šxš¦¸'q!Û]}$¤sNãL7ÀŒ’æáD¿xÃ诣œP ´$'ÔPY‘èZ6`ùÕ„Õ“_@[w‹N_N{¹ï žÍOmu«âömV¹Î1°Ê�@^§_ÿ[gR@èk›p6¯˜…0'²¡Iæõì!N±R(Ôæèç/…ˆ}³Wy˜¦ÏêÕ1l¼½ÀÉÆÔ À}òÂõÿ:²³œÂj¾O%+à@À�÷9òìtÞõ9Ó‡a¿!¶Ž|c<ƒ µ�/úW4I¦ ±R�q‘°þm‹ùª2çgµtâÈ�‡!4™2˜Ãdðƒ*Ò0(Rdìkò²Ú1ŒpD*Ô¹º:3ïï@"�o«.—.w ·Cå�ÛCJèâ#`�«£n €«21"4sîÁ*"Z›ØâüËëaÕ›YðÒñàmŠŽÅï‘áöå*�ú¯âôc‘Ëy|§3éN(KIµæâs]’‰)ÊÎKêÛH.å/o�Ÿ6k8\º(ýÈõãÞÚÝ—”¼„œ„(» Nºgؼ9>lõ¦ZVîˆTd&¨™kh!�IA]`ÞÆ÷×d߀ÑA(6Ï& 3T5ãF�¶û=ö„Á²ÿ#$ä˜ÌgÛ—º‰L! %ƒIºßê�’ô‘÷HþQÍÜ…3iøÑ õÐF(7T„A»Ë&taë,ƒ =ÂXŸ¤é éŸmñ…‡lNô{4¬¹¡’uÀêk­-¿³DÔ–®äò¶‰ÿkp‹XL�'p;ÎÇýðçÁÒo·71Z¥^ÿD�iw®aø¹êã-ÇùAârÕ_Åmj_UšÃA€"¡¤†0|Të¼öÚ°òÏgZ‘;r,Ý?µº¯^E0dÊ f«®µ ÀØnf޲‘àéžíÃLÈ''ÞQÚª(ènˆ)*³îf"ªE·ã»6óR¿Ÿ·*žæíË?ˆ�% CX»J‡�-,>€ü.Ø[‘­×;vïfó³Û6ÑkrØe«>¹n}±ñÆe9óµ)ïªHÞy&5 ˆƒø·ÿÅbP;3Õ;î¶~ô�Ò#ºð‘}(Î:é�õv]o>�= ª692,„�ï¿Ç•°##§Ý¾² `Q8;X 2F[!-ʆ ×±Ï'YEƒ™I^ÍGåy¡Bö�ø"SyÍŸöܶd»ÑpÕb{ƒ—ÔNç)¼ô‘àƒAôዬ$&… Z…¶•[Î�þNü\8ŽmðÏÑJ{[¤Á8ÄáðÅÔ'xT½/¹BË{üU¾Iìd—(u¢ü8{è‘Üó¡ô@énÆR‰÷a™µ&ó Iÿ=Æ‹s’T;?¬v/Á¸mž ËPyÎ`Qå·#|?-ÆPÊlûNÖþꜫ':\DäBªFœå0�ëvΣö¤9èEF¼ûŒ�gŠ‚(¯Z—˜wY-Cl3m‘Ñ>I6ðk¬v+6ì9ó1&®ŸäéÃ@ߺ©{,.»Ð+R_vj.FEÛ¢g" F¹S”μG‚©µé+a~md‰‚» Ê ò‡¬JIrT¡‚ö[¹·õ¯"qµ_ïwù‘&üu§øŽX—_ù¾{˽Oº †O–Qêð¤­)é,5ÿþ—D·Š MývÁÕ S3§Úóò«.–;50Žä2tëP} ¸š8íhT™>t\_;£•&ýÉÔœFEWÕ“Pf�ny¼¼ÉÀ²(.©šõgE[‘$1èÇ]DóÔ„2Q(›íüþrךW°½V�¥OºÀ5þ{/ ‹Øä„=*åiϪšGŨ'=G_çsÆÆ”Ó ?Œ­Ef»¢ÉÄ+  !C_ß"!õãz æ&»‹g‹1ºÄ)«ƒþú{Ÿ±l£©ñÐox¿:BÎÍ2í#ÝÝ[u /×q9êdò¶C¹< ¥a¦«L]ͼûjŸSÃîû±†±Ä$ÄN£wrYAÞo':Žo[³($ ™-ì\š¿ Ú{iÐ"ìUµ)„åiìO :ž»">îY͈ç«ÚŸÔf¸m XnEÞ9P Ÿ¢¾Zøì�k<Î Â ÊÈIT±À ŠŠ�¥Ý÷2ò…¡¶ËÙñОªIbÛ*2‚Î�¨ Ù“×|ý*IâÍ–ä#ÙìÖ,[¬6¾zŠÈ¦&ñ3èxŸß†R%6vðÕÁ€‚Fºd‘üÌax ‘¿øðSZ¹[ÂéYlí&†­Lì�~ÛK×nXcQâÔg8{ciÀ#óæu‘¡­k¶£ÅŒOåÌî/hžK¢nºçì=Y, ê¨ _mÓÆš!QY@4ûŒràIX†TJâI øZû#”M .é"\¡ �”>µô‰¥¢×½¸ÕZ ­Ç1÷Õ'`­Y{Cg[LŠ[|ÈD©j“ãðÊuéå´*…`m&„"Œ�0î•’< É‹Új¼Æ2À=ãìKÚ9ó¶”¾<4ƒ…«ÝX–³8¶\[ýVJ�2¸4®¦êôÍPÑ€±ãb­ZŸ« ?9ø"ƒsFz �÷ÈÆÃ†�Júù™{«Hûáû@ké"±žÉŒ¿F¦œ¤Ôþ¿)ȈVøO–v{�u½Zhy�ȧ ΧØÏüX-‘Àþü,Õ8m"îT$”LL`çH{H©!,>( I /F|ÇÛXÒ¥¯VïÝ�ˆDWá•Aw±Ä_;ÿygêèŒ�{Ñ�î¢à{4$'M o¿ûCêlô5߇ø£ø«?Ó»ù„ÞK9ÝõâÂaúåøX¤„üÝ&O¶7H¾Øoaµý• ~N´ Ù<åhõd/_UtùÙúlç$�Æ•n5© M´k;Â�œN*aŽÐa£Ççò_ë³´PÃ7*ŠøŒ‹¯ZÅ‚’®ÏGXó1eqŽ.F[·¦síõbSG™ &ÉûµÿÁnÌb%ÏôF8FÎ%±1ÇØqáßnÃY/xò)Ã6d.N‚ÝŸXw,ð1Á'T%Þó¼ò9a-ž$üZ8¶¤JåM8mh?˜L«°Xõb§ Ø õHí)‡·÷È{6OA(Ev‰+¹L?œš,›(8i¡*PžJQT[¨²gè29ÒŸÜ×òy+Xâ@ t6¹_©‘G“Ø¡ ÒIyµBÄäçgvÁê.ÿŸY1 ÈÿaFà1Nä*uý5VWDZ;mþÉ’ LX§£ÊF©ŒMèØÌ),‘ÖÎ4?ù­ýêFú™n®‰šwµ8ÁÒãráͱ@Å+�ÊIR{ã²ÁX·üØWÑü“áXK-Y=cô½HzFbJÄ/ÝÞ>Ræ&z�;TàX¦aQŒ» A[ßÅ›¥D>b5ž UÒ#C0 þ!3».•§ëìjàž) é�/ØH÷CY©Ñ8P÷xPe=a±/�´Ý“.ÎÅBÞÔÓn�G ³…k?ƒáLQXT^œúKmÔ§?×Ü‹Ý2ˆC=à’œ˜ðKöuñé–tþͨÖÔPÚºÒÚË…g)”ÚFÉäºí;ÜÅ ý7X½08구� è‡í�XÝ£j3Í”Ž‹€Ã«�²�€LxÕ,ß+܆ìVc”'Ýo­tEÒô™�;ñ<Ö+Ô°S¸(¦½§E·6HX /}?�ö~)ÉÚÕÌòkS‚*BãÅÝ! üxT÷ó;Šè’Sõ0öIŽ „’V Ÿ9ë_d+g¾õ—»oLqf|‹oœÿ1¿~œláaü¶A…kk!ˆåõ”a¸±q<þ‰ŒÇqÐï©Qwüï™—ÆÄC>üô²ÍIÏŠ˜lröÒºPm«gaù€^¤—ÐùÜ@Í€ŸHñ+X¶€� ›''–· � ¹f}l€F”Ã:Ÿ’õöçC¯°p5 BOþpÖ )ÙP¯ë Miúµ+àr2T-ç&!r‰�+Gw¶P9gnzOÑMI [Wk{¥¡¿—iŽº�O5ã³�§ã�k…kE ûÄ@€gj°ï(s§¨VTšLƒûqÃ^l7(`o9,?¿‰�±À›d©�«…¿°uœ®£g|<‡°[ìbX±ÁÎi.r¦D Ŧ-ÁvUó 0yn^ ó9çÒÏfªMp–¥ù‚Ü�"[öÄìÍ™]LöË5p ˆa¦‚þ—=ÈOØ“«Xöüíü©Æ+‡±“Ⱦ¢fىȱÜQ÷þÂ5F3^¸M:¼Äø&ãäuýÄ ö 2Š·…+ùþ‹Læ`Ň$½�dËee¾£=fp9úDuKyH4Ä «²9‚Úlþj t723k»¦E%Vjw–ÏjurÁüV·”ø_O†íGÂÀl-~{™]8^êÝŠµF�Ô}mþ¨�}§ u¢‰VÖáÌ <ïoáÓýɵ¶|0LØÖ:ãˆh6÷HÙ[ŽË–l%¹ ·ÄRZ»‰‹FzÎèRÀüð} Ì:�ø•ÓðFìÞKQþȧc Ó`¤=Â�6’Ú ´ÆSçûMJþ] °óm ½¦ì„ióáx-¯¯§·§+±M’+ì æbX bÛn,˜Ï|�²mÈm47)©Ñœ6ÑuJæJZÎ,ÎáÕ$€Ä—(¶Á³1 5]TSjç#N/7ÞCó`�èŒËV$ƒ�DiTú’„(T!óË¡ÚKe‘3H(ú;¡¶á^—ÊšM;Ç8Vû¤¥Öëö·âÅÊf&ad J¢!žD.nßÁ†0¨½ç=$ã#G7uk¨e�âôí¨žù·³7pˆJ×X^ÙXqoÍé*ÄQ C­»Ù ¸û¹£½9bÕœ#.d€=÷a ? ypwüYÌQ¯~oE…‹J·¬p'é¼÷WG ú„|(ãŒ]µ[ç Ô7–d>þ�(![9Qí‘‚‹²Ï!;×\~”åš�‘“N¸û4¶Ë œS¤¸ÐüÕG¸tÆ•<Ïù:]Éá'ÏEx‘ 7Jú¬ AnXdþü P<Ïo§ëß0žò/×S¯•LQyS:¶³©­»�ø¢¸µÚ{üö"_ÁZêœöê-óoFF–|ðÆdJ-à¬YæVkMÆâîî01V»â�Ód³ñyA¦£~äÔå:Tjª½Ës]ywx²h„@1ñªLÝ%š±µø�äÀÍc«XT0Móï¼ê«ôg~,zÆ‘iø”Ší–xª«ÒMê©zˆ¥,~÷¬j¼¥ÛïR'·n_PíCªBönî­}¤Ítở^Ø¢·GBN±]-ï³åÆiiy÷2aµÙ?i¨{¬fpÅˉŒÎú”€˜ÒÍ_é±ûkæxw‡ø�˜(¶×°„Iò«¨6-È'9ó ¡ògWx!  øãìY¼§:ÎÐí»È^©-ÜѺdzåŽ ýEåöÒzòå¡ •®«x$íá"* ©WµÝ‰½ÈL²<ËLÂÉŸÕwóCiJAÖᄹ±ä%È®ì"eÄ>'?è›Zlà0#ns3–” ò|þÊ ¨uÃwåP…{ªó©}CwŠH�(s+`vµmœ`_0òÏÕÄàÍ‹AÙÇ ÿ´¬#¦zäb6å=Áû`9/¶$¨bØš�ç7ÂÐ75ª°•tPL%"ž3fTõK^®¿Å l°3òx*çîqj¸�nOQ[Í^³Cz� ò¸ådÍýÑÆ#fgî Á¯šÀ³æÛêäé2ˆ“´* ÷´�ÙY(e×çÎì6±W¨Sìã�geÕ3ÛþjÀã:ätެ]Ú6ƒ- ¯tì�ãÈÇ�¬þ7 »’ÓT‡à(~W««i&îÏȺ¢XºóT­ÌJäÂn–×’�k‹Þע³”nš)™Kº‰�?›ùdã¹ÒÂ`f°Ó\&&e¹‚¸3CêñbÅ€DÎ GæZ}[¼N”^µ>p-�R„C=C¹HP?©‚nMÇÙq;ĹÍãç]áfõ@z ¢éÞ7ïhŸŒLù¥$œŒÕOØÂ©K¡µ!í?ªÜ÷Ù_YVBu›0À2mج^]Ø7Ÿ0Ô—~Âdwò¡_a|Ɔî{rNÕjqÐ�xòŽDm ¯ÁdA“P°8†¿º2ý Q{}ÿ½NÃP¥hÚ»j µ×ž36ˆË¹‡Ÿi«5N ßø>’ž7…™TÔº*¸ÈôÝ4¼å•K[¸åùE¶ÌÇߨfüØší;m»yþ+« “lo!$ôŒSo8'ÌM׎ ÐÆ™)þ›L KúÏJ}P£…Ö Ž�Cu ÂQV®N°|ˆÏðÑΜTÐ,@Êœ•2ro Šÿ`,DTö̸U‰�©õö3ÞyAVnoD·€Oßl�)¿0cÔÄ$’6ÞçgÝs­A¦gF“kýi,7ˆ¤y9dP šK]{ßéÕRøàf G›ÝñW^Eø‚¶ 8¢Þ¤Ø­&$æMo*:Œ~€—+ú˜öÓç°â§Ê‹X&jÏ£Hñ‚¦ç�RóA`J¼î¸îjv”»jë¥ì[¥'È?…P”ŸuäBÿõñõÍj–z — ‡í‰©yqÐL±fˆÕÓw/hÅMi",ÍF|¿UºRP>hŽŸæZÐ_Ù¸‚–S˜²gâwÕüI‰Ù¡†_éKœ(u¥“�0�Lù”ôäØíáaÛÔ5ĦWA`¡ªBÉÜý7«Ý´!½Ž0Z6MÃÕ[n}‘‘³«?€Iˆ0zÊÆîùó9@<Ðí(›»°9ÒY@…fŽwMéëÔ¦-š€”ZEÜ#º¹ÌEdèå�5~sÄ ÞôÂÄ…¼–�o‹*¿÷E¡S/À5�ù¿¾º„²oWq ]»ôãßÊ»�K2iÔ! ™2Çë@ª=I¦±¿@–Iðdü�VNUÕ<’±§ÞPµqóMê¾ã¥a‰‚¥GíjR|ÚFS©É×’ v›[— �º¦~TÅ`†êgPbM©Æäÿ� “\¨“:‡ô‚f•_«Ÿ7žVué"ê Ý«Ïz¡f¨}V{ìÅìÕ¥?!7úÚ¸¢u G'±ïù<įJBY†›ÓÑ?6“ÎVê§°úº¬{Þ¥oh, ?¿â'f�\(?5Ùéƒ>%ï­ÇÇ4 aP;·ñ´~{&9]ñë9ÞO´b·d¡Æ!+d(êŽèw��:0€r(ff?ËV…Ùú5½˜9¬¢ß0 I™œèùCHÀ‘\üh…`dJ‘âè\uξ;þÚÀBÊø÷Z4®¦ ýâø¶!İ^¥’ŶTâ'ý†ýIHf[]en—PåD sÅÃ;HÕêÈê°3Kñ]o {ÞiÔ�ÏGãæGšCzYfùO\¾¼˜7¨!�K&Í®<Û8¬\ò_†éÁx1÷yB£GÔ}¹tFn%*ÝÏšlI²|])÷Z²2’ã�JŸAÞá‚‚zT-|„‚¡A• ͬË-=©b¥U°"�(314ñ4xôþsh¡Þ�xOBx§¡¬�8ŒÚür˜qà^ÃËfmo5¿eH‰D–¦ÌArBV•Œˆf- ; šNÐQ µããó�.ú®=–iE[Vm¬na¼¢T'M—÷^±åÀÉÓvá7–eµšÞ‚dËw ›É½{õ…”7¯—kj]WúÞ¿¾f¸¥Ï/NŽÇub¥æE(9YL¼—Y¸»Ñ΄•—½ ¹B“X¢j¦0ˆ„¬Þ9—«lMüM5úõš“ázhM¾=�>0ËH¹¿BÖèÕ­?ñȵޔ5‰dî¢Aõü+wÓ�›)J·~ÿ£×÷²ÔÑôÒ#ó’².z Wºlâè©xOJe]s¿¶»ÓÄcõß¹àq™Œ•6¡C}Ñ%¼3Q^ 3ÇŽ3,“=¡–§¶äEqaÈl†E+…lM�ÛùkÍfÑóMHßÚ8>ÙÞO¿Ýýå[ÏSÀV€\)1ž=“V¹‡˜"vóaˆ’©¼Q�ZÑîØî×=ªMJ¿Ç€2MÄ,†�ò³„é¯R´=l0j‘̬E‰þ—� ÆÛVìþ+d›”‰L«}¾ªGº{çY6AU`þ¼m|Oæ¨{ih2À&RÍ/eSâtHÁ³tˆOáŒ�€Š³1�)�sÑ_7QÁ�¬{WlöTò€\ÓYÖ¬áLN²­ctKÿè]Øç½¶u% ¡^ZSg¡¸ZÈÚ$œƒ½:½ÕbZ„û«�¹üR«R RþOïT?iʨ4J÷)áoŒ»ÍÙï¿_M†?^¶Ì/pL�!ý«V�b´¨S d3„÷-q½B‘Q<#:uLwš4±Ô•Œ•¡�Îzê¢Ôz_Ä76£4£§ÙZÖýò!Âë“i:¿ã‚­!«>‹üš8Õ<­McVí:˜²%óNráÄÈM™Å#Óq²ñ¸aRTÌ 3«îØÅœpWo�¶¯0á 5ô›=kø–ª»è8Éú>2×xŒçkþU ýLòo¾¢LP‘ݾ©& ääÃÎ µ>T'êo›Õ=P÷V¨€ÑxÌñH‰êý; ©ÒÃç‰Ð6*¶¡ÉÏàóÚ5TÏLxÇFßO<íšÅ=8zXZ£aïyHn„äõæ º |„\޾5—ò¢wlIjtÑ4ÚdÌÌ.ýšÕîÇqS]:[-Ní¡À·Z³æÕø.OP¥ãûËÜoXrs¬¼U³ªÍ½Í#áù½ÝwK›ñÆ÷Ÿ\‡R³.×WÛ Õ0ï©£Þ^ž¢í úïYìÏØ¶sNá3L?a"š„$Üäÿ(8^Ïh$§sP]†;)ˆ-|‹“0Ž‚<ª‚”âèÇÝú+hE± ¦^©F�ØgKPÃ&:œ¹ƒðàÿ£E•\T…X•ÁùÅd.>k÷Uh�ñ1Úõ ÉÌ6³Nðà6¾œÏ³Šßðø¡�RƦ™/y™æÆ^BÛ„ ¹Wwé·~à'ºÚü]¦°˜ü¡6é0F̬Hn,€È Ù­d� $6/ŸR#Xí¿¤IO!cßWÇIƒ´† @eÝü®=Ôê嫯õH/Œ¦Æ�å)¹¨¨u Ÿ�“ßr…Ž‘3þ¶�Ò—d\òµ¤QÔï)¨�¡\‘sà,ÙXa »1S–ÃeP[ä[êìí*¯8U?õãW7øûÀ?þEÉV¨ëŒÅ=äà)-–êdè^Ýí¶¦,GýÉ®(aŸ-_Y+c¬5_¾¹â«À;U©ÀäúÓÿí„n ¡Ð «*�f3óL ¼ÿt}ÇnMNϘåV™ãìÑe*¡®šzŽ‹(òúÐ1~¦P²‹¤Á]ÙƒÂ`Šq2¥�Œ×˜ïÍCßïȬi6¹Co©h"èìúXxÜ �˜mF6�ç5'öCqžÍEá \òÊ»n+K94¾—þòŸ ÜÝ«Â:i£ë ÕÐÒR]q½n�HC“ì�Ò»sfÍ5e ¼™“ªðf”ìT‹|m9L~’–¦nîbýjXE}û(ÐÍ_.¦<ÿÝG.YLV Aœ…Dh+TE‹gŒ÷ŽI*vÞ©Âç`á!ÎÂWË F;�Z8Ðݹ=…ËTëÐëßÑŸ�¤½ö’XN�ªÁž;ü1¦ƒãÅ–ž”LLkÏ�×tã ÿæ€ë˜½/HÊhP í! >Ç�Fú‘úý˹¼ð\ß<ä-êõLNä¶|e–PNiŸ@òéb ž1`«õ ä?ˆ÷Šd—`‚B5`±V˜2nFùùòL\5:#g\²'sÈöèGät¾^ÉÌÍDˆÁ¯¦(Ú#HÐQ)-&t oz¥ ƒ"-)-z¹Žýz3Š`0鶇² ¼ m eUýõQYì´7²†‰ æfªÕW»„ÊHÙ'.[#2¿þHt}5/¯ü('ÞZùõ¥``;ƒlÈ ÁV·çŸn˜@TØö†éÉ)öŽî#ýɃçÉ ‰'p�ÕæK.>gh�˜[ÕE9Î#VÉEšëä*èg!‚…Amí-uFm&œÞ}:ôÓOÃ;‚\Ž‹ïÅ0·œS“[MT<ºDD ºå3.p�ʦ&3EÐèrA×Mÿ»™E‡"LDf½R<:]å:¨?v�aUí×+�¶8wÒh>ꆒ;aã–¬™dMó/%~]o­™|�¼FY-ª+ Ž?¨§éú"â8ÚÕ*Û#Å$ž�uå…]»Wkºsg¯ÝHÕ¢‰ †±~«‹ê Åu€·ðúåw¥˜œÃ©à ánáÜDû0\sÓ‹j,*Öfo\ü÷£>Õî+¢‹3OcMÍÓøˆÇb�� €†!,w¿ jxyzÜ5Vë%?c‹`à ’\â‚Ðâa™óôÅ:Ô®>Åÿ/ -³m~*Rú8Ž¹Ë“ÁŒbgÆ¿?�º £NŸ@>}}/úXñFêч@ÇwÑ Ó4'¯S¶µ4DÅ'wO±Ê„6ò�‘À¾¬ ¿BÍ&+mrãR§Z!æàÑZ ™ÿãƒyyÑTP”‘@_Î9 ©h†æt÷ŒšgZ”è6.çó9øZÜ;[«  JÀñ6áÔ0Ñ‹…×®”Qi� Ñ8jo>œ„ijä¯yä¯3눛å°)©ß36ä:2E‰ÒnÑŠ1>"‚i©@@ßpL10Pz|¼ðšÜC®Vå­¹Ò>Öá ·°iç…&g9wÖ—�¶!ÙjÒYµ—8µÄ&< žo°ÚËYû^è]y±šÐbqìÉg)9àÅ~�µ£…M³âB3àEòFò-Š Ç]Mà]¿˜ÀD¹—# š¯6;h镱°óòi†*`‡AQšÁ�‹.3Eä’‚ì6ˆ< €-U©ûTÆ_÷/‰�à0ÐÐÒŠ&g’¦Šßû«ÄA–%ÃíiÆÖˆ†ó Ø!¬6ûƒ�s¨Ç¢ÔÐDÀß%2üÕ‰Sàâxíémëºh/j�éúŸÙ*V 6XcN¾º#KPºì§3)ëõ^ygÑÃhv` g^Ÿ‚¼.Ž‹LS îãð׬y뇳ÕÁ$³Ä6ï’{ä“ÃÉs¯Ž½s+v™Ì»¼ÂzíƒuˆY2ÈzŠðÍ2Ë`—09!%ª“xD‘Ë7ÕOÍ&63âˆp‰Ë‘ݵ>ëO8¸ b‚ðhöô�+]Š:ðîþzØþl~ÍžýÖ{ä<šã?è—Rß"`Q)_Þ�O …ó"�´v÷Æ×5‰¾ˆ¡Ð³ÿ[b²šÂ¯±å�Ÿ ¨J쟭Í]q“ <6•\è¤Á�¿ìÔR«¡ã£¦/r:Ò_‰ã·Æ@d·óø4-pPš¡ž¡×«þ½~XÈHXOæ?Ðü&­/ ÒS=×�líÃ5y¹oE‚R¾ý~.‡Â”ÑÊÄ;GqS½üp�Ö˜ì§Ïtk“pZ¦ay ì¦èù“É`öõÏ¢ö¡Ç~ú¸­_R6žmïǦ|u5�,8�ñ¨”L„»ð�=ÑÙmÿë3Ò!ÕSº$‚KTœµ†M�Ö¢†�h¼‡°aÍ—‰Å×A¡ÖÈ,õñffúSÝö9`©ŽqvQÊQ½+³ê…ºÒÓ”©ç*œ´ux¼”ÖjÈi¦[‘ù¬è\ê�%äüïòu­ß±Ün)\¦&L’• hkG¨õöæÉò›¯af*Kã!#óCÆžN#BîBiœ­lòÏÒ7É&÷A$ë:Ñ^¨�ðñ6J鱞{‰=*Gpƒ‚y±Bš–�;|5ÓmõåxÉ»¸\ݪÙ*¼xÜfcßk“u&àÉäà—¨ÜP žeÄã÷ïÀy(N�N²êëæB+«4¸Sì»Oœ†Gî*‡åHrŸJ¬¢lPŠW÷O[­j^B<üæeõ9RÓºƒý½š<Ì)áú ªäe_Y§É´Ht©Wu‡µØ´;�i•ëâp|ÃJ?]UZÌ>]×ñàjAðšbXަè5¶.†ŒŒ”ŠF‚¼Óé´wxœà¡j0Í¢aäŸÐ‹ÍÝúº›VQ+nS€ü+ vŸÙÑݺï“8ô0L/µ¢i¢ØMÑp®Ã i—¾‰ªLhQ°õíhC+í.X&˜Õ”•PHòX»Œ¼+½Š´%ðõÉð¤ô‘5ùÌËÅ6`ßùÅ�r¹=üSÌ\vU@³ÿ×É"ÊRÄ¢P6c|„X}°ËŒ’>Ó›A]ÏÂ$~¯Ý´º±ÎKE@n,�8^Ár™Ñ4�"I¡e“TÐÀË'ÕsÕÌÜü =ˆÎˆi7ÒS1áyÙ—��—öÖ3=®uêçS4·ÕÁêõ<¡!œ¨Et[�…íÙ�Þá¯Ä0$\þU½ÙY0ÒÍ­yö(Ø*+¤€˜Á”b:_Í6JÎSëÆû¬o¹Æ»Éûùü’kE®ÚGPî{|zÙLõÿóŒ�p²öØ‘H?ØEë¾,˲˜A£…Ο½Chž¤¢&m¸Š„;GN²+ Ö}s8à‘TËÓI†[’ú—dQR5ÎŒŽ;!·ï˜þ>Å#ñ´ƒ…@[¤˜fÙ‡ƒ>Xj gâæôÔlÛxÚ Ý—Ûl\¶ý‹2‰_–cLv¿àþ@ªIœ’Á3�+K’©ãø5½\„¡• o-î Ô’ôz¡š°çé*J9RÂì]¢Ôn`Ç9‚´jòßÂå\è5€W*zF&u’ˆÐsÆþ›FU]Ò†¤œùBSÊ[|Ïë΄¡3~7l¹8à~‡é n‘¾§ü©'Í÷ñŠ:1œÒ3&qh6jËBbm¦ÓÁ1X“ÌÓÍá™Tƒ”.~k-F©Âé@P¼%ÅŒ£1ùÆ«+�{¤és? �Xg²Ã¤D‘O >£6]¹ìÀXµòhÁ%ÑJHŽ*ÀQäO6W¾hþ%Ú�)d·³¢‡ýü2ð€sŠ3X~ϯÓjݹˆ½­J¡®§ÿÍ(Ë�¨äç±ï˜pÄZl`[”°)uöj ñíÌŽtF9Âþ¶Ôá!)jßÑs6�„Ìrá–�1ªeÛ=›Ø@‚°Y±¢µ ²¾�ŸÒJ=¥ŸKH+•Ë:ä×Gså::ÈY ¾GÍ_ 0ñ°?; +oM·{Àtï­üö>§Fg^d¾¯¾?¹ľ¾&mSß7SWhÄ‚Q¥$ôhÕ<ˆ¬¬æâ’¦¬“_äÆA“8ü-¦±æâÂÇ=DJÉL÷Ÿ¸º"Êóºt�¼i³§YÓéž•/h™2kuOñ�Ó’1ȨGJl©çÚ^Mš(£Ý{æÅ-بä�® ï\m³­�û$È­wÈÞzM6f2Œ+ë°O3|¤„ïöç£D^Q‰&_­„úó:è°Ví‹Ê(/§óŽDy©•Šš‘sà+CAõ¹·Õ­e¿cIÊà(Ê„d¶¿¡"É‚ÝëI@Ì-ç õrÄÓ«¿Tt'Chë×ÁÌš|~R¹Vk¼ž¥ÏzF*‰¥Ë"Ž{§›Ñ›?iè…™­1Qz!†b)®�Ììð–ažŒ¬œ~É[ˆ7¤c’eñhZ5™ãHÍœÔûæì‰íaÊçÖGÿ±y̸ GÅ+CðžÚ§ ¸4–øes°xfÞ¯­!ar‹ãêõè\–Àè›JÙ#fuýÊŒFŽ,ÙóÈ›Â)E¼‘ûÁ¥>ìŽERå¸(gˆÍ #8ì¸Ú®g%þÜ #Ð]†Yƨƒõ™Ï7>2}¹TÎæüÍuÞòÙ:å'/Ø|ænK-÷t÷ ¤Ì±Ùßåö°ŒwÀdøt²¢”‰˜«A°dû²ÆÑ{HËH—>¡ +úÝ=8Åïé‰K|8õ‹¢/z/¹–td”Y͆ÊW`v_‘zk•ù!Ä}@STSPû°IOaÚL;™»…⪾¢™ôJû8 ËCtDú*§ #2|%ªã?Iî¶KxëÚb'q‰B3Oo[`ι3<\ì3N¤¼Ø�Ø\\ñÎìà©´ô[ñe.­‹$6.ñm‰tâX<ëí”4»ùqlü¥&Æ�Û„‚ê)få]³º‡Í°?¥Z—µzx—ã � J4C¯˜8QúN Ú¶)%mý&…NIÔ-C¬1Ôj>0x …çŽ@:Ï% &ÄØ…�¦e¡ñädz, us¿! —yø'Ú¬ ¥ƒ°7'=ÚÜ´UO@P£5û·£“ ã~•6Xjƒß3„]N°ð¯þ#ýZ¿¤D?¥h²ù&Kì¦ÿ!‘¦e3“Y VÝlZ}�“ã°Q¯÷f˜‰²9º‡)¥ø`ëWšÉÅ[“-!”È¿¯v�*Ö}„½‰cV¢ª ú>’˜G¹'ç!ä¥K㫟ªÜæpšü^Ÿ¬c�ê§I“/âa£ªüpâ<õ |z“ëÐ}ðbA€EÞ…w÷þj�jÈÜ„:˜0IþÞÈÀo—ÃØîfé¾Κ­§ôÃ7˜ÉÿDÀx÷}j÷ÌtÏ¿áXVjÙ=r)ûB‹¥G&Všµ—nÚ B·sáÅ6ߪ)+¿Õ¢ô±èe¸uðZ î-Nh^—5ÀÀÀξÔt&\Oï cRv%lù'÷%‰¿z{] aÄޡݾ[¸ 9s&†  ¹‡/Ÿ •™ÉùN‚É�ÓÙìëR0û'PG&-X-5‡é¹òçG VÕüünwô3BLÛÝ%EÉŠÜ-³â™eËI£é—-Ï€¯R¿©_7Þy��T”›kO˜S½Ã`²ý7ŒQ2:—Ð5¿iî­5 @ü¿óè‘Lðù˜#yÉÛª=ÎŽësÜ#Lä\?‘'²I1É9Є˜Å­T¡öÉ‘L¤E2¼‰Íú,ÝK5/�Ò±ZP3tÎFÏ.»C+™JÛ©W{ûQ«D!ŒiŸÈ°VK�—/{^µ‰yS�’7FNŽŠ'é‹´÷jÏök\ÏF"›-·-Iíh`|´k^ùv´¿‹–îõRžk_î{ö�nxC‹ÿú§;¬ ½ <¼>›wôðxê´»ÐQÜ_­ž¶@Èí¤Æà ¦—Ò}â¥Ìмv �c¾A�/×3…ÿ¯pO`F`¤)ÿnbE{>ŽflˆÎ‚â×è/'»[£4"-&+€™™˜¡ÜP=êÅ}³# ç"4ÅÁ…ÎÈ\ÄÄóº”vÈü(±ÉTúþjñ¼å®þô€/9O°ULÛ:ðÇn•Ö»tZ€“ÕµEfìæPAb¨TÝý1)ð=÷=Ãi ÕâG5ž{%M«ÞÓ7ÆÕ¥­‹³LëŠ*£È" /aKDkG�ÃðêÄI:+>Ã6ŭи�{‹Êþ�Pç²Áø4ÉÊÁ¹——§4¹ÀôTu3,ÿÙÇ�ófÅêH¼4 .LN�ÏøÉâÄOãAu§ø�^bÝéÓ£€YŠ .å ¬§\áVcø¸NÙ8•z20„ƒEÞ:ûª•$Ü”~|+h Çgþà–*íó-Ÿ½î-S]\c>D¬[ps‘–Ï©lÉF�¿z9ˆwþl²B½�t~·|ó¸;çuP¿j¬3Ón˜V¶ÿsáõ—ƒf£ñ¨ì¡H­Jà ¡UÛv·q�¹ìÐ1ûŸ_û�Ή/¨í~¥hQ@pMîüt—x&�çPke²ÄOË:+ÈpÁñ ½l(ø¸Ø�(æö�‹6ªGàÓª�ï³HÂêY•ïøâÖÖð¸E{±¼ .hwð™léáü‹zoJ7ní»gXÃÏð#ì[Ïô‰·Î8JX "ÝÔ§ÀކºÃÖé)M.“ªºòÿzúB (h¸8o×Ùýš¨€ä Ê,Þ8Ö!–VâH˜»¯:æ¯,hÓù¸yWYŠXÏD—ƒé˜évÃ(WÛYÇÍ|?囟ùkèª?ê[Êð(û‡ãcïdÂbáÈBֈͪÀÝß¡X÷Ÿ^² i´)�_AéôsnhvuEh] GÅMî‚öðçÇíð/ÌÒAì¯:êN‘‡_yÔâ6< 5@¥Ü .ÛäÚ–ðê3}•w›’¤oî¬\ ºTy²>95Ä.‰•x2‚¦z”Ç};lDtÛPà­ÔT>¼�Äpêß û"¯‚£"Êû÷v»ñcý+pÖ,f½Hôè4ÞA†ÃxÖAè²Õ󸙤Ì(†0¥•„ÙЮª%mœ-r¯)Lê   Ð<ºPÈä+ªix¿_R–þ`Ò[‚Ì¥œZ_ßs'S½"æ›dÎXk)ÍØ6.Kzõ\ 8; €-ÊÙ1û¯ØðîÙ ‘PÞÕþ¯WÓò÷ÑtÉ€T2´°xÀáà}Jƒ“» šÈ¹ p£ÛJ�¨§Gýq/)ðúì6ÇéÊØ/î¨% ×¹4,feÄ%ö vW¶Gš@;?Œ†—�¹Ï�‹|Ö �n0Š�ƒ{e&iE²kà˜‘¬kv‚Â� Ø¡‰²÷üBn5Ô�v^B Þj 5z±/Ÿ#£„Æu-@E|a);áIf-°µ¡’Kµ, Ò ½Îf0¥<�gÓmku k–T(1C›¸7Ô`‘LÖ?ºÄÕåt(â¬?+I“÷;ËWî…c7¾-hÞêP+ðÞãï³kÝþ(eaPK9ÔŽçЈ ´Õ#èêHBƒ“×øgt²cOúµÿê`íjyY§„•=¢»gš*N•[TI®ÿ=N_wnof ŠÀŸa!†üåNó™1fW�ãÌºèƒ zd‰–?†yKnåTÆó’€àtÕõ,æ ·U®‹7øM„ð·!�Ý×Çý1‡SôE]¸qà€87@ýh.1 ’.q‹^$s}H‘¸ŒÇõ=ôtù“†lISdž¬ÐM› dJm€Ù<ù5c½p¡!‡”á 1wÍg¥ j¦ûÀ²4LaÊýgÌzùL�gò6 oCNÓ{:yý]"“r¯p¾�_žÕ^HSÍ}*Ïö÷Å�‘»öÛbékßà¡@¯òÐ7�ñ(…2®«¢Ã×�…3nâk”±�j?6‘UUêa�tý��.ÿµP·›z&ÿ[-H%Ö/Ë@žC»�…å�Âu a¾Z,Ru8‰Ï¨æ‘HØ“ô¯/,ó éå܆äªé¹ÍäYkØæLXU+(Û«Ðu6ßàпmj_ÕÄ–1$Ù%62ŒÕ[ý¬ý_ Õ?زƈúe€RÕªZØT‡£cW¯*xˆÍW‡"T}ZÒ&ª×�“ ´¹QIe®÷àräëâ×¼©èÀ®?À¦,Ìñª„‰Bé×R5o40(ðÂn'Þd4æõ´)‘•^°iω¿iä ~ÁµdžˆÙ(^@±S8ÂÒ ÿ+«Lñ\_yãH.ïn9•z�»lqQ;–ŽÖ¾×ÜGÍL\´$Z[hŽMiš=ÎokYtÅZ×{¼&3È8ßiσÄo¹…gÚm¼i`.Ê“/å@…ÌÒ×õPhd› Á³o—ß^�–ª7;�­V-Ò?$oºáN‚zF®°©bÞ,NQ2(–I�ÅV‹ Yù’O|BÖ’^²~-��ÓÎ'xçÛÛÓH/�á§O5UÑOß‘!ëh­¨Ž]б־8dJàqàØ²^Á†®7ñW[, –»®ÿ‹È{tÍwÐ0,Àžl¾C©ô@“K¿ÉY (‘Ÿ¡·‘!v*0Á]%¸ªm*pk]ŸÅf oÐD¬óæ  ¼&>üØŒwÿŠ^þ>„§ËÙ±|ñÖæÖ ½€{ðIþ}läó_ ¨¡œŒµ’¨)\4pàäŸ+¸èݰñ×' fÁÿnâ�ã=6ñ.Ëðj„ãÛ§œü›9‘Fyåþ�ÃI7Û„‹ïiÃʼ[‘u­¸/‡e›\¿‰9W8 ~kÚ!í¡©8€­ü ×å¿;9C¥ýLNö•�É0Du3³ oÌç-¨LG§pþMlkL�bñ2f.óÓ%q%g@]{9Å×·QÙÎ@etŒª!06 5y{Äm« ÅeØÖqÜSù7z•|ãÿ›’~‡îQÒ¦YúDBÝ §Ý]Ìõh¾�5ÛI¬Œ�u´&¼¦Pü&7$zEbaå2zÍ¡=26”»IÛÁóXåKú�¦=+2탓l(ŒPÁLÁœKK-ÁRs[gqñøÓ'hšMeõ,a×¹×¥qV¬ò¦9 ó¥/P3С�ˆ—‹¤Ôh¹OˆJAž8élCÚÍô±=½{«Õôt߯£F\™JíŽäž:ñç"öºqªEBò+ÞÇ}¸Åfkwxpè²… Ä7&f¼YÔ°ññ[GРׇ«)Tº5)ÝsÇ×àŒªa&tÞ?iò[B\Ro}¸øB·¤–J›"(Åí3“SCsÇð¯Xº**4«Nyä‹TÖnÊø|_y“”�،ԋb‹œðžEábì à ‚ßÚ¦Wß®†Ä*n&ÐU ù�†FÞo¿æ¹!@w ¾^Í Š½ÌÁ·~Ruø«ŒK‹y©6Ðr¥vìÖØTÃTã�³ñÁœÞu©ïã×ì¤ÚËë½w�Ü3X±¿É€ïŽåw«nŸT÷º((n4éHI#ækE‡÷d¬C»ˆ³òÎ¥)«²ÞOé:AÂ2�Ì«˜ŠGWOÌ­Û‚v®£g¸®|ïhðRÈß‚Hk|.E¡«í§Ï}úçg˜q<y*ð×FH®pàRS77ÇɘWò˜BŸÁyö˜ˆ˜srEJ­ŸOßÁ64‚1{š‰H˯vèËÄ›&0?ÈX–ç¶� QÙ¾°ùØ(qRº¤òÊZËaЧ9ÀW¿…ŠÑŸ…ÐÜ�ºv.шÜò›Úµä§N–*.±]¬T/ ¥ò½ÔÁ¼÷ÂÄAG‹ƒdï-‡0/eB'7«Å,a§¹´”¨§׈ yÀ¬s°îoÚ­aÿÛÿ‚8§QÑ9<±…D×Åâõæ Mg•)dë§ö•Bs¨µÌ9�" Z®Ð�Û°2ùâ"¯’ø¨ËMÄyPÉ=RãN‚(~zW{Q�²t—¬ËšÞ<ä"ÿ WK& -z΀N\ÂÕ;¼ŒMõËÕÀŠ·@')œo‰HdDWèêí²ÊSwoðÃ[ólûˆ5ßLhéõ�¹ûË)ñù_/9T�½2ë ì©À°ÏW"œ´‚íLsÇ4Õª@ðë;±!�Ñ+SPÌ-&¨@còVõkáu‰ ÏïÂ+–ú:.âuÂôжŽÐ¢H)ôùÒb¯üÃhi9‚ƒÖ‘×`¡Ânˆ¡#nyMTîÚW"8åðu;T�~ÿº@«­^-׫Ap0?cyì¸}8¾cýTc©ì�Ç¢,x6Ž5”¡|ÃåV©ˆ)éÔQ+I$…tÖDÂW/%Âìb~nšÌ3é×¾¦îp‚lÌ€Ò·´› ¨W÷—��Y›µN«Ë~¼|ÒTQÍýnùº™‘Yh1FÈaM:Í._Åï$©ŠPƒì%/€j4#ÞÎ�åõæþþ¨fò¬|Á�sE_ìV£G‡›Î¬¹mI„6=³ ¡nÚÃWºZ 'º/·ÀôÌHˆšñ‰"·#•*åÂʑą§ÿ7ƒ#ëóQÅ[ÌÔN^‰�X¬ÕÂ?†uË�Tû»L¶l_ÕØ¿»¡¢ƒDf��×12£­ B Ž?Â̧¦Û§;xÿ¹oßq漊ÖD!-Ù±ò§á»³lòŽiþ•¾Ž˜Ó�–XÀ’T�›˜µþç ¢$˜�Ë@*V6¶kY×·§4‘’ÔBÙÐp^§è]‚÷»Jôgá*¥¿o^.£[²:¾E}ö˜<€é/$[…ØY„vGözO–„V·9¬ãÚ-�½BÁÖù…@Hw¤øˆ –…¹+fÎJ€¼rõº® ;Vl¢h�‰œ!¼üRÒ_ è9�ï ¶°ù£àã´Ê;P –Ýï+ä)Æ•çá pç"û27<õÛA“ĔԻ/Ë�.Ùx8z0A­Õ¾½I£‹1:æ6ïË0O•¤õ3{´R;U´ø#ŸÀÃ츽}q@)Ïf‚rÍ5©^Æ-*šÿ4޹Ñ[ä ߯¨JWeä7¬„øWu'Ê#Ü<àÇ{wØ-à'pBoqé]Ð.ž°¡-5Ø Jÿhemƒì‚-.]ÈÙ>Ï× yH›<�Tô2Hú-¥ë)¸ª8Q`ÔOF¿»o4›/õ±ï) Dàí'ö—â>2íín÷ž€Žìç4If-F9^bÊ}Þ!7p¾Dê€Ñ?Éé]ÜÚ·^hkãcI@�O ØöZ¶fÅC^ IZI›…2õ…z$H‰ÔhqùS°t‚=©a£×…xÌä“ëÒ#*yOgžÀФ¾—Ì~©Åq~^Ë«ñ©øÃ#•TðCÜ¿°¥Ñ4¢Ýª«ù„‘�&CšéV3ZeÎF›�ø©…ehxY÷ÂûE·gnüÈ·€¦8€™UL–²ïÕV”Üœ�7Ä‘|vöaš–%6ß@£\ÛF_ÜëªÔ‚/éÁÄ¡…èÒät¢„æoкžMf12ÑÐh˜óÝ$˜ž0UsÏ`´k8�2)¸:Z,sßD£UŸ×Ñ£Ç{)’�º<¤(®ÊôŸ^ûkdÓ�#Æ*È-O-1¾=ˆáÞøSÿÕ¨~9Ùñ¥x˜Ã¼N†˜8€½M›®Q¼i4G/ÍW`ú†÷wþY´K½Å&)ÒÜ)6ï�™(‘iÊv§m<�˜¿Gýø™1Ð=¼ý‘ùü"ܔܤx“ƒµ�Å‚WÕ ×�WK!I1‚oW‡øj)ä?�  ¥4 òÛÿ]ôYIÛ€ÿlÝÈ.p¹V©«£¿wzªy£Ðc×Ì»-G!”eq=E³Û@iOj3¬¹†*ÚG »£Æ¥‹å ܉؋ÄtÝ))*O¯ad„GR‰½,ÒÂæQ3‚@@0Ár< CÄ€èþ¶´‚ù}Nál)6�åzc¿‰õ‹ Ÿò§=µY³(× y ¥ƒCé8ÃÄ” ãP\°„!=oŽ‚öX³µXF”A\q–à  ³* €"'¨­âž$ŠíÛøà:$“¯í«"ER~÷ÿÞçæš¯mEãìøyµþGôyEâVw´+¶r/œuê-Àðþt+Pr—3ë–? ­\ŽÖL:—n©gÑ—;}±ªÍ°áüo¯RÑJ›rB>^…Ͷ<¤Ð{€ã"™Ó%öUj@ñKj…§”´"+�f�‹�XÒ¸ÅDnƒCŽ®Íêˆ./d)ùʸzø-ÆA(ðÓ9�óÇ<·u!¦t|l¹’­–ÖèÉö!ÜõçJ·3:×)+,1ü13̵lÕñ¢æÝÚ•èTêéBA«l!uÆ"ãøC ç¿||r3ûœRT’îÏ;[-] 7«¹³ïúâˆ;Bǽþ’=“Ë=1ºÈ±ltH|y—`6xw.t÷ŠŽ\ÛmØl¹�ßaÛ‰bŽÖ1ñ»éŒþ¶­ºõê¤År@¥}‚RÙ|×FNkt×êT!B¤¼Œd…¯w)Y}y}?¤˜VDaÜM“@�Æ*¼3¯7rÂ+tv½FÖ*·¥˜Fâ:†íɤ°,‰‡äcG­%UÔf¸/Ýl抌|wu=ù¬Ë‰}AŸ2îäÊODE7{Î`R’W6×/áÊ×½Rä Ì † c$Â>X\Ä©?;BžÈ=q Lø/ÆU|’ƒŠ…PIš‰×9Äž°1lv/Ò—Oæ©j1¯?Ë-s/;®Û˜âåÿÇ_ö&H\z°pß±öµæ-’À¬²™]Тæ:èa$Œ±,Â5j8ŸÆ“ý"80Þœ_# $ºì…ºÆŠ3ã-lÑÙ¯·à�ÉõÃ,±jpV¢.)²C{_5£¤Ã='í©jø;>£H�ªfªí¶fä wË�â¶cÿÜ£öYG²­,á¸%PézŠÎ>`¤Ïލ' [Ö§èö'ÀŸ[ 4Qd™àSÎR¹‡útÙ/äš÷‘IX8ÙÓÔßÚáRÕÛ?Ó_Eù®º›‰ÍâÊÇSÑ©báQ°×¬�ªyèñ!¼á”7ß,É´ñKwôµ¬T kaÃÖ)«¥’«¼¸Z“ýYF‡DU®ÝAÙ-‡ *‰ 1c³²·I‹.+–Hìï:WJW¾dÖõŽ-p&Æ™cwiÔ5JÌ@ !e]€i@9%‚€âñ’a3t¶ôꢩù뱫 tMm¹�·pfÑ”"CïÈ_|{Íó*ZïT³‹Ûy³”j†ié–ÌÎϽ¾ü—£ÔÚV�¦7Ìma��玓tÚØIöñ{l<- G«Ï-/*Mägà¡îk^R[û_ÒÈεXÁú1`Q/ãnCà >Ë5Âs?Œ¥L© �ÓÛƒ~ÞAΕ·¶ê´r d¸#^‚Lƒ†8H¢O(–% �àì�m/¢"’ -„÷Ä/^‚U­˜ˆµ�ð›Å!HXS¨îfkßJ’ëWû¯@:�2èñö»ñlÑnñ�?�m쳸Œ“ª`½°•¢‡s[d8‚GjÁ 1äÓus`ÿ6PŽø7�=諎+]HGÐûˆF øÇöm~ìä/r{¡…¨ó¢…‚'Âu¹EÏ`ꊇ½�#±O V°c;Ò˜ò:S¨EDü£�“DR#Æ5˸bþÏ& Û¤-ÕÔ$±n…Ë‚€æ:nŒSì«+=µ¦CŽ|"Ò“„”Lþ9¯Ïâeà·öy„1:lÐÙa¦;s€ÖûŒ\ecñŸþo6x†-oTÓõ«wµ7Iy6�DÒà1%äöé%>~\…:€ÓÏ%Là÷4âH* ^Õ‘|µO–eÒwl^Õîb<.—‚Ti)q�â¸�ƒ� YYÚhW¹|Ó¹î†kU6©üL›tPw•7ÞÐX4 ]�oOàC¥D=÷Ö1Æú�oÕ‘.Hõˆ¿Ox²ÞØf$XRÇp*UÞ4·×r ]ZD›Ÿ$}qoƒˆŽ»üºþv þyœž³«ÞH ·‚k ¥aœ Kµjí’Ä ë»ydU(Í­ ûçwF}a×ñê‰?±ªPø\¿ò¹k™!–"•Å ¡çråóÝŒ&IQ!$ržX¥mÍ8A ruË`õŸ$�j—çr3.äþëâ�›éÜägd6ÜÛt@œ²¢FQ\ßЮÐ3$жA¢Ý=Uª?â\ãYöZ™XèE´¥tû›ôÈÍœ~˜²;�ZÒ€]uó™°HOÜ¥yR[ÙâÀŒê9�†IáLCæÄ�3ü.ðŸväË—pJC‹ªDŽ<ñCþèÔ§ÝÞ'z‚66ùÑz°ä‘ìŽEo:tçÍ¡4�ñsìZhÔ)g—)’dkijäé-ÞÔ ‚��Û#ô9Víë{02¥¹+«êÎ4Úv™™3ÑÀýz|G  øÄ|„êÜp>àû=¨s;¸àê™ã‡ªøË’i%/?`:ï]v?0ômOMñÎ!8�q|¤‹Áè#Ze�Y™÷¼ÕbR%y×z¼Ýu¸ÉÓTø@¸,íý˜òô#t6"aTݲ†u¥iY|gâúJ¼ûs‘ˆ�RðlÊiÇ»Uùð%�lé´”Cw9ÆÏ|¯ RÅæÃnÛ&Lº$(Ü:hÜ7gñüïÐl鬭s³Ð^„³$v tÞHƒ4BP—Yæu�z®Í{›¢dz£�~Ž“L>ÏE‡Æqêæ±ZRì<êmŠã„5GëG{ã*¸œ3ÝoqÇS kÔí§yÊ,rhµ¶v(Yä…B&³à?ª¥üdsüTµ;66ÈÅé‘WœáIܰKIûÌ¿ KºqnÜÀ­Áw}D«` —@_à©Pò°aýgåí€S”�1"P�5Ïõïº�HèÎÁÕ“žò`T$­Ïµ›Ë Û�µNoû�@y mÜßÅOvŸ…ßÔ¿…L¤c’îíÌjgŽ�h6 b†ešF¹ Ûsõ»í¿ƒ2Ù\N{ÂêÚwýšªÔ”‹„cÿÉ sôªuWØD«C�‘ÿ‹] yNqÏAÖr«¾ÂÆó–®Ë3È,‹F†TÒóT*�rBŽ�+@¾%2! ló&zh|õï[�òEËŽÐGã§ï HÃJä#:ÍxQmÖ*|½Ä[®ÕwÛÕ)uãUd¼Mqì±dËtÿ¯bᲫš‘IýãMfHk©Z’¨Ÿ[Çj ’ñŒÀò¶ØØ¬”5<3„{(n“HCÊJÔS¢61¡ËãÄ…FúšXX½|ÝY—.ŒP™"œM7^{  Ã%ÙBÇ»¢0 »‚=þ€JaÑ‘«ç¼…�ʾ¼”Cµ8xÞÉçŒÓÓÏk㊂È{¡È@õÅ^|qö)ûvì3噢'\†�‹l]ê�+,XpúI½)úÖ þÅ6`1$Ÿ¬¾gxY­Æ8­}@×UTAŒ)È·‹¤ˆý”çc¸äu'[1Æÿº{öh÷Ä€éaàÏÏF΋” _õ`Îþ6”4%iª&‹/™÷¤èmü#µx‚È™>ÏLkŒÐ+·³„8¨BŒ¿*½®„Ò!<ál×€ê‡Ý¡6Ë!]êü‘Ë_­+¯>Âê¤1Å—zçmnÍô?'eÆM¥ûÄM³©"2ZËŒ®ˆ¤'Üøù½öÜÓÊ-êêklz0û›S_)t Å1Ar�«.‚ÏÖ–âÒ²:NúŒÝ\œ–ˆí$bTŸ'ºžÎ~%~¼a¥;Ÿ-ÈÒk„sµÉ/ÎZ¬¯Ñ µ<ž §~Š¢ „î®øõ¤™†X¶YÚä´#ø-óÆð‡¥8Æ3ƒýyÖæç­�¥Pùv7 Á©«ob·Ç‡kA“!`÷ì1A4厽= ç–ºw>~yÓ7Ee;0–'3MmÜå…Ô˜Ó—uÛPÙž?k¡|…d¦Ioݘ5Ú³¿•HÖ»â²ãê »6{‹H$M)_hL‡”& ᫆h$-iGåkÁÜѬ¡¤]ßÃñ¨20›_÷[¦øC�ñRøm¢ç�ÎVô  T�¦y¼Ü[Ÿ8ÕG¤Á¼•tîÎlÓÙß]$`–…ÖSTÞf<=9È3HO(Üü $ÁØüJm/äŸ{Wh{…â]CïùMoâÞÅ êfyYDÚXxÕÌ"OìÇ�Rßú'nÂéÞß�,Ô§¼ñÍÛj};OPúìO £mÓƒ½0ö‡ÈñZȼ ôG²ééHÖ­­â;h”gû�ËÇì }5ÿÔÑâб‚`iªs€J’8³Â3 àcܼñ�K”ܤØ‘n…OÌ9Õ•êLã�¢[­ŽŒÆtObc;&Š'$^Ƨ^î.k#ó1áùô r›·÷ÚŠ�|ÌÝ#ø6£€·˜ùxG~&á’¹®›ßÃTò]Dð‰9  u݉)L„ûÜk± Ç€Õ¸>“¢«uQ®:ÁÒ_„&SpqX@ª}½)G~¸@o&5ê2-D³Ùc4K«îÏÀ—be�ßð¾Rø8ŒyŒ ßú¹qvTчVóÓ—Ïí ¥ÉRéÄ(³*¦ºHRPŸS)} žo¾H$?—4ºøè 50’óå‰Ù÷Uͪ7—œ[]Â¥?'Üî cÂÉ�ì-x¶¡îÁœü…¯ýÜØrcB [PÜ{Êuˆ•’V“󼚣ŒÓ\ÏÙJ†À(ׯ4™b™'Æ�^NpÓ4D ÈòÍ�ßAÐòu<¿'ƒ”ŽÂ‘­/åÝuòr@¿t 3Ÿ ÏOÁ½v†æôÈü?]—¢’£HD!óÂÄÁ×q:‡ÆXt†—÷yè•i>0¤ÁÅX÷³Ä l ûmȱ½BoSU0YR;J¯'¡o‰×Ó,:hÞ·U5x¯ð*£¹K²Z4TÔ1P–bÖ|3*És^Öÿ0[ cÃQÙ mí.é †kÖQ|cÖ¾6{P�hšið·Ñ1#ÃÊ“@÷�,PÕhCYšÏ¶Ýdn58¦TȃFŽS:ôXÁ”U0�¼Eu ¾¼’²yÍ5EöOìÑ!DÝÙðE<_j†3ï¯ÁÀÏD®l§Õj/Ýb¯aün¢ÝNc�üvˆW>çl~›Å{žnY€r2E ᯀ}w£�â[ômP›Ü#Øð­q¼ÒßABî ÄÂjƒ¡ï> Û#¥Ò ” ŽX½uBk$ D@#Ž26ø•ëÀèÓ­/Š&îõìB¸ÝpÓZU‘íu}ìµBQÕ?äóì7ÍrU$üüZ°«eÖˆí醅,.òž¡Œf0xó—zd¹U+?9õq™@} Pªq:’Á)æ¸ÇâÉÞ/¨&4êʬk*O— Æ,¶$÷Õvý`á̹>žZJícÓWNx2Cö‘ýçÖË�¼ª‹Dˆ­uõâ§Œ±. €ÕrL�¦ãÜ¥PIÃ$±X”0cåõ¤»^ë�γ&|pÔKz ¿¯ÅÐRö@¸À{ã5ÐQÑ€Ó‚qª[ìë“}s×BHZêý Ò°,,Ày–€Æ"ßœ9‚©¬Ðø0aûûéݘo;¸ݶ™Ç_ÝxeLÊ1œsmè_Áä÷zr£¤~¶’ôÌ�%iF¨Á¹´"kËò*³-¼^HÅOÄwœiÜ…‚Íà‰ŒsDÊî…è’³r1s#faŠ [&Ô[Ñ�vV—U¬5(²k"¡Ã³Šrè]¥w�ªÁ¥¸¢5�Î.È¥¶Ú<ê>Þ"Èd›¨ºÿ�êkúHµ)"móÛ´Fˆ’ž�UÄâJHbâd¦×âê8P]D ÁÝ ‘eÌ7~¶Ayèî51ƒ�à}µK¥EÖ:€*4¥:.B‰Ð¥ ³ü�Bw溉PŸ}·œ¦©Nª¢ �7ò·öÔ§Ü…JЧX'Jz\¾ˆ‚M'™ô­øsîÃC¡@î³Q7I®ïWŽ4“‘#ËãU%¶íŽé3ö°Õ}ìÖ6\2ŒœÃç(ýéþ–N"1KÁ݇œ ÄVp¢ÜSHr‹v|$I`_㦘 !ïÙqï]=B=¢ ŒÓù…¸:3Zdö …¬Å9m 3˜¢C2'°ç�óUn`ÿ@‰{ã«•bŽŠ}UáK@A¹V^Æéž8ònÄ\²!èRþ•ÕÎÁµª‹üôO1…wXs=©¡ú»"8.§‰¿å·ëÕ„L~2Õä5^(õ<üO?Œ y84î.ôÖœœZGæWK»(HGr‘JûdrÃâ¢YD|d ²!óµÍ¥ “¶èÄ{]60P…qFÑ«@_ÈcöO%?­ò sIh¢²Á(å¤é+Œ.w«µ„Ï�Çõd&¡Óg‡óho€í*�iEP¥ �Ý7ÎùºÙöD-ŽaÖåê@kóÏÆã-"KøQ©Ô1YGKM ¢/�zânsÓ.ô0{þÒ!:.�ÙÖª†j3+ÊÆËÛU˜ˆ$ýÀ1ÎH‚¡��#¡k„[Áƒ`ÞŸ.hRwÕ[¥cÇ'8æ¸>Ð3ÌbÎ�¿ïœP§÷‚@W Uèû„qfºqæ"ïó“ †y²æý.ã2oøÊÙYúŸJúVEýÁšdøòwÍZ×0Òõ†•²ÒÿÅÄêz¦èfɲ«êLhì¡kW8x¼1¸×ï®Ãu)”H‹Î̘Ub¹†Ò˜AËPx×ë0"š×ÊA§Z<’w<£Lû€,ˆ²ÉMàVµ—~dŠ_¢ÇÅôÝ·Xn+'äsO‚ƒJœ‰*¢û }Å!ðYÔ®à»?£ŠG£/$Á|èZ‡È­!Õn7cÆ=2¤É Äbóæg ®¹¸ü6ްv„ýÄŠmÔ¢�AoýÇHï²Ýµe SÒÅ1Ç–ü+Çè‚à«2dCr{ê ²0‹´9Dþ-ÏWKB .=gìyc£2ç =; `œøñþÿöž€[œ¶Õ)–y†ªáýŠ?Fê¶´ÊÓUteB‘ádè‘¥Œ<À7o_@ðŠºÃãÒ0pÙÝ#ù+�Éû·5äÜGC“$Õí‰%Bº6Õ8NýÌ¢Êl2ð»/øy‡Nìf4˜N*d;�“ðž{iŠ[Òú* ¢Ò÷x�ó".h�‡6â ò‚X½›È㋚¶’M–�“¨bxH¥Y9ÂoÛï; šˆçÄÚ7Cm=…Yš`Æø Ç®Gu(Œº½¦ò‡Ý»;Í×téé 6ìÔ×kÑšnºYæRåH®² ê¹\‡ØêêÉbã£KµÕxš“*nz­ƒÕi››œiC@ýèš¹úAÿ™ dÊMÎï'—Ldº˜ôŸô&E|§Ü@òb€ü¸ýz³¨‹^Óda}ÕÛE×Y™°ÞÞ÷$F“{Õ�z,ä)ZÒ95Ò¥ÞÇ3½’C9ê:XŽ O#³€§„4‚awpöðK’ߨŒ0ùø¶ª¼› dg÷÷Éã³±$Ýù ¤wP©êÚv_&ªŽÂ %ëP4æl:~s@…Je:ª9åMÕØ«È”OÓA…sPM6*ªñ…¢;a´ù€·}ÉÅ.~c|ª©cÞ^›™ç¡~=a.iEÄòãsPÍ©SÌÏØ49>È8îõËïçy;Ãpx‘`¹Ô¹ÒÄÍk~~î…P‚¥aµÀ-—/‚×;¸¢©Ûæ*ÙÃÁL¨[â˜è?-CY="¾d=œÉX÷�¨Õ³q8·³‰ÕôY@�∙ |¥×‘Ôû×ó%XX Ö-ó« éW›uaUý9?�°Ñ–0f¨×3I“å3ë¤ÇCÌW& %¾hu…ã°æm”뉪éV°îª ¹>`ÕÂAðÏbÍ·&Ó¸÷÷=]AšœËõE†\ù@cºæ(ÏN*’c&‡•« ~Ù—es Ï šh³0QÚÎCïn�3ǪÓâíAÑž·c’>™ã^]Ì’ÇMÔiù>u"æ€Ãàcn‘h-Òg™‘Z1îtl®Œ¿ÖˆÏêõ¿ÀKõ‰rMpsØil§—:¬!) =Þx~ Å‹²q!^@uv{¶}ƒWù:LÛ7 ƒqGà½È)è0aA0à{??`ê S¢–.¨0¹:í£€U�÷Žì”àU÷Q}¾ë†‘ŽÈš€vÜÇgÜ�Ò]Oï•O¢êvy:þå'ÕÙÅNKiç‰JØ@œÏƒ¥˜ëÇÎÇ]HÉæÛ»´[B$µMì.¤ß¼1ßЧGèÜË„öy=+Ë/?ATK﨎ªhíäÞh­­Ž±iºöÉÅ5zz‚¥0ÁŸ®/+¦èIÓÎŒÐ��ÚA]t©/_Õ·v™+�­^Ïø~ùgÆåw�ßÅo>½*É7P[ø�dî(x}�—N Wö´{Ú7”à<¿ÎÒƒ:0Ôjoü »pq´aœÏ3Õ Ásr koìúB^�¿«î=8…–�·„]˜éŒ]!Gœ»ÄñûlÜ÷¢ý–—µVÏUAÇŠ°¶ ºd;~aÅ.i"W_Ý6z2,šÌx‹žãñÂ�huŠnÖ3ù�à œ°P}¢„¹A9ùµôFó u¶~‡Æ¨3ø³§Êk#‹*|DØè{¾ÊžÝðdi6E‰!e�D‰e˜Øá+lݺûÞuÖ»5vÛò`¿õ¤�”å·/Ù–~}+z{qV¹®Žc¨Ø�°éõÀá°v©aÈÙ¯Z³Â^-êˆvØY—ÐÔœ"ͦG£úp lØ“-²|r&ñ À\k€v9å™Û·“»„Îà]‚È©›G¼HHUxíjÌ– ”¢ÛIé3“üÙÌø‚æ×V“ÉÛ½]c •Ijp«à.¬q˳€-¸�ЋïÆóP’îÒ¾£â9ÿÄþv~TbäÓăª#…·ëùì}û¦Åþ‹)’ÎCbwŸŸ J·…ŠÎNI D ôkÆSáÜ«)šá»:^Aa[´ü=ywvðÌ@Y¦¨È¿9!÷\¨ ¬‹ã`“«ò�(“�»ç䲚 i}ï)¢¾«åxç6=£ô %ù–²÷2Øy\]Kkw¿õ½–iêðzþf6x�Š|œÝ¸iŸœðH3R~ÆØmªôNÂ4„íiŽß÷WËŸ¥_Î�‘Þ•$¢b¼QB)­1ÈÐÏþ(¶ÏFD|¦Ðãu\–BíJ‡|A¦ŠÚZ[Ï6)”ù¹çßçC”6z/ÂÅ›ãI™Wë)…!Ñ,6½;¼C€ºÛÞpÞ¾ÃDó*xüƒ-tƒ•ƒ½\ ®3Õ¼–° ¼>PtÂóFQM€×?u=ZîÆ+®>ÕïÌã_yUè,Êg‘Ýl üw84TªˆÞî>@È@õIBþV^ðîÿ1è·2Š  ÎâÕ{+BÔdpÝs ®±(Œ¨ÿ²`=‡³ VV5eØ ][ÞçÈœ‰ý付b™K¼ƒ‡µÆ[|·Mоz/8<ÑF´Ùé¹a#ê·—fÞÚ nÄaû‡äíH½/š­'q¼o¦‹™PeŠÀ£U—C�|´sN©”e1Ù„àö]NP…/Ss“³Ò ¤±ü6Æñ÷Êfa±!Æ’GŸ.ænH›ü‰Å£™¨c&Q—0ª?aŹKQÁ7d9òg,ÑÀ Hx»š$?Æì,©gðêªçE0zÆ)IE r½*M¤®U,§¦ó,ö ¼+l”9z¿þõìÿ®y (¶j@§åY±+ÇÞ³×k)4Çê‰!¹Ç�—«üØ>E;DŒÉ�çscqÚH%é?µío`F`*Å;™ÆÆè%ïÜß›Ãâ•ìóé+ßÕ'ú‡äÐ6µبÞ9m3Rᦽ޾(Ó÷ðÑ{îî:®QÊÚT¿¸ jmØèNÄ‚ý5Wu¿‘aè¶1̧‰Ò’V\� âÃø˜»^PƒÀ稅×ÅR=IÎ฼ôXI†«Ð×§÷«;¤vÝî û­œî Ïaî�³Ú?c¼°ŽI ¹Ò/Á¤8fÞÎÝ–ØÏ‡OgÖ:!Z•ânf I°7›ZÊCã¢4:£†¼Yh–Q°ÞÈm‡DôRl£ÿ³¾œPôäϯâ}¹¯SKºm¿!î_°Íïb”[jC/½"Gd*¥ŒÂ%¹óÒžž8Ó*ÛlÊ ÷Cל QHEÑÚE%¦+•à37¸§Æ‡wÂy„„0Ûä°ÿÆFsÞ¿�Ô¶„i�ÏgŒöñFÍl-¿Y]C~ÜžÓä`SxŒ”ºG‹f( @»aáݪªÎÏ©ƒìz”�&tŲe-‹FëÓtÏ®û÷ä,ä!q÷„%QHdY»À–®y\ìú€ˆX´áMVÖ›:)õEïןÅÏ¥´i�|e½ÏÔÿn3g:“âf��b’›Ã>ÅOõO”l¾X'ã$«gýÑuBi9=,» ¢ìúäv¦íX†ß@Kê8ŠBO÷â=† +H6óî&LmN»�Ù胉å'Ò·|�B ´�·Yî þ Õ¾k‰Í„ŽÇ—–!y¾ñÛ2ø’!�H‰±¸A—"áp^tÄJšûE6'¢YÝ´è"A:�Rç4  ‹?(8’yàõ®PU•›ë­ŽKœÅG#ÿ}ÙÐÍÎɸçúkƒp—‹à)ݳ8¤õÙ‘(q`EÞÇ9…ŽE&^-©?CáØsô�öÔ-ÞTÆqæ+„’ŸÕ¨Œ³,d¼Iÿ4ȵ}²¤Îœ¿VÓT]-(S26µÎ©jŸEÆ“²™Î‹´K–Yy®ŒñÞ‚I“Ú–¶§ƒËÃÍ<Ör:ºÚ-hIò×óù̳äÂWUL”7›G…]³d^Å{ò+Ôú]¼Œ=еµ”ó±Å\;QH®>è¡òv¡^Öz¦zËæGqܸÙ!ÆDJÒãa×þ{W€:ÊóÅ®þþÖâóYY“9ºÊ$áq€Ú‹Ê3¤79M©¤Õ ûþ„µ5[ü˜™»4± �§2»:˜ rÖn~­1¤ Û”©h¹éÎpN“ ôg÷£Ô бóÃ3…s×á§8}œãã$دä99é7{lÐäW˦¨˜táÕ“ãÚØT:²æ1)’„¹u8 ñ‡°WrÜ>ÌÆ›ÿ…¼qâè×ë97º] �xÙÎhÈ}Œ«di%ÿ¹�hߦ�Ûó¨A›Ù?Q Õ8‰?üñÏ¥0³Žñ�<Ž;2+ªUŽô{– ,eCqÅc$yMl,çT}¸úáO½ñ•õŽëx5˲ÃSŸ 47غ¨3­EŽXòg¶ø)›Ižos¾Ê“ùíä xè_Uß­ïYS‡€=“=™:#MBqèóÇD'�vj §›uc„ŽÚÏm2\X6/SÎc}?2¹fz i ýd«ç+¼�Ré›CÄ+MR¨»ÿ�þ0ª¤¾>½£ÎÔJ#b€Ç–©cDae÷¶ì mäC¦–l¯Vßú@x…?Š>ÝkOˆB"#ÀŸâ­.Iêýå)XÊò~çÒ§ª«›È0!7Ãóäv¨1¿¢SÆU8õ¹4QÑß說ÿÄ%öf$ ›•’®�1öƇ#iÄMŒI5«ëÖ¨CèõÓƒfZ6ydDäù ³Õ~}zö¨n�ã±ZRÄ©œ‹53p›ñap=ö—ÁÐ,qJ6ÀL "Ñy�þ–¦>%ú›ºê�-±w‹¿"d·w‘m‹ Edj/‘_¥»½mB`HuØ U™ÝQò›…‰²ÕE¨ ÓÔúʦ¼¸É,jšysP2€ž¦q.N~7pß7â4ñåìÖq+v"Jò‘‘–*ÊnÅsßÛÝûKœª#E ±Æ�+¿h0yK �ÓÄ-ù�¸£døD&8aRÙ©€B"þ'Únù.¹òUó­"[ÕöGàëqª•Œ}ëÄßq‡=hZ2Bf®R}æk¼jÐèÿíØ#îèØ^°/ ãUí‡î¬Zš_3ðÇW­!öŽ´Ö2ÆTŠñÝêÑñKyF§_iîÑöï•F„Ž…0BÞP…°\ÄTF¢çN¬ÕÖ„ŠÄÐŽèGñEKg×%Á>ÍŒjv`îÛ¸ék¸Dü¾«.¡o}¼ *Ù8“üÚD©(jb”M|=qyGÂJ>» #Oᆑ”%®Ç3]âY"Œ‰%Ÿav'ôÇaïŒáô·›ˆè™ŠÓ¾…cÌQ. �µ2**mÇóÌÊLàès2ÒpŸÌð Gçð A”¶Šƒ�’%>ÃÀü¬€ŸcŰ韊ñîn|!@ •Å‹k%sêžÉШHº�;ˆ%ÉÚ9yÁƒ'× T.÷‡¼Á¯¬0–îÁ-ªloÒLöOSzv?€Ö`“×RÓü´ûñ¢¡¤ ‘·SU¶«vfhR ¹ŠE­´1ˆ?’“"�qs&8u­“)3�¸¶ôGŽN~éÙÇ䥬¿q`fA Fÿ’tã[‡µqî»fÜRb– ›>¢Ñ–5jöäk–8˜/ù‡�!P4BÀ¯@hõ,:º0.‚i`èŸÑŽŸ¸à‹¢È#÷çº ÿ1�á¼&ÝV²V£-0´oÏÔX¯§›Ú,½¯Ž:8Ÿ -+×Ç?µ»£»fDz AMšÀ:k�Ú9Z÷í”äÞTW>Iœ…å˜ áÝLC糓ީ(w)�ñUÞé ‘�aW[º´oŸÕ ³ù(�ÈØ9p·ìWq¹3ýJáÅ'ª±õF�Dd¤˜4„¯»ñuzm]×(×ÞŠÞ·÷'S„‚I—–[wÞ<áZV¾ÒJáÜ €1à*sÜeìº(¨Bˆ³¢-UÚu”»\šy_d[ëp9`œ¡ÂQ\8®r¢+_½gLý—@hìÚbDÜÜÒ^»z]ºx°TîÖG,¯ãÝJ’Ù>hØø‡D{ÅØÎ‰ mQvhV6—Õί`žü”-F½.JŠŸÈÃû¶¸ø3Dæ%×ÏIí®}µéWÅM´�2‘ÀLúÓš‹ H!OOD¦1 º©�`z­ü�¥>ºŠZ— 9Y£u aYkϲ±',„×$ÛñÏ J ¨Ö¹¡�“¾ã~àn_ÍòƒËG²ß—ZàKm&>'O´Ò=_uÒW…kÍ›¢EÊ ¾Ü=¨ò#\&úûïðrö‡U@�„Ðvîê íbÌÂE¢¥ ÌšC¶)_4,%Ežl÷gØâ›ãõ2nû"65ž)´5D“ä–ÍAd~D|*VÏÌ”ö¤KfcAè:ôûËx…+ø�¹™»Õ˜Ä´é8ÈëÂèJ|ÃX¼�Ór?§ë¡Ç¥™OÁ~�“‚AŸ”'/×(¤7…ˆVª¥—”¸¹éÅY1¦@ÆWï=ƒ¨c�/˜w/ûñ#é‹ì\(¡ÌÝ/“ôáËç&áip^©mñ6ªB›•eQsï¼ÆÇ¸à!3Мlä¶J‘PV'úá?O,03GÉŒÀ¯·�#•Ænpb>$â}´/Çj�tyb¤0ú›¶ðæNæÄÇr'à-qâûŽaFT[‘5£ª`�ä]R6¨i=5�Æ&NÄbÍð*¡H§LºÃÔ4&ñªY„ÕW硌2ÓÀùo˜(*œ&í*3wÄ‹¬¼É£æ.Nžâfgø‹œŒ2KÔ +ɼ w×@ügtèàA0d²ê9Œ1ã “°4{ ˬ0ö¡�8¨>ì”@ÆX¹% ô(ðNâ`Ü6^dÑ„^¢Ÿ£¡6õ¯ì¹5�rkíŸ.¢Ÿú:&Á†û÷�¢gá*{R±ÐO–pèõD­óÚÿÁ,³}äãw€båàèÛܬC˜•4έùp6æ¶Ðsñ_žd™ŸÙÒ3Û…°o¸Ù¦cð&à ¥«ojEã碉'�%þªÛ¼g®“›‡… ”·µ²f‡¬(Ì÷ƒ±é¸ }•&á©D~Jò!ŠW*ÕAxÕd%mçç;@ˆÕ|ÎS·¸)D}`òDòoÊì.º[ÊFÜþœ9‹Œóõ•Ì#r½nÕî¯H`$šã¡qöa¸¢•èÃQp¿<ì—ˆCP¤LÍ׫hә΃àã-8lÃŒ6¹ÔZP²[»÷¥×Áß1\ª®OI9vÁZa|˜gÏ”eºð÷¼…jiÜ}9ÿí¯J}™¡o;ŒjÌ4wÞ [î~jnáŒÄ‡â{ö�cГï)Jb‰èAã{Ì)FÍ)ÉqÇKÄ­ì\{õ|‰ážQ1µ_@ßK³¤q^$¢ša³Ü ²Å•Žë½ñœv²¿\§esNËr›ëšŒ­Xƃ¼‡×0¡Â(£_£yë|…·ñ?µ¢GÛx;ú(—;Ç·ƒ@]3¼Hg#ù»ÇctÇQ}ל›Øp\áz—á{ï°½#ùÃ$ÒªZgaz Œ?Mú§ ÂǺ ›'ËbzõtÑ}A´1H¥çZ”¤<é÷O�¹‡»ºÁœZ.ùâ¿k‚J›²"i££S…d“Þn7ö#;õ-˜7G¸„Ô² Ûg‡ÝTw^ïÎÉUå›ÜñŒ!×X4)ÁJhpøïý¸^Xá)JŠÆ³XXHwƒàÏ5Í,Ü1Š·=¯²¨‘ôçÕ�×¹£'T|®’hÛ­"VB=hYÌci{…}è¼fúHy®€.‚ÿØ«¡è•4@oJD’ÒòyN—ýòxopà>U‰åšÃŒ^qŒ¦sEK¨°>$ͧ�„t¾d&³…ʤ)É)ÃͲÉÛb,8–=�ãfü¾ý…æÐ7<4±X’� ²põ»2S»Z9|ñ7#�)©ã,p™å"ëa{$ï ˜ë®Ù`^×nccØg¿×áN`L¦»›/UR B"�'«ó˜¶'�2ûFþ¿@¾´‘û£õ•ynkr·»ÖÔA[ÏBëIsøÌÜbmÝbõ:O_z®¸³ylTQ‹0f²ÇrY›¡¶—%)E"ÿì—VÃhFž&y Ì}™ü‰Ã^¦?¾úÇ+æP Q_7w Ç¾¯z§Ø-²HõÉH¬V’ž·ÔY+À§ªÅÔŒù6—V°{¸zGštCˆ?ƶÃòŒÍ„ÌVDe9Ÿ)ב®:”¸þÕÍb‚êt´F„eù D»z@౟à&sùŽò÷JÜ‚›îÍ¿‹ÚïÛ™©½'yËÁW ì¤ü]à¤ÿdd)ÜвÎ~å=c)#Ìôë&N �ë•6õïÌäëÕ4Þ¼,ÒÝšâ™;²ó'A?!¨~,,Œ\æ«�okvJ7äeóˆ-'¯‘™™_ A™Étý•F§`ºŒdùS…X(ÛWaònk†µöm ž˜ËKbš““³ó x\{e0,±žÐ½%©ºñÚ¼¦2‹­Y¦“‘ÚåŒSw<äàÌ;W•ßê‹ü†¨i1°o(�7x÷–6ŠBÛ¡m$?b­Ì}QÒ|¬xÊjÆà¬œÕ$¶2Ô@F¬ÑúE(8 Ñ]l ©ÃjWõRµG¤²�žq„Ð?GEØ-qÉšÀá[ù è+Ü�Éôìw¦6-£Ê>s�¬ÿ:C<‰Uç|ñÕ«÷ÙKqdi‰˜O›cô6"SŸŠ^V «¡K wbèóTà6p44îá.ˆ>§ÌÕó�@¿ Æ'ޱ7ac(¶÷ a>Ñà�C¦Ý§¢%¢ÏÞ/ �Ä©õ'ËAlî¨TµÅó½�ÕLÅßê úy_ ŒòŒùY Vm©Z¤ê'z›`ü]ïîºá;lÂþVÝàâVžyB”ÿ4 b�Ù{ƒyÒ×Ü3i}]r+§©%O&Ðô°×kùˆG¥xßÂßèÔ,jøDø÷” æ_e|0„35ÄÇ?Ö;%³’€0´ÿ²Ñ·Sok!X˶1+›HË¥G4ˆJ¥ ©“¶�<â ßÐ tå;|®FGÃÏ^¶þÝÿ†÷lÏ(tö.¤¹B«O¿’&ì*ë>?¾•6 \Š œzϽhމÍñ·mß6¶ø;¬ÈPLGë¾ T*3öLÁ´¢i¾6Ï 1Óº«TKŒ(t¨ŸÔ<¼í³s*\ÆÐ]XæÕ¢~ÉÆíÔæA}g]æ�; ¦½ Å o•ƒvùëï’»aäÑð[�jluY¸úÇn$CØ.úíFêŽ5¯ŸEtý�7èáZºf*Íxz Ôh~èwÒ••óZðŠ#~Œ5WÆxDÛZl·È³Ùº]Wk¯=¶×'[`½3Έ1´ÓÁÊr¦»XÍ�ëÛÌS>Ó¬#VàÏÜÓTéKýcòÔ胛÷·"Wn;KÉn>>O¶Õn ]è¶GÃbPlë–�_…º=‘-wO/KÚ ¿µ�´¶Úo�B <Ì €Ún¡�Àz�¯ÏrmÔ|ï<ÐÒIqñ­Åe97«ï¢•‘;öJϸÜi–Rô®CªÂXÛ„•’Œ3o˜Î·Ÿù˜Iü(V•ôð6dî Ž^—¹¤ì‡�&1»3)¼›ªXÌÿeznhâµVM£8/¬Ü­Úíƒö�ÕñÚ'Ÿlê)îàܨÝ&‰D PÓ¶¬³Jž¥ÌÄúU§œ"�ãì{c°b¹¨¹Rø—C³¬wy�ˆZîHžâ9ÑW”8ÌŽ ‘͹"C) Ç•U‘ƺ#È&acÍwº!\Ü�üÿ³í¤î/í¸ïòIÒw�¯zIT}I.rD$íx$ÙerïE�grH£â®¼ ƒšÎÔ±0&Ú~Y/S«su‰›I&ÿ¹¦|­¿Et]¤JÿÊêðØÄº‡‹eËóG‘��e’ðºÖæLÒ"þéÅ(û Ã#ØÓ ÿ€Ugkùˆ©ÂŽüex҆Жû‰i+ÝÖB‘f˜ìbô�0(Æ‚4<É+<3¸ ,™?;Âþæˆ?ó¤˜ãÜ̦ÏÓd+оÑ`ì½'Ø;Ó÷4¼Ò«Ò—ÿ|=“£×Œÿó«ò8?ým¨Œ&*c½¢(±â¡ÐéÛ{^CP­¿9À7š€–›7‚¥8±�ܽ�Ø&üç– rÚÜvP‚Á|qêç¡q3Hsµü˜BUD•7.2”qEÐpÄ+§C‰‘Õ6u{Sã÷§Ob75œ­öFÛ ÚÂ�"WÊ¢»`êM)»nËÚyÒu3ÛÙþMžÑ[$¬„r4\B| �+ˆ´µ$á/1"yCºã:¸‰bjŸ·÷AßÏ×{An ñ]åbS¬¡ 7­�µAÇ·'~D«f5\A%(¡Rg¹‘û©1w¿\þ"ŸsÔ úQ P¸hÓ©¿An’�N¡!å ·ãìôºÉ°²YF*�I©Çô7¸ º�c3íì_›Ÿj«Ç_÷„[–ì¤Ò8êšf4r!Z5JÇàœÛO�"uÐSU€„�¼ï„zê3îØÖ)¯|�¶§$-ú[š­¾5@cCdÏâqæøîð+¬+à”ò[º÷ZÍKï~~�qÆG{x ¤ÜÌy}܆䭎c]ÊUOÀ»~Úç�Šeúè¯UÑJÞÝ�@ZÉ–®k±Aª’N¸GM"~SÐmš, ¤“·bËÇ¥åü™Í8Üeô…sNß�Œ¡c¦øþ°j+ûÁsG.6”šqÍÅøý}¦œÍ€„¯–¯�Â3Ò†~‡mûâ ÙÅlúS—ƒjÕßš�ŸfOIÿâÿiu‰9¡6L€š.= Ùjò€埙­¼GdÎ/*ûÜòÎ%Öa¸æ1žã+p‰½iñL) �ô]?ï*™fv!k¯ ¹Pçl—u‰uÖçÆë¶˜êt+Y¸ǵZ áŽç¯"éþèÓ{©ùÙ’?4ݯ6ö‚: õȯN¦Îï]Q¨)åo‰o=â|rÊàÆáh[ìBõ§´ã¿[6ºuVOº®&›�” cˆdÿÜ“ÿ2û£‰Öß»<*q­Ùä×^CÑõóÊ`I§2E¸šOÕÊ^M`+C• Î�â¹{_Ûù PNˆÚ>°ëØ!™l¾!öÅJ<;á@ãö¦YA:.�¿û^Ä �èMáØ2môëߪOk2º1}L2|õ/#€üÓä¦þz‚ØØ<¾!üÜn²³#1¤ê›zâkè‡í͵àY 4Äš]¼­>€Z±ƒÔ¶…dWò?&öæ®™£¡W¤ñ> #ž‚Ø*¢ü"§Gº#¿†€@ù¯ð]F ¦¬Nš6U꺭†øµ]ó@:#ì™~±ƒiãâ¯æ•+,fÑûÕB«ÆÝÓ¡Ý3¥Ž¬¯ØÔAaá _R§V®Ù,…ööhLˆ†´Ëæþ‚Õgà#b‘lTá½_F:\œ©$KrÅÈ}íkÁÍôçØY ð%dáâvo^p�ß¶¯¯ÎN,þù`æ÷UT7±œ`B1O~ûܦö1å)V\Þ«Ëoª*8O×߯<Ç™‘1eè¬æ˜³™PR•”PÝÖ‰ô"×P”pÔ¢G0qÖšVó'0ø3ç—uáG8LlÙvºFQBcÀ–±V‚ÂðÛ¾ Q`.-/Ádz”zð[>zýµç@�‡¥üѸ�Ó\m'ÒÉ� šîÎ)I{•eàIe6Dj³^Ï!°¬)2+þ�gÜCŽMë{`”2ÞÞRdsÉI(^Éç,¤WV˜�ëì¯ ëRƒ‡äx)‡ëŽ~ã`„4;Å…j”ÎPídü8Ò¯ý$¢àŒ€Ó~“n2"£MÕß¶| ëy{´P)CèwÏ$bR ÏàcT.Ï12²"9Ódzº* îËÚ!÷n+—çú«ÏQCoù¹àŸ8S„}6ˆ‹àÛ¾§“<¯o(áW¾°Rd×�û7¿{v4ÕÖúÆÕhÃh寑’÷•÷IJ©ÐÐÑMµ.Ü•Û=±ñ3©1ø}3 ²ÔØR%T@§Ü9qM‘Ph5-븖DÁâà#°ýØ“Ç`Ídè�pÄt„سŸ¢Âs» 6„µÛ¬^rtî‰3ô(¿àC‘; |¶LýàÃtBøø•º]—ÑÌQ «d#;Ø©±E˜Ÿbú—{0jêå‚(Q�-øC‹žK õ-OPcqHðaØm¼.ìò¬J#(�)¬˜³•áŒÊ®¶ÊF·Ý�›j>¢Xi+à�± |˜t«ô6Y(‡ ü-ÿ‘VôEÓ{lÖÌ€0ŒÂ;‹¤»-A¿$«jtñ§*+ò*ˆy¾¼ëR/²ÃzÊ—CÄä~óÀ†_SµÊ4†•Œín†ïðËlö3sÀ¦|_È{œ›eR¤0�Ï‚˜ ­!¹?D Í��Hd¹�©[ t)�ç½øOà f¸ì$ì™ |sÓÎ%Ø~T`OPQÀ¦MTãb9‰ºé·ÌjQuD›a�À!²V–ÏÐns[$ 3±áL[9Jb:4÷ÕL��íèÆûî¼ä¡l¬¹ÜLéÝÃw†r2„«E•Ä[ }{ŸhÅ©h··›iÎÔó™uMR“~O"½ó«¶Á†LNvNv}Ì œ¾>=âm^åä5×È™äP0mô°Fhûµƒ×ŽÅ•Ô!ìRiG6K.ýáÚi2㵉�äŒ\E£]àà Œq¨Ž2�3+U.X­¹ŠØÓ ØtwpìHnýA¾UºšË>Œº¯M©¹LáÀ^ŸFŽ+Ç5¨¨w�?xÓÊ ñ2›ÇiXÌï˜i«Yœ4ì`”ÏÊ‚íjPžÄz¶�?ð^2:òÆK¦Wà‚ ÎW¿À[±ö\©*ƒ~×íÄû/Ë:7½ÏÈ ÚçÓ8#¤ë¤]!õ‹ª1‡y’Öð†DFqª�mü ™ ݼê†nPUp‰ÆS-z'óxv08O W?ƒ8§âk¿µ—ÅÜm7±)}�DzW¼å¹Æ’v÷’³ºòÊŒ Ñt¡SQ^êd«Ñ`ßÚ¼P8úbͤ⡑[K5ê*䃥I=ô‰«whtíx’u:cÑ»4|R n ±Þ‹&Ѱ‚† 1Ú}ª ;É�»“6-Ÿhé—üt¹zõÊ•�ÄDê/È�˜F»—#U%ÿ†H»fañ�·aîÖÀ=I‚i&o.}ÇSÖ»‚c8í³9T‚‡í¾öâÖ#î!v ,Çê.–]߉©�±º¿ÝØò¼Þ)*CÞBgXp€,¿ê8|¥ŽâÆmÕ|ÓF|ƒGßßÉu�ê$Ô J¼£ººQLÆa¼2Ò  „qj¥âáz¯ªAýy�P\½}Š~ñÐd¶IÊƧy¨:^—šfe"¥‰wnóTJXǤ=7È N¯�«q4Ex›ðáš°ìAŽÛΘ/^àe�h¨¶æ¬fro˜¹n å˜RRœ%%„ûÉp[æcÛHwœÃ”sÏÛr¸ôIÀƒÄ>z³Ln($¶ýôæG3¸'âM¤é•õ©‰¡?ñ[ø:�¾!¸g~Iy%i§fçû­¢ ~h£‚Ò yî±BÉ�îPo�CEjÌOÓ¿> U‰N™$&CDì¸ ÈΕ%¿vظñ]BH7gŽCF€÷ÎQËmpê üö;ñÞpW�>eq´EŽ~S½ðZø("‘!ôªìh�cq¥™Îê˼¼íÛ5”Îæ àfü),$ »IPj„ºjάaݘKJUKo()úB¼PÉŒSÜ•'6»ÕÜ‹Pü6Ó¾õÎß¾–w§À¶Lþ°ÈØë²xW§wC_’šZc8@• å8óŽNk/šêýõ4SáøçÌ_nÜxxËð³„EÐp[0µ–�.¯¤+{kŒG°¦Twc@jN¾Ù•RýU… Π&×Y| /�«.sõºþ•3…vÈœ84YÜß—ãqÉ¿·¦éÛfSjëðz´‘)^þk¹T×/Øìl>ü*ÆÛÍáâ ¯=â�ÛO¯Œ?´A¿¦Sû–þ|!³ìÖï8{bØìqŒ! ?¥Þ„o߬ÅȾ˜æo'C®¿Kž15öŽm&ýz‚0ÏÐl:�:qA4]í*F}=€%Ç]+Æíbe¢+¶Ãj­ jlìæ·M›·Æ’Ö}!ÿ©ÏØæ®ë c‹³!ŠwUFÆ£ÒrKIìÄ·bçcV Z5ÏzÍi$ÀH§xŒô;['lmTos¹²0 Þèà w™æùüI‹…–±“C±—äð-ˆ½)Å͆)G/z2dÊ}cß'?õÛ~²¤MU‚ÍÐã.ÇýeœéY{šôÍ�(í¤�Ú<õŠ~ÐnÇrIÏ+@Æ1Ií gO„œI&’z³¡­J­`6L·ãrž\Ôî kR˜ÚXXUÑõ¡=€âs†GF “;ßÛUwk B¹c¯Ï( IBÙ\•áz ë­‰èkÖø%�îT{IÎçLA{ ™¹jPdàïsGú`tqö×zŸ­'eYÀê)®Õág­lìg‡¼¬öcXÕÆ}×6é”åâ››#ìÍbe=OIZWEÅZb?–±U¦7†`0 yñ¢ù¹Òž–<ãÍÃÈ6IµÎÑ6«îCÉäxôl<ÄÀ°ù¡�Ày·¯¸Å-u¦ë!˜%OÄs9�3V-ÎV0 ]±˜]Æí2¨¨.Ä·¤êoˆ?}Q%ŠP�2Ç^'õÏ·JÙøŽ|2 �¹M(M_ÒËI9šé¯ãx,F³`$Lþì *´XÈz5r÷·ýÒTÞdÍÝ~k‡ Å£Û'�ÒÄ5€i°&º_kATlÐ4mçïK ½å•Ò_­§º+îñ¼°x݆í¸÷í�xY냬¹>fÁ¢ON„Àjø^¥yÕ_@�ÛHÆ"W}§,í«&ˆþøÝå¹ðIáød˜ ¦bìÈhû�ÌüëÍ ™Ä,ë6Èàã£6µ²€¤<�©ú¨ø=ùmñ½4÷ýFäoã!R›5]ö½_yA1ÉúÕ{j;·xLÇÖ#šB¿ð<è�Œ&³%MÙ„ ø-Ò•à®jvP^\ŒaŽIø )Fz�QÜ—Ö £B#…­9 <äÈ:Rp¾©FûÍRç¡cNÒ´µÌ„a‡‰ˆœ‰{ˆ$ËÞs¾ë)ˆ4xÞîÀ£�ÍA�×.²§ÛÎG öãÚ½‚ˆRÀ3Pn‚-eñ> þ ×€3~¡¬°³Ø1â`6ʪ{™ØYx†™©õY•jqWâï_êÉ}cÊ·´Rþ$ÖäZm<Åa0ø^;öi­uoø¬ZOQ ܉›J#­UÃHi³û7-íÐ �´E¤^O‚ê ó ]— òÂ`Ű øá”¬ÿïoߘÔåÛ¼ÃkžŠ`‡â¯ùñЉ ê¬($}00¿E¾q}ĵ-­�“¹¶LC!ôVÿ(ÕÈ*üþÓ1¿0GØ3Zг.Õöúé4r¾ y›ÿW¤zÝ»¯ këɹ( F‡Ñ9h“´ÿ+ƒ£nzðŠÄ=¨ñ+¶-�JÌ6Ý§Ý ‘�Þ,ªCœ­xñ@Žü¨gÚíÌšÒ| +âtªé Ñæ%}¯�j ðÚú¬éVîHƒ�ßÊ=á¥-ßÃj3%ÓmuÒf–¯�ß9*I'½Ý"§ŽÒ‰þîÃÈ 8[±.϶Ä*ZÖ7ƒ}Qû„²ãä*ñ'l_ÆÃ¼xOï/.{›Ž_eÔßšÖR-¹nûÁQ�\4ÝÂìø0—µšÚï+9¾·).µ3‹"d‘›‡ŒË’Ý ržðe'©Äq€*‘Åû±øaíËR½ÜÊêŸñ~Ÿ‚ú•ìNá   z`Žš”¹Ù³wi%joYP MïÅ3nf–×í¦4cªbüc€ôiQ_@Ñz¥ZªýDÖB)°£ZÄÚ3“ô<Š*o³ 5˜L“ý¸›ˆ âTT>~ÀÍn2�hœŠ‡c>n ñÿñV,ÍåñŒŽÏòXÔ É.=P™âÄg»&°·ph½vGRFyis`yW®¾«SÿÈw6´�sÔ¨ÁÑ–å?373…Ýã2,•¨ô†_Ÿ†öMŽÃÊìÁk±ú †…4b°ÇÿŸœÀu±æ…º'ÚÁðùÐß(Éß|Ä� r‚$°ú[ƒ¨õ\ó82”�”~qƒ¤#?j*bIÀR]7©&n©ÚÐù&Ã?mà�põMÚQrW®[a�¤P×^€6@ªRo ˆÈT.bµ^x¥=ù¨C æºÑËåÚ4 hß^jp¬žRù4Zh %‹x~�¤Sî-{L.Ûw ”kèkàÅGÚùµËÂ^ýª¥»LR�/‚!È‹ em,rvæt(næae((L_‚1˜ôCö­GyÙVÔÍôè †3Èñ(vЫ ™Âã­±ûNsI˜üžÅê5~˜T0xUTÍèq2>§Ñ %t‹õUª ƒåL_»a6Òb „Ê[TÐÄÅöPa¢´v¤?M½Ñ�ãDXR²ÕØ¢¤Óžì‘ṯ{ඃéêäæjæ7{çMŽ•±ÜÔØÚ bw–œÌ/4&¸KÜ�ºÞ?q…ã*7áé-S 6j"5J ç‚‹LO|›Ÿ1¼’iYo#ϘӀ[»çâ1ÃLö¥séZúÚò`*-Ò·Én6|ÿd%¯féÞ ‚ùÀãµÛÊQÐeÑýEà”"´X`b¿|~�÷äñ©_µ¨Ð¼y¦y×Y0ñUX¸"]Û©ƒ‹ûfƒÑÇU>9'güñ2óÌ@üÉg{ÈÿÜ}5�ÿ ÜøÊ;|r¼õˆ\ç .Ú$â[—¦ŒÏEß|Õj«ìN‚¾ÅcK5゚Jm ê²¥vðÙ¨ö%%õ×ÎÁ5á=¹²Ø©Ëˆ‚ãñsÀOÖª˜T^‰ dËÍŽ0![z"ºí(Jacøúvñž€ Ne˜µ´”@­6ù$ž1§aq¾_ ´m”îçw`Ïžs# ºæ¥ê¹R#±ô[cNh=N¡½ÄÄË‹ø;�åXëxî4Yô7aHË1W_²RI¿ èíCR9ÀhÂu“]%{otäS£Ú‹~~j^dGXñÐÌ k_2"XÕ„ý1Ci5+cé—dÛ]D¦‰‘kºc"xÃúK‰kŒö nì9ØÆí¿líY0¿Ñ+3GÏŽ«hé_„Ÿþ‘Í–·Uã#É£ÄT*�cö‘På”à—¡F¾HÙª¿ =ŒÇH¡ëy?Uˆ_¦$\Õ¬² üÒ›sŶH)á TÑöOÎÙºO å˽bÁ`|êõÔL&¨[¬û™¢�(§QZnv!¶ÏíÎÃlA™Â=«Žëí³§øüç’ =ذkVãxºÆð BA/ÉfPyè ;Òþ9u\ˆ$5RqØÆ¦½Èžœß«‰ý¾yÁJ³ä•5ƒÁÊï]fc›|²»5i@¡ "È\Yå _ÿ‚YBžs½·1Ñè“×Ö‡�µŒÀ¹2¿\.d–"X³�žv> ñ˜ œ¢�õ*:ŠÎ/[Í‹CþW¬Ôñ…ëIãWë ¸šCsòA^Öû"”ºÁ»‹ ¸WI¼a{h�/w/ë ™^À,"Œ?RJõ‡F°§`Ú¥7¨á,Ô Ñ_6¼üÉãˤO½½µÄn3�%NÙ§Ê|ðão&v.t¯‚…˜CŸ Ôò�)‹(”J€uÖóD&Ð^ü�Ù˜ø?ôÝZÛ¾eðT4ð U­î˜5a}L€ªTö倸NˆÒ•0›­3ã m|Ô.p«ý›„hq g‘:©4øÞgG žwÎsç`µP…ïPÏ,¼1ç6L\XçÌ«–óÍ[¤5ä‹©ú¥{&ÉB_•Ò9�è~ ¨ÉqòKF²~œòØÅ7ƒê Ì‹é…ì›5� �ÊÞŸ¤™m&¥hX?c†UüêKÉ6&›Æe ×°›ÒôNWœTYähQð3DrбÐI ˆìïë8ï@”|ü@éÅm¯:)ØD‡oŒÎ»DJñ·Ü®F8T�$òLâÍŽ=kR_.Ê‘àXªo}‰ 5ÊÚZí !ºeÞ ÝÑtƒr86סЇ¨Ž§ðüÇöÁ1 DàytÜv´Î |ˆ�Ȭ¡J“vœ²lÄuÍ`Aý -±¹ÛDË2q/ÿP8i®'²,«—³Ø>\ÞqÛ»ôç_SÛE³�*Sê×Üõ˜#| Ë\°áÞtÛmùboTw]ré�fÚÆ‹_È*PÑ ïÀÁÐ5Ÿ(Ä EÃC>~½º¾È¤·Ê¡�e¶£m„Q 9"…ÙŸ$ç5{ôkGÿû\·è³¨B àu½Aä „d«lvˆÛq« #}Ùõ#¤4k"t?"<ú“òh­€] 7Ô×ÞB/<µwr,0C“dùxÍ ¿Ê1�r o­Bo@ØhNêã’ žÓï¼®ÛOÄ’cú€zñš*ÎÝ æxIáÊÝQĉ+.Ñd³ô˜kPLGPÇVI³q¿@çh� óŽÙ¥ÝXUØHÚö… úªÖO02 ýI‡ì¸f¦*É[Ö¾VùÝêzXa¬9žOÎêàÌ@ð*éÐÅ*[ÒhigÅ.C4—´u´ê_±½äR¬%D�!ÓDu€�X¶~Ã>žg…€ŸÁ»­!»†;¤Ì\WhUõ`ÂÙ }gw–�‘{óž,pqÓåò­„G8¤ž̇ª¾)—zDØjR»û:Ü=ÒÇ…KQÈîîÙ_;äLö%¤�}n €ù›FAõ¬lôç¯ò˜µK»ûW“0ñÅ×ÿZ±gÜÇ8‡ÒB‘øÔ’w̬Õtm=T<òú³Vý näòpÏåW¼K±+;ŽÑ/¢eX¯§NCÃÅ(àšTKU€ª,‰_:Óˆ”öˆWÎÀë¿�¢ÕW£�ÕŸö § i�i8eØóÉŠû¡G™Eз³@wöš¢®H†­®HÅ]QL Áø!Ó Q#­û-·¼Ó“5BÌ>^Þ*é@„eGe½M?�.$qÒµû½†ðø“#¶¶nR oʺ6¢óÔ¥å�Gܵ…×±[Š&’õ¦˜\¹¹çÄï·‹ä~ûÖ®�r^¡ŽÏ5E›ù¹ÄzÜ�ÌÕ›†9†´­Í;<ýãdŒÃ/�佨‡‚p›|Q–·r…ž˜p!@èÊàú+‚'I—`îcݶ�Âñt�·aµ“#­³ÒŠ$,€£?âÿ2èTп�¯unøªç»«¿>ìK0á¦çæ¿AÅ`K96sÉ$PF�¦CÚà:ýLöBÖ–b&�ÄOn‹Öд­^««ýÔŽ™ãeâ&‡ºÔ½QN$ �LuàÌfYD\çý!äð$ÂN�k"‰ädBéUõ¦™�«uü1€Mïlr1Ê*Ò“?R¬‡‹ƒ&õ�Wûá$y^§Aê–ìZa†Pq}âU}܃<ÞüANàzÂf†h½½Pýn-^Ñ¥A©974‡)wœ÷qÚqþãpöBµ¬°Þñ!ykŠ CÀüÎ@æÙ£©{dLG¥P�¤Ò|(‡[lUtîÒ"ªª9‚ůõ ÚdR:MAæ[) ¿<ó,Å_9%ƒÒ¤F¬³ûXñ­óê{­sƒÙj‹ô�Äræ�EÙ¯oÞÒ-]â§Û¢˜êìsþÆëªýØ 9çí¦c;UdñÁ‰‘™ÚCÛ ü‚ßýÑ gVÈýA3ìÒ!ÿ0’8ñ¹°›Z¢¨"[gù•Wç,+lè~"cŠƒDÿ¥n‚"9ó-ýmÑqž Y+ób:ã›éÏÏìS©u+z:)G)5R4—QZeKá]¨7øbñ�kBꢈá÷k•ßv¤âUƒ'ÁÔ…š³å“@úÎáÅ�9À‚l£DæV¡R‘4zˆôœG ¾ºkëÅŒ´¸bÖÌ}Õ;-‹ð8ëf‡N‘ØÚgæ?ˆÈ§ÂÉ�­œfKRΨ­Bä·ï/7±âLv81�C4ì÷MpÄ®w9ËH¡Â".ÇCQš×ãØ­®«�«^óŽÃ†êï$;¬b÷{aŸëvínÍtêÙMzzU0˜ºß«ž#7³¤ì§ðj;¨Ü˜�×¢zÞR¡7ë§±g��cí ܃ŸP�UÎJOœGb°àÙN¯½æ?Oó4(Òw�’…ÑT`õºØ†U÷š`ž»I" jżݸESÿ]S`Ò89ÛèKƒ¢ îYäu½÷ù)ÔÁÉ»4سeÂË$×ÂuÎÌ–BÐ[ .iÌc´ÆÄ“—5U$èQ~Ì…ê¾®‚nÁóuµ½sPä''äÖ—n -E—r+ýã"ÁïîË“ã!A”Œ$7ãÒÛ�ìNš3hÓ¯­Uõ¬•�–\¢óÜá@?1MCTLë®#þìº ó²»Š¶’«Îâ�Á[”ˆ4•ϱ¸)¶}oâÅ×ÛÍ @DÚɵ¶ íiüÅø¢ßv#"@\‰ÛÍl VüˆWñ¿wf~$÷ªÿw>¤|êÚANõÀH`�v¦KÍÜ-¿1…_°x <ÂÖ#”\<9 êT[ù��v¸›ü$ÐyÛ rqà ò"dË[ömQp—«®#+çòúé-ºbu-ºßÙîmùhPÍkNj˾mÕ¡O…½ÇœÜK!�x¶V¡žÊAêJ=Òh$ö½;hÊØføW::L¶f�óÂö°Qï5½Àms±I2aØQâ2hi XÖ�Lë%k“Q=;¬Lßš…~€3H²£.ï7‰¢ò]ˆlrlÁ~¦ê õ'½�ào©dÀ4ëµ%’ž¤�Š*u/`0$ÁUy ÷ÙºÁ3ŸÍÐâÿ*aPQäð�1Ê[ôX #>ir?ð{ŠÜæ Dh¶á†öDâ–±Û–PCâcϼþJ^{Û6îbÛà ¹#i‰Ñ¤Å›³2nf©¸åx§°V’ù´gŸ�qO.ç ÑfØ 9?  ™Ô~ñ°ðŸ†Â¡aËý¼¥HùºÞc…_UéîV�“:®`ºHçªmN\ øz#™åÎCGQÇX©_žœø˜|Î>©P%k˜ÿ™m}ÞOHÁm0wÝÈ€‰H ö¯›q`ÜÚ²’¦!r°E�-ñû½|$¡ÀÃùœÚ— Ððūư¡P±iªÖÇ{F%Ë5ÂàjêVƒ¡|½6�17^Æef‘nkø n�goÚ48+ÿ×pç‡3´.ð¿ªñzjs­õ® Ë*ÛÁ±wE½Z”>aÈV•?¿ïEæjh’ý^ª–þ I)¸O [mœ·Š`qþÊú;®øÑ¬°ß›ÌoÇdù0̤ñ¿sç�×]`Í4†Eº†Ãºüoçl¨BUóP_ŒøTL¹Ê‚É­™nþ./Y¿%D‡gxÎÓ=“x| ¿›±öså?,q·:¡¡'Öy$0O`-0ß®p+¢€`çì8['$˜F:0w;^Ç7NÔç>oŠ«Z›Øt©K¹¥m^“bìç�Õa=%A¦±¼ç3×eF‘žA2sGUŸâÉxJÐkn�áy9ð!vut¶oO5xžŒ2ìôîÂzŸí]`fÀUÀ¶ßA¦Þæؼ65Wç‰ÖiÙ\Él~E*FLlÓ)<˜>Ù¢mTu*¢¤„4D04ÿ0å~”ñ G²A�\3Æe@³B=oàþ7óª6‹H‰.â`¡vŒûÙÃ/Çîåµv˜¼—ÃÏk…,Ö´Wš±þ®Ä÷›ÚlÃjOo¥´õŸ:¢È�‘$ž‘À#C@µ+e�îp&9+y"Ã'6ÆÓc†~…-baŸÉ^Óá+võÙLW#4ëFN”ç#æxˆ|"À—HòLõÃmbÜf=—–ašÜï"!/�ž›ö9™‘it’_YOâÞk�o�s!VpâwGËÕ¾IÕã꺯¹:º¸l!5ŠVdc6#ýŒ‹«NöñÚâ‡!ÅÒ,öþšœ@°4¡ß )6¿„Y,5›!Ïr¦Ò)ÉÆŸXü}O°þS£þB«T¯+Hfä»`ôÒuãàœ¿Ð¡P¨í!Åh�dó¤ë¼ûãÇXmÑ';�2ÎÂ9O÷€a@@�,tà­òiŽD›QÕšëÍ+Ô‰Úõx|IÇñ»ê)øFÚm M‘ô½d�n»€U*p^mÀaI•l¾˜# ÿ@¶ÚÅ••n é“…e¾X‚#擸’¬þÚäî0-äY¹´ga êÆÆ'$óW2•Ÿ¢97Ãyo ¦”Ô.=>³Ú@˜î``(_¢µD°5Ïus°¨!j‹Nl1³GȈÅjJiû ¿[€�v�v«]°‘–�Ÿ«ý`œý9‰æ=%Õ%ZAªœÕHüÿ,Êçsç@”¢×÷†iU�(WÇ&­…©Ð"¬h¬€å`^NLÑ�KÙN«SÕÌrè`äæ÷¿’�ƒg¬ßãZ ’Å“ÉSÔÚ >îhJÐLm%‘n‚ÅGÑî¥H§Aõr—îT ÿ;Èøýë…mØøyó;0S ]îR’WÁ–ØhãûÚù;àßÎV²Õ, ƒ/£§ÌY*óF¼ÝŸrc±\D _B7=i–wK—,ÔE —Úiôj)Ê„Æsˆ°ÓEìÃŒ>ýv™3 ¶Ú¯�)èìUAv˜eñ¸vˆÃÒ:�«n– ^ÙËŒ÷ÓË âµ Æ72íñ«ûÉäÌ.ŸÛ&[r£]ñ¢Jçݿ䔈ªÈÔÿÌÛôø âÏ b”¾$MÐÂ~�ä3 x¤æ~8¶[Ø=¹Çs™#*zòIuº&€ ,ÓmôÌõdßb*DÙlßõª9Ú1G;R‘ûb�ÎãÁr½jÌjWšF‹yE‡c¸É­À(­ë=¥»Ö4Ó �:™œÀúcûe¼!@<DºEº¹ feœu©o†õæ«ËMQ%GtÓZ±ýº›K4p‘…B �1‘àõå£ ó‡T…0îüEsÅDhÿ}¨­ëÍ÷˜ñª‘.)-Þ]þ UQcYÓ9줣û—]Ê�4SŸHT‡ôú%=;£\ª¦B v�êMdÃ`W°x�ú‡=mÎÒÆ‡^�BÛÒ_d“驹H ¨G éJLÿs9Öž­ùsðuz¨ãŸîåt3KµZ8i…‰Aü¼i$dÐ’»§_LÍ„eæXmìûBïýÏg�hSù¸0l`¯{&g£/eQɉzÿšk}qîKäsŸNÔ–ƒÌD´¶ ñSŽîgÀ˜µ�Ìb*Ü+N×hOQtå¬Ûóë}#P="Þ­K«KÆb¼‹š¿Ôå8­Y‡ÑAÆ+W&™/$Ú—J}vJÖÀšãT©_ñšDÕ€ó´\-ÿŸàêÉÌV1>»‘¹á*o½väû¼^T·±h[¾–_ÃFƒñEÙº\nÒlÀÃ/ /@ŸÞúY­ð:ZäRñäúº�WF,(ùÒ£5Èjǯ·û•ÚçâfABˆ�ܲ24g:ñààÅø ŒÍ3h@¢hÏÞ`£^´�A4HÑà�Úólë0¼q†51Š_Àc% '¨‰ž/¬"Ðǂԋ `[ˆH Å50¾A &¸¨I<›œ9!YõJ¤Íÿü�©æDšTާt‚¢U¶Ú(•»Ã'£’)€fï6Ý9?<ç~q*ă°U#QÄ÷ÿ5²2L·>šß}OlÃ�s"~†U‡Úó©ÛÕ±U>ÆÌ¸áÕ0�Ïåã�¦lÛ‰WÎúÍáœN8¨Ô�ìù;:•R[¯t½œª8î23÷[àßò+Þ¬çWð¿ò°‹pä)øNµ}‘N³þ§C+}Ä3ýÃß{ U3.ò\ òBþÙ“È«5à *³¾œ5îh±Ž\­�:Ô R?è­7Mé…\º+8�aþFë0a éÎë–h·ü¯%TZkù{3ÓàÑfJlðYàTÌn~“@Œ«âU¸Oè½$L‰¤o�Y ­G³Y˜ko¯%&n;.h× {w¾Óggfý»ï\o¿ ðªeè)ϵFÅg2Ž‹ÚèHÆå({–ß“Ä>P Í£é8’ï—ª عš•å’Ž4±ËSp!Q_un#ÿÒ7oNLŒwˆ£õí‡Û0~Ë„˜ßý7–--z²ðÝM(Ò“Kûb÷®€MºF±€¨VZ@ÚÕ ™zËàG[ÎfØÞÀ®¾Î&§Ù(Ðn9¸Ó¹‘M®Ul¾QåËI�»¦n�RZ�Ì:ˆzÂ4ÔXf¤ ^?í†üCe/ 6¾¥®º:ŸÓzŠéd_×{1ïÜ %݆Ž{¢ Ï£%ÀcAÐ?U Àý‰ÿ×ÙûHlkGYÍàþîìIa“~‡«“ÐM3­×äIqä‡èï_2­×â þwÃî=qˆ¬‚I¤¢çrÃÖéNeÑ^F]Ô¦êm MMngk@7Ô ÙLJÏ?£„wÎÅ7C�V‹ŽÐWòùégiòÁ±6â¬{`H‰ž±ïö±¯j.Ü:X—ŸÈÈ¢Íy·$å�—ÞŠ‘q;b2ò?ÄTàW¿óؤHlO¨ÌISv³Û¿Áìý+‹ê¦x`?3“ó×PfÊÛU“âe�ddA*qg@òýílÛ <饮�ï‘wWá­Zké@°ãŽÍ Âxª³ÊÆ‘oäD"׈ ú®'¶Úâ?º×ÖÖsãèÓ�Záž^Qì(6i‹˜–“‚!j¿þKø”EÅ×f¢VA3˜@ã‘X1äþ3FÞ#Üg?™”º_rÐÿÔ¹CÌ™¹É‹/°’Ìe”«T‰¬¯¶ò|{Ã_ FñƒðßɱæÚÛ`Á†i{z‰ü�"K`KÊX=¶¨nŽÑ�º7~dš¶Eû¬ö_;I+“Ž=w”Ô£{F…7°wÐrCìÿ,ä`²áõžë[ãjð:~*o;¾ ™E÷JשÅ+q\¿/è¹óimO`Âë+·hÍã�g¶xœŸ;YoŒ„>@€L++Ë�º½\àÈFRXn´¥ �&Τuï1ŒæK½óû†XMwY`G©Ãi Œ/9¸bÖ%ŸÛ$¤Î…óM�ÐÂÚÁùm¦óiÝ}ÄÙ[¬3ÒæË=ÌÛ0žofâv¿cbÆÒ]b 6¨©w�íQì1êþ~SVù mƒ4ñN#"¨0®lEênéÍñ ™©Qãiú÷°ÒÀnÃ'«ò¬Xccȇ+I{Åáºh„*B£U�|GçVH&CLâ 1…ýŸ¬`Gh‹ Ö‚;eä ¾©¯Ã°íàÆ½1—‚nÞý2�ùú-ƒd . ëÌ "0ƒŒœiß ´fÅ™*B<ôƒš”Ç,˜9¨» Á쵌|ìÿ²s¶É—!04ì’TU‡æ!í{—¥\Ñ9y`6Ò¥%:$&‹×íÈôQ½÷K‡Ô:KÐëéèÏ´Ú Gð!ЉßÊÅ¿’ê…&¢QÖµÐép ¡Wg¡6úHT¢8È×STŽ€šú„¤Ú«gßP’‰©¡Á9º+Óä²]Ï{¤µ ¨Muuž!»2®,ÉÔÜ‚’øçæ¨ðsÆ>y|˜(_@÷䔿{BÞ—`²>+ïÛ¯Õ@q¥ò¸ï¬­D!ÅþMȪv<Õúçû«‰›†W"4ÌwAòf‡„G”x4`“…Öôp?² çסéc}ÝË?ù? Í`„¥ã N±  ß{¢˜h™ö«gI+óØÈ಼ T†÷7éÝØÄó”t-Ë�h+V[–ÔýwuO_ÐO”¹)†:áÃçWº¿µÌb^àȱ—wyÀ‘k+Ôæ^0cR‹¥åéfjÕQ'?Q÷�ø¤G�mE1Ê |(ªæÿ¿(0c¹ó~ ´!×¶¹¯±d³ò&(”­ÏS3C¸®óÁ…“€liÃ…zþ46~ûÚ~¹¬¢Öÿe€~iIK+ñí›…•Wk(]­-¸È °Ï ·„´áç!Þ¦ÑPÅ3Þ±�àg_ýGy]•±&¬CQ31KKg3zHzî½Xi9ý4x»®žiEyâK�6mï· Žø [î¸ïÊŽ1Vqe (�ùÒpÉ–=„ á·óW4³ÀA–õ>⯞�7¯óVjRÔZü‰š¹Þüzasâ%-üT�NbÑ::#„ÅÐ$~ïTJ ¼!é— 43¹n_*#kÝ;’ß©åÂ[ÿª¡¾·[ ’&.Ö·æëû dÃ�+'Õå œÝ¦~�-W7Gr¡‹c#y¢÷é/Íï96Cš±UmØ(31ÞX#ÿW°hÄ©*«¢Æ©Ø„¿�§š†ßœ•ÕÙðP: w.Ëh@Ü õdÝŒëôã/È1ª±dœ N™k-†cyâLUU@Û¼Ü-Ì�‡BÌ|aða Ãú-³•¾6‰Eå³ýeæNoçV§챺ka<@²¾Â'oXœ ì¦CQcÌ5ï6ÑV÷¬Œ>³*ò®y«¡¡ ³ ¸�ekz̉`ofªèº“YOS;H­@wÕfۇ?¸pN/+è\˜g¬òÁˆ¥’%Ò@µ ¾jS¸3€»/±Z-r,}°©æèP¤~ì‰Æ–9„ŽƒÝŠ$„ nŽ­!�Fq À3RÕ6@¶Rš„GHzL ‘Gs�ÏœºSÌhô¼Ãp|Afäú¾6ú Ź÷1È•ŸãIž@ýo-_ÐtÛŒ©¡R;!<»ô�ìXìrîÏâ# `·sž]Õ·ù^¸ÿœÿ1 Ö’¹Œ€À§ë0†¶3_¨‰¥~yüÙ›§LºìÈoìjF,ESeí®È—*rho@Š$”óz1~ë³»¨/�ö]ޤxÝŒ1áDFÌ«à n³wI3å+)o‹˜E;�aÞ_àóŠ›)àÔìÞ¡ÁJIÛ¥’ùÙ%©’ñÿU¾ÁqÈú]ìp` @{Ÿ?Jgò™ú·båF´‡±«€ÛÍÄ|/þ¤U†É�‘.@«Òì4%Û{5Ýf6�D¥»8¯Œð£ÄEù_´.ðžƒÄlSjšæÖ½j+ô64 Ë ø ]„ëyMhÔÍ/œ�+?·GÆ|ä1hí|î´ßÅÀœ'#a½ßûѺò/‘Ñë@1G™òÝÍqÃvWRoøB©8´„²(¤j¾öaRh'$_·IêY(”FÇúf=XÅ�ŸYfYï,0¶<÷)¸wÏÍUÔŽ¹Ûzå—[íH ­Y‚ØBÓ#>ÌTmVAÕÍŽÃó¨K'ë³V¸ #• ½­Èõ÷­F'Òn\/$õÍpé» ´g<<\€-!wʸ—Í'ö–AíAžßúœÎS32̱üö_Ìj(å©ÙD&Õè2ÚKŒ¢iœ‚–ÅfÄ D7ˆ.iv²Mqw=rÌ$á2Æ[Š£sn…Þ–ï#‚v³j%@ÇÊ…jh�wƒú'4*P¶˜qJ776ÔZÅ–ÕGòKd9bÝòÂ4ÚëtOE¿Ü0íºì·¯X8… "`yŽ€ƒ´'MKl̤õ!U—t],L*Y’5#w{jˆ/�{Ä0ô¥ßÖ/�M^>ü’ :©§L�“Ó÷šw&éåá(ˆ2'ôH*sI#fÓÝÔŽócfóÇø@+a”ëHèØ$‰•>©èLÙ&/˜—�Á3&vi\¤p“S�’¢ w8ëúaIhM­~¯ûÀ¢%‹%]I§9Ñ å¸v`)”¯Ÿ^=:¾b8<ª#GQðgÒòg$ÑJÞ1íÈ`ñ¤y:¥{÷¬ºBUÓ;¦yQtí/pÛ«,šméRîDP¶�‰¬B:–µŠ2ŽÛJ'Ö9Ïì|¤6îªÎ‚Ì"ðïð¦Lo°â½’ó×Úhfÿ.œU]„Asz)‡èæ³2§ò¼Aˆ;¨©«va¦�Yª2=ßœw]/AÿˆlŽK®ê‚†Õߣøñÿ)Oºùš{‚Y)N麅oF5“îȶ4)6Ô~ø<`„®Ì¾“¨¼HYa¹²P‰‰��îÄ=-#÷“úGáݬÈ/"ì5/ÜP¤‚EÈn1dºî<¾�¥´JrJÐ÷Î Ä÷'â›{Î&]·Ÿìfë`¡‡h[¡Ê;PIõã^JJe‚Ô�†û›ŠB}®I©¡"@¨ÑÆZë ¼ï-kvÚœ ·Šk�ìT‹‹Œn;·rqc�~#UAB@žu—ÔiŠ‚§c£4ÔœRd×’?ÊVÍ`ÓóŒ¥ÆGûV ŸG]àÚŠ«ã®r‡å¥(ÍÀCÞ;MÇð-MGge?¬BüÚ´;ôwS$è}ûµYÈ x_‚d¢£œ“Ù¾e!_2ù9Á³©ô•)ˆsH¦®ú¯›Âä O}Z€üu“Ž´RCŒ8àìG[1ú©WØ#žÛ·Ÿ\Æq׊0ç¾Úq‚ùòæÀ˜§¹ßsÐeAŸ›êÙ`è}ôß;—ìÛéÕŸΣ¼YR¨¸¾Ë["ú·Ö¥Ü ò÷uó`Ié¹#´_7Àè?” dœŸZDAOIyCãÆmAáÅe/`Sle�y¬ (”K2XC›EÆUߘ¶ý��ÊÖ¤“å“gÇÆî.��<}uYSi^^•Õù~Q{�›ÊSl'5ãlóµÇÁŒJžÅëMèüªM‚ÏÕ[z4˜9© �’?]"“ìòúô-�†Å<\¸²¹¦Ó¼"{“½šÕ�Í�V7¥œÄà̺-¨†“´9ñ‚Ѹd^ûÝðée÷ý¤ ‚ËÒáÆ|$Ž* Úhæ‚�Ï¢ìÑÂ\ãlÕYµ c<�]Ätýß•\€ýF× pm%éÈQ:åLü&3íwýúùfÓm¯âOÇJ'Ó€³ý÷¾Rÿ‰©qôQr‡ÉEt4@:BO[lÝÆ;7ÐìÃÀà§’fîtÑ�·(»½Ø;ÆÖ�Ùjyðï¶T¦¤«5§‚ã×E-â›"ªxF®ÂÏ Fð˧¼5Aù­Ki�>Mì°2�q’K_R"Öý1ÿ™•˜\$´’‚KìÈ=é ÈæGÜN�j|�ÔåüÉ|?Ñžéw´+Ò�]úxvÀã'È„µÑa…rö¿P†`í3-¾Û;ª6ieS”gxU¤½¸Ãcí(À:î¹)�z§°{ Š@Ç[„|œI·…XDvFÅ�¸7"¥vC\ªJÍJá4ȽYÁø™ì-ßÁ*]ÌŒ¦iÖY0–O”»Óþ~üg½ž°%Xº7ët�O©¯Ó†í�úïu!±1‰3 ,\{žá”¿¤y§y%ÚsÅõ-ô•³VñEª³Ié£`s ô†¶a£Ý=¨³¡—½¹T,Õ “i /‚ÚÛö>Íß~cŽøØ ð� <å°kyð¢|<² )‡j��´øánf’×/¬•Û! ´!W™–캹oÌ8‡Âe~¬*z¯à«"5G?”ŠÉpë"Á® <Ý; xe»tnº¿v f4;‡²Œ6òS©³Ó´ôU)bÍâï:£¨ Ψ8N—‡ÐÅ(áÌöZÞzq}œ� ÍCþ”ýàgÀî²_é€ÏCé*po‚Qã£Ìu�M¸²º]«Ý*r|Ãcª/ÍTÀ¯L¨õt磜Õ0½ŒÝ‚òœ“t�c­1š?t�æ”–35›\;�Re®HZ oÿ ºeD ï"~#ˆeK"�ž—ÁYC«¾!åTV‹ƒ¨fÅɦmøw�[Ø#¿ÕeKnžD@·>¸L~£Üò‹‚!oß(�2^I šIxó;=‚‹uF°r©¢-•8/YÓZ£ÕšJŸ�¨ä{n’¡L:#3¨Ö+V�)ÒäÃ=”P—Før�4;ÄÆê)çݧÁ&± ýêš®û {¼äß¿«UnB ƒaD©¤‰¢4Q¬'‚[@<²pvCöZ”ʘ´ö˜�Ëøµ7OVM´Vî|,†"¸��þd£WH8®ÙZÕÇåB–™rud3I;e®¼Æèþ…!‘€ÍH.¯z1!0âŠÖ’ºè÷ž™ÍP¹H#|Õ)¾ Ó_ƒÏf&Ò×Wi†yÁûñùJHá¼e]j£’èi~Ƈ�~“çâða€Î±lpÌŠëBÙÀYT+ÆC’xKz�FÍàòts·êì úˆ@éÄž®6oØcÇE/îxÏë—ıTG–¹`¤£åši0tn¶ÛÁôªÇ*«EÒø{M§A½áÃîù“8aKcÆ<�9V°ÅÛ^0rxè1ýgñPú“þy,1`_›.…“àqÛ,tÚ¿�Ì—€t½º´,?FDº‚\èk¼\%øŸéqâ4œ‰PTñÍ\^jŒ4îÌ–·zág$­1dÿÓmŽ &6Iˆ¾¤�ùi©Àà+ïPŸº÷d  ¹!qÊ#9™i­æ‰íƒˆm¢Ñk§<…Æ "¾ˆ¨{³$_jç)|bÒþOœ8qÇÿ)°ØÈì¬KćÑȲV\ j-ff,)²$ŒÞH•ùÞwÇ({ç\Jýó”%çÚwìXu²`ƒ-ü2hÉÚQõ¡V;/4ìl«‚m^ ÚQv#áñ"wžVnäl úATCû„’¢•Š‚Ý‰ÌháëEaoAA,lØæ¨¤üi³ëa¥¢7j¡Ö;&ö‰ V_1Vd±¿þ:8"Yñ��‚:‡²«ˆÄm—‹Õx:d\TÀQâÙ)_—x_rÚ¿‰5­/Èrâ§O2cräd•-ÚBƒƒL†ó@W¼)ßÛáÚCFØ—¸ÄùF­·Û^ÞÕ´ô7.LO:”`@CI;:˜üˆ)Õض´¯Åýçi£¨,k¿Å -R¯×†�]%°d Q_ÏM« ÒñKFÊ¢¾~鮼)Ée’9E(Uyú«¿Dv ~¶<[ظ2§òú¾÷ðÌî_k�e¶^«/JíZ^Jh+�§7Ljƒì2ͧyê"¨þ\yK‚µûò޳r[ÛÇk–W>ûu[µÙé;çêO/é$ârf8 žø@óÊ¡~WH¨�¿é5«©w\øÙBU•¿» ÞKÁ�œÝ}¯£`©“º�ø<…#à´#³k©P'Yý)άsXXý¯d^ðjß�°`LŠ‹¤ÓøKoV-cÖ}þIÛK³—�Þïrˆ· —oüÌ-ön04Ïþ&6ÃYZ(¼Óã§›îíH¬yh.|ç?MfX�Àì‹%‡Ã6“ZQ(7ÜÛÛŠL6y˜<…í Šr‰3«Ä«g"'i?CYqø�÷-Yeµ$ìÑ%RwcG‹Á´JœÛPaѼގ[ŽUšcoBDãSØ~+kVÉV‚U¸b¡™hò*iªßj¿f9vw|¥ÍÁ­¾ˆ^NnÒ°œ|üš›]âlJÅ3ñ&úúÆo½�››\�bKÚ£Êdd„Ð*¦sß©*Èø’²FÙìM_a›²J‘³ÿ_È&™gáð“H[·�ÂWŒ-Y/oÍÏ�–RôéaÈ\&aÛÀc ¹ÛG;ËV,óÛëiQä<ÓÊLñ¶!-B—1tÔåÊa@ÇcâÎxA�e'SˆD€­XÊ »ð ³sˆ <îNÂçqeuÀ f ÿ+ÕGˆ…&1€Áæ‰Rž�ïéáQb«½L�kMƒ¾uJ1Uûë÷)=$dcóû—Õ�øóis mä´êáöA©¾êìŒïf9¦@§yE+÷áÑM~UßK›ñjMÍå q\�€ìÁòÒ(Åùlk¹4=¿”A£V™�gýR«ø&ˆ/Ý–ü´Ó$‰}au~ß>‰*öU¡»­ °…À€é�Ôœÿ]¢a±M�)õ‹‹oœÐH,µOßGXJëÁ¥€}Œzv‘†;Ï>©ˆðÀ‰?WÖ¿’ñL»‘gtŒ«RtO–éå4:®ŒD{$[§g�½¤ÌùpQ7†ÅXÚÜ0ƒøó§Îþº\.«. ¥OY¥¢íŸÿð±’Æÿéß4–áº+…�»ËÖ2†ÕÁ±�’ŽÕÞ„‰‚là; xÅ`%Ë‹"ësUñe"Þq‡ Ð%I1á3)�!r ud×�ZÍB.T®4 ·uÑ[îíÃ8ë{K7ÂÚ{~+&€Fš¥·2ܲF+&ƒŠjÅ•$8(ì×nÓ¿î'˜²ïöü˜ +y¦©½�mâ̽{è?%çA§<ûŽ!é—;õ–Œ¼�³ZþÄ4Fß Œè²Å·é&&3t`®.Îœß �µ–º~WÈacö'ÂýöHšn¦ Qdž �¿‚(v±ÿïžý¬H¦éô]bœ/A³]Ö .\¿º|èÔ¿õ,=¯ã}è Ýò¬Õ•±6æsž¢²»Ëï�’-s�läÝ®GqiŒ,¶„ÿ½ Ì,#¨9Ž*ôh¹éúû,%È^©—Ï'И-kÁ|âs'}Þ ªÓñÒÏncºä/�\æÒÀò€•e¹ô1=á?Pç„kN˜›¼Ç¡ß5Ò»5Ô"o¥�7Ý,�îû�iTl5ÕÊ·÷ô|¨›h7¹egÒ�šõµîÇÖ/¾eq{"‹Q 2üw™¶ØŒ&ƒ`NFé@ûMÑ“—>à´ê½LDåô­ß«ä¶†$þ.´ŽöØÕúC�c1wÕa�T•C×B ŸšBï§©ÿêà™ @ºq¢w9 Á^#“G(ÃÎè‚ÎÔÝÍÔ1èâˆgßB)Àf¸ræŸï›°Ì­iîÕŠXÖÝ|1ºBQì9>Œ…ÅÐ�¾Ò콫ש‰Gxd9Ð3´úÛ�¥ðé(3ÑZ*ê)¦póÚö¹å«9ƒ°è¶æ]#ª™¾!±ë úq¯ëœ•cBÎíÂc$Æm$öÓ]8!ow°¤µäö`Vê?œŸG2ûNÄS…ÂʽZïl€›&©Ýu^®Õø…Ð…òÍj÷[[?êáéœ�qÀKñxíO[‡E‚¾ÔDlRÙ¡?CÝl]M" ¨Õ¼àÀÙ„Ca· wÔÆ$ç¶Êç: ‘«ëRï�j_¥õ{÷Xñ‹oýÙ®¬]“Àä—o6·˜ÙÝ”k=µÇ,Is›Ié[‡O ú뀥Û`iÚÂñfÞÿí`í@žÄÎø’��Ï+PkÖT¢€€ÁPù7\�<¦òÔ'ºâJ0ëB_®�þ#ÖôßÉ ¼ [¼+D–mÊÉ@€j½‚�FÝAÀ_ßðíÆî(w�È챺‰MÒÚEŸwG\Á&{èŒv+Žm"y—´Ö�üÑ|¡L• @™NÈ‹-Ç£Kð‚‹¯}9-“$Q˜ÆCðvNæ¨#œUa†SÌq¿*¸ÃœáÛ€§ ¥Ø9þu‰ÃŠMÇ�5ÛòuX4®$bý´6LVµ"½ÆVã°/ÂΟÄGª}…¡N�  íÕ,üC`L‚ïp{ë’-¤ºÌ�p&œ6?ËO µIÞÃs½¿DÙ‡ËlNü%ÓoaÓ]wˆ3È!†©é í’IðÄV6ÁÊ…Âì7ú¦­²˜ËÛmû€pÁ³Èâ×$÷3ÈJº_€xŒ£‚w à�‡¿6®˜i÷çYyÁˆ�÷äÊØ]V0Çi-{M NÅM�]G ÙVÁñŒèú߉)–ÄSˆQç ×�~vÁl”þUØ‹;.Ñwò—J³zÀùè‘Ú–tû©Jüd!ršcR£ãF¹b# kÿôÃEÆÌˆ íÐÎ :#Lp¾3—´Yø¤µÜ„V„U|i¶È–­LÞÛùhIÜ?“^Ëps¼ž*jçë¿Û¶ÉO@¿¨/›jño©Pé¦l±Šw‡P¨Q¶*’GºdŸÁ~àåo»�êì}ØF©ø<"è�¡ºIi*sUŸ3¤Ùµ·‚’zdÇMß»ÊF^Ægs1Ï¿!y¸çjz§fÒ‚.Ð�‰E Ú·“Ó:ë§òR1g%‚êåâŽèUjÔÍk.�[ÉfCYûäÿHçâ?¼š Ž*Õ5Uñ„oY#æŽL?È,1ß>Ÿå=þ)Éb‡›¶‚o$âB‹ôUl]©¼Šo=‹;;áþ§R¤¸Ht7Öõ7bÁìs>á¬Ä´†»ÅŸq¢CS‹*o˜îÖ*ëÃdâágYuj\Ô_ÎàÄ[SW”éÒÁàx§ß§‚Õ9XæäÀÚvrÉ%�UC#ð˜�®âüÊfãî� 7мu¥u2ZkZ^+q œVX�Õ0¼)ŠŸû_娒ç˜?¿÷Ò˜Ö[±¾"SEr–° ë^à¤Zª�¦§GV �ÅâÕü}Ž56t’³õ*(Œ¨¯Ún³[ CÛééhÂ=¡bè8…úíi[7¤+Ûð©ž8­B›ÉàFŠzlöìÎÏÇEj6eæ-^§ð� ¦2»^ç„Y×­»üÁC’çÛ= øUCÊÆZàK¹×%õx’&¨¤ÌþØ`ÒízÇ*„&£‡ƒñ“Â¥^è�Ü4œEm€2ÙŸ�/²ž>D…xK‚c îÃ¥Cøt£¬½jGN(é¨H×—Q-ýu+»ª¯ë´t:"·€sÞÖui& ©GzÈÃàD» 7kͰP'Ͱ8�ÿÈÜÀ3Cû%±}²ò†Z`õBqÈj,×ß-Õƒ2z³qÐ 'ŠÄy0¦ï¾¾w¼ý˜ç7«á ŸÇ=À7I8ç|Ñ3T~F»(FIÙ¸”ô ãóf�œÆë³L>Ù‰j ¿ÄûG‘­¢òvéÿY÷[ÈT6ÐùIèÐÎŽ?îúT[7¼}÷*°Sã(áÃ}akúé}UzÑ]£&=ù€õt+¡# 5D*�l/tÓBÚ�€º—Âè âÛ> ØŠ¤´@Û,‚è̳Jû�åß,ö´¦Èš•î)Y*DÃ]õºµÅ±[y[h^£xd“¥%$R<'tÂðç|d­�ÚÝÓÀ— «;&�$™¾uŸSrAIÑñ,ƒ×+ 4µ³8Tù´¤¸~5ž™TQIÓá\¢U�šÙÎíÏ�]þ� :LòÇùŠTžm5wÔêƒ×½($—W‰ŽÏ\´Û¢‚¶�– Ü/éI }0ئ’¯›I0çBnºÛÚz&`s"+6œÑPÃ{±Úöµ7uèMF ÙÊœqàdB‘zÙ£ñ1Ÿ•Ò|Ôtí>LK“2‘êFȰ 0ý6Œ/óFŽ\Š 0Ñþ« ï,‘Qµ ÛÕþ‹ E6…/Ûb¥¦–U+D$zÔ÷éý<�þA{ejgæèq>·¤"Žœ3--øü;˜ïßkaºÑB×ÏÜñÄaø+ýk¾Pö>�j°Çx¯tCïB À]xx–Sç 69óT˜´Ôrb¬óú9O‡5r ñ¼ ‚SÕŽXj¢u°Ü”í¬á­˜%u;—ºVÍÐZ×dö�ØðEŸ2MîwJ7³\Qj~ÃS òä!É!ùDcl%Æî¦ÊS %‰˜�·E*¢u¡D¯Îšâ«Ø×)ªt$—}TM yQþ÷V÷3Éë4X“\�¯�.@g?J�–}–†oÌæ³­‹ †cˆ�âÊdj�§‚œ|™ï7J—*¬âîÅb­‹ñØêÄn&åçP�n>Zí||Áƒsüšý×€®‘<„9”ʨ´â ’Ö[˜½pqùa¾5�j�óM×Ãa#É3Ç)w�~¹3»E6Ô‹¿RŽëã @q‡â©bN=RF’ÕÕIÂñ•› Ö²ó¬ï黸­€á.NŸCe@ÜYÞ[ª ?�ˆÑ«Ý%Á¼UzdððwOÎògtŒg(kÑïY!V¥FöÍþs9†Ê&ó#i­J˦+Ñ<À³"–SÚ1ùòzË5šOœ‡{¹ÄÀ´ÓÜ>ʾõvV5.¯ç½ŸâL\ˆ ÎÈå Z'’.õÈ׸2"ÜÎð@¯® ±…±ÉÒÂK÷îq©�MºA˜QäÛ¼rµ»³‡¬�nŒOÄØF<[5»²xBñÕLލ>׿¯+ßÍÛÈ^ȹ¢Œ3ûse,�1:“I!0ÐX¥.OÆ®¤[�Ù, å¢OÂ¿ÛÆlbÉñ2·XŽr�žt—¹áyÚ~ënŒ–ä¹ÒÊ‚3R:›464çiæ¯^;…gÄÞ´Àršu¬c–®è8¿ô� t­1éç®ñx·ÇêÝÅŒþ�^,á úÅt± 4 ûÐöê£+x<4Péx7zžw�àÈlû0…q�-Êê q™ÍfÂ13ä‚úq“ú¿âèj”U$kE™È~2]yïx 5hx‹�ªl> %îW@¥Âà�‡þÈaç4ˆî«SHŠx–�b›k˜óåyŽ€ªævw Ÿ”šáNíñhIÅI&O6µ€�â]Wù–^pN›©ÀpnNöú»ÕÂà"ö‘´× o)™»€3äëp‹G¼f€·(e˜s©QVÆs¹ö{÷,­¦’œO8¹…æSM.Ey Ã8ž§Gà^1<Ѓ=­Ë™—ë|Ù)¶çJÄâLà_}–äÜúû€ˆ¢œ‹Ããm+ –O”uBÉÎQkªZj�ï÷˜TÄlòÇóÿ!Ì7e½¶¾‚•FWÿ¦q꥾ ½qD´ÈSŠê²íÄÞ’°QzÄX©‹ZO�öJk-;º¹˜ÏÐmŒ*ìK,Âñ Ì¢šˆÞ}m¤œ#VÀ‚7pa÷W„íE‡T4;Ùs.yˆ�/þ׉®—…½Ðú�Ç? �'yØsVKñU�ùñw°]ÏÆðmÇOλFñømµî % à²J2<ñé¡Ìl§ÐéùhåáôJàÎOAžï� ®ÕrÀ�R�‡–³Ä¸¼†úã¼�B§s÷´‹@†¬cfmæ^·;¿Û% ~]¦Gn&×�þvΰ¾°¬[£=IIcVlÃݤŠ!)ºc“ضªnºmŸ°óAµ‰Ø�ÄKë †ÌÎ*1�qB€>T ‘ëÜå]zè`úi×M†ÈœÌý–xì3o^mÇìµtÅÞ¡¯O+øâ‹¶“—‡ÙU—ÀÐ/:¨¬{í )beHâ4‰,L¨µO7aØWEf÷ò%UUšRmv“ôºrW?õƒ¹Ç\HJi¸¶ökô$C™8žHvõå�:ˆ GÇ¡ê:`ÔüÊŸd/}k[Öxæìb• ­.kƒHìM5�ó4Í�e-Yl�Ujð^#XxÅ;¹Ri�æLX]Å8‹’T¯ªê5�9áØõ½JŸè|#k¿q‰7=1DIP‘¼Î ~8�Y½C ³2tÐ�Ø‚ž]ÂßÙw=“Z*~¢`Ú¥êi@ÖØ�`òî{Š­kÖÈļš¡÷<ýÞÅÂÛäèdŠá\>µ “|�úmaiW™i®$”a(^b8�Bô’eœÑqó¼QC˜È¼|÷q“”ßÖ¬æ3¦�V”{é·�ä…ôØE�-j#›Áü¾•­HŸ>à>ˆ`Œœ…Ä*÷âOÚÑC]þyžÇ,¹—Þ9ÿoVXIc(Œ “ÚˆOÏ‹Ä*.°:Ỷö�ëV<á*€¥¿\ÅdÏ€€„!P}+—™À!Ä×¾)Ɇ“ dq´€ÁËõ¯RyÚj­~aI+æ—+òÄÇ÷4Øæ×:Maû ÓéU¡¹WžðˆXâœËìYV­Lúr‡1¤)ÔK„w–>çÊþÎâKF&y"ã]Ɍޒ%Ïðè#nû01…@Ž¨Ë¸rpÊÂ2›fÅÌ\¯ÀŠ—.ÅÇÈ}™yÀî‚óÃ�žgóaèªÑ©—r‰M°†ÒBöî]sŸ)¬‚ëW ˆº�^ýŽc½ÒžN³:ð€éÑp¢Š­T‡ˆT¯® =Kvcµ³4Ê4p!ñ@r¹á7Õ“HF8�Áõ@ÙÐÚüÑPžÓØ·1»ÁK f!§N˜./æ"Ž×›qÅ"L1è†à~ •ZÎ/=±MIÈÐ`ø§ƒéHt¢ UpœMOÿŠì-Iº¥1 a]Ù3Ežæ�Iκð%¢Iw7ГÌ꽕.Ú2àýËõõ³š·†K…" Y…ÖžÁl’s~q·GßrWsû«l?)6¦.U²íMïH–Ã�äå·„»‰�²sŠd[«"n<¬¿äG$+»È„æ�ÚbŠàGB¾4œ²Ê£4ÖÓ­=eErâ~i*O³™&¤4ù"Ñž õM*IÖC|{þ/%±Y Ów']hÊÙä•òÃålËöSìóîJÂåí!Rš:+™¿Ë°ön#hr#Õ¦g�‰•�ò߯ëŠäê•›‹6ñÈvb~!:ÂBã’W{[(ì·|Æg>ÒäB[ùä6à|ô?Y�Ñ@OùÎT` ä"™mV€¼ü<2SŸ×.•\£‚�³�ºWÓ(Hºšt”uœi jI•“Â…“ÏŧœÜêIbÛp³ŒüË:ľÛ½yÀB=¯ˆ –3@}°S•RÂ^‘Iäõ£2þ-Ë/ÿÈ“œp2=A­©þ1�¾¥÷»úì¡õޏu„ ÖŽFØÑåWsÇ7¬m’¾¦uµƒPƒý²YŸ²K9XŽIkÑ}F^Aë[{½vÕQþÑ%(ê§Žï{5÷5Ôè#äÀ¹Öµñ®·�øÑ!?i¸-ÞâÑc2Îгû�k Á}^ÿK9‹ ã¬¾²�Òù6£ŽBý¢¯ÔÀõœÚGÌu¾äÎðS/ux?ÑQA—¤¾oH¤Z2½çµQGâ²bÝÔ@§ÐcÞàJ S"™\úÌ&ƒžW­5®bt`ŠÉÂ;jó˜¿Bÿ1"© rîì{õ—$dÓaÎÀ™P5¯ëëЪq•B¥�ÑáÑ9°4ßFry(Ê‹ùã… à.i$ÆP Ñ5Ÿ†~…²Á�ÿâO8b´]]ƒ lœœ¤Ö@r‰8TM(øÖÙ8pºìì)²xn©Òª2ÃJID®èéBàí%c+Ö—˜IÇ>ûJIv‘î� <i¾«õAÛ/`ªëMÏ©° zx€K¢™¿5HÝòô—œðùß­¸Ñú_½¬ Ê-ø0ÿÂ0‹3¾p´E|‹ìÜ•Ù7Â$ŸNˆ¨Ê„Ïu± 1ä~ñOk5roß;ô—0ß§Gs1U©Qfh^˜JcHR{‰�",Ó4aIŒ­ð©¸sr#*A/‚ûM¡ @! $%ó2ðʆ¯Üò]‹È_–`€:“q�…üd”ö!ë2òÞ,`ôd)孾׃ºÐ¯+zŠÃf›-¨gø^n€v±ëw< ñ¿]�£÷ÐcFÅyZPýÞ7ÎÌÛNjUQIŽL7 .Vx¾SŽ¢]àŠxE[�`"§ï•´ À†§¶|3aL;]@ Ï‚\ãÍQœW ÍTÚò(©ÑÐ<"Ó*m‚-û%u>Ýþ-ÍHà±�Áø –gß߈]æ”%§·àJ~ èጠ^,>ןݓð¾1µ·7õô|�¦ñÅÜæöZoF‰šÄu O�¥~2²T”O!J8Ù½€ æX´ ù±–Ö£— ðß%H„Û¯ÆX²ð;½ÆÔ9Ô æ2‘e³uŠ”Yw#N[ØzÍúd  ç<$”ô9Äë±þ¦“¢S{m–ú�{à‰Óð”�‰øÍöw& è'%Ï%ñAníòà£�àºPED¾=zN:o/Ы×JÈPðrTha]A(o ¬Yº"w'£ÆÕ.v4ެU{а�—Žþ2^rM¶\qì©*µ©ä#~Åä§Ø¶Ñ\ž:>§6‚m$í}9'I~[t5ìu� r‰5zÛ÷AÄõÙ¯4ñá´.ƒŒ¿°Dlí³|Û&{¯dE�öÏèuC�dãINâýج •}:¼4Z�2Èò»Ø+¨áX½Nê|µúÚ_0±¦A*�7ê^cXÅ!_±‰QÌ4%Ï›¬H·�Ý“õ¤òVQÞ{_Ò!öŽ…¨Îl~#©0†Âˆˆ_‘b$ Œ*Oœ9æˆf÷ónÎÅö·�¦@Ðÿ ÓzaˆiJëÞNPUŸídÞGYcäróD‡n‘óÛ�<Ïÿ=4â®kHQÍJŸù©*³e¿R-�•V\ €hŸƵ-Ý áQÿOƒ÷r;�,íˆ,�ˆ}U³�Â2ÃøÌ±‰qÆs§8êÚõ F"ìˆ[¶b52 þΆ¨ÎS“Grþz~þm<£/ƒ1�PU|Zš•0†Oß#�œv¢à­•½¤çUM‰¾ôùúñ*)®:“"rföPI´€£ŠÜØjËZÕnC?C¢\RÍoraÙ5bÈI( @›hˇ]¼X³:?yP) Ò…)!ˆc 3#™4«2Rz„ ³3CâümóD‰¨�|²Gs+æÒÆ'À’½Ð ë•É©ç¤T Ï´Tºo",é~#C WÎë5-v¤‡@Z Ïh„:ž¹¦H;Iœ<‚ì?©†ª¦Ý1b*‡ú,F>øßÛkP?þÄIÆ�רÒI=ÓÀ¦DR�ºÙyø ¬nòqሉ•»a᷀䗸}•D¶X{«ôÕ?P¯q1(n8Ì»ÒY›Ú—}ùDµ½ïƒ@1³Êé,%ŒÜ½gGEi.YçÐ`%·”…IÛ™Û"D&礧Ð×&xÃȹ¬Q¥¦Ç¥²-Vxץ߯­ü8¿1wêHËSh•u2ö‹˜ħ{^wœX˜Çj ¿¿$æzL.b& ½”Q«Ä<›t°l^;}€¶ÿ^œÉe£ö8£Ÿµ‰»'ätê#„ÓØ&OFà mà;§N£ß/ÇØnÐʰýJ²)8/é"™�dX„@ð‹~ÃÏJ�|ñѤ-˜Â�ßû èïâuékLÒà�Àžˆòü¢ÎÔ œ€ÇØ�Áóå Cïbövødf6�]pؽ܂CØ–nÐxÁÌojÔõJì)·­`СãYñ´\Ü{à[Wä j\ñÕ¸x"µÝ�¢Næ¸!Œ¹àJx;3¦�C{nI6yÖÃÆHÐ{”â�{¸°“„è›%Á­Ôw ¨H…Ô`Æž>ùyÁ�×ôÁ¡ƒ²ZÙ2$³À즊bý¸ ôUZvÿo¢  #ìÞ{–t¥Í·ÞŽ{>dJ™I¸&issÅ!h%æ…•s·?Í¿a„µÉXÓÆ†®RQ—küò¥,›ñ"Ä6HÅl-JV©î¨†TEÎÎP¨�8ÎÊ @/Öô ¼1#÷a µrÅ„ž6Øx+Åu$ÿ˜3@@µ«†.â¡×4ž}´ /‚°·'WÊ(œ¨u;0(?ª=²N#¯¸2i¯K�ŠÎäLÉÅ_R«þ€–Æ�}¹Úï$ûÃÚR˜µ§‰ê$±ù¬Ý€W¤+ kwŽïÇqK,�¯³ˆê…Á»lôjÐw½ó�¤2V )u«ñY÷X(œJ#%¸1†c[Z‘|Ïi¦ú*­çÿ…Àf¡º�c«Aâí=°FYïoHÃÒþ‰?-yMŽ@ˆŒ.'Nªt­G{½ÕÊŽÇLÐ1æÐP�&€*‹Õô¬×a8îPµEÃBñ�œ?ï£ÑTdô:U�u`¤½±Ñ£O¡esáë~³ÿ?šÚ„ÝX'W#ó|øÈ¯fY]/zêno$r^,•ïäÏ Ÿ¬qEštn>²¦�8bJd 25„·ª®8ÌÐçº@]Á>Ö¢Þ+#nP|Vö~ƒ¦tbÜæ„΃w½g¸P3D)§ã5Ëœ›o(Ãñ„M�áš Æ2èˆF ¨­ôKó Înu¶ZiÕ*·Ï#zÅç²6.£‘^—×FTmÕÔ'b\Ã…]Hå2B|½¤¡é=¨nAá3œ”kÄæ7ª.Ã'ifÇTRÔ,•㊕׻ gu—áÂ@2ÍZžoòï&ªGFÓÍåÄfyN€çÖS®ŒÓ¾WGaÀY¸#Ó¡J£×gy´�Nä´á‚Š|¦Oåë†gûÛct¬œg0óÑuð˜Qûº¶_iŽ eý)J¬¨õJž¸P^a㌡Z¨]Ú¤”D[~´ø¦Ãkf³qG˜ëzU–YâkEƒR£RÖ4� ô®>ÚÉêIÛ s@n¬¡ç6J¤vû,Œ1g'•­C›bû<��Þ%š6ùÉhãÏ,Á¶Å#]¯8¨®}¾º�–ß¹µÈ7_à‰H\"#!±QÀf�Y¸F-B14h‡<Ø �‰3E(�/¯ëV®6FÎ}Ìߢ›Õ¹Ý¦ŠŸ}µÆü�vSƒñ?Õ¤�>®þ÷ïÓ³à”fžˆsËØ8G[V;ƒ;rø=BV1—µ?ë½gJp0€«!*ÿõí¼o„•hŸÏÏç;høÅ­~a{�L^ûøh˜_A§R­R…­@ï±k~A|štçv |+«SAWÅnV†í¹Î¼ÒX)´ýk“K‚`2õOQå~ôÂ¥©7Ð]Ñ’#§êdºÊÅÓ¼(þ ”ó•- *#¤â“U– ºq Á,YÚ¡âpl¢KÈ=l8ÎY“|žà�]Ò�¹/ÅNGyû7ñ®5 ºªxÜ 0ÊÓ S1ìË�ðÚøÎ�ökAA˜Åaq†xPK~†QsÒ¢C¸!”øcnjº&iïþ‚£$þ/ÌªÌ ÆðÊjL‹0ƒàÇaLÏá+J¿ïä)|j”Pè‰ÒÄ?]Ê‹ÀCˆf]ûØkàÛMXµy^)ðÞÏþdyè’Ï�Ê…'lKùcὌÕköüÖw-ü–FÝˉ悮ˆ) è#¥~bH­Šäb[�*4¢­;µà¢yþ¸äÏgWP•Îԟܺ¤jW¿Î“—!±;¿lë ÈÑ$B›B‹Ë#x Àr~ɹ'Oä׎‹0tÎF©Í;ÓW›àÈ&��ÚŸè2ÐèÍzÛô*Q(ß¿°â€|˜ÅÓR}úGåª1�£�-a͋ݢ'5êõÉñyõP¯ ³„`è3v¢šã{+ŸvYÎ+Zwwî�ÆÆ¶ÿ=ã]}ä•çxuf o –ãî'{-1ï[j=Ô:æ™ÒÕ‰Â�P—§¥ÿòz ÀF&z[9t¬\hP;ovšÛy×4M Qéõ*ä‹ÖòŽöȉ͸’÷˜o3—©ÁdÃÛ¸J…“ �Z]œ"ÞK½Ø(‚¿}OïépeØäcQ£+¨ž¹TÛ•ŒŠ‘kª|¡8¾WP[°%ؾµÒ¼ºqÝNM,°L„ô` ëSùiS<êð@l’¾ ëàÍ@¸±@xõ^¥êhÇ© HiÇ…´\lm^õ§¾¨WX¿±å MW3sRŸ®–x5p• z –‰“Äqè/++2ràX“ ¬"#â³Èv¼ÝiœyhNÜ”Éòë67 «IŠÜdÑæÑ¨§°ZC:�e,ÜÂ}Ö™šùgwˆÎ­#Èv|â+êk+P\æ¨F‡›¦ÑñÛÐ /Î3’ÂŒ>¯êÈUßé醔JïLÞ"áÆÄCJ'›¾ò�Á +îÍ¥hÊD VE;ˆÃ_ˆ—ù1Z—o"TQ+Òú¡+­áË'¸‹šÎwâð,Ýê^œÐjbXÙyòÒœƒ›)Ë\TŽã©æ0»û °û÷’_y�êîõ!šòã©‚ êÄèNÓÅ2eV]ÏOcÆÍk¬ûñõ`R$-¼*ßoU_‹cŽÞ&ú¡y\ž‚êˆ�ÊD5H,Dp)l핾0‹[Ti-Ö»·‘‰àïĶxyÛ w~•L¢B;:ˆdŽQ–/sF‹Öüì…*™­ÓhgfÁÃøY7À[Âu¸W™UY þi=3Ü áV›§îF°�NxlrÜÌ›v>ꇼëq ÚÙ‚ëΨ�Î8Úw²¢¬Ý-AQ4>küŠ°ÞØZ)¬`5q(pe?Ö�„’xK/‘*(ˆk]†ïì�T8™GÆŒþ¬pÛ¿8lj³€R­–<†á6Ç>HΨg'Üî(� Üw,Cê‘gžªc�y�]Š7uH®<¿‘ZàRÖè±È‘É]¼·¸i�³¤âKй9“uOM€Eœ àf¢®JìZ'{¸§S�¼'è¦ußÛ·‡µ�üpm0q%KML‰KªÉ7ˬۇ�ô}Üf¹óä±· ÿZ Ùe7³_ `àèX•¶W‰uãÚ9•y2¹òàr Ó,‚¥bûûo�ï’Û]äÚ­�üIB;\N°«�¿�ó=Íå‚„§•1wûR‰~ÉÞ¿°èK§CÀœÆDxÏ¡8Û\ ô"àÚK挬Ü7Á’µ¹ x¢6Á%|Só-Ž‘cå%ZAÊ�ƒü˜™ ^’“¼ö>3˜¯5ú]MŸµ›�/ÏuY?dGíÈ�Å‹#A æ³w™ e�åóâ<Ž~-Ñš<€õ ™È”[ÏhÊk 0í—ˆԗwEQ ÐJü/9®IéÓÍVJúŸNVy»‰>~ø¹¨%œ™KçÏ!pƲ‰tRº*‘›ò±S£ÎWN´×öÀö)²'ª³˜¨)Ì2q›T tن曣K?á骩c+Mîè\‚+ FÉM»O¶/D¯ñr«Ÿvj78{A2áˆ�]·U$7ïíñ[îÇlØÂú§4œ´îÄ€?kXt\!'u[¢6(ÙֱЦ¯ j¡—gp¡<_ªi|ò¬ñ3jFer §>m¦�Æ÷Ì�럷™ûû£²X¶×4�I�–.t||` #³ò_+Ãfìû!Çú­NºÅk«A:äâ]Ë““ÌœøÙ!£TZ YŸóŒ×îþí-¾!ÈöUÁ-Ä ¤P·3îqÑØ¡JF¸‚�}:“[ÊñÅ^¾¿›èsYÔGËбŸ‹@Jaåät?`Ðú `\’uö(EÕ¦wÃò#”ì­%R œ8rpÆó}5ÿlPò¢kÖûІÄ}̃ ÷ZÉÉCü'©‚��û‹zcIU œR<Ü©µñɵdË»(�%ßTË…¾ž'–šúéÛÌdws½m�–ìį™ÀkË&#§À5 @2nŽÕ†‰;ÉŒ\xq±¼›âóðì1ú‘mBK€D w˜R­%Æ“ÁÊ2}v­‹W- x÷ü¬ØzEÅ;d<Ÿ(b/ظš¢=Ñþ##bßÖ?!¤¦±ÕYä…Àç²(õ{‹±ÞÍuPš_¢)Ò“¢)ó»õÕ_{¿òÐúg:Po¯:ñÎ�ݬeܰ›pK½bøì•¡Ùa§RÐìD—ð�2G‘xss7-Š1ÿ*Ä«)Ìd¸¸SSbq"aD¤_b\€�fç5ò´¾ § ¾ˆVE:zfŽqµp¥,χCécÒ×�œÏüåG÷X;Ž{εEë wvKO'ÅÇ¢i¥ÿ•覉ÃÑ€©\Ò‰-®K#ÅŠ¾—'„ Oœ"a�ªÞ˜ê�Ä#NžˆF[ØR®l³€Á¦Äoy‡¤C¯«„€Ã½K‰ÁW÷Ú¶f‡µF… µ�UJ‘³ÙAÙÃÛÅœx¢çÕ|#AçNÅ8|„ÞW@ÍÕf'|²T] ðàÌÐ Lž'�Žõ$y_8dbêG ºß œM&¥»eÂP:NœK�$›´Ð¿W5ñáÛ5*Ï®-x–„[3™iDØý¬S¹”Ž  ó¥7_D¾’�Ÿÿ¼dÍ› Íi¥j¦¢aDsÛñ©Îæ84bnÄ1bu¹¾ö\øe¼²§Sü()ñ,ß2lŽá°YlwY$ˆÒW·+°šûí4¥²s¢Ÿÿñ¾o‚r/¢q”LIîð´˜ÏúØvÜŠÆü …üj¨üø£Y Åêü©³¼É1rù „´Œ§O-ùœüBŒ¿÷‘äû„U’Ÿ˜H½Rð2®à×�/ý„‡B¥ 2ÏïbÐ1ƒ™#¦G4¤¤ÕIªÄ0�C‹¥€­�YÈ5ýáp^ÿµÞ?ÊÚùÃ�¯ù‡Èp€×àÛ&ÄžØ×TÖIa Ý pàP �+_2Ôwi. ¢�üãbêœ>•­ûâ'÷ÁL|ös÷Df+n>’,THXÉJ.Ó*¢Òè%�x7Q–€¾pØ»:{”Ö†�`¹V wÔûèw±•fŒ¦´’)@Û(­D^…Úõ0å[Ÿì7D{ 7BÂokÓ$&}$ Ñ…i4 ½ˆ9æËà‹Ÿ`nþ~[Ú),À_39>Á�É~NQ¹[ñ>ùÎ�ü¤hÙAÀø|2Æ�ÇÞé? 9Ø4êßaÄ÷7®¤7™î¨Q7Zäi¥îÕ‘[EÎ!&ÓuZ¨5 cŽÃ–4L�xåÿŸÔ,ÐÅ7)¨¦Š¦/wOo°Š\¥bw´¨V§5'�Å1´.ãr‚‰lŠ-§Už¦ü>‹7ž{ |{÷·—=£û±Kô{ql ®k=�µ1£CÏÌÆ´KÒ‡£M‘t—ü˜§ƒò9R~ÏIŸ²)ضC¢VîÎr󌖼 _AVÜkÇ83Á� •®A‡Å•0Œa$§?×ÜT+jBÃÓ7ÅQL®®©`ˆHiQha•º”TÑæu@i³¥ß2Ê`ª¿G‰ ÿz KÄP‘IÍÞ QÞ)AÒ÷Ò.©¼¬®÷Š7!|—Tn°jÁí¤lþU9éÜÊÜ× p*h“êz'ópNZ…/¡ŽáèË8:Æö¯£7s›µ]QÊÄš^Ç’¸àz¶•Æjì.�ƒÚ°.I¨ö÷á¢*Õy¡%½·ÇÖ f°Ö>z­Âà‹Zîè*@ãßæ>åÅv*EêÚ½ 8ùñ‰HÑ™çv…¤a(â®dü #�kê½J@?.GÍq3QÑÙ;¸O!£ø¼ÖôÒÃÇÆœ>(µÅ¨ïífXûùvâ3Þïç%‘ïbdz¶.ù¡ß¼|¬X3D¡1°R}†çb°~?#Cì¹c∟æ>ONÙOe}4DKh¡/d.aÕd¦�Ù7{ì°ê2î,úO-âï›­¨•ý†0gï7j r_ƒ—ì™Aùü‚ó¤{Ò˜{r]r›æ¼'â ŒÅxˆÌ?¨—ú2 Ú‡�%ÂFÈuöLÄ¡·¢ˆ¾cu‘*‡põ³îcðÍÚfïãÆãx1{›žÿðtÀ$%ÒAûò¦ßm¼,B«Llºä�=¬¯±t”£rtÎY|á¾Ýþ+S§<Òy·»¸ÑjY®ñÛ7v³ß†ž*LÛÚF”ŸlHÉl sw\€Oªz’|‰ktÊ÷º¢}i(ú×JB9B>œ5.„®溧<µØ^q\Ò�pÀ­“[ìеiÓ³(Û‘&ùKµô�ió²”1F["íÇ4 v‹ rN&ïñfÒj”`7ÞöŠéÂ. ›½Ï”¥Tº�å}ì#=DõxErØÓJññ¡©¦p„ˆLd•<Î`/PÖšÐ/Û}ˆujöάóLõ�Õú­-å�o9IV0á)ønÝJmKfá€ùâþ½ H¨KΘÑ0.þãG@ü¿ÂáÖûcæ´av)–‚õ«1:!±H™^oØ8¯Ê *"^в 9ñ±*åÄ¡þÑí›ÉÛ�öU»a+;4æ¦í¾ZÔþ†5…dõüÆ„N–wé·üÜ9ŒÖ òâE#HÃ$ …T ÍŸ·wDÛTù1ï Æ áâB,ÒÒÎx?¨Ñ8ë¶ÿfàŽ¿†Î R>–Qðê°y¡Ëú–ê…Ęh�"~ÿ++Ì5¬÷«¤tÙ{ ‡Em¥ l]z¤ª@YLŠÓÒ^W{ü\_Õåé„ r Œuw €3ì¤~™èÑÎQÿ‹Ža�¢SùùÄ$ët§·ˆ™'†�Ü?š6ÄþÝ…º×Äc°u`°R÷w±°m,ý ÷ÙƒDo³¶²bpËC–ü)zâž°z~å À}Ž÷æá–ñ(Nm‚ ’Ê'¾SÂ7LL£IÌüŠÚÙG™píúBOßÄÁoxv5t4ªÉ4þÓµ_Á:ÃË¢�¬Jm11½÷­ÙVç¯ùN’tæ‹g¼ab÷õëhdã (g“Ê÷ª,“¬ñ´ÞN3pÒ©�‘8"þ¢“—Œç¡â5èC�êEbC>ØØ}•h®-U÷´‰žx;Á…µÉ¿¯¡wä#÷ëËÂXm~|øx㎠g̺­Ï$�¡Ô´6j´ùcS0ÎU‰ÙíêQ´o ó¥ÇÈà;C~6Níª :S-(• âǸY,p/˜^Ü{ Š®…!û*…Îx+¹äU¿È"¦¨á‡ƒ‘ñ¿’êc«';ØÊŒN‘�T0²MUís…ïò€ò‡�ÕU°&Æ'É«™ˆÃÀ=“·àsÄv¢Ë(íw«àâî;À£¤¬¹WHÿpÎøZßzdØý‹„¼<„œlÖÕy®íÖFUµ¥¤¯ŸÀ ªTN‘gXù¼·j ¥±8HDÓetCi£1‚!ïïóµßlØr2Þ¹Y½Ê'ãD o*û­Ì�n[BéÂuÒ\¥‰Á 6º`‚<Ýj'2ûo¢uĤ2w„…k²þœðbŒöò±xŠŠµ3=!Ï’¹2À襷§šm&Ô^.õf“,»±Ûµv¿e*èùqr/ð"ÙâÁ“´þl}bò�éàAMy†.Ò‰—çc)à|#Ìîg"ekœ uéÌhŒWDk˃¡6Áð“ôJm ð;wú�!GŒƒœ�J¤l–�ºý]ˆSÌÔù@À‰>!šj2�¹¦Rñ ¡\ØwýGR§?fQ.é�†©†Ä�;¿Sk¨½ûïð&\|îu)fšªùO®¦Ñu„A`8��•Ô $Ò}’§P¢a£á½«iÝ#Hs6ΩlJÿDSt!—sWÜPF¶`ÍȽˎ·  K‡%û]3<ד‘àÈıt&åj Ř]ïD̤k~¹)ÇV¦uÁÕN†Ù£E7ÿ§‡Ço�a|MX±Cú¯É1x›Óüܦôcz ÜÁµ z1õžOE€êôAžû°4Ëy™9:§XxØÓL`h À¡ÜÓ|úD^�­»@Â@B�Gìã¹%‚{®3˜6:žÁÉéll™Ä³ž7ÆGÄ øžái‰¢]â™jEY¼û×ú•Cù¼C3ö§ú’Ðļ½ÖÀ|+­t=§ªòt=W'a§°â‘:rùކhMü7kVñ®ß‡F]Š~d�ì•ÈB¿{+Î’¾R}Žå—Ijguêý§ôk«˜n;ô7=«Œ÷r…Ëùð‘§‚T½Q¾<¾(—¦z–#ó6¶èÌq¿™Ûv·�c8 O7G*qGpô-K÷DùÄqa[3œcºÖc 164aÛY}ùi”3è+Þ™‡‚áË[ íƒo!�š³Ý'Ìvò —§Z8CŸ(ôŠNèøeÓ]»Š<�|¾PðO¤Ý‘2.à"£3šýû m@[¡`B¿Å%À•01;8òÇÚ±Ø|ܾÐV‰–Q2á 6ÅZxÃ$‹¨`TT±V &ìZùjJ‘œØ½Ä †’|Â*Ö”£àT¸ôg’{н�@­XoPî³Í»°p¢´Ot“›Šn%?tß»ÜL$rÒœ-ôb�bˆ2GK®H>ArVÝÖoÔ*G¸µð3X©Ü…½¢Õ•àZˆ zn 84‘²‰é�Ô1h²`%Å:Ç ƒ6\JV‹M°Sbà©;mß]Ÿç“'‹y�Þr(F� :IœÕ/[øÙgÔ+[êx(èAË?œˆ™y9%ÂoÔz¶âàóîÈ ÎÀÓÒ/ÀL³[ø8½×ýZ?&¢]Ts«=ëåDµ=OaöKýe`X²Þz7õ¸Û¯†$ç³äÿÛo8%/@L^’9º |ùà!ÃuyÏ-VÊHy’6ÒÝŸž„IÁkÜäü�½�C‘ñ�È‚l#Q³i‘)øÂ€�Cþ[ê¿7Ô‚³°Í:ªå�ÔÛòÛHŠ–Â5­�‰ÊÆNºK¸ìï -w™HØt5²ñLz¢_W|{E€£Öªhbg=k.&ZöRdž@F³ >âÅ:¯Óïœ/ÿeMüŠ:b½çvª4@YRF™½6NmwEH$³Œ¦ÕZ1Í‘qúrOýþ£ùÜ2´±â®ü‚-oÜ|Ù)œ•’J·rìB óg¦õ^†¡,ušõvS^ˆ_€¦uü@ThÓA+.§èWYÇôÂTÔÙÃ>ô�äY§ÔÇta*“½\ñ”8ì–©òn²'!eetremotesf-2.10.1/src/test-torrents/bittorrent-v2-hybrid-test.torrent000066400000000000000000002626751523411710200256410ustar00rootroot00000000000000d10:created by10:libtorrent13:creation datei1591173906e4:infod9:file treed42:Darkroom (Stellar, 1994, Amiga ECS) HQ.mp4d0:d6:lengthi6535405e11:pieces root32:Ž1Óž%Æ3ghéx&ȩø }‹„fD(ÌÆee28:Spaceballs-StateOfTheArt.avid0:d4:attr1:x6:lengthi20506624e11:pieces root32:$ÉÄë&=UÝ„í,ü6o`©”éÈ Ö—œ‚.ÔQ®ee51:cncd_fairlight-ceasefire_(all_falls_down)-1080p.mp4d0:d6:lengthi342230630e11:pieces root32:i}›SÃa…†~]ñ]º*~�ýÚlšíꪃ¡ÖD‰‘šee12:eld-dust.mkvd0:d6:lengthi61638604e11:pieces root32:Ç©jÁ ¼2ƒò�aÄçXÿ€:³¥ÁšÒ@µšee50:fairlight_cncd-agenda_circling_forth-1080p30lq.mp4d0:d6:lengthi277889766e11:pieces root32:ã kã6Á853»¢Ð |¥ûÅÁ'ûg¾‹ü8ì™ree42:meet the deadline - Still _ Evoke 2014.mp4d0:d6:lengthi44577773e11:pieces root32:Ï©OGyjéθÃn—HCPC’’ºK"ù7×-Uõüee10:readme.txtd0:d4:attr1:x6:lengthi61e11:pieces root32:;ë/°Z³5Ÿl'Ê-nf0óiòœ�_ᘃLÆz•ÈjRjwvéoÉ¥0MíÎ’C¬J¸lD/8ÔäÄ€håm͈qâÑceh�¾gÂ>Êö5c(éG!¬�L@ÌD¸üCœˆ±ýs¨-å}4¥YIÀ= Á¬áÌÔ }˜ ÿ@Í£LŠææ™»‡…ûÅ)Qè„ TåŒH1ÏýµÞñw‘ÍÂÒ,¹þÆeÒž¶,î€åÇ´¢©ê¾ãGÕî6‚nèÒUHÝ�Ð ØA®Ð-é <´'^2� cDq…õ1enÃqjØÕÛ2Z&îq…×X Å[Äì@ûàzL0C�ßk°™ñrxìoé¤dƒ÷ïh-Àâ‚ñôÊ^KŠùPº³1ÛNVç_¶ú‹Wg¶òcZÍ«žTâ 4‹íµ“Õ'öF#‰Í�G–ÜŠ¬�Üî´¶Or÷‘J'ýë±gŽ!’‹ðÈäº-®]Õ��ar› ´}(ò0*͸ÕÝ¥sþI’§Çì¹ä (Fõ…홦µÍà«ksÚ'4hÂǪ0"ž›^S÷Ò�¹ïw‹…žÒ¡y7%ÎîÓ§¾¤¸”­òEýž–�lNùܾK'òÂŒyÉÔ2ϲ• ·J¯îõÀz/<»þ–Û½AáÄ[SÛYµ�à„A�[ýÿQ«< èÀ€ˆ�)qW÷.çqü}Þ£¡`ÙÀ¯¤Ù7Q¯‰ŠœfiAâÈ;× XÝüáàí—* ÕjŸÐ©üj%N_ =N`œW1fwÐjcéD«mz‡{µ²:i¨y Ñê¨ÿÊXõ+ �Ñ›4Ô¦ØÉ9°¬!zøßVäËß"¤¶f%Í¥é4€òõ þx¤7äÙaÙò¹ 3ÅsD¤‚øP•°h×ü¸É·ýfá, oŸñ=9¥¡•äÕ­5"}/01h0ëLãØ4³4ó´TžòNP‘ŠîÇÛ¿²HÝÉÀ‡ÔÖ’éT‡g'̤C~†#& EWMÜòÇÆ7cz¡`;vÿш­ |[Ô~F³P´œçCûD˜ô–»‰„:º›s7ø‡%ƒ+0è®ö«T°WóÙU8õªO±ÁKk8e §}!Ä”�ŒÅy‰FXQžõÇ•™š‘Th©KÔ›¶î#æI<=° ´Ìå;0+WÇÈG¡²Òt 4 ±áŒ#ƒD©ÉÃ)ŸT×ã{[’ŽFÄG¤vˆq¸b´áÔª’«÷Ó ýÎ Âbi¾XƒàŒX%üz€~#(ê{Y¸%²¥)ûN†béÇÈ O'Gñn.ƒçNû¢ŽirâÈÛ7<7î M>°BŠWK~á¦ÉÐw'Ú`Ïr¨»è0ÀAîê ¦íû_«¨¾ú—kÆpÛÑÓ[*�ª°p?–¨|9±U1yáóáÔŠD+ÿœq 0&{ÿò¨wY…9ä¦u)âý½Î³½Fû™“b‹g`âÝúw„ºáÃ}?clÜé_«2" ü§@zвÇëÚ“‘w<·3÷Ž!èžI®`#RíÏ­v¨waXolà+¢ƒ#qp›ø›‡:QY¾€·ª¼Â˜«²?j"�½»šQR.š½¿V‹/ÛˆM,‰ª€2ch¦-ÂÎ"oEˆ®�m1¶O)Ê�ç“ù÷÷¦ÐFÿ ìŸsx×QªÈ�…Är¨Í¢y†NoÞ@ìãµ°Šw-ho4¹å8½Ùê-HY z¥A¼‡³¢>a¡ÎP?Á÷Y•âîü˜„,)ìõdÖ!‰”>û×w°ÑØò«¿[Ü–<Ñ%ÿײ=NDÆl¶á-ÎØõÓ.µžòo³›•ZÀD�™XýAºn·Ê¸ícàUÚÐìOE�Sϱ}ü=Î^�<´ŒYÁS3¢IþN}–T„ˆDÕ �¼o]P$¨) …2–ùD2è/:`?/»Áý¢¨¯ëu?22É.3@èmÉÖ¨�t@?ÃÞQü»î½ÍûÝ®�ËN¨¯Bœ¡¹Þû™E`CݹƒAvd_ÛÇkÕ2�Œ\à¨A¶t &QaŠÕ.8‚«¥Ù<�›ƒÈ˜øîÚžè£beó \ö]OôªäåýçÚlL;Oö Ú.îÒIMÜÆÄE¸h�N ³‰ó²’ AOÖ©ëæ_`Ö5çÉxµRtàÌ~vÄ�aáñ6n”¡ø›wþ�°_]¾ÜÁbrVÜ¿Y ªÀRþ!1#ü×ZJ‘…á.u,¥¶“@žæ °�ó-Ìbb­¬évNíÛ^\#×´ILVZ„¾ò=²’轚? f‘0ƒhÕ>JÆÐ­…Ë™*Ç!Ç£�Ãí�°“ÎÊØ)ÏPÀ6w�¾;7×\´©™´ÑF)ãë-xî—v‡jSYíõ…=üów™Fa8© ¢þ`y)OЇ‡sL\ëþ"Ž8ñï¶ýÆ!Ojp§ÂùZòèàô‚ÛZTý™ÿ/V´Ë™R¢ò–*˜m/ “�ƒäÀrûÝàF�˜XÈ •¨õ4Ô½TQ^�ðò@«Þ>ø¿Dé³#(&�"™‘âäQ?BÉ#ùh޵‚޽· mô–�DI_\÷¾L ­ÉeM½|°ßà\….æ! ‡_) –¨ªœÇ¿«gÙ|·K«SMË,_^KøÃº3æà‚0eöÙ¹ÖžPy�Äæ#!nö–T&Â#«E�z“"¼z’ÒêèÃ×¥ Ìœ'�?ú×C¤Ž'÷]ù¢ÜtyV–�h<íxCgCÞùíŠ^4"Øâó$×ñó_µä:5ešž�Zå‚S]¯ØÂLh×ͱ ó©@ÁExpkev;ÉT÷!ÚÛHèü„ÖÞ¯�†‡¾=B¤ÛÅá·É ¸=Œ×Ð̨¾ýõÃÝÐuò$—ÉÊãHàJ&³]++Tã½8þ� Dy¾RóÏ 7·p:ȶbçc NVÔ;^ê�ØvsŒë¤üXêmØIY[ÛâÉŸ6Ú¢¸zZ·>t �ÝlÃKϧþoDÊQÇÆøÛY±¤�·Læuàœ9oµi*åª�Ö1‚–wHxôø#Žÿ‡. ä›.çÎãØc+;´q‹ÿtÖ&9´ŠF(n„ŒP økšÎ´&a䝯›æWy-Ñ> ú()ú´µ€+›f23“n~!?³Jpèl‚­™CŠ?á&iÊY/?PЦ¤ƒ@Y§›ø!}9!O¸Œn#Ê®º,YåÌÖÌ�eè9øÓ¯'µœùVá¾`2[#è줦øg©'Bæ3œˆœl7?KþJeWÀ×7ÿÈûÇÓyNg°ØKå>DMMüÂEq§ß™QKÜ¥Øòƒ›#Þ‡N^áš2ˆÜYïœ9¬wP ]ëj½wÝS@íÕ„Žù:Ä^¯J¹¹í©ÿÛÁe|,îªn%.ø®½²4MÏ`5ÈeúòýÍ’4:3„ŒŽ›ç!çÍ'£œ‹¡9}‚´Äø‹BKŒ¼àút$T$nŒ!ÆRñLsE—‚@ŠÍi„ùÿ‘G ×ãÜ‚•eÙyõ»¢¸Ø7q‘Ç}É$õÜ@ìTê0Â{»e™Œíï= Û¼v®4ô×£ÕÆ½µé<»¼zÜ$*“¬=Z3 ë;«¹p"HÑìh· ‚»_®ˆ.�Ž˜¢ó*Þz‘R_±à€ÇSÜÏ­O žÅçµèŸuèÕ* •­Ÿxº!K»7hÒ|‘‡²>ø›DËþžO�ŽzH ø¸äœ½™§’ÓXWj¥¼{â8ð‡u5QÂî“–(SKT½Þ>k »Ìà•`Ox§îµ½ùCʱ6x'²ÃQKv~…ZgNè8¶ É†àÊ(à•Y´r >$K�Àêí>¡]ÉD &I㻲(Ir·ë€Á�C0T½xÔÒ+C Ë÷í¯ÜˆàK«k' �(•Ë(ÚÀ°à ú'RFÌ-ãÈ�"²N”dØ�öÈÉb½œ:MîH>¬°’³3Ø M¹Í_hÃ_ó~x­Ti>Aç–U¸95`¿üYoV@¡uò¸È|óì>`ô¹‹jýÇšìÖˆ?ïwÜÛÖ2e,ß¡01–¢¶åÙHÿå î‹d¦’ Á‹¹m%Î{ÝËì—^d)¼ñ¿«DbKÓÞ�÷j†jˆx_èÕ1Äeå[‘¯Ç}e»g+HMÂTçöpöƒkÔç4éðH ÷ÁZä×Õl01wKýùÔJñ} ‡�†ã̤%Ò¥WEù3 ê³V·w´Ö2�Þœ’ú êyqLkV�‰Ù®4Õ‰f¼Æl*F¶[G9µ‡0¬V˜ä3³üêUjÉVo O ³Á Þ{Ì5†ÓDÓ~¤Î›X·)£dÿI-ШŠY)†‚|7²¹'@åʰÑ�µ]¼D¶y¢;pt„{bÃáݸ,çºßsš�qªÎ6Ÿ ð�½]HL˃E– u²+ŠŽ=‚~-Zb‘Ü;CÕØ†\�¸ÆÁƒ týÔNìÛõn+ÒH¨“P|1üë¯~â�rSëe!9‚]œ3�“²²|­’~dº* tߨ&;rsÞ^®‘.\u!Ibü‚58¬û98¿nÑn½ØÏÔaŽôêû«8¬®Oßë­�YøU8{ëWÑYµÃ‡ÐŽ‘yYRCÀYÑüÛrùÅŸ¸µ!>ÊÍä>|zI�Mä |ÌVʽ^ Îÿu μª¿Q²)ð›ÚŽÂ°1¬x3¸—aMðåDŽÁå7pÖ’bOGö uc×.Ûëjöuôé&„\׻ܢ(Ê«-ß¶à wª?{Û<‹_¦".ƒ˜q}_ {4h!~Vz¥ÕC¸ûÊežk¬‰“š\ìÌrKi쯋^üëÉÒäíÔÃ7¯7û �B§ü»‡ÝV͸ÝêFÄÿŸK(25vîÚ»hõȼêÄ¡?9Çæw­ßQu§¶Ÿ�(à³ =¼uÁÁ¥Um«)ÝàƒÁQòͼ”ª2€É[çûا$­:u|øµH†Y}Ñ,Ýì€ü F+Ç1 ¬¨ª`ØjˆvŒð8¬ä’æÀËÃé$™�N%²ÂÑÑ|`âjÌ"¶Ø°‰ÕP·§s¦B®ô+v˜ =Ê�ã:�Ev‘ºÐ^eä‰6Gÿsר(:6åPìÕüè¿¢N­cËÿùuYüîÀ L§5˜`ë岃~pþx™‚|¾ÔïM´ «©(–¨?¡(4Á‹ŸÃžpýáe‰æCÒT\}Áý—¥rçÒ–)Äh¿SÄ¢Žqýß.F`L«}µDÝL?ý“>Ûl‡P *j@TšzÅiIt´££¨1æë|aU¸ÈúèbŸÖ_\$¬@—--Îú+b3é3[59\G’£Ž©uO¥òÝÇ0lHºª$ñaNãLÚÖÙ®³–øQGVêü¸oX"’­a�èPcCˆµ­mÆkp¦Œ6Tä4kÆ!¯~£˜ŽqCìß¡A€K&Å9å‰íF•º„y0Ç®Á™ÊSÒ�¼ÄÓoFhzMÔs­o\Dgö<·÷h„ú3ÀOž%­ÊŸÕCviXK"‡JŌՓ[âBKa ¹¡»ü/ŒQéÕlØùÀÁhuîªaDü6:¡uó g*Œ¦e]íÂçñ‚ìL“uëZI¶ü«Ô}qn=¡˜òŽ?ö@¾ÑЇý•7,–uær¨*¢ýÀ&¸ ã]Føï-„"•¼ûÍ5Nªë äP‡‰Kƒmf*•w.ò€ÛëÖ÷ò—ºû^KbпÌÅÄä*^BŸµB^)è BtÝt�+ô$�bÞ©=±´„}S9�£çû‘áÙWÄ]8.,ŒR¹IÂÙdëía™©šûÙ+…£R(²-ZºíyÕ¬ßÀ¸íÏÛO8Ÿ„�Í“z’ÿ`\ °b¢¹©ëÁضġբk1ûÛ3¥äøx�ë´fä6}˜0OA€, …b�áû»r×ãßkÕ w,V�9¿¶]à"BU]Ym$«Bjù²ä�ÛÌw9h>3—±9ôŠÁB‰”°êî)¶|{·–ýæÊ("OíªÉ¦�»œ{ˆú,_Þ¨¹àµŸDÀÖ©{çÛ!…¬žüÀcÐúy)Rv‚Œc‹HN<º-e�4ë²›i+C¦Z7hË…±‹O‚çÒ Úú†naŒ�üT¢)Mà #Ö@Eç¬?¨ ë k ¾ÜÄé�w�¿ÕbÜ.¥V‡Q@ú™*ã&ÀÉÁ äMX§]x˜»Ü¢³ŽËÕ˜M‘–#·$‚ùC´a¼i×&'ÝŒœ8ÉêÔ:dr®….7}ã]áÝèEA¶­ƒñ/5½Û]Ÿ¨âÇzNiž•UQcG†Å®TÚÖfúCcóËüG•¸¤×|°ý¸šWϽŽ�«>tÑò}b�¶‰�èn*JR5@4‘ŸT¯fà °ôƒ¦¢Ù³ik]Rm_ûj¡'’¹1S£šŽ!s9vu§–¿°ToZÂóE¨âfâúŽÞÇ£[Ë+þݱë†j5G÷Möß9‹ˆ|vÀð[ø.2mø(C/ŒóôS¬ªK°Í¥1H˜Š9“¼©›<0òxI„#±œÁûð‡z2a.Ù7"à2Õ7}UÜ%RÔŽâ{�ÙÝDò°Jâ@ õ*â õÎÖ{Çeºð®ÊÃÏ6Émú?›¹>4ÜoòŽðC´*NO’*ƒu52ú›�(,!ܴ֞H¦&AÌOÁ0ÄP‹¦8TÎ’‰bGº¥o4”¡«Ñ‘�—Áã7^Ç­ÉhævÈ ¨pîW”Ót,±Upys盬¤‡e-¼Ÿí>hd¹ˆ§I؈x­´E(¶JøHÏÊFÈÊ}BòwäÞéM¼2ä=æLlP&±‡7æŽø<�¤OK[³;bG™¡Ì¾²® u¤•"ºJ¡Fò?&ål�-†“ƒa‰åÿÍYx×…ã¯-Ö0ä�8sݪó]WVDCÍêcUa }Zì¼±Äv`ëÎ"¿�Ž‘§o7–7}p[ò¹dùÿ†Á~4…zŽŽ<Óó¡(Í­ã¤À2ºÔ²ý†Ç;C»Çâ'D»ÆÏÒ8w<‘ƒ�=Æ‚Î:¿bÿ¼=¨2!ßë‰R!v¬(²|eoû§10R:tR]¡4¡@©õ¿gnt¡/�Ep”ìw˜}#ÞÑR½'­€’ñ�\-ĵ,2q•õîŸ�!c’jÈkg´ß"‘uQRSG Õ}ø žãle‡ÎGÆãC­è –DîMeÑuùúöº=ß®¿/|S‡ú& uІQ׺—œ�1}ÌÅÏ�ÍR¹‡jŒf¨¯\è„iJQ¢£É3„WTù¿oC¼�­hÚˆR ªŠ×�Z|XLR/œ+ªßF›ÂÛÅ>ò_Íê0OHú5?7ˆ_4<.™Ô—»lòµ8è$]êD˜ÌÖ�h Ó/:*m‡Ç_gÈ ð”6å§~’Èêdº™¦0Æ ,]ÜÝ3od"!R¾š&†!(Ë?ò±MdÉéÚ‹]—´Rî.¶?T ¸�Õ߉0ã’¹ïÙwC/PqI¹AmgùoäB¨\Ù4&ð`± (H)c.TÜÕx`ñþÕaVŸÕ'ù3ž³hžJ¤mÿ»‰ù)…c¿ºÒ|¼ÛúK†ÿaÌ “q¢¿£9£};û«%2\ô´+Ë(g[Ü3 èçpü´µô)êr4'eÐgLÑwí�ï0Yî�‹´7ÚÀ*ïÉ/¤¼Ù 1=¹Ü�·%ȉmñIž2¹Gþ¤úˆ³©\�ª”>˜ò™™ ¨Qê£U‚Ïô醸úœ–29•àô´‰ bw¾9ðzz~Ô„‚ú9¨ŒÍ$I’�‡`ž‰Âôls×±£Ûb#6¤Ã¡{lOKÕ–#ËÔwˆ"öPˆBˆwÅ_•M#à¶n6pÊxéϧ¤`!έÓÇmÁ$r/K‘áÝán`Áß±o}ªGt|=‹ÅÞ÷OŠézMÇȤ5�$™…Ï YP"åQѾ¼Óƒñ÷rÅîÃ�¿+½’“XZGk¬×—ˆù罡\E1âH·Sþ|a‘|@H»‚ÇŠU¬üJßÎî}—&ÿƒµû~†2ƒàÂ�÷E’W­ÓZ]Ë 87âP·V]CºÃ§«=r1ßeaªe‘“B0k©ù¡ùWšMÄ�5Å–±\3ŽŸz ñÒÉÛX$ÓyÅ…=¶Z�ñI¼Hœò6뚈»iNêàô^Ƭä)%ϯž·NeNŸƒ‚àqù¥x:›¹³/^ãzNÂÁ°¿É7Ì/18O\ˆ€/pÈ–ê^àœ šç":Óô$üú„ ¨@¾�*xË]ÎbM&$ó·)UÖß1µÝºµÒu¢Pvâ õ°ºbl7Ž×‚naªw .š*öXö"ƒbk?N8¶íyø)耮eìqroIÔu‘ó<¦lV)ÌGæhÚ¼ÎG|qæ�óKú]Ö<•™}wx‹û;ÖéÔ›:ÝO´°¬Í¨PßÕñH+ýÉD3�ë¾V&¨–ó;æoñEiK!ÓæLJjŸ>¥äÇe×öNŒy|6´ ?ì8˜72ò�‰$‚îÔÀ³8SÓP‰¿[— Ãä9¹¦Ë¡9í ÏÝG×)ÝwÍ+ Ρ±ýcJ&¹–G‘Ýe€eŸñö‡3{»·Æ¾Ø.¨iœÏŠÖœÞ$á”SÝv³4” ðáÏÆ�kk!�h‘AAŸâììÊÓóÖ{bf€kmrCä ä©Ë˜ÿU'ñ®L.›¨{¤ú†jŽv¹�Ûšt˜‡f<¥ð&âL]!K$S±aÙ&aiä-ú»¨©B‹;À ªêüUޤžÖ}õBà»-b“þ¨KaƒAî×RjøK$Ä?&–C<ÛeHŸ qŠÍ÷Fû÷è–‘¤È—Wó$aò46m-Áv—Qþ£†ÕL/HíRËHUdŸJ�&Šq/�Hf¶ë¿†&%=QCóar”�×ÝÌ“Wûm‰*M<Þd/,'ðaÚ8ÖRèøeK)¯ÄÉG"£ @.wœ·$ÆÑv·]TKæŒ �xûˆëC¯{ÖË�¼–âfFcWTBê FœN<Éi”˜SÆçÊ„N°æaz­{´_$Oþ«4?v»áeV^óÊèi‡%| Œ¦ ÝÿR%iQŒ×¼Ž å/Ty‚î´�8y¡g ¡|Ù¬©Ww*Ö2¹nÀS¤{iÚd‹ŽW2$CÓnUÑ6úc?n“È„,�ó”èÒ1œ ‡0ÔŽ1á€äë§z¤ZòBøfìÚÃPcF»ee/8xþAƒA¦ñ’!…ñÊ=A-é�ÈÜ{ÑE»´—ÍpíŒD¥ó��WSòr¡èC(ü夞[Dy4è¥ >7‘Fw>ûŠâÔÁzþÅÏD„+ü‚BÆ>ÍñFÚ s˜ñV*ÕxJ7èF4AV„¬pí óåÇÄžhÕß#æ/�9::ÐäØàÉzÐÙáHz/O_·Ý ï·<-$¬ÈšÑ÷VÄÿNðl˜½Zòr1sÕ)ðѵW…È!&ûpIÿ]´Ç°ÏäïÓæGŸ€°û×kä]Õ•u,S¬¬]�ÜL†vº2ô0gï/‹ò(2Ø×É5ܹ}îÜmüMšv7?úOÄbïƒÆÿ½[E"~,Õ†?Ðt׉ ¾¤ "¼WØÝ'Á ‘¸x “µ1¯£‘‘Tis*ï{\OÚtÔYµJË·k�nYsBÁBþká’ 7w�ÐÒÑðÛÞÞ¡k+43áì…ü2Æ9IÐ�¿ŸžÎR*�7c±9E.(³üUøÂ¤™ý^s½uç¥Á–ìxÂ,Øýµ}Û’c’âèR¸îur{¹Bà-*tõÏC wÙ¶í6ÞR¹Ô·ŒÛ“[lƒ²l|ÊÎÛeìêÆÎÃÚ7Ú�åo “¦}ïZkS $vn÷,îý½ýC—™0|2 âÝC><Y`}àÕct~êKoeR' fÊjÆ÷®‰«$à<ד…«÷Ï迃À‡àûE+rˆÒ¦ñ¦”ç©MüÛ"b‚>½àØn襛D² ¨2÷´æûÕÌ¥³~ÀºóNî#�~Ù"¹Œ:̱=9Y;»­ð¶F þíSÜSE¿÷5BH­¹øÌ¥ašžØ�Š™ú‹2xxn%ÆÔýzà¡ûð炱éú¹?P35”Z¾�ž¾èBsÞéÝ:å¨t,(ŸËKûeà�GøÙ×—[ý im•ÉÚ®N^ì—`ݼœÞÏ÷c}‚j{"3)²mWÒÌ‚M£O,Y±ÚxÅÆfb Ü¥ebÀÅ]vTˆ ‘¤*”“�J)ÜáV2Ù„šød G½†y¯R”­CRg“X ñÛ#¾A+³’ö¿64š¯VˆaÁÒ…Io[©q³`²j�*œd§ñö›æE2™ZÜwEø´àðÐ6HNk¬ë5t5XNÒxkáçn\âÖ÷$*¬VæMÅ+k¡ ©OÈ“O,èµTÍ`½ÓQ䕽ޓ÷ Bªi~/IG¢(_�ñóIO¶]`4¿Ñ4uÛ7Ÿ>Gµ˜-ÛÂ¥þŸ�z` ÓW¼à�îüÝk³AçÍÛêçè•ì—v� Aû«òñˆ�~an† Æ`è½æ~„Ò\ÇzÓýÄ»‘†ñDæÓ,½es=ýçU/×xÇ•+otã]xÌÁU#I’çþPüÜÑ’>0ºðòRÛ@vRž�)&T-=—„ñ‹‰wÊèq~ÅDG3éý°)µ±¸„§‹¬ÿ,ø­fЖ‘Q�o³×Ðá­3Ô%—…}±s^†€†²ðg€æ¢ÉÔ`�¾ SËe.(Šo¯ ÀàaM¸eR “Ãh&û•+l¼@èí�I˘×7q‡šöÙFç–G¼¡£üéuBGsiàÊ mçó4€8-Ad›Ö”k^ÁÊ{(¢ÑW Ç lÅ,§âCÁIÈöyl•¶�VcÛÂ>l|,Œ¬sù#§5•²·¤XÂÝæùot† *OÎ×[äë�D¨ÎDªô-“·°èY¤Š„«zh/€ŒdΚåÆy)ÌÚ˜ žJÀ$‡nAºp¶âŸ6߸–^0—h™5…^«núÿ2#ÀªLmãXS]ï)ysCÂö‘L> ©1p´v|/Ó¨¹˜ê}`{]ý¡Ìà&Ç£\K¶×Í(>@TÖ�ÏS°’›w%„6(rß0Ç´önSBuÖaøÖ�Ì> eÙE7Зoçõlmot“x›íüÄS£¹{"›*;LïÐr¼eÖ°Ì­l^vö¥Dª™n&5»V´¹­aRÈ”õ�£Ï=‹Îþ€ ±è>ƒ0±ñsäá×òõOã!—‚ àË–„ÈhQKUÛa#%ïÏjÎÝ8'w%áòC/·~"m _ã–åk,rεhªU)A×XL‰öVeå„ ù’(†£GbëÄ{)Ÿöhœ¼áw÷ÉÐ1âÑž=¢Ò¹;-|3†=¹FÈr¤ùÇ<-%˜P†12Õª†ªsŠ¢åvIÐB Ë¥ Çó¡ *††ž—pŽ1ÁV²^¤÷ê�à8›'S"YÔ-ÜŒ�Åü‰—ò}Ë´EP$äÂöܳõÒ;��“€êÅeÔÈ(2æž\ûý�±T½µh:祕ûÔÅ ¶Øo+Öøñ§m6-=XO’Ú¹zš«2Û `2-àáRq�ˆ™©’¤"{Ž5õb}”tv¡ù¨/;}1Û�)žÕeHŸkÀ,ó›^yýÇT*³‚º¹yQÇvõÙ‡�w, ›¾å'ÜR–ûîO/n¼)‰s‚¡öV:\8ÿE«Œ¸0s)ä9M6F íV½ ü‰ÚÉà%W dñ¡9øå-Š™_¹cÏì¥IÀ�ªTÙº0èî�Vô–øËÊïIñMQœ-ñf4ç¦o9ãÄÕ®ÁÏ~56l€�¨kKÞ°P.ü´3ç']|û@rð¬‰'ßx„f¼ÄŠ!*9s·Û쳊 ÈïD&üÖƒÕñºtå®å-~›Ž²9f%+¿ÞŒÛñŽûU]»¿Ä^;}ÅÔñǯé¿1Ìb.$´��Û¹f‘,˜Šè×㥄=úied–½ûˆOË8–Ÿïß/X†Ù�µeiÐA`üÆ5�âdüíí¤ï©0šžvïÒ(N1~� Ž�Þ«ß4ííHEHðŸNáËíIwÔ[Éxxht©òs¿+~gxÐ¥Õ®ÄóÓhˆ†Ù€‹€’àC¢„׸œóGÝŒ:|Rí) >,ÉÏ2GRˆ�î¶›Å&­´¤ù.Ê#¬îæú„‘n?šfÉšÍ¡Í ŽƒO…Š šÓ#ŽÊ82À1¥óJ\ŸŸÉ±4 å?›%n¶³äáj�¼SÞ+žã"�[æh¼î4“axô$“4žïÅÜ[½ì&dOðì[˜Z_t3‹”b ‚Ù_öý›�ɓ͒߄’~FÈ1Iï¨AEu5í^t¤8Cx’ÑÍDÎö°²×£œ¼4,Õ›O¹;æºk¦ðIþ×’]Œ\8½§¶D �Üó+²>¥o`š‘vMC°Š¶±†!».Ùƒ�êš3 ÄL…µî�yÑec9» Ò/Þ›¶" ô­èÈ#2îw¥Û ºÌÜ…i‘‡ï+Ø×U‚?aÓa§>&²ƒ;†R§ò(\_ ÷Ñ<Æçm€$Põ›\µÓu4X'P¾lG@S7 ¹ø)ªŽCÐD‚Kì6jŸX$p’é†ÌSH#b@Ì š ¹ñ²Rp(�vúð‚w=,@Ñ\Ÿ¾sù±ÓfWFDŽW!šÀ¶J>î Š�~ªÙ�Kôôe‡»™¯k$>½ÇEÕ™nÊñ�£ËÜåÛß8à’}Rr:ïm^­J3.Ít�ÌrÈdá]‘^¡‡ÔÐÔ¬xßÁâ„-C÷ú»ÒÁ¦?s9õ}¨Aá†Ñ[–nÊoî?…Tîö/…o_Z]ãp%#”®`3T ¼þÉd¿ta"˜�®ÓO3÷ÿî(Ñ,õa$ÁÆWNI;,Ž  ÀcLKLžÄr3�îÒ…;͇o‰Â%ë[íΡ}ì„™ bß6 ~ G%ƾœÃX°ƒŠ€ÎÖr 6¶¤Ãr6ŠÜ»£¢÷oœbHÑö†üA½ãÚ]“ òs©•¹°/P·¶—Û®Óqz4uéç“Q•jýÞ2 ›T·B©'ì¥öÑ(1õCïÊ=—{§Ã(@š j….¯ÆS,I8¶ù<–h’)Sfz3+òå3ûV':à­Édã[%jòéDõkºëp¢$’i&^,�–ײtx”Êè•5ÇǰÏîºÇÊ3ð�:�Ä�3uîÞ¦Ë ´òI¯|ئ);¯ fÈcÕ°ƒ§%DÐòŽô}ü§îºz4™fu¬/ƒbP_ù�äß�Ã()¤më Ö’&Õèû§{P¡©öì™™ì¡Ì¾Úªp—ðÔÙŒœ²Ì mtc¸ û»s£3VÍT(aGsêQªÕúq�>ïw†�7°b:§ï'ÈKUÃrùiÓI”ká·6ëÚ€òP¾ÛHFŸÜKÉ—5gŸVÆÂ¡³†Å¡ç­'{<å½l¼:ÕwÖÙrÝê;tK776Fü[Ío®otM€G£¨ÈG}u3i¨Wµ'~bóæ«��Í ß;£i7•ûwb,S(èÄÓ²èT¬âM¡Ù¢®8nÁ`©cûÑ [ @=~áf{–#ßWÄýVäšÉ6¹ÄIHfõ“µ@ß»ž©¯v)À1”v«4²Ê[ û¬�ÉqhÎ{ ­�m5VU~ãz'ܾs¡ðV;#éæüêÍFA�";Ø~QÎáÂ�7壳Û1^'p„:F žj•êÆÃÉÜ¥FáæZ©sHÙê—)%1+¯-¥* ?Š1Ó+{þ  ¦Åi±í�Öç ‚0Mj%pìó¤™4·�ƒÑ~ͱ$gÀ‡ô>‚£ò�/nYT))L[â¶CËíÉɺÑúë§`z±eѤæ áô6po�Õ65N3ÁÃTÙŠU§ ®Š„‹h§ë2ؾ ”ý�zùÂ;:ÀmçwãáOÒí>Ü·¿¿4‡˜‚;ŸðoŠIBdxÌù³tC‹·ëE¯ 0vRõ3z�w6àå#áM£·©sšDU(BÂYQ ˆ#Ü ±—�GŸÇÌÀ:»p"÷œU¬g=£clØH%õÑñC’MÖdß° ñü½NDe(^x8,œ;õc6=ºëÖö~QÝ}¤ß¾Ã�†w¦qäSP(··8èK ¥¥) LÛ =WѤ¦‡Aå,¨ hÉrÿ“û`wƒ¹¯Nÿmj§e¼‘ö6s‘ØðÚõ!¤VùI׆5��Ðáø’g“©ß½îµªO1n¢­*CÛV—Crf™ò°eƒ¦EâËñ}ýËç~&ì´Aó™Ÿg¹ŸÃåˆä:Yiß�Iûè+ˆ‰sØ…³”ÈñË9 ¿9¶?÷± é `‰„ŽRH«Ð ”²¿äÒŠ7©­²i5äêÌL¯rüì[kq7¨ÎºÉàUÌU™Í³G] 6eŽiTs¸4R"2Ü?ÇÜ2³»Z]‰Qíx†¿ª&MÌ…‘© MÕšÔ|ÓrߨIÒeúðÛÑ~£3Í0ºœ3¥‰. ?ò©ÐÁ„–½©A„9ÜV¨—‰pz„)Ÿ¯A¯‰FÀRÞ1MÝ&A¨wgÓ…Ö|½ºÙ diýò^žÝÜbèt®Ã�Чñ@ýoü-üìöÒÊp\›¹-ÊŠ®!³©Bûèð»¢c»­§€ÝX_T¡¨eªf†m$.Tk‰ª� S™Ä—ÛÕj¤S â½æˆ}Ú+×k¿öëo¢J¶9�¶h–Ÿ~�û±(“n Ul79å Š¶ºU-#˜wX L`mK¾~”|EÎ7¯È¸´YgÏõñŒÂQ´:•è‚J° ‰¯Ç·Dj«ëÞ‡O{ªàQ×$ÈWž{6Y§Âÿà|5-‘·n½Îh2è'¶Šap‘*Dw˜Œy‚}±ˆZ‚‘[y×WÅ¿ã<%pOBC°žŠ#€êwÒO’¶ X)çs£òœþ°€ ‚‘EÅÔ/>ç˜æõË"ÀP!»èy í-5ôË‘7¤ÂÎÄвX±×zÏÐÃË­ê[ƒ‰¡C”Qého4S`“U› "Ú$ô]ÇœÔÞ»úÞKÚ¨÷Zà°k#•¶MÔ3æ‹ÏyjEµ‰•U =!×�ÄÓìÞvcÄ�<”Å��î™\žn!”8`êÈ9\TUCæ:*åtanØ›Ž}²R£¡Vxšæ¬\Hü)ÖmÃ0r ×å®Nó{àV‰½�'(ª@L¬­ƒêz$ñ`=Ôb( Úø"%e­PÏÐ,Sb `A€‰þû AχˆC$=Õsq‚pÖdÎî¥{†’Q­ÔÁ]NÚ‚õ zÝ•óûÆU)‰˜œ^n-Dö³�3áÏDB¶±IzÕfrñ„Xk{JD»Ë}ãGßYc±å(DÝú7tkÓ…Ÿ¶@]̶:s2cÕš:¶-ˆ5Se¿nÒdágéV¦:™Ëmfå=wtA“‰„;{Y£“ù«fI~ 7Ws>çÔÖŠÚ‚¾ï>æ~�¼NºòîÖ‚°Ãm!ðQÕùÒq?,0Óø,?Gé®\±ƒ'Tï)¦q[Þ!Í.ÿ¼x—óõ„^ß˰ &9ˆ¨¬>¨:Oô¤J`¢É�åÔÂZO�»Qµ¢ÆÚk8˜IhŽñïoo á6'Såh4\ôû²èµF�G´áæMÄŽ—Þ>�ÚFa½¡œAzVùpç ªû£õÅ~>+ mQíÂPð¤E ÎPzV"¹¬aäæà„ŽÓQẅw½kì玲ÚÞ#Ð*Ö·h@¥©™_çy¤(?¶�_UÏÍæê‹Xµ�7Œ=‰L�Qí &©šDìD)ô‹câÂÝ+¾Bíhuì G(ª)&L±Ð®Dª½ÒHXf'H7æ{“,ûòxé€ç*™Ó�eçÁ/63«r¹2¯ÊP˜5Tm-ɳ×~ÿ¯Ùn¸®«ý_<ölsŠ­k}iøñ0\Š¢A%óDù#Í+â[(£Aï�&B‡¬eÞTß ë_ýõ:G£ $á jfn#„ÔnÈf¿Á8ܼH:žL%$ø®�A“UüèLÎsî<Í¥žlù}>žè;IÓI²äíIA‹r€ÏžÂÙãÅÚJÍú…µÉJº7W/%QrùÝÝm{?;Ità,— Ƀd—«@ ‡*¦³Ûr¢¿k‹¸�[÷üÝÃ8ÅB{i‚® /’Û¡{ÓÞ·F†/35¤åÝX6Þi×$0¦Jorx²Å{ǘ¢l%çÀÍÀ¡ÒrÌ�‰Œ³búÖ™Ÿ¡9dHðziJy›FçFx€ Z`�”ë>3q»°qu>;è�A ·üc€ÛX1#)‰OÝÍa‹£+3Ñ„mçãcë–8‡¹,É5˵$ÛW�ªZò#7ôI â²÷wÉ\€Ú}ÐDXï£;Â~sˆå^]4¡ØwVf´ñ_r‰÷0ð7ÁŠE€Q¤>9õàÛ|´–ÎþeØœ×+H!�õ)P€Ñ0Ýâ*å…¿|ñ8EÉ8´ñ}èá„UÁ‹c cÃCß(ñ~ŽÕèÏeàîOîæ×|i‡¯ÍÂïí‹{‡$¥ÈçŠHkÏBÀøà¯ ‘Hbð'Çë[yf@.�þ¨Õ¸²S p]Pð¼Ú÷Dü‹1AVgwNe£©„y4©Uª"ÖÐç$\”:„´Ê-µT›™ ²‚>”ÙT·+0–œ¹NØ#˜xð–d”Oý:ˉ‹ÈƧ�ÂEx&h€ÁsœØs´3“ëqe¾=]­/׌ÁÁÇ$eT~Y8ð§ÓqÁ‰ [·>­Å£dËOí³ÂüXÜjÎåHr:xù4iÍÀð2œfÆ¢1vf¾‘wY„®o¶ _ùÎíf¦x”3¼K–š.&5WüÊàajñöV{æ(ÈÂŽ8ñøAÒ ;s¦'qôôØ×Â&qi²¥âÔ”úD\ß6Œ»fGGðÇÁw�¨gÕ¯Ï~ª·«{û¬ âµØ6"®A:ÊB½•À žßw`’wɲ÷—…‚5ÅOœ_÷õµrÏpäGäóy�Ù�«¤§äN<™ÚB襦•*WH>)„µŠ"›\wL€¶EÝO^a�†¦8 5¢U¦¢Âk5tœƒî¤ÃÞxx|¶u¨ ñG�U~@o~GÃ�eVÂ`9$ RT{©ÝÚ‘Ó9POæ,ÇNAë.Œ¾ÝæL®Þ~ÁÏ¿Û#àmFO*»ñ´„ôèÊÅ.b)ÛK?Ü@×A¶oÉ{­~t%§…,ïù¦›M}wyÝëhäO£‰K;i™§: Ïð{åÏGÏÚÔ6¶¯}+Øœ IÔbñ7M¾bÑ2�œ£¾M£�¨�€*ˆëÍ©¶âþª/IÓäÿƒé…õ³Ðvp–¸ç� Jq¬ù=Y¹“}}âµÝ’í‘¡÷^©; k›mîM‡¼P½ÂÀd¢QÑ?ü«£?Ò!Ö¶.N <¥üz¸pgd�DÙÊYô‹Žõ),©¢ <Ÿ¾nÅã^ç÷�žXi‘É«W<êߦFz æz ŸÃ¢À�ylm—½”æþaÜ�LÞ½ˆ§ ¼Ó»‘µôïÒ½3O�ŠžÃM颧I R�ÒE9 ÒÃÃüÝS‰„Pý•SÛ\úôùãþ¦‡�ö;#~WÀSér1ÛK_/fÀû:}«õÐj{Y»ž3<=Ýbdæ¢WX_GÁ)lN¡Û�ÁA…ýƯЖÐñŸÄ ”q%…40ÏøP÷©:Ì0ši+¾#dhfäDaãëfÞh„¶P„q€±¯¾!]4¶är·+b/^€¸77kñ}4ÉÞ &6Oäm÷�Ê̹y߇Zæ*#¾3ll‰”¼ø6s=À%ÌÏ_K8”´ûYZñ†ôèt¹ ßF`C6´üÕÎæ4˜†½x’BfÝÖ•”y4¼{Òó¸ŽS(eQÀÕzšþ/�ö!Fýþ 3p�àq‰÷ó¹ý;/d ø½²ébq¦�¿ú¾àœpœ™®à$y0Ì7?Ù,Àa’ôqÄËÆ09µxÝöØœU߯'ÓO�¨Á†ôör9ßBß$9Ö¥nå}±k²Þ áñðÃ͸¦šYm6l$ÙÒü�ekW­z£�€Ô_À�ØïÈF1ï6j*Ù€RH¯Ë°Ç׫Ìã ñÉg´«V·è� Cò]‡x„Z5kq›r¼þ$0ÿ§<$ "!KªR¢d4”�›ò‡]þ´#_žDÎ(]ÓDt.¾w,eåXÖÇ��YzÄ V2‚õõ?áÐÃÙ9oZ¶²ŸÈu-=5|s<ø¢a]²Â1òˆ¾^NLÈyŸ›þýâ°†ýÑkÿÀçÛC$ïž·üQU‡µZs]­inãèþ#QÛX'B&DE‹yà]é/`^×ðÀÎÆùˆlÏ1 ƒ ïÛϯg=±^wðeºÜ ýŽo’dpßm†ÇaZU(ž“zA=‘8SŠü–hWUç¤ÔÛGõ9�áÞ�)H‚3[Â5휋T �§�æäy•”.è�ãø·JBnªE9ã1CUÌu¸ô¢¡G†tsíÄ.‘¤é=>¡½­¡S0OÒø†ƒ“T€2ÄúÃ�“îÅA1†ïŒŸKðlcÆaG©[¶è%AW4¨½Åÿ.8‚ª Á€8‹“2³γhÙ#s™KàõÖ.ÏÓÀÇq¯*‡ÞÛqíNUÎ�™õÉÛs£I¢X�³€Økqoa"¶Ì„´ÏÛKã+†9Â�Ã?MøZËm»¿˜lÒtØ£{ýÙÕô &øï�•¼iµ6=˜/ÀcE ï'7øvUôч�vºg5.'<>“|La Ç7¥I³À£Ñû²‘º°è‰äZJúŸ¿¢8¹.�oîÞ¡6Ç›wQ­[½a;tJk–‹]Éç¥ÍN7’¹«„§î¯rH¾^.8;œ·je#e8%o�!bE°³l„1wvØñzüŠ?È£{r� âÚ¤�(Êðó„µ¹<$BÜù}üL”β<.SVR—è¸í=ŽÎøÑVO5ˆòÉ'íK®ò|´XOÍá9m©ĸPÞ\÷8–ÿ+÷£3¤×q†½‡is©§ãçŒD`!¼wcÍŠvè-¯© ² qk!zµ8Ž1ž}ø¡ÖÊbÐÌ,'%æ WÝuKšDb r�¤Ú•­Š!dcIx›ŽP{Í΃™ÿ—[Y9ñɈ*þoâBðš|-nè�Ù¹�Üú?kæŽoä€{”™…áåÕú ¦w˜ç’�æøØO]×¾¼½ˆqü¯ ñeùͧ~8µÏ*²ñœ°H–`>Ä×.÷íé TÊÂÜkƒÑí]²œcËŠAr4ê¶DtèrÉáü:ám="H :Fé[[£¬÷±"|q!ÆÅ?&ow¬®ÖHÒf©äÿr`�ùαÿ#U0)ÚÍÇ´º–– Óf'ºIÍÄÉí’ûH#?ˆ„f G…3ƒKëfïÜôË3™#B™~4ЕäWã/¤�Ê“­R‘�®}Ó: ‹$Lˆé8ɧb £Óª²6C&eËAv“zhFðÚñÃ#’kÞ ëgÝŠ û.ñi1M_¬×¥³ÙQ‡@²üú$‚�C<‡?‰i¦3¾­g*é6=@æ®°ó–úz¬1™òQQÁ""·b¢\QoL‘ C”.íp‰ïiÕØæÑ¢ ä‚yý°Í« qÏÃ�XÀôtõXù¬°M‘Ø"ÿzõ^Z 'ï )"~Šõý°ÊÈÚgŠHka£GÂ+Pª+´ö› þJÚôPö°ŠCŽ2�ð‰H<T‡sZ:à ‘0ÁÞ&Ow�Ġ˦æ!Ž£´´ž`è�Ùwí³¿Ì@„[fgÄ�³ºy;ﮬ²îôY¸Ï¤ýé'ÉËSE“”áé™Y–Ì»zó„žó���SK–Ÿ§§+`¦�7eXü¶B$q!)Îô©�zujÖ›+Ûõ¶ ˜u ¼çÛM�qå¢ÌìݯóÁXóÿˆšNo©g<@cÏD©^!:¤ªÑƒþ€Eø �%ùÊÒÕ‰¤GÊAˆšÖî¼Ö‘î(‡¿é{¹¦ôKú�ÅòXC±b/]— «o”ȹÈ7\±#[X«�!Lv¡6‹ñÊÎŒ¬n¥/$.â3вÕÆ÷ºóÄÿ~$*ê<…MK€‚TOr´¢�V˜ÐÀPÖlA>õ[n( ß’¤Òõ� l|TŒ ˆ¹ÂîŒ(ý™Éã[æ¹�™Ë¹FÂ_Â3þÅvGë­¾e,”yKp¨Ô¡€©¢‡–)Éón@/[©�½$ � æŸò¤çß3�žJ;ߌÛAê+q è,8ûâföÔ¥¸/uzÿÉ�{ÔÓ=~¯"Œ Ê_¬þ0¦PòÖƒyaÏC ‰‚?l�"o¥ä-Úgr(û«1�‚¨ïáŽ�åj•ŠYåW ýE»}ÑÛä1¬p´Oap 0gíOzFF¼<+\çç_ÖK0$Ô6 wi¾�9¿¦.¹T=¶‰ë‡…ŠJýô0ì(Oô •02-ŽNᥖ8 ù\4”¶âÒçgi0¸õˆÍ/Ò;ú¢Ã$ÕÞã×¶nœÕ!j €×ª'ü/ùòïžlK˃|T댋ƒ jõRLÔÊdM|m‚©œ©×·J¦ñ‚cf O‹«Ä4Rs|Ý̳½MÃw”]z-•hð>SÕ¢haÁT™òâ­&Îd€ˆð»•aŽÊnšÈžürÓ°‚!Ü�üØ©Pú¿†®×0»ŒÇè›h<¢Ó&`צÇÄB Ÿ�‰]ÉHû¯¿~Ó r­øä´íÙ‚gý½—±;gUoa<¼Øp…Z^Û”œF!©œÅñaáïúVp?hîæ0vê•‘FßQ„ô6=õ�T¹ymTaGEò˜OCžÄc £‡Mk)Á3é6¦šüäÿÉA,¼h‘Çœ2-õ-T¼ó?9Æ+ÚšFNûi?�b˜\ì_±Œ)¤_.&}ü4dy Ϭ”»¦ñ†ÏC¨g�,»ûӮЉ´øºùxäƒÜß}ΣÆP(³ú)“g®Å—O„y†\>F§¨C[z½ÔèÂé¤?;*ø—Šã«“ÂÙŠðëê@UÆ´šžï#­¤ÎïÍôñ•=»¶áU'‘²-­�8gð‘W”×ñ©ûW²É•ÑU.|å¼™ÚVD›ò¸š4yÐÕÜŒ?÷ðD+lÎêh-µI~Mè86Kªo *@«=ðîÕªsÖi©¦g¹¾Î-zïžÐã|óuñ ŽiË.]èP·©Z–¤¿ ¬8+1u|¯ ›Lrr°°²øEÕ�€ œ^#<…$&GìýQ=çPëv D4wªîAªB‚fŒüuLÜòñe‹¸¾[@[cŸ Ø[6)­$îrAWZÜ=ž3sêéË’k\K£ëø=I@V(é‰xsEò;j¢œ!@`hûéKûóòT"вBPd DuWbá,Fæ*Së•nÝÜRö°MŒâ)µaR¦jÉ/#äÅÞ]¨¨mÏiˆÐê[éI û«xéùÝáë¸Jëpd‹ú¯kH›=(ß”!{ÃÅ„æ1¨5ñ_oPe·"ò6E.ûy‹ú¡16ÅEšˆ ЭËЖ�ç7+5þÆv6C‚ed™)'óZ…3ú,“�OÓ�Fj:w1–Ò¬Vêá×EF …z�«¨†ãd`|AC Ö»W²6™$ý„êÕDâÏWTõAZý^ª��М sO+þÊ]¶”’îÅ¥,QÌVK”"ö3;Ø¿õ±œ&�ª�”%ÈfœÝ›ñNÞ¸�ËÍi™–ð КƟ†­#UB·•îÔцïŒã(•ÄÏ$PùýìÁê¢*�oß2Y½©~ë`c§VR÷7GDFÔ…j„<�Ìý혛`â ¦sÜ ¼[Ô=÷ìÔG›°2Š%ô¹ ��°Â¼Q:‚ž¬xi �:€pêŒç*ÚH6m’.Ø`#Z®~ܨ¯64Ú8�’P°À™‘ꓲb®ï¨õMË0ÁEá¯1f‰î.¾ö}r¨.qáFß�ä…mþurÀRVSÅØÌóBà¥OÊq‚Ü¡å÷ìtÊ\äw™…Š^»Ä[lþJ�«liÑsÃW-+—d¤§ÐÊߢ†Ý; C®K*Š^Œ¹'{¥KR’^ÎÇ5ÆŽ!IU¾©Ú¨«¶.ÎÄŠ™ûí+Ȱ)ÑÀ^»µíçmÆÔÔ �…ãwKñ”�!®q|GB‘b­Göü—\ �¨â’ÕÈ�ÙN¯<¹MÙR€‘:аñ.¥‡­re¸;°#¥$þ[ ”= þj³|tÏ8Wef{ çâš;„zp±nÑ5‰+ú¬ÙX>C´Oî€Û£ñN±Ë;ä ²H“<&¼¢qú†" @ñÍŸgúÖÍÐ!Wîêĸº�q‡G‚D$¨OT›ÀÂöøÍqPä`Vùõ¶œB?yüêIs2îa%"k× [Þ2‡šx ˜ÐÊF]´… Ý_ªLßöïª S_H ì(ÞHKl¯ÈF ÐéÂaÏ�ÊñK/?nU�§ÕoÉoþ ½WF7|†yN�CŠ4Éá�\í�7¶È*ý›‡jæ`›åDˆ ›Ø€9^|Üpmhï�º‡Žm„Òž£´ábû»•ûPIð°L–þF·¾®¤™kí>£ýw3o™Ž8Åõa-.d8túÔÞýÂKÒµ))�únæéê´�}vm™1ôNöî—“%‹Ì ¾QI/¡Âij.h4¤) ¹‘<ì_· à5܃ñ·5îæ‰àà¿Ü4HD'w”•Á°‡_ô¼8¦-öØ=äpÞÊ„©ªØ0]äZó± e<12(W¤öéW=ú ŠNÅq¦à[³÷.J­<ÿL÷ð…WÉ4 ƒ¹èÓI§øÌìÞáÅ�`Ø3 ²�££‚~•ÑCÙW5}/èµÝMÈT‰³�Qx/.§KñC¶ñþ1Ì©'<_Øìo6§´ÿªF”HÎÉdíÿ¦«ORDOjR¤ëâÖ‚· ‹†ÆåÀ*†ZfóDñç©èN4Þ¼ú›âŸðV&X°¯çí}ž§™vË›’ÌÍ$° ¡ÃêòþT-�ҥΫ½T$_ù÷Ç7UàKÂŽ­1KÏÊXÍÑ\Ó~„jS¾ôqTª†ºß’Öˆü>uq/ìc»èA¨ŽhšG·c©LD‡ÞÃÂ8vO rÅî³Þ¡Tg½3q‚4–¯ìÂPΠ~Ž?ûCw^zåS¢aŒ@Ÿ^òìn;x)$‰ÿ¡F ,9„æõ ,Ù@#dJ¹ç`JêêSœ>ú)™,I� ¦ E3ÑyÝ)‡þ€3Ù{gÛåÅ®(Á®d·Ô8•gXåÒ 6ú:²õpðÅz‡ï€8`Vƒbë¥kptˆ|Œ—³É²¦ù±|ì*l°n}ŒhSE *ÓVÚàm5¡4|Ô¤^Ù‰cvò²�;{WËh­½Ç(�Ôs"�Cέ‘þ“ý‡ý¯2L�qþ ãtÆ,é¥ßìÒp2Ò̇^ëSvz›ÃM Ý¡E™B§â\Ë«K²äyùÝÿ-ZĤxE‹;Jü|´ô�Ü�_ç…‚~ ÀŠ‚ææ½Ð[;€<©¿«Zg�+6lƒ=ƒc‹wïzíÃw–^v4µ`AE±@[è�!‡��¯7lñ.ÔóKí1yW 0”i/Qs|åþù�>DÜögÞøíkƒíÊÌß–òÑãFò9â¯S¾X¡�ÈÏi‡ÿªêîõ€°ÿ+>¯•w XbV£;±9@‚ä™L˜U1xà:%ŸD~xSÑ>aµ ¨šaMÆ�é ÞQöÁ!µþúŠ�=ûØ(±½ÉgîÏOiU·ÔÂøS¤%’KBÙk‹.¬üú¦SÆK787nÅe(˜x+wPí�®§à-ªÑCËõEoúç-ÅTïsMqíïS`hqù ³VPcUŠß™¿Ø[eH2»žb¸qYQ'îßk )ƒÌ_BIú(ãY@÷ØE7%Yã¡­éÕ œ9Ñ 9lµ”÷gê†â²‘¥+0.VÛŠùeïõ `Rmßûg3¢6‚â¼L9Èo×Tj?™ÝWC1PÂyËÁl’U8Pf¾¿‹¬ ýêUVÄQñáºê˜†·ú‹rÊmñ�ΓQ´S‡�jDÚåÅ;×Ïøɴ?Ëm}û”ñ²°ÙQ’‰˜KCPneùUÐd½’©õ3¥\«ú– í0œ•!29Å;#”¾ @Šì³øµ–×\¼ƒ©|ØÇ˜´ÿ•ló…KFªøÓæÕÈÉ‚#OCþ AìÞb¼åvÓ"XnÓ;ªKyôÖæxl�Rëü BDÎäèqÈé^&B–tƲ‘�ØR×DßôÎVÕ0x's’ÔŸ¯ 9žÌz6w�îè~]š ÞbstØô66^PdhŒÝ{˜?÷m­¡†ŒÈQÉʯçïÃ/Y÷/�òê¶C ¸%ð7m«Q`Õ®óW3¶Ï›ö¶‘!ǼåÎ.V&!|§,�÷¤œI–·#<Àý�ÔÙ ]è¡Ð‡"`Öê• º¾âAéLÅ,Úê«óÃT’¼ÈÇžõ='P©ø6�ÿ*½̺OŠãÄãu`Lê…H]w®ÑSôè]ýQ˜]>{ ~×î)X¹xœÎ²ìÞ9Âéã¬ö�1â*zãžN€‰ÎR©úu»¼€˜Rî�Z0G§ˆ‹ArªæÆ¬ €$h¼ÌqZ“½jCó¤Ð¼i¿È˜Çnxz_ó§µoœ­T®�âô@›àÂSmnÓ ö�•Ç©)% õ»g]½õ7lmÌ (OO_4ñx×h™×‹¶�B ¥™y[¯Ô¯¶Éw%ü§!³‰¨xÑý5OjhQSF“O龉jœ_&š—öBÙ¬Ú­5¬úª ŽÇ{¨(cñ¯ø™r³¡²+1Ñ ÈC¾UŒãÒ»!âgæ?‚aŠªÜÑ3®'ÞØíþ„j™ú�s0î/�y'Ÿl©SÓ K? ýÒ¯'K„Õ îžî²_«b6þ«zSü°ïZ°81׎²7#åëM²œŠZ"qw“§&»®Ù·=�J¨dÿ*î¦yùåB#DØ9MCCÐ÷¡!Ìp òø}}6æ)ø’@oy™àùT^ziÆ"œù&o�SrQPñ‚$Ä5Cô£u¶mÇö×I�G^öÒ±¹ût3tŠþH/KNÙ#V’ôNÉÎeåõ¨‰/ÓJbÊÐ?ߺ–@e N÷¶ó–¡ÉRíSWé,^èâÞn}�£-ú5ß=áì©–.Á¶u]ûziïyr5¥N-‹\7`a]„'™q­±gçRF=×[i bq^Aøì™YæÁ÷‘ìæÏw|ÇÆba²Ò„l‘´£çµŒÙ–…å ŸÖÙ ¤lé;õ•N»ú˜zßPzìÈäÌ&íÉEõr­]³=x­èvn €JZÛUÑ >Á·Ôóî¦(о³)ìpi[¬¿œS~½ìh)v¥:µü Eé u¸møeSV bªÎíÒjµÙµÙd5@èTDºª/Õ'ÛýK3‰qÒÉÈ(Ë©¨”�Rªª"˜¦Ï"¯ÊF@÷´WÍ4þíêK1d+“Å7ðRŒ{ñŽò/êïöC¾Æ¬jç.yÿPC™{¤h?eè+ è¼hG ã¥:Ùƒ¨tØ×ä��V1~2LÖ5/ã\o!’‡5Ù›ë6ƒ´)^ bF‡œ`ü]Š ÜäÍå¼÷¯†·tê”v³[5§Þµ;™ˆÔ(1˜'=öH~sÏñaÐEëó…Œ`ÁukGö{kûŒVt. Σ·žõFÞr³· f¶óíÿŽßô&|¼Ä£sBݰÞB‚‚§3§Ü-9 ÑË4Rú¬^f w®$l[JË8BÙæp²mS#�Ê‚U3ƒ¬ÓŠÿ‚kò–6Ñ%öziMy¾Bi|išÚ–ȈD_*³j¶Ðdµœdý,—®ž‘U\ÁÛ EæÐü]ε&lç�;ÓO­'SÈŠ]•µsH�Ê÷ª¶ù¡&r²}Y|[ˆUSé \Ö˜fÆëïÖÐØ~(pG?eQk/”c"Ÿ¤“HE)t¯*<’µ!ûáRtáÚĵ×{âÛK–  ro»‰<¤TD+Ï3P€^òMÀ‚2è´Þ»À7 s ýŠÔ¢ö…¨$ã°SÌß\þòbÇ킊 Ü ¸ñðqóÓºfÐèà7u„ÍÔ|T¾Ï/—®e9qš"¶©A‰ˆc´)"A|àÔl)#v :2ŽDö[ØçU]¾î?Ì¢Ò‹Q€B<ŠŠÈò¥A Çÿà”Äa/ሆEÙS5Ž[t]óæÛ�ÿcÂ]b7/öò\ÜBK$y7Ls¡ð;1a;pØ<¿� _âϻΆ‚%¹Ðà뙇à Â?4‰A§þ#ÒÍY�u5bŽýñ,NÛs©ç>ÃÅAÊ�h¶@’W dc�ÿ‘pFÅP÷[Ë�!¥/ÒhìÔçY·üÏd¡îדSœÏúÐ;ù|üKÙÊç3Ý¿'† ùfn[¯ÑÛÀÖ÷½8ð�ˆüÛ¸€%J<n¨èû”ÀU�ò´GξÅ(�¤Jg¼9š1@bïŒÖ3kZ©ˆ‚êq[qÌ �j-^Pö<¿X•8ðO°¼Ì¦H»”OQ6,n8¿°0XDG ’ñ=7shú§ãæòf[”öÂÜ[àž-zÎuóÐ 9ð!]î Üõ_7F…ü]»%?ÌYÊë.Z+À�å(¬²ïû0UÇg º”„�û*(wµš_ºqÂn%±kq<°¹Ü_u‡ÎïGÍéãôÑîjÏF1þ^H¢áà¦6Þ¯#Î�VQY~•hJâ35µTߨ–Ùý™²½ Îw{÷*V³ˆëœRûë½ÖU†yÊRd[ûK¿Õ÷Iˆ“ãÜÞ{Ô9¯Óc/‚½Œ”›.¸ª0I�æ¿ýÎBÀΉÍ?Qù¬¨á?þêÖë*WûÄ5½i`cKͺ’ÿB™ Æö!÷ºC½�É\é|7×ì‹iœ>«bÛâ/óìÓ Ž*Z•Ñ6K’(ÔãÒ-ž×³Q”!„^jÌÿ¨C�Þ ûDéWõü“+9ÖÚÃÐúo rrk|òû^e¢ vš;j§‰5˜@ízHü~LÏÂBRß=¥N/î¶m³«šh)#üt½Í ѽC¬—³=B¦´\Sj<ž*7¼ŒÚÆŸn¨XÔ€œ¦6rP ¼ïÃΤ�5CWùxO¨y.ÐÊÚù(q!ãYŸ‹Go5|NÛ˜;;†þ¶EXíEþf-5µ¨~@[�J%QRUú¬7D�F5ê¾]ÙÌJ†áEh¬ý‰<ÍYÒ± «)`G‹ Ò�[šaSIlà>®ŠC½û¡¹oY 3°Xk^56.�²]£Lï ¯‰6›ƒÉt³€ó6 _�Ö#Y¿ïk }P�uNò"ûƒhðžW£¬ŠU%¯VêÀ�ŽU({à»ÁD ™¸Ð`×înM\ñ! QIð¸)L ©5 Ѝbyšbäk©Œp])`] Ëkß«ùOé!*z¯2Å:Óþ3lØFI½»I­"DZhžPdð�„¡†E7²°çÁ:9zÒÇèÞQ¶üK«?Ö˜jêbcq|J0-Ñ}¿2ÿgéX#ŠÚÉULÌ­u¸¹Wv¦\XËËæÅ¥®5C Õ£LÁ\[FÇ…dºb[=mtÑK¾Ã2�Ûßq>;óEï>”ÿÏÏ âÚ+ÎÕ8¯C›ú…‡·,'ÿÚ} ÷Ô¥=7AíÛ°ix]ac90…«~š0'Å‘ÍqWUî¸Mv}cÀt…>£ÇáŠÜi{½®gÙ/Õ”%;×_ÙŸç�AbNM?Ô5z~)G,oâͽ‹çq; AÓi&Û<í EYg’]1iiCÓŠÕ[;ŽŸ]Å{%Lhäë4¬¦"Š0BÑY^øâŸF¼UíõV.­ß=ŽÞñRÖ ¼#÷i½¿s)H¬#"ó•�œÖÀ¡Œöó¼bsÔ Ë¿�Yl”f� ÷ëEc¼¢ ^ª¢0ÚÜV]òF§S¸�7ÑzÉN‰ýÃÚ±†/zŠ\+2!2O¯‡DÐ… Ú`º ÄW|¶DR]�Ñ·ª–?›¾ïJ ¡&_™˜Ãa#Û½ëWî¾·#Üž¼¸íþ¢CvÐÙ‚?â �ܺ+è8ÎâÛœ²Ôã8ÅËê^�ò^œ›Wø<ï-Ž×̤³Îæ>T5ßåüEIÊðˆºúHØ�¿âgNÖH;ݲÝU"Â`c‰wíþÙs¡Êƒy½Ö7�Äg3[»ŠîeY:ø%6ý óxgR[ˆ/‘·Ò€wÿ)rg¾™ C¨ðÞä*“Xí¯ ªöá0MÏ »ÞC¯«ÒÂ5�þ¢Ì ôãÊÐ-Õ (‘¤ ¡ãˆÒ�}(Ü/Gòc” 9§KüyïÕ[9ë%­†ƒ¤u Q¿„M>‰ô¤.ƒ‘ýÃ¥¡du9úÉ�º CÓaì;µ•(ÌT «[j¢ Çô?õ*õq ñ¿9ŽIôÒjîŽß%q=‰ì›©nu«`bVÚ$éñ¾Énªø˜™²ã'ëó·Fó„@Î�–¹�uXœ/«ÚÆK+š+Ü=Œ|ÅxM8&¯õB�=�Ë_n6Jf›®½ ¹÷D^;³dhãµÒÒ6t‹€4Üý Êxµyì.üŽýTО$°ý‚«”´g—OTÑ= ÄŠïÊ|cëek ‚5ÈÆ©E “äà*€Þ[xòR/uVOöÖÉ6Ì�×E$ô�º£ÅP´§P²öhƒDþ|Åõ�B £ëVbh£î~Šw²Á¡)Ø×+!t‡¶kìH°J1¢2Û8•;¯Áÿlƒ°œ‡cê'—†–H›î¡+¶…ˆpàËøpþ®4¯äû¾­�4vÓL6>!-ë7×Ë«C-¢ŠÀdèäAmä ¾$7ÖÞY%V7t4¿-³Qôñ/ÓÉY²Òʳ²7Õ°š›Wög-kGëfŽ�¹09zÖ\£‹›tÖÒs›]I)€—[n³?ð/¶b�M">ž02€„ù¾‚ЂH7W´MÍn„”ïↄíùÇz+•RÀžžs˜=G‰ÌYù�!:fÛ+ðæúeüÍ‚íeNa ¬Î™' ­�¼Ñ`â«(]Ö‡mêF†Øq>Œá>–„e_'Öö_¥Ž‡ÿcVµÈLJòÕIÿ¤”¾öª-Aôù½Ó[¹ÍLÕ³£÷$äÇæß1¾¡8¢Ïøß�al%Ì„¦®ÆQ—$À2‹‚%ó6¡Ä gMÊšu®]¿„WÌ“ü,”›�`pó€©~Œjíüwß)|zˆ›þ¾uõÄ¥â[_Fq¹š+Åþƒ%m>˜Ã¼œö¾1…I®X)´s‰~¯“GíƒÎ—þÍ Ï”ó]«Ý–nøÎÐ+át¿¢Éš¿ ‚7¢ì%¢�UìÖ#õžpfy–©ù®f!2›éÍ {è÷¿*�þ¢£€ ‘õ’Ä‚ÁyÂæº%R¬@clq¦aÞƒjûu¯*ÛwŸ@oLŒ£ªZ[… WSJ°>W;Æb&á×÷cØéo>.-\)ÐÛ+J;÷¹ÔÉŽ$HnVQz¡M€åÂ2˜‰¸£BÀ´HO¡QqPÀ‘R±Öì‹„f®OPuS>®ç‹([¯eTr�¼!eº÷OF§ÊUm…ÐÇÝä¬À3GfI0aT5ænæP²r¦%W�|ºh}ÜÇ�ѦôÙUÿQ Ø€¥DÊÃx IåÏem¨ÔÊ�³“½ ýay ‡7ý9Äî6]�!P¸®-ÚëÑk \ K ]~ãâ[cÜÚÔ¶ÙçNM÷"*è|,$Þ/òøiÀC¤62·t9Æ«*ž“ëÍŠó.ÀsRz¼ðøÕ'-K5.¾¹Åy=ÕÕÂâ¶fBµÌ}�óÊ£N{×JÓ‚îé°ZA>æ*~5�ý«/½\21Pl_‚îÙD 'ŸoîÊ!‚ÂÕq²Ä=Ú$j»�N`û…HenŸŽÒÓ1El½@X‘+M`õ„�i)н›Lš´wŽDðQV€fBÇYÄÓÛä‘pÑ® )`{üø2ëü:û�Ñø.s Ô ÊÿѧQ\S¿ÝÏùÌ2ýúÎßZh–q^ÿt TቖÃ>zîfÛªäO-³¶Œ2t(´ˆxEߨl¬ˆ—_.î¬OÈ¿£Gîšóc¸¡�ýÈÔÉM#Óþ}Q2‰•ÃK®° wž(ÛÎåû»aaŒ–²0Ø‚Zm޹7u\Ic%ŠY“ìß@[6ö'Ñjó†6ó±�ZºÊ:……чȧý(�~× ÚVrÙt ¹0àë÷Ò_56޹£ŒOCøâ—²^ymé|¯Z³^–=ò] 5…  ìèÄ;fsÆŽÎÂë X´6„Éåĸ8çcÏY7p!'eþy5RûV ¡lkõwWᄾ•»=fc‰g‘5=ÇFŒ£@Äàg´d+ø›e�KEØá‘çÛË“Í>]Ú£ú$%T-³7\òVl§zµ—ê›Ð®ï‚¯ÃSµðCå²½€Lâ§¾rÙ‰¾¤XáWâfrpr–£—Æø‰ C§ÿÈ%�Ù9­a"† /‰× Ž:4n*`¹•®Nðå9W(5‰Aؑ神'E\ Qù��È%»PNFÚ#aÎ@–bb1.;g4øÑʤ8À‘)†=lj° ç'·[¶ìݧP"`¡X]w4g߯ëÕw\¸`¦ˆá‰büG¿ŸPQ‰·#w¸¥“oµ­.*7•%��© Øõr˜³³�S6 r?M¦^Á*·k”׎|À–¦�ÓÚ5è*f8ß�b°¨�mC r>ìýi_§#o~PŒ:7˜ƒ8AÞ±Àø‚êÍÀ¸›ô_ãÅ5n]u�ícëÆ+.r¡.b£¸éšŸbêNé¤$ A^mú$lžH.aìá^w®`È�&� "Q)v©×¢l€…€²ê¶×ö7øœ7&øü ¸ÓÕVx¨…µ(QðïôÚqé 1¤1œ8`\��mHËüŽF{A´eCRÑß5ì®” Ý÷E�ù.WO4†›ÐܓÃ&N°•™™[’Žj$«ëê…x¹Þ‹›áˆl®cˆ.4÷+\„MŽ‹oèô½ÃÈÌÎm�ci?„¤¾�EÔ�:šU™Žžns…ç$ÛíYY‘Â^Q»_B+"V�7§ˆA–ÃE„ìMRßuŸïZ ë•ƒfÀ&㜠qƃÕÑ„„[d¯Qx–Ð�O‰yÑÿ£§¨0‰Ó8Þ!‚eE\j±O¢>ч,2~ÕJžm7 8 ¨‚°Ð 4h7£×%ó¸�E̪Oƒi'I�áÜ«Å�¬Îº¨½!ºÒ,ÐéûUL[�Q!àO¯Ü–?äñHõ¤Õ »gcBx޾£Nñ‹9”ö¯c”ÜÎzÆt�ƒŒhý€÷8êµV¸¼K¨ƒÕêí¯3]ä²�˜b‚\ä²’1Mfy ŠSÿÊ2WyßÈW3ç):á¡lØP¸¿™ á:è{Pj’ÎðúÝ#_#ñfé�Ä´7z°êêÎóýÓVÖh&Ñ¿ÒÏóvÓÚìWKg-"ª’ ……³Çô¼�Ãôg�¸\ômPœ�C¿„ˆ9°'€gwˆ¾`ú�¢ „»,“Ö£O‚465–à�•€„’“&:‘9xö=;–[Øë¡`ÃUå›l‹ì$R¨W‹ß=š6©ó5‰�U ¥ %mä!4eÊ�…¯6™³1¥ s¨;6pï•®ÂB·‹AñI¬)°økÛ>iÁUuM¶âÜ[%ìS¯Òlp¢ôòó¦?¥�/dŽ—F ®Ù ZÜtbVoà Âóeå2}i¡RɱBIU�~ [öAsÙöqµ•}‹Ÿaî¥É¾—¾p;ë§RVLŸïw'“ýÀ¹ÿ°â€·l�Îd]»¦+èžà÷ÿÎ`•²EŸB»´ Úo;õ£!0Fq›8&ºé ÎlÍöá„hl&l=»2@ò ð¿ËÞˆ†°@‚$Å4u¤3 $œ�/�¼2­úŒ²pc>öjò.—EH¹~Jö›eþ<! ¤k�(€Ë«öõ¦Ÿœå!„Öx?_ê6¤¡)"[�È5žþÎMf#!Ú¦”k¥cĵ:†Fh‡³K�Î ºŸ|nºOÎJÈ ÉÂÇjÉ8~28WÓ*¢Èž+T’H/D#&ªn~Rñ]Ÿ±Uavƒ¸ ‰»Û£`»ïGX.�L&’¤nkBÿM¦ÔFÆ›]N?)8IÊE÷йGz"�ç 3'bÀý./ƒ­$ª6_;A>wt6wµ �D !m°á©áì—øöµöW˜£ææß‡""ÜX³ ¾<_Žy;X«Ù˜s  �8B%¤qñ„ò^ºTrçãûô•øºê}™} éwÛ˜ Bãï Úv·Ç˜9‹| 8þÙtvõxS_åç­Ë!ÔeK�w¡%‡ú+Í€’gb)A¿…\q à·å_F£°ó³ –+OW%¹`íá:è>ñ.lÅÝx�Þ ¬úo åùéÀ[²Þ`¾«ïkç|phŸe¯Qixxvz„VLÒÊN?Ñ&Có£ƒÚØßºoíõõ‚ °eÏ _üé¨\ÕU€_ÉP¥Cûþ`NV×À#©ƒ§­qvdà™Ô“-e/3T;ÎY7Ò¾@«ž_&‡t9ìÑŸ5@ÂÛ�ú%�!xš;çë½KÏõÑI6¡s/P¾ê ̇}�)ÿ�azØZßݽÔÌb“nظNSŒHÆJÝ^ȬÁ0èVuXªp˜e¼ pöê(·W¸)ä,%(¬ Iœüòæ»Ð´é9e楃êšNæ­ŸCcv�ópŽäò ³jÇ[æ*Ž î¿s¾l~†øN´·rZ¶¶»ž ÓS(›LŠÞZZ_dQ©5z’ªws³-–ÚBßLª»Brµ�m£æ(MB Ñ}«è:k�e¤Û)i£á¼ÎÒ›aÍtåêtïgÑ\wªíŽŒCKFM¢Tf*’æB•ãŽ!ÀÏ”Ð:æ@;ÊO €Ã€z”z‹b œ÷’¼>½ºLóVjû ÌÕˆÇI“)æ»QX™l^‡xFR/‘ëú`’Ðã‡4,ÏEN–ÁùmH·dØ$�´ž¹ß7g”†6qï~27ÈT…>‚É˫kZU�ŸÚgÛh‚A®n ÷&;RéÅ&гÒVïO1)÷ônJIõ,y­Mp}¨$Æ–#znæ“q*‰<Ö³ #ÇË5�¶É@~"SnÔ×d)=QÂ)Ê�åÀNI/£¶“éØ<Ý—Bsgò­nÞ½îÁ¾Ÿ$½. uðF#BMúõXVm�(gïQÈ(.j ’(¥Ÿ´8„Œ/=ƒFìØ$ìæÀùäJ�’‘tY`nña6©ñû�¹�tÁ.Ê2CëÀ´·êýââPéWál«îpëÍu±¿ÂŠLlT�÷X•[á§S! ÌnI^Ý8V«†§-쾈RdÜUJ$�ŸÁ¨µ4üR„ãY»ŸâÚð?sM[Iä¬EùâÅœ&˜˜Ë;å6p=·A¸øz . ””>ÆÅãöä‚%n�2µsÀ3�Ë6wñŒR„{ÝLYq“Ê®É.H]— ,škxÎU¦«,¤ôk6É8†r9MF;ˆ]è¨>4Óƒ”0¬RürÙ±‚¹ YÛ]qåJ“>‘¶‘tÇí &¦3öft øêºâÔ´›òSÒŸÇÅ_bvQ–»€Òëq¡“]9 €Ö\ Ž ç%àÓøNôò Åî/d‘fµ†…º™_îY9š…û¬¤ÐQ�þåb®éDmn±>¬¡±³Vù¡T‚Jï|»èä?³É‚�–¹P«çû'¶§¯ˆ:Ó£9ÖÙ㪂ÐÑ}„ç$P ô©Ì<&ê¶0œš*î ìˆ‹A {à½A„B]Vѹb?Õ¨Úó)ýšoºQS.I4Û=¯ÜôRÒa»>§ùsFGO&¯Û&Ψ=)Ãð«5u0�}³g8ÕøL 1W¿=$%šÃ ‡2E½â+jt‰²ŸÉj¡öx47ÞxÕNûcŠKNûÙt¡:#MÀ¾¥ÚdÕ´úÐdV˸)®8ø`½ï»ï¦× ³X9Ÿé(Ö ÄðŽ*”ÂÓ¤Q`/â�@4ë1·ŸÆõoCÁJC¼5^Ê”½¹¬Uäž^/èŠûãŸ'^%ý7“Þ¶ó2Bwj”eÐm“4‰ÍhZwË�xP8KÞEKìsŸŠ×�žŽyûÓÖ{Cüûã"0ÛÛnÓÀmq«ÉL÷–ÌÂïÕúc1&ØA =NÝé™2Ô7­?a,—_àær5^ÒtêÃÚf'8]:ÛêÐv³.ã­�áO=F3R^´¦Gð¯XŸeZk 梎0Ú»s¶òH6ÍXýÚ6ƒ•&Kš îÔ·'‚í,1¸Ÿóe�t›�–FL']½»J†Á€›ÊÛCZáTÛ Û“’Ö†=G§h¡%‰?•®<ç¾ÔǦРŠ] 3úŰ—N\p žô…ŠÃù]¯<ÍŒð=žÞ~VÖ§ì,¯}á4¼Ô´Ñt¢á§¹ž¡cÀã"Â!t&–a”D¢·ÌûÑ=ÒeÌÇ+ÿ£šµºF�­€L‰|Í<#|¶-À›‚y‹‘‘x‹> W† w3èFiVEݺYct>וb&>|ÌÄ,¤Í�Pz©ók›šUóO„a°¾Ù±3AŠð•_ 8¯,Ѧº}Ž8¾ÎP 'ÓÝ¿�‘Uyp›1Cš¾tèí„©�æj8žÌ @ â¸JÜ”U§‚N³Šã`ý¥ Û5ðmd‘E܇)«5R0$R"Ì#–½yÛ fSõÔëu¤²û6–ï]V^9݂̗¦Ú´X¨À¯ŒØio|ƒ_M„\øG½£ûÓN{‘êÕ:›/öÊX;¶ý3™‘6 ÃôSt%ýOTÅ�v¦±L´® a�D”oPÓÿ¯µÔ;dÌ*È@rôGF·2(Ã燯_~2ÅOr蚤§ž%ŒŸe¤ÁRlÖ†N02ûÈ¡H©0 5/SÖîsÓ‘Z¶üÉ3Hýr°‰±¢™R/gj¶4ôÀ5ƒ6•"7*ƒžÆÊÇ ÏÕbKâW[ZË=¦b„U#–LjíüIQR£0œ…EÓÁïªVZ5¥kj �ºH¶®üh}Õÿ–ìÿf}ä$ÄÍãÔßùdˆÓcp*õë�y¹.*$®YÁ.i8ç«_jà-û"D€Ý©JK]Õ¼S,CT)Ÿ7v²fÆßßýâ5¹4Q¤gŸÊ5m½S¿o!ÜÜ¿bÒ~?©¬p6èœéØÙÒCÚÚ¾ˆ+×;µ¾>í¤•ÙmP¯|œÜ™œz_°`Ï•äÄÜzñCL9ÐŽ×î‡A°3a�îÃbô:ÿ+†�f³|8`bË�B£!ëc¶¬šØìÞ@Ït²þ�õlgZDA2zºÈ(6&éØ58N›Ýû ;‹‰ðw “ý¾YKŠÄ_Ÿø7–¼i�­"_3âÖqå)à5“œk¼N×Wpï ë4%�ÆO©.jæ:_Fšê"ó»¿ å Ü Wò&mæ‚J§ÝØòŒPA­_3ºI $›‚„4ï£Àøü Äf$°‘gËÆ9%;A=ºJŽ2‘b‚þ‹WY—zÉ–î@0HŸ(h�¬>ÃÑ:A èÃû±¸¯Akk¼~³¨K\‰çîÄPÏœ'é=[9bÕÑxàÔîÚŠÁ¶ 7�ûߟ…”ØI‡o§y4a�brœ£½¯ äLFp ô6ܜȴVÌ#VMôb¡J`ݸ¸2IrËór;ÀzÍY ×8‚\ÇeØ‹*OiãDrŒZ1Ë’ÁC†aœö÷b“Ì ùê«gîºÏ�‰ýUl¹Ô–´Ç‘ÿrXW¿ Ńô‘”É {>.ÍX5'dÚÙ~Žöƒ$³^ñωØ&ÂàâƒâéÀ}¼í w^Vçô®ëoÛq€f}0žBµ¬ùîvâ1ó|H(ÛÑ™Š× XƒÝI�ÊóÈòß›lQݲ?w 4ÂPÀ¸ûv¡WO¾‰ÀŠþî¦ã QžYo´¶`éŽClìS?_a¨D<t@1"ÈPÛƒ�æÎºVÁNS!¯‹.…ApÙ-²Vª Õþ˜RÝn]�¯QŽéø_€g߃L«6*æL¨Ç�ßè=…ŒrÂÃP#ïiýÃ7ËÒØG㇠$P³ìqßW¬jêÕ@ƒö¨e�Jéío:ÕÞ?ûòAÖ.éÖjœŸg‹v7¡)öþhЛþ£| 12ÇãNdgÏOjÍ÷ ÂF¨~ÔE`^^Ãå Ý«ûWø–\O 8$ Ñzaûqñû�=)JÊjLË £t’#°Çä¤.eŒ.ÈPLc�‘ðUÀ{'|Ë¡">¥e&”*&ÈI9Zëe 9Ø‘æÑ{ ª>è¿§ièÿ4ЭL»·I¹‡ m×ö#¥~;°HÌè½â±×!‘'ó1ÔL®ŒHî!¯?Rôèq“cZ7ñkÝfŽÿN?š ]½4¡änÆÉN÷\ãPç�š p:WÝ^_~Œ“”û&µÕ èøqF‚S“X5�y¡ØT\O« [FÝø£¢HÅ*̶áªx§ äIM îB|kò°‘°÷ ÷G-B¡ìÂNµÌÉâ*௫È*¥û©¦iÖi� LRrG.õmœ¡ç\/�¸OàßÄ{wim\¤%ùöɵYMH¨Â€Öúna%e’ ØÅ€2·B»H™äVA‰ÏËM¼CmÝf!qËVA1)§Éºúé>Ùz²¾—=‹º t£Uè'ø‹Aá¸Í}«‚æqg+ÉÄRà4ΤF´Ó€9|(¸ðõˆ³£šÛJ»Où�§�,¢¾½üf'"¹CshcóBÊ !œî<î§/{f0Ûõ@Båí+ˆ©­²{N—ö¢Æ¶aA¤hŘ®hÎô–s¨jèˆfR‚L�É÷7G…W‘ðë¡q°ãøG³kRÙS$×I+ÝN$q°¤…žã\W•]ßâÚC¢Ö·¬+ªÑL/?›j±kGxRÂxÄTs 3äÛi3?ÌÜ·_ŒõoJéþò2ƒ˜`ó²5sKÚ¥�ÁXÐP˜¯.’v¤þ"Þ˨¶9%ìH|ƒ)´b$5öñN€§¸ ªƒ27º§sÝ ì¥  ÃRï°³x‘U€Ìwsm(¤t{²PüKʪ‘�n–X#(G³6÷ÇÄ= T wîýô@�š� J³ÖòøÛÚB öå'€…^æÛÉRº•H#ÐaÕ]kˆ‘— ³µ´V+ZNæÕì¯R|ÓãüQ¯'Ì\±i„ 8Fº3SW[‹Ý ¶VÖ²ËÅNk–ÇíQž'�ñ%é²V ã½Q†ÇØ�æáaд–z@Iî⎕Ã9J/ì#S™òÑËI%WÏñ?ÏÃŒTDŽKe¹GLUœµÿ#bo±”aã¼ XNôÜêÜéն˾@xfŒÅ˨<‹ìj1‡q龓‚LPÃ<߀^‰{¦X¶)ßd îó@!lj,‡d�'Ÿè­×8¸|HIº€í8C¶K¢Áxß2Zë!ç±ZÜBæ,“ÕîKÎãñZí!¨úF™ÞòvBÑ&¼ïðIé,âR�wÒBtz)sïˆáf¥ XñAíšfë&kÍçûÏPˆWt›° kË×#1î[¶4®P'1(Ïæ�ÖîT…-Êç_d J¬ìok…Bdxý›Ex¹¶JÙADrôN(ÜP.9VÍ;VköŽ„¡n`tmí54?›•<ƒJ~ Þª4srŸÒ„N¢)@å’¨LÿYÐ¥Q_™µiË¿8GýÖß°«DÁ_Uõ½0;™WÔ·Äu™6Sb΅жҦM·z¡RšWÑüZŽì•*¿À¶þÔYº÷=á©|¶ù I›æ¯×ûÞaŒÐ¡‚kf£;pÄ?ýi¡ÜÁçz–�.Îþ� �ñ¼_Y:õòÈ¢M�B¡/U³9ïÈ ôÙŠ:†L&'&@’:¨çáN.'`ÅŸÊÎßÜÙnÍ�¬ëðpÒËþhk’}Óöò’~Á†§°Ú¶Fäž‘åÀwÔú°Eiª:ŽÌ¹I1’rÅ'Ã.LšP?$8—­Á?ŧùúê¶;žÂ̺*£À—OÊ”YM;'„ð}‚ìÝbáP"¸-בur*0gL4#ZŒ¿ÿ¼nÚE€ñðYvŽÆÂcég¼w9tn@0ÒqÂrØ6=-b®<îßréŠëýu u‹A >ý[ËXŠPØ!m 5k¸ô$P8êÀ©£z²fÏ*0á@ :ùŽÞL+ͬ{÷$wóí›ÕøRzÃå~Al˜ 5V>ÊŸš?ÅeÆ¡³.fËÝðF þ÷�vßlc„ã:”“ÉÕA—(þòª¤ozÿùþ;[*8ÖËF8Þ|†ÇÕvuü®„Vë?‡ñT©ª–oö•’¸'|~|ì`ç¬õû#Pÿfwþ0B³~¶A]nÏòb^­½sÜd2n“ÐÔ%šË­¼r�0M�¡¼ *Õ6NsÓ�Úm‡ÌÌš¸ìð*©Ý²¯R‚9\ݸæ5z¼2B”6 ©‘ôZ;•ßL·þ¼$²#B•™)”K_Ø¿QšLc˜©U «Ï™\H&Háø…ÃhÈGA-%éa f ©Ðƒ´8D?àQ`{ïHW¶;Ü“<îæË«©+<®iGœÕùUõpÞƒí#· @¸Ã\¿ÉØ9áùRCbÈÑí?fú§ŒAÌÎÙV(jIï¿ÄŒÂ-==BÛUÂwõ¨ 9qñ…†ËœôXd¸$ØÜ]9f ‚Òò¼ŠkŽF=°MJlwR0„ò>\�z@„ñme«ËRåix17^lZéâ ìTNú»KÚ}¿Å甊�c)±X†ï¿,Eë2È$¯þ“„ñr—m’(ßiÎ Ù[˜®PmŸ@®;â²[�-ÃÄ$U‘<Ü#gŸOÆåõq{hv©”ö$À TÁhi¼Ýé�˜™K:Ô�-,ë¦Ô5|!Aaù9peK‰¯/Þó¹pýá t¯u°³Œ‹®A xOy4MåUÚíµ£Ž2 ši;Œ¡jàì^i—w•ÀÃÊ÷lSÞœ 0%8WÐtÚMˆ„d�¨t�#ôMÿCEÀH‡ÜZ4ˆr/sIóÐbSoÄp~íèüG>êí¼»#Eü;ÓÆÃ]ß&;A5�¦(-' “ÚÍ…Åêï?/ƒÓ‰Ë#ï�$‰?;(üV²4Ù–Rõ‰�ψEoz°CÚ?·Qÿ/´“µT2ÙeL'+4üéu*Œa…¤ÑC¸=ó.sÁiö?ËØ&Û}XF©Yè[U|»Òàܽ/­.½ÔëææB3bܼi¸”t‹–3q1þ°1†¯?¡Ô…]™0Àp^(:ßÚmp¬ª8ð+ÜÚù‹„ïK†ŸÀºþk¡¥ÿ'¸ïS@ -”Ch3ëè5•Nê€ÞGBÈí$ëL˜7Ó:ÁNÎüe à(Yzÿ Š®ÝÙ‰k¬òž!ÐÌ8œvnM0CÓ=h7Z«˜kZÓ�ëC©lã`( Jé�§ÜÝÄg«ÞIù|[o(·Ù¦Ÿµ†{½­ƒK­hNsçCƒ÷³H/ºãÒÉ ”â!ÿTÈþÿTNö·9jx°po^Ñ)6k­yâ™\Ò$ðWd•jÖÌ ]‰7q¹Øÿu1Îdµ:ê¶r Æ@Ýß^r”THèŽä)õ�ÂYº7–òlÇ8äýR�\ sIÍq©:tëÞ†Owú¹rùt@5´·i´êò�îf\Ý`¶nè iˆy#¡î1¥CBÏ`j—Œþ¬@îb„OÒ� À:PÉÜvîóÀlÐzBŒÑÁ3T{ÇCݯk3¼�ïc}ŒŒÕiÌ�µ¨œœ;í+´HšÎåìËÞáƒ�ö\´Ñ! —ž(öi9‹–°c‰Dƒ$ÔŒ¾!Ø_dë~Ôz+—>1瀩ŠÊ§ÀôÎGpàH$j÷ß–•p`ñG«¸}~ ©ìŽEAâµ,“ùKV†·Âdxé•UîeŠµÑ²iÔžu�¼ì ²yÒ®ÅGÇ¥#�‹VþÎ?™ »+º_¼�ôd”‚�Eˆ`7ÇJ�T:a(¸èL ½T.ƒ Î�yNuòéx›\  ¯#°kØïf‘Hâ´5ŽíõøÎ5W![Úb ÀYÜzj½¨zUš¹ ›ø¼¾ìð)2²êóçkZ¬¤Ô;X#†²é’Ý‘Ÿû¸u'¿Ý&l é ;ÑèœH�^,þ©ûªùûLJI;—k/3G•çZêš|u)¼»^µöEÄ7¶cø&çér¸p|ìê­4MøL ˆ6è4£YH‘¾ξp4y#O3éù¶›f� š˜Áî¾90{»ðPôÝØB¤©|;ªyð=ðÞÈ�ÀQ} ›B �ªÿÎnØ.¯ˆ;éþ$Ó'[åmò±¸–T(ëvÖœW¼Û“Ûß›ÜQîª:ÍÝ3–tTĵafbÙ`„Öz]†É¸ÿÖPï<MÎ� ©…¬£þ¨#Ä›Èùg•2¨ª65óx«ÆWD Lë½µÞfH‘ƒSA3šcMY¯œÇU) h>ˆ¶h¼�T¾C³/E4ÜRëVÒAð\\®þ¶A|“á[påXT« $mH¤ßsïSoȧ“¿…$MÒÚž3À5ní ™<_š²_|Ž/¹’ôm‡œ Ü”´èγäœå/Q~YV[ý£ði1f±@²•“$ÙM“~T„)%Ad"d{Þ¥îÏjȯ%´q5/4jó¸ z|öúI.«Œã¹/ˆ;à‹�=�ó�<ùp÷}uV–'”Vô|=‡U¹àöC©]®Í`½y©4”çEœOíq÷®Ë£8xdû++kþ’mjØ�yQ=‚«©h<àïÖ£´&¬Ù½J ÉŒXÌmv_S<Š™MëdCS¬%PUq3ñÏM Fwø>ïîÍfÉì|98¸KZ{ëgB-¹—Qèx¦Úàrc‰ÿ7ΠR,c¿wŠŠÞÇwÝÅ>ãÖUq0‰yiÜÿýY5'‡4"AUºð%øÎ(h¾êI PTè–ãùÛbÖ+Êt· ¾ÄN`tæ6“¥&b�‡ñqêÄ̶ê=2ºCoé$~Z.™Â:pÞ^v��£VÜ6¿ø–Äÿúq$Y-y395š~¯ÖÙïñ'V87ë)¢ò¹‘è.·€ /’‹9Ðù~óüÈWc°)b¥û€´Ìð*™FµÁoÓ U¬mEf8%ñ6âÉe 1²@] Ðú o‹%ŠA¢ß*ÜMÜ#d¤•¯ l½üþÈ‹Km„è‘ÅhˆË·¢ ÅJ†uÊ­mšáò~@~£ýÑ=X“ø–;‰m³±Ò.D% PïZý?bŠ‘—™;�í[ïü鱃\úƒ†Äã!£q"`n™)#Y°Oܬ/`ŠV}²MdX:Þ�É€®sl=sÍ9�ƒÁàtÜö¯Úo;©­óo¦pt Œ’Ñ1Ê1|oM€³Z&öëq�o^�)4ú¶aanÕðþ¬¡ö]À€�0 y‰8|†-ƶ͠.!mžÎºº`aƒ�`î]šJþ&k›x2+ûŠúœk úõ¤í�)rÊæÙÄ_ WÌ©_[‚ÈQu7¼Gg¤Ùçs€ŒÌtÐ;¦·*¸ÚÜãÄjtàÕ«°_)×ÖÁ±<-ä&ÁE¶*Ÿ´¾žK¾w’œÊ©§Û°Ãlµ{Vý�%ø/: Qç–ˆüÞŸšÅ—ÚpšÎ†ÏÓ"—Ì;Ö,+?Pç”@X2>h Ò&vúj'‰™%Ï·=ÉÑå&hx�°¼&SZ/Uú*âóÿVÿh¥¬RgúõŒ(O�—Éù.•Ék�Þ×ÑNk�Å^†CghZ°õÍÞÁ ’#Ç] >,ô¸É£oyŒ³¸‹¹­TeÑð £Ø‰üöñBN÷"j0Ïf«ànOH7KÀå?ž]Þëà ZÐ6ÁÃŒ=G „KÝË ŒÚ}m˃f’¡ÂNQùú*„Ã1Öw^�§a¢ଠSÚR°Ë�ØFmw˜î³úi%œIKå¶ÂÏ„Z p¦á»îÚ�p]ï’¡½Rð5ˆ¸ÁÔ|£ï$kBŽã¡ÃWV¢Ù˜8¢ÑVÊǰ@ÖB ‰ ¿r¯ ¦¬H¾Ñ¨„ëùóÜ.±”:”7lLñu´^ö¥ÖÔœºŒ˜™mÜȸŠ&-äCbÑ›|²G$�>í›ßÆ—) |]q2Ռկ sŸê9ò²} ›RmmL.”Îôí:ðÇ©^±—#ê$Ò Û›ÛØî îÄ0çÚðÌx»ƒ“™ÑÆò8R%†Ç°�Ní55�©n!ö7N€è”²®«5–/,& }ŸE ¢X_A�Éïmvz;{Zÿ }òk‰8SCžœúÀdùD[/›u®wZÎnD'F²„¿r³ WóGޏãNãÔåU½'ýj+P1e’aé¡sر­=æ– ¾4hÕu3ìßïÊW`aÓu‚�ø =©»j‘õ£n ¶#rÓh”8TõgHè󡉰ã·ÔQ× Ðûd¡ðVL!Ac�Ê«q‡aY=Ì€£Õ«·øÚ0ÔЍÚ}re»§(Þø Íí¤y,ä3[€��4LèH2Ýì”f§á½¤O0þán6…eàI$ %ÉÐèÏ(­Z½ÞÈ|ŒéúùAä?Lfô¨+Ÿñ²ý¢ÕtÍ/l~OmE\Àæ†aˆ�¨¬íZëc’ìÂÜz‡OOJAÞßÜÝÜ¿ZK/ÂnöïúÖ9Ç …zŽýdg{L^ogä'Ü‚T˜E 1u§ÇÕá ã]4…ñ»â:ƒ:è:ÐPúŒ²E�R<–q%üTDôv'“v<~ƒv+1u_TÕ¨CÊ_¾´�¯�áÙ^Ø33 ¤¥SÓôá]$$kòN_õðé>z I+$Ð,c+¯_µ�f#ù Oõ™°�ù]Äf(¡¦ËÛ@t38B>šezØÇ‚%µG§‘äõï‚–2žÑ#¥NFߪ ¡Ø•žÄÅK« Š;ŠJ€ôÁ“[)YëêWê&`ê+×EJ€Õ¯Aò ¨þWkȈxNç ^ëÄxŽKnLn (—ÖtaòÒÚßÙÑ ÕRLü!jdö‘ç³ï¾·YÜ‚úâ¶q&Fê¯g’:q ÿ 1—g]��žZß5 å^ïo%P¼Â™FAºÐ³Mü :y›>Aâ! ôìžÎ„žúW;V:æ9¡�Þp²h¾yûȇóSª*êÙç­Œ�îH›¤ûqI£¬á>v”_æ àvY¬Ô¿Ñ½ôD�ýðWŽà„c)êJ¶[úa¦Ì68$q!SݰÞm.4ÄΫ¤D'ÊqSù+ m ºÒæ¤(Ò¸‚]£ûmJÚÜ-Äm€�’©·ð—dã“8¯(mÕ.+™ØÑq$Õq‡ #þ×›Dž‚¦±Â½^6�|Ðk<óÉC�ò†1ܰºD>#5M?¡K0H?¸³%À¸-+A•¢¯L6ߎÕèkŸÞj@H{ž>½oz‚0ðGÌ~OEÌ"›\8/ÀY(¾Òníl(À�é•a‹ámFæ€6.Íë>ª{ò:󯥠XÐŽ&øÃ¶¶v*ëÙ§}iŒÑ&ׯ¸¹vJ a‚Mõ˜\òÀѲäb³E¢Si'oŒ—% WʆôÆ= Dºª’ õIÈòÚÿ]Í='é¢8!UkËsäùùâý'Ì 51_J£ àã8ä#¿k“ ÞÆãë�o³QNOÀ˜†5'NÍõsá�·ïÅ¢‹å¥Óˆ´ úW<Ûn‹i�a‡�ØrKlÇ.¸±s³ÇúËgéíL#À¾Ö5kQë)c~`oÔè“Ëä\R]|Rjœ.v×Òˆc—”Se�«n›´ËÞßø˜¦å ö oýS¶6 vçXen ÞKôgô]†ëŒÀÓ¿Üu&v.v½õV[Udj/Ì=ÓmqI-i×…ÒUŒÁhž^² ¼ îà¢ÊD(�”†¾ èX‘â'JÃç«R³±üí­$µz@¯ˆ^Ù½÷‰ù�èí~}ºsãÇ�äg—…Ò�ªÌ'w8cɼ±WŠ�öŸZ8Š Õ,ÄÀÝ}™K¾£È˲š³Ð�=¯jY=ó”×ÈêOWá�è>‘fm$ø(`G4QNp2‘6ˆSËF™N�ˆ)`!0泬hÈk4I™ò¯�÷˜hö‡IB²üâ}-j‡WΪÕÛ0-HŒÆy\»ë@;¢ˆ¼ZŒW€ýeXJ¬ÿÐüVŽJÛ$h<ª è!Ì©#û�âÆc:B±Ë^²á�†Ÿ´ûåWªme¡HŠ_?ãþ Xåíx†p€°“Ù²ö;Û¢‡“ÔX¦hLÁ󌞜o2�–ðšD°RáÉM«âryl-H£xóÜ›L‚ó›æ)6ä¢ :f[ ¦W響 ´öþne~òA3dPVõF”bgv$[ƹ�²);fÊû†eÒŸa„È›Î8¶;4íóZ¡d�ºÔï½-7ò: –ËRy¥“ä"’­ËäÖñ2áD}\ž\zÍÁ˜>…SÇ@”ò?–éˆ/°íâ>ò>(‡\/KŠ,âû\�íE’¡C>›0õû0kg*ú½s¾×euh£5MÚ'pn`É1¹5äHJŒT_A|¸dÒÅý»†‘êɇ™ÓkpN cÎõT´¹ÌDKFÕÖ쓈6¦­‹S™¥7ò^I“‹.˪!‰FùÄ··Z¨]¢­ð'<Ü‚õ÷îMøTÍô= ¨tñ â�Tk0oÔΩ°­6‹ÁL�Ŷ8:øj³“ >/À‡’—éÖ1÷@›ÆZë±®=¤wš`†Ëša(×÷ú!�× ddÒC“�— ƒò}ÚЦ;G“؉ ®)~[Óº.ˆWv´Uñ‡ä%õsä×:�NvšëÚiSIÄähvYq´h¼2™�õó“ü0=ðöÛµ1Ék­`P‘¡À.­Bþ/\ þºh�8º{|m:�†m”ß=yß3‡Ì¤X Æy�ƒësVÝ-^ùj2ŸØ«ÁuJÞélqù"!®®aHõþ|Ô�>ôiq w4Ô1ѬÄ%´>Óâ×MÄÎg‹ÜY¾4éèv õËÛ–@øòœº×ˆx¿Aí§¸‘¦ ò(šm4u>ƒ¯c4ò“ÌÝã0îZý óvѯä×–?qÊ›æ¡Äm×�DUýc7a�7\ÿª;le,F¾[åë�´–€ã Ÿ,‘,IÔRãO/ʇÔLrÕë6ŸVd›½`® 7 ¾QïCsß)t­�ÓÑN£ÞuÊèûìýÅXýŽyXAJÄÈô ‰¹¬$.®q¿Hé5ëÁðCqË aRöôOp}m]c~³ŸF‹¥ç§J†}?ÒM?Ü-X~g3èfšÉ¾Îýç¦lX-´ÿUöcZÕ¨~Ý–#ì‡Àý£T&,Öò“ÞÿÊ'‡Ÿ9þͪŸäzŒ­Û‡‘ݘ9"aê{XûN*¡ªí7�aêV-Æ%ÒÓW { ±g¥¶fç/ýåžúw´éoÝžGƒ·Âƒ Á“nùÉ€ ø>�{õm±Yà ™´"èÞ×`¤ÙÅHö1rb�6Û㊥®e“2#Ñhll ùãˆ,ºì²-ÅDZԢ9‘eżSO6D‰#ífW¦©¿ksù0$reHÂuRP.úⓃJ�ÛL1\๼a0AXçDOÖWši¯eßp#%´Ô,λ�¦Š£z·-MÑnWúT87Ñ›Á°À 8S$ Öéª_²ë{øEÏ(W`�ñ¢[)D�©ûòhZ€6ø�³¢˸ŠL“›Æ!Ü)ÛPíÒ²ê–í�[ãýd�Ð[Þu•˜|îI¦Ñ÷B¦&“Zõ«$©·g)j Ãã¸x[ýxù¦Vñ b a2—s��SC³K¨K=tû-�s~‡gÈÛ‹åÇŸÈmÓ‡å;–Yyò�$4è«òdÍPz[Ú¤ÚQ±bÏ›ËÅK›.àTzõÏÛŸFP$ïê[á§èºF혀KèóKäȤÅ–læK£·‡«Yñ„ãøçÀÓ9¼Î'1FùÈ¡¼kaxdæC7ý튂óÝv’H4lQt­v‹M¸ÈD[Û™$¯@ dÒ2Asx‡™�3SÎhžº† ŠÞóGØ„k�€§EÅöwÖPåXl^ð”‡ç_èv¾$RIɧWts�§³ÝBfÛ>¤1©x†ËÑ@Lv�ŠíŸŠךaÄ•)ÝjÍçÝÏXƒ˜ÊK_!«Øvµ}ÂæÀVxe�¦’Éà‘ãÙ¼«k~+(¤GOúŸð;D}>SΉHðõGç7ù•ÞÌ{‚œ•—=©ê�±ú6€ßŽ’ìÌ%Ǻc:/X®³‹§Þ .1huQ4$c*²™‰áf6Ïs>¦å³<üéŒÚýÆKHB«¨S£ôß±h†“%�74'«Ï©Cög´ @ÍǨËË„“¹xÀ\¬®°¨&6?÷ÀK“H=HþNi/^©¬ÔÀâðnK,±²Žr¸lѾ¾Séïd¸ŒÂ${¹Ôpp~jÓ^%ò ð�¤ƒ k8”ÖÃø§›F Z7daýuþæ:p‚ryê k%ÖlÐT(׎à~ç÷y \­•�8q³LÁŸò H.&‰›cü©\½îb¤í…P\ÙhgXY{ãß‹›&€ãÃ…�[WzÎÁ{Û «~'µ΢NÝoLý@T1#j!â j¿¦ë4"yOF/¶î–æ:¥œ±u¬oû?ЯºLf©Š_¼¥Y ±|³ñ˜‡OòÀ0}˜áITÑ�4L8z’˜%�€-½ÿkõÆz}-×"É^e óþä¥Í�4+4’ÿ)ˆáÂÓv¡dûYR_o@Þ¹t‹še¨�')ëkýúö[¥m¬éæ éÆfpl×É'JP?Éáñ”Ev·¥�âA_Š n�eøÿÿg=s=ÌÛüˆrøÎ¦Ä J¾�®¶Èiè4`Ù7ÎðFt¾¡ÚgÅúÀæœ8Õ ¯®Iôµ‹ù!ÚcV¢ÿ[‡ú¡!åØû§/Ír½›½àÑ~=G|ùû@�û4î»HŒGÙìDЗ6… ¿9asœa¿×†Ùx7íH âñ€Å‘½ãžU§«Ž”ŒG•ƒ,ì®0ÐÌÖK¹×y` �9£e�Y±Gú&)E:·ªy{þj�«á¹æî]¨Qiÿø—é܆¸aÊ"ÞJç?ê=¶fC?i ;Ü;e‹š�'S5^×Ó¸w>Ö î)¡KNÝH{è{UÖœôsxƒÐâNZ業ܡËìà¡øR¯I._é[:‚´¥íËüMzI<0mJ„Õ Nè”eiÞÎãnÿ­qÊtÞ‡Ÿ?P |ù3$2# “ðã$¢{M€©Eì¬ <î-ëqt4Lja,¹‰†`”Øûç„ûÔ€éÏͰÚ=^t&öȾƒ²å‡kh y9urMNbç±�EÃÎu½‰  óÂ>©†ç&?4֘٪Ό^'o]Wuò´yÎkýW‰÷à ºwälŒ ×G(üPOQ%Oo2(%›[Z;£Æx0?4òÖÿ&™E–L^ UŸÍY\X5GÇ8»90W éë+P?‘ILÎ9ìC…&%_Á<™eW¶s+„‚ç€KaD[�žko¢°íIÇBoS„’#͘–sô€C‰ÍXÍèêî´ï ”´Õmç"#(Ä¢¨Q2ÌÀH'aCIÎD më@M®f2 “”Ù(˜¦ ‰Š¦AÊÆûn‚5oÛO5›®>js}{t•Ëí¶¤�%å/’#S½<ÇŸ<­W@”ð&Óéd”O=Õú`‚ ÃQ3weüõà½á.jŒIx?ò-­t/íO¤ˆù6q9�±]g…`‰Í»>Õãâêc=ä5p3�‡ú‡l}5Íp�?2´Íâ8àiÅ*CÅ�ñ0ÎK· «&c7|EYÂø©Êçx}Ð?êHÀ@�º6–MQ¦[A'N\Uk¾¸�}Ôù8¾½sðÌ&6âLƒñ¸±ì[z³¤YÑj-¦Ì˜{ºßäfŠj’%›ðKÍGÀsà‡Ì:!°×Ó»Îk¤Öf[õ`dpXˇ8åT#yŠGdå{”ü#—ö_ÅõÎÀQ]º•3}AUšs¥|0/@8©“ѽ:5v©ß ÚÕÈÚâ¹`J. }T9,dUVŠ i‚8Fáp=Û¤¬Ä˜µ�ýé­5kÜÝ-t\‘¥Ü…³.© ;Òð€…š†Q‰§î„ËmðA‹ZÐ8§Ç‹Ï- ;æÂ ar@ÛycÏSÿ}ü×—èx/'oyº¾\•EŸ.ÅO˜ü!º±ÃO�æ­©¬ü^D¬çù¥ üé�eø:ßÙÑáv›Ÿ*V˽<³µÊ?J,&LÞ:ÄÛþ±ç_§Ê?Áœš�f˜2š¸¹ñ·@‘[ô{„�=ï¢X1iñãéâÝ2–Ô$S qÆS�>Óâ?øY�àTP`å[Yù[€J”ü1}ƒïN¶sTc’´AÞ|€fCºű{o •¦„=áQ‚X@LúÖ¯…—?Z¨xœhü–Nà÷È:ïÆÕ-¢l;Aïu[m¥`ü•v½ÇŒÞÒHÄ—¿’‡Ä-<ñÄI\FÂLßTû?¬{ íù1ÅzF=ójW(9iO1sy°}¬7Ëuc%^¶8zVoþàúžñØT¶Ÿì¸c[G)rvJtƒ`ú ™5z•Õ4†Ìã-skKÝ×5ö[òjÀÎO;½È{ D‘ž�Ãx½3i¼Àj9Tƒ¸gY̱WºÍØîùëãî9þ ‚#IÀ"}Ú^H$åW#.=7@Ã�á dæC�fVý?53¼0p'ˆì¸;DvVQ‹•Eßå·]‡ÅüµX³ƒ ×6‡BP%€øFΰ´©^Â|x–x<޹ErnvgQ�¸-²°h|H_5vãÚ)TýÏmoÂz!_9U_nõêÑö¤~%Y» ï9!º[óßäÏ$Z>ÜIê-A>&º,†KÈw­‰[ÌØ»ïKX‰”Ÿ¸%A_†k}¹>Ôû[ ÑdK«ÓrÿL#¥ ²¡“4eCCi™àbºÊÔÞ;šfi5 3  À<Èä:b%.—'”a³@iÚÌ#ܸjNAª„ú—U…M�ÒEÍë±Gµ‰7@±¥¯î1s§69΀ºÍ÷ÁÇO¹£ÞذÞe+EóVð‚m¾„ìDw'‰àï(ú«øüé±Ù¨ýI¦áq—“˜Õò'¶(Þfó�AN.Ô:ó¦Ñ_è¸YÄî–`—§.T:Œï~»iÌžà˜Úéx v¾p=Òő·½[¥ò»"¬šiÒ½M!NܪGÈl—ú>Ï)@%�¯O‘¬�#LinúNmÓWó©öˆâ݃[¦#¡;Ù‘/ l7)wŸ|Æá…7à%±Ô�„zv�¬Â<:ÊDàGkF†£‚1¸øÚç<òQÁoìÿ†#ÖlF)aK œêĘj¹�ÖÕz—îpŸ?P.ZÐ…wàÕ{¥èXȱ™xSý«7Ùˆ«XB&#u?›?C˪Hø>;?Â;ÖÙ¢ºMë¼TšjwH×iC«[áé¼QÐ)g—(Ròý�‘¹=yÁ×—fà ‹ã„« 0A"¥µ^<ùF](0B4ì–DWªÈ”Ú¯§%¸.úƒ±aÁ]è>þ"P�ïúâÀñ;b3ûÛòó7:½:Í$1„)gz› 9ëŠi‹ä#z«,Ø5›úÉç}®P/¤3ì »Žz-‹LpÁ®Ô�†b<}P€å•Ù¡ä´a1È•l·ãDÓÚ @Ý› ®–ÉÇÝm?€aaSCÞôK˜f×õ3žgàð§×±ÛW›gö»9wŸæ2j&a¬­lì#ŒþD©j+ÛÚ°Ö?$Ø 5XKТ‹XC®ò„6zR\*;HS¥c©§õç?êH+>´Á®òM_º<ˆ±optsg*-"܈Ù|¾ÔÏïöC_ýAÖ÷�=ÖE]&€ý{¸†�#íXœ&V«í@µß·?óé¤he"i~«K^uô´-8ökUB›”'šh»’Høýu6‰m#s‹í�ÁCR�Ψ6À§û5vˆàµd–Þ¼b 6Kf»Fj:eTÇzA=9�ŠËw$Ýõ —®¥ÝeÎÁÛ'lôb¯›íé<Ͱþ<�»õ((A*dÐc)ÏZî†/“,n=¦¾«�¾)¼<êŽ-ñ.‡ß!år7OvŒ7gxM«:CçÃÞ¨ðŸàÏŠä¸Ø’Ʋs^£ÏÞ†oŽf¸\ªác$bîwÐrF0RÅä䙢£©EÎLìmþm7\ò*º5½<–LÔḭ̈ðp”}’8³Mi èw O ÝP["%µúvݨòғζÍÇÎý5Ò½ã¬k÷A?™f…G@vÚZJS@-æ‰t®¨nøôXÚ*!�õ„IzL=2ªÑt3?–ÅÓP§:s5µÀ¼-y‚ žmµjÊëäç^—íê‚&ÅBìeS+~‚LZ("XCn’m°³p;qZGMÒ¢�ŽJ¹ÄöS¹hoÞú¡–çrEU®±i³6 ÿ^‚ÿžJD&?ŒÒ†Ì…D%¢¦(F;]M‹›å]á‹î} Aª÷¬'9öZgššgEÊ=À‡pŸš*£²xà㮓KaÐi’߃0j¢<Ù×jª®ª2�}±T‹³)€�‹m)�y]û � øn(@ OͲD�[éë^ö/ƈ�Å"%º²-cG�mâëÏP#Ç÷çrבk_`²ÄŸÞ @T,"Ü)#ãØ×ffcOÆÇÕÎkRÒ)ïq›0¼_KÈYr;'õ�—cq—ùÙÛ÷Ä¥ø—kžÝù‚� ó²]" ‡éáêuG÷žfœê_¥5ñG’øq§›oÜ¡ºNïYŽ�­Ê[r.2Ò±·ìsvqÒ‰1¬Wzòmä¥í/C)šô/Tażœ k!_ÑMVDM±ØÛQúìÌ,¬PŸ$ƒ@JWÏb-M7ä¹F‰öýÇ›ßXgG^kj§\ýÄ4,8€LÑØ�šU¦Ã5ák�aªcÄÆHE&æX3ùT~¢aßH�àx…ë«tZ¹DÍwî¯ç§ÇO$Ef|4K©ªnSµfè˜òꟶU{óº6Îó; ‡Dì0»q€iµÑFNVÛ‹CgŠº÷úPÍ“\ÒZ{F} )¯R–ÇÍÚÝÆ†_yd“|‹ŽQµ‹?hÓSiç2ųšI8u¹ ¼Bi¼�( ô=Tú� Èj7 `ú×ÃD¨^h0‘-·¢V\«'Œ‰wrí»[Çøµ±¯ïéKÈ N�v¦F»ù5ôkì 매 ìˆ^+wÏ(e)²·¹Ÿ›ìyÃ-XÔ—ëì�†/®À;bk #ý­¬ìò(V+—3¿kœ•ëvÀñÈù²ùPàÈÓ£~ƺÇ_äè@†Çm¾j3FaÞQ®ŒßÇ©Šû¤¶‰ãaë¾®7€ñúÉe×*S ø­¬õµÁ >R½D•]äd6!(:ܼ²�­¼½U¡~HÅÝ¡B´éy�2è%ðeõZ¿ˆ9÷|t€Ì á´çïÂR÷$Œï²‹«ÁîœÙ|qÞ`züUÐÌPS7ò[÷ÃA<…œëLâ÷0.÷Ûúg�µ€S&K-è/mV.®†eMœ¼¤„ï£c*{¦Øð²µõÃ�g­ø¤s^Õ]0Ê1Eu(È$œ{ÿ<¢Xµ#˜—ŒNÂCèÁt,O2[^�=¨úî¯Ó"Z�Ø?‡Ýkx¿úáÐÞ YÑŒtV²³SÈé<óvÎØ"ÔFŠ{ÍIíd¨^õGxž0Ï4lû5]ð©ù�×}òaeE¼Ú…ÆV%U|¼D~4UûÕN°jÇ÷‚··ï·RsîçâÃ×’m•®E�£a(tÊW§V¾Í@HȘË<ÅÝé?¾HŸ–W¤’Hòaç5UGÃÕéðª®O‡];Ì{@‡ÕìüÿU_½ÒÜ\\UºÊ0º®Éo2´[ `BÚ¸n>ÁéVùyî<<®IËîô`Ÿ ºT5_‡KµaB•yùtYeÖmêsœqÔZ�&5—à’”ã(‹ 2÷‘/5¯ [ÖâƒÙ`0‚!gE³k-Õ‹#°En®ð­0íÙ�ô‡ ü²ææ�!r¥ú £šIŸ›.îîý?ÿrÛ%£R(lÇi]¯¯}·lÜŽßd?AÙÙÊê.Ãlmw'�<ÚN…É€Ï „C6J†åó¸ü$K'X°PĘü¢„We~.`ºp ¸iÞìgeû÷‡V5H6Ùÿ¼÷™a.¹Éë}(�ÿ(=MY§Ç¤‰B›Â0;qR/…UEê”�ü™ý›½­Ëwê7¡Q¶ÿìXï~arEà$kÛ‰‘ȹŠ9l¥9T…Ϙß3Á€«3„�2:w‚¯~Ç+âÓâr’�¶—7Éú%½|¾"íÙ RQoוÌmoL`rÆb¹Ãˆ&&_«‡Mcl¸ÒxÖju:hÈd'‘±y1LBOiyS“?�<.LN}¥©u EШvGu}æ—u¾ÖC#}(ðëÁÃY�æ^SMª!âö)}�%²_Ü“FÕ€?§-£†F›L°a ˜݇¿ ËFói.#G˜Øšÿµ’P?W£UÕ÷`€á]ß#U˜¥c*‰ØêåIÃ4ÿòó®Öø.é,˜ª˜ÓG“é::¤ùQÒ¾gZñâ›}x¯�Y§ÊØ]a#S ˆ¤;y«,W5‚¾N‚æ5©/ŠèGí{ðXa;0D¶!t‚ÎI¹áÏÎÛWüîk¹à¬ÆÅ¦nŽ¥„ÑÏàÔE,�¸òO†¡ylÉ—VM³æ­œmGuYSŸq¬ÙÂŒÕ`K3×$º"? ÿxxŽ€ZÔ›|Ày˜§®:«�*G”“_çó’‡ÛJ}w”3•^ʈ‰õÕ ©±6}CÏ�ѱúÃMø kÌÑã^v$Ç¿IhUI÷XRÖ×'0îD „VMLÁºZ«l›¼”(GÍÅÇ*‚<¼l�ü‹ Œéõï}ÉÝ•=©^ת¶ÍКRŸgùŸøÿí�ñø¨i̹øïK;ö¤�ÝB>ÓV˜d5}M{yºüÀê±µQ¼€÷9乊ä’#†þZ§/vྚ49dçp`!²óíD\å0tì0r8ô™L”5":¹ôCoI:+>™€[]â%¼ìËJ€.nÏÕ·‚\ÂÚŽB»°/ÜγüÜ)¤?Ñ0éI)êâ�Ã-8x5Ä}‘[Á˜ñ×ÈÊ9bT®84A¦Úpc×ô@$Ñó†ÆŠ˜œ&ÈÛBÍŽ Ë­`ø´Ëx�÷Þ)•_,ômš¾L'Ç2 d߉UÃ+q‘ª‰!ÙMl!MLa~§5j…a¢<ÖŒüxµÕ:þ+eA5³Â*³õ´Ù³éX ¯ûØŸÛüÅc"ûúg2£²¾°êñ÷j‘q´o£CÔ�L°]p†»õ²jÈÔá VGjªÿ[DR²Žš«ßû¿Þ¨êKAœjN±Lúm‰-4Õ�Û �8» "¾^jwÃŒŸ¦3+º5]ÙÛ7ô˜f3}a»½Äì+é~Œ/®]òmTËb~‡?ÊrAþzÓS&}ØŸ Agï³?¿ÁËa¬øÞsltåâ;—1å騂‰Šx²‹N ™‚¹ëOËߌ2wí ð±W}<‹å^\ëLY�·Vé[f…8ï(õ¢ql€ë*³r-µp§oËãÕ ÷œ¬.¨‘ÃãÇŠA®LÅ¢vD�üè΀yYt•̸IE:ŠEȘXÛkeØ Ò)wk2µ®'ÓtÿNFjŸJ܉ív_TǶI(GëŒ4øì÷äù²qŽ·ía\šA'em•ÆèÄ+Å»~’7ÛÌ ÁÔ”}@´ÈÀóåÝ W'Ú—""Hzª¡„¤yv `ÅåJ{s!‘¡.õÉÍz¤YS͸4!}ý>„Z“k&1“·#Ò ÞîÖ¸‡��ˆ§NÐQ5Ÿ—œ„î½Dùa´P?6Cn ÈÙì–%¤2£=7ÕÌ�ÙïEòqŽêO&>¢IËyÃê2ò’×ñy /c•ç›°Mt£=ZñA®ŸD‡W5Zl]ÿžc‚:,aì�"ßs~J 'ß¿h"±·]^ö^©Ž¼ª™9ëúã955%†¼dÀÉÆ™Ô¾ïÕ¼ÆtCI�àR‹í”~sBÅ`nfè‰U©…Ž·½å�8¾…7lرœË•°ˆdº‚­!J��2e5î¬Ð.Ãí;µßÔâ�Î~�HJcsZš\Ä$ɭܰ"·„üÐ*•nŠvOЪÀ}ö¡©hCLuF›§¬Aôâ^’³³ªsxFŒ‘õÊ\t×]Œ�BâH¤Ãðs‡òÖ�qJz�YëÖÕݢ͌¯#+Ñ� �7QêgTчžÈXÆKZÈ�¥ú˜°ÑgŠ3y5Vó>ù8…q+Þ´ÆÍ QÇ×Ó¶'©€ÀW‰<(îîór�?^i\´Ïæõ2*Í`‰ÜE)¡¡æë‚„v6LD½/ FaXÐ{¤öÁ´É�C­J“ð8£lÄÌÊBîUfÙÎDz"éöáeTls¸YH3î¦ÿR‹ßG=5â0wó¡9LèOŽ“‘xHך>�s :+Cð*Š/9i%�xáþŸ/c�Õz¥R$IòÂÅŠ£ÒÂÚkPa8UäË&Ù%4µN¢¡ã9°þTÄÒ²OÚlZî�%Œë™ª€3 N ½ÑaùšjÎoã÷ ò¹]é+�Ó 6ÍcÞ¨�˜ï‚ü„Iyê»Yò>(ÄSÊLJ{GA³kOüí'f—o3Üâb!šö"MRt‚T¨Ï% “J¥Ç|“,$g¨û–ΕûBIŽá+,ª¼òVÁ5*~] ‚IŒD:ÁW3En°­ðr(•ÊH^LÁXô–uwÁ;è~ÕÞ¦Ã~x5™Y n]kž\^¤BÛŒÚÞIü•S,&^Bïxd@àdØ#'(åcQ’[sƒ‰ßâ£.D·OññlN &R`…»çdñ´–=nåÒá1Õ¾ɵH.e?‘¼6g3Ê™8z�7‹À ÃÄ3+sÁõƒ¥Ýj˹޼8~µ¶¸æó ¡ëé\³ÕkŒ÷ÏN•“›‘íW>ÈcPaÞõ< ¶ÐÇäH){û�ÚõSÐ…ɹ隸ŽäGÐe¨Q)?"ÇÚEžõ1‘–NÄÝaV÷›î×’üÖüÍY&¯hÄ~Xª•â [^¸�#z†A¶>í„zÁxÝFzž�7^Ž�iFãhg Â3˜,Ÿ0þ’l6Aîó ÃÒjõHt=Éa€ sp”ðêô2Ñ› �Ù,­%YD¢nÚÁ 1ØÇÜ ®kÉèë?¦,¨wE¡Ž%Éℸ^rÜôÛ5; /áïCŽ-< ñ ÍMÙfêÌn2r½�äî~ÇÒ£6”\E¡$@ÌÆr7§"¢® /¿o‡ØÂõ(©2ËFÎßzèp4Á˜ñÕÌ6"ÞÇ ÝoV´É¡ÿ}P?ÿñvE5Õü„? „;6Eny–ô÷m ©t j0Ð&ü7ᬚÏCPƒõ æ!×ñ¾`¯& T¶ŒËí|´¨ôLßêÉ"ü`!»aÅLJaõ&/ùò’G°2�·îL.ù—¢jΦ†‚~daÿêy¿Šä�™à€dÅ€AYxRƒôÖ´–ª @ôÑ¿­ò|c<ìÚ<Ž�·"þºøXFÿËîÔrek½«Ù�M{û#'f×<­0ôp¸ÇÄ<¢ 8½BUßÚªK•2OÕ!^·Ór3#…ð©$α®� “�'qüY˜^�pÖˆ-×SΚŸ‹ŠÕFÎÉ_ÛÓDÀÕѪ¹½.pÙº�fœ Y—öqc†”êHØñ: Èb!$ËÒÒ­çñ¢ñ`)ɾ+ñEÇá¹qiC¾,‡öv*äëÍ9<~çfÜi4ì¶å„ñ;suo`©°ƒ‡ò^•t;ÇŒV…ð#+C‚£2…«kö­Ç¾ H²¤‚•á"܃R“]k¿�BzR†ü ©Ð¶‰Àô# UôlùTÖ05G¿ÖÙÃÿ· o+ÿóE —påÂÿzÑã~õnÑDÿí7æUgeÀŠï»ðXƒjúh×C€®Ò½0S¦ {Žb?O>£’ÈÍdøn÷ÀN­“m’nÔ!ì.$HÞH„VÔeDJ86r­¹ëᮺSŸk»újà =‹™7x¢pQhÝüÎ~4n;‘ �½��ÉÝK`]ª�#mI„ ßµ·<:Û-¼÷¥(?#5ãb¬tŽf“– »ÜáŒ�«} Ð¥rL¸Y¿ü=ó¤GÉ“—j®œ…r ×&‚ÚìÛ­ÚÜÍÑ Uc|&U �Ëd¾³WG³ýÁ áΉP†¾•¼iãbÏ pGÔÓHª‘exÚ}xnþ´âzÒ ôõqãÁ2n&Lÿþ cêÓqww>aôù—ƃƒL~+2šlÚžÃyÙ‘’åQ¦U‡ØqSœÜÆ9s¨Y s\T2 E#×_§Rj#yíóåÑ,DcV¼X½�üSÚ<�tîÀ! ¡°ŒÃ )dïz  _å?KêÈA1¼a8ßÛß«í K§ýÜBåžæµ[¤âR B=ƒ{Ô'.‰� (ú¨�®"½Y²Jžûæ[É’Þ„Ö¹„Bó6!T€-ž"(žÆ+ó顦cÿØÀ�3Ü773˜ä@Îó«ªIó)M.�±«*OåÍ+ÎPBí*·ó‚™ÊwßÍe32:i}›SÃa…†~]ñ]º*~�ýÚlšíꪃ¡ÖD‰‘š20896:‹ŸšEÞ�¸ÖFh‡Ô˜/{<ÿØ4 –`мE?ì¦d¾æ!A+ÇBX%†N?™% …†0{××¾ æ 4LÈé‹ è¬É„0:Á‹7ï)  Hųw&Fˆ‹ov:©ZñòÙŠ�ûRWN‘Çv®¹�L TW¼0ÈwLˆžË{)ž ÔòàUÈñ/¼‘e]¼ WnÂè20C@–Ž%±¡ÄLÜSC¶"êñðuJ‚?¼£mìs,w¡#)B™ÆyÊ+T—ï["ºÛ˜­ƒpkuÌ>“4]^·¹Ò¦Ï<ÉË/ö2×=6¼û(4K¿òÖñØ&âôÌéæ‘ßHÚIîã˜×鿃RÏ øŽç @ÞÓ9¦q_àRˆ$n{uG‰Û˜b‹ Á?¢gÑX­÷°y’�.èIKÑJ‚›0"å¯9w¨qÇ.$6·~¡ë\d‡1fµß3«ŒxI~{šâo­ŸYaÍç„h¼ ð!t­Ÿ5–åw·êîc)G_œ‰³Ýb¨áSðVJoO®jåüOû8忬× ä;Ô/`%ú麒™JúÈ[×ǧtO·å´vj%4?‰H¼Tù›äc»OÿãÃr33s¹/áLM Ûv�^o?�ÓSf�ŒU“ÈšI†ÿ¤VqÁÜÅ�c‡˜Ô×»s†�û•}¼‰Ygg¬L~ '´R-yœfüª!�Påâs&ÁJ9ŒD‡[=ѶEy€«¹•u2|+»ì4×ÀëX5ìö(z£DHH²•“>Ó’Sºuue‚9ã qÖ2{‹﹉�”c› 4aV5ó"²ã¸%‹ôÑæ ë‘ÎÇ ±Œá)Ö,S�ôRƈhpÖ…Çœº_±±ßù ŒßËH¡�–훲4¨ÿ�}�/7ÿßdDâ߸(2Dìz‘ÆS‚œdøz=|¹ WÐHÒžîÂ’U�P Îá¼ßÊÊŠñ9 úéM¿ãÁÕŸB/•kÍWÎÿj'\Œ`«ÛÌœ”�4gDšÐªR.¡„"oäðÇO½:~ކzV slwyy�hÍP²õ (¹°Á×스µ«Çñ8–c”'˜ ™Âþ-ÝóÿL v !ÀÓñÊmFAË–Hr'ç�BÅOöè'§·äœBµt67øï`N†þ7#VhtÖŒ·¬á\k°—YÈ¡9œf,¾Óö³â?ÆÐA ²DL) "qÁ»#ƒÅœÇ}Ý™Ý'™¦ýàk5‹8æ?~‡ÁézõÕ¸yá-ˆ‡!0õ;k¼1ç#‹š¬.ç‘ÏB™"劵KuJƒj@÷c³X)ýá£-�7€ÞA Ú³€JÙ [}8Ðlîã Yhc‹F†PU´NÙŽ/G/‚æHCÿÜÔøáB‰(³Í f ñË‘öa]sTët>ßEÒ\¥ Ë¢&·±ð¹“ÃDÃBês9qÙÓ¦¦Þ�àm`ãï¦[X¦p¹v½À.nãpùZ��æ¾<|ålàAy|ÇV²m˜dW¨¢è‰? j†ÏP“‚fÛìôàêÒ–ÈQ]·ô% WGiE_‚'íþ«4M+£´ièPó&ºõQòÆÀ+VÂJéßKœð ³Tÿ¥hÖ£k‰áØáüʲ�Cå�òÜŒÑ{˜y/ª×›7©Â?”zÃe·HyƒæŽº‹ªÍÉ0-²Xçó7[ýhÿ†ÅßÀý‚Aýü~#ê„÷}d·ž¢ª YÎGØdÍ„*Cô°]ÒS›@/eTyª«Í`¬ÖÏTÚH8[[þMã�ö ¨Î-2þ5xÿ›{C•ŸäÄ�¦•ècœ[eº¼[#:/´ž|åNÑgÜ]Ib¬0 è7‹€iD1•¬a ûyá“@ìÀÔe"üRœ80¡ÄˆŒìR‚g2,\RE‚Ç5$…N‡&•Þ;!V9®–ÌFûïã…33Yn>Õ¨.6ÞOH-?†qÖ·±#ø'�ã¡‘€”¼$u6–ƒè©Zzª1!žHâD(Ç®ÈÆùfhžyZCçV#í ¨®/LǺshZ�±N˜e�_Š Ý ,e©bô)'—1 ÉÜ3�…�ñ©nú ¢èŠójPHºL,T‚± {æ©·'ÈÁ¸ìÛ*Ü_¹ÃDö‡]ÊX¤ª¡ãš:¸t •¼$Ál¦"š·3……öšlWoS¡G><×,ÄBɧòðid;ʯ—¶Ü1-bß’ŒGü²TX_ØÄîJ;óÅ:“,¦Îý… QN}N"M8k¦·WÉÂ9À9ý›Îñ!DP5ø�^î@÷”¶’¡�öìâOö0}�d„÷�þÔŒt…Ã7ñyY±;�øŸÀ QÒºÀ ™„Ð÷£ö@_ £sR¸¦xÓð}½Þ3zØã÷Úl߯&iÇ‹äZéIg Kd'zz±5fgFw©hfò ‰¼ŸÁ$‡|«Çâàné&Z´yÑþ 0º&Ã"º wÝÖµFKì÷hY­7¼Ÿ�DŠ…Á}Ûäyt’l»où,Ð[•}æšæîYký 详UІ>U„Ž?Ï—…ýv• OÎí 6?µÊª(Õ˜R+$˜Jí�͈L§è «r§ò£*Ý�)¼1¨÷Ä;zMëß¾Þs„A¥·¹8F† ÿ_:�éUês,ZZFTõÔ;}Q‚�ãvœp!ygþ¿ÙÊwê ¾o¢ 4§È'öQ;¿dI)[ =ˆ½hbæ ÿ»mýM©­hÓ»Á > ÂËVO4`þ6}Ò�¶ÂÏå§°;Wˆá¼�chjðt»aËSi÷Gó‡^_CtÕŠ®2 ¦°ìXyÑ›·´¾¸¦.ËkIh‰×$Gj�¯ûøªlPB¯ K˜ò[%Óc’©ôöH<æÁT¢ÇNuŠÚç—g,<�nȘdÇ3¦JƸ dÌÞ¿|W)ÍT¤ý ’ãæ×ù~ ù;¸nBJp`Tv˜?aå$�cÛÄ%û0£òéå/Ë%É`M rUÚÚùRŽÞ)³n¥Nê‘ `B\Èÿp®ÐR˜ °+¬ræ Sñälfqär�ì#t$Ì py06!jWd<oåÆrêQÃו"úÛP"MAq¾ŒB†#Û`ÃzEš‰„è^ãýñÆmŸ ½µÃG�•´ê´Ç(V ÎÛŠQð…ÍÛ˜eß>"@;–*¼±7|>Æ\wx::6…™2¯kë1hlɹ�Í&{ìøíîEï—T¸Vrm ²•XT­ªUûÍÃ{G2°`[{·:²`Ñz‡4–Òë·ülb»�rÐÆ¿>®¯=wÐ|[³ò€ÕlÙ]Ûg³á›jqž:?�ŽE¶B¼¸þ^ªU{ ö–²äýÖȧŸ˜FÇXA0m¬C®Çê×sïÏk@Ó‚X­Åá¦æ±ÿƒ²êÑÕËK›UH°UÙTºŸi£‹:þá´e¶½¯Y‚ÆZð–HÊí,·ƒì�¿®Ê'e¾„.“§Øý—Ñh’P¼0öd¾nBª1í·#ò_Ë5’Áqߣó&F¸ØФ=ƒýÖodÁZú�Å­»ŠWˆilYêœ@q|ã59ì &ÒhÃ"YûAÒÑßGß¡ê”vÄAo•ÇÅ4š&Óv4A°æ“6€ÒýÌ!†oû„7ÕÒ[œSmµÝI]� 9:&ósh„FüxÑD¦øîÝßD#(M¥3)ãä#©Ú&* ËW}ä®­¼D‘¾G�ž7uªÕz±»qz?å’½nŸßçoYI¢—NôÙ^P% þ~(ÓÃÜ`6‡jýR6�9E>ÀçeK¨ÜK(Aµ�ÅB¹Á‘YW{~“„2på–'Gü–*ܶ"Æg-“jfu¨s¦|{ƒ”UgÐd0à/ø¼*ŽŸbX"ð{H¯ð®Ï Vmʳ~]m‡çf4]Q<ü7—8.^ÓºZ%cïl†±ßŽVgkYÆ_x’æÛl&xœò§ß× b7¥œmÁÿd1�HÀ�‘èìaˆ:tèƒÞHÈãï`Ûd•Ãw×kª:’kWï^±Ù.ÚÔ$‡u¿Þ2f‚e,³±Ciò ˜®ZØ@Óg¿á ”æDD°øxD4ž‚Á8mT|QÆO.¯!¯cŽm”_8/�ïŸ sóu‹”­ÊØÎêÅÛ×=AÆW'ëGg€Kȱüý�<>' g1Š@Ýÿ�Uýi 8GR»ó<™Â~ðñ[v7v‰ÜÏW Q÷I|Y_D< p88½ØŒJ¤äp¢“¹£TÉ“�‹^zÒì%8ðyW©5«Ó[ˆæ´×F�9†6¢�ù�ÙÆŸ–÷Ë$ ÅçBòL]ÃâË�×ÝÏÒc¼¨}=Ó!€…‹Ý·š:È•ŸÆÊßt&ÖôŸ˕œ µ±ë,èš·²ÎtèâA8‚ õ½ ý$ðENˆ òòR¦É¤ô3>XIgŒà{JÜa-Çx<÷‹îšQ ÛN9âi>–½ôJËCÜ¡húæ×° äE…á å(ŽËË)C�‘£LP•~%G�‘l„éïOŒY²Ð/} »4½­SmòªX=€dŽÁ¥ß2ûœ3¨±²øÈ�!sÇvŠ0UÁ\¤×®°ÚÐýheÜ9±…‰p‘îrñ8°æˆŽ¼³ó¿0r²Æ/f¾lÚœ¸ÊC’> L‰ðÕTóc'Ú>GtÓbƒ î“)ãÂÿ€³¡âDÝ*ßÐqnд²3X娂ɭ˜¨ÅZŒÔ®„!©ÓDrXqˆÔ®Y}ŽBV²ÉÞJ®:mØvÌS-¤Ï¦góLFäëL!—.OÁ47–5Óç¦éä˼Ԝ­¸‚Zä}c�Jå;^®ó[¿@ÿÕL’‚}ÊËž¶¶.þˆÖY×oüŸE î#o'4#±Ì«\�ÕÙ%¼•R›ÿ=ƒ1s±t2Á*KäøRòï[ÂaƒÜ©¬êReJÅcú ªh0usäª�+_+ˆ—jvW…G/½“|ÿü¨1Â"c½Ï8N,g'ËΔܦ/Œô9|YåQuÅ+Th¢lüQl‹§£?S›èàÌü¹ïý¯^ S™ñ=Ç k)y’‡.äIë¢/INŒ²¦Ë�À¨ ”¢¨.ª@L·Ê-â¦ÉaùƒÞrí�*ë[ßJúT5†v=µexO©ÄÆ6¨ó;êÿáÄA¤2{¶ ü†ÛÞD®'@×Ï¥òa‰ûŒt)ôVnþ¶B ¿´f�G²dµ%U�¯Õ‡@æUé2¸vùPÿÞ¯·žgçuù@¨¹û�“±Z7²{¡žÃg#7)Iž6øg/å2`gMÊ+dѽò¡Åé¡×Ttq’Û až�8³Žø…4Ýð¾vVïÐ@;d{žÜôß$ΪÄY!Ù1š¬` ^•´Mhh©Í \M»6Z{mãþÔPÀ‘Ò Û]µjs¦‘&±i–ƒÉîã†E9ï¼wãÍüÊËKùW=óÃR{Þ¼„|9’k½ãÓ¾B˨ü%` �׌›¡ŽÓ)…6çaâbÁæ±™BÉñÂÀNIëvŸÎ1ÍÈR*¿“V~¦49a~ÞMÙÔ�Žv•$OE›¢’—Z凌®T±ÉŠšÁÜm(ä ö¸>àâ¿/ç"7\%t‡—ÏHZ4¾\—íž„¹ÊNÕÿz{ëc³vÐÖèGò�$v�7>#Eï|±ˆNô�ñKñ5íŸ�T/äÏFgÒ5®ÿ{ÒTžúX™3¦ý‹ú[_Û`jÐÒÐ~àÍâ�Â’Cû@d ÅM,¥cDÚ]#ûÕ…Fý(…Š´ævÝF²&ŠùñA08J†ïÊ ˆ3?óÓ˜–�aøNjÁ­`�¥i;ø¨*Ä|wà•Èqœ}nº­wb#Ÿ¿z¬LÞ5tƒ†&Fg½1?†ýbJ¢ÿõ–œ®'—Ý…mh±h%�pÜ4 sáf%QSU}ñ§.ŽtΰØàžžéÄ6t-Pã)1 �V/û¡§Úw·aRuuë~g yº÷RÆ„e|çñŽ4 ‰ÿû‹Ã4gg™VÞ·¯þPWM¸Ï»ùW �C­‘8dDStv‡m—l—Z¹_V\í¹-$ýŽt4�¹ñ~î AÂjèoÉQԇ͊ /Y[l\+Fð–b‚”Š�=¬¦n"í[ÅÚñ"«ùßûÃŽk›y.iž™4yé{‡ÁƒýO1RœÎ9çã7@2ý£Š%„j1ƒ !‘!©8BPêÍ+ÿXÔu9HÜ’ö[E=Å�ýBξ …L•ذiJ”²9NE¤RÔêË»wÜ&ìóSü"çÊHsé`õ–´£-áÛ^ã w%î3^i†3þJIsû7Vž�&–{P>£gSêS‘�ÌĹž †¿®Â ê“‘;šý¹”;1ɰ©˜&Œ ŸêŽºYú席‰C4ßÛvã„´S+Oú!Ìš*ì+Õ©9¢{ÔÍÚÔ éÐé>¤®‡1Œ\Ÿ)à_5㘦²¢çÝitÐ\郎Má_éå¯(��îZ³A–æ%ì[¢ú²5í¿i�ŽH»Àx»Åm@ZQÿYôl?¦ïÖ¹†¿[ ÜÆH�Šì�K:~ÕDà¾æÜ‘Ð'³wÍi:�oÔWf¿îdz…‡”X0|Hú|O+3—ôƒ]mÛWŽ@ÄwÊ8àh`[>l�¨(øƒ{µ²5i¦JŠ/ÂPl9a“ÿÛý𠥕Ìá!\MaNVŠ8à?Å%u{U)-zh­ÿõmÒŸ+¿3Ó¿Mz›Ó­²'¸ªvãá,”É—SpÂZþ5yPñ»ÅÑ´KÈ5Ld©7}±éSìúª¶Õþ‹1qfùMepp?3úïï$¦D!Ž/Áú·�ó¬ô\‰Œê¡‘fþ+;›[™^KµräGw×Ç<í1þ"Då=�íBÄXRn+Q¬W6ÏœGWŒÊ…Ä‘A,X)µßQ;¸3Èì!hN™ Ù:ÆM ‚9>Ǻ²[¸2Y��“�3±hþäú_Š‘C`yM›ní®ˆ–‹^ jåÙjȸ%´|ö˜ã?ÅÚ…-p‘Ñp•'q¥Ôâs‰I¶slQ4ÿo¡¶ìÖ\÷§szŸ'%L‡r,76C£!LS))X÷ÇBFºç¿ÿü«€6¾yMsnL®[¸[Â*ι'�Ã(30÷OÙºh§_?³L–]rþ¹:&²~W‚Ñî-ÍÇÔÏáåãD¹å¸åØÑ㦒‡sªt‘FB%ªÕñÀf`ê–!Ñ iÚ¹€JtÎÓ2ÍE°ÔZ%‘í_í¡t…ÿ‰}½™ò¶¦2jŸœ×~¢*‡‰É26‹,ˆ µs–uÐx<ƒ„ÕvoÊ¿iu^÷L>t”ñ÷òÛÃ#½ ¡÷â|*ßcoýx¾l8ݲ&"Á5zÖö×?)w·¨%�¥ªö�Û…³‚lˆÕ .*û—S`cs³€¼G­7Œ å6k¤º©þ.Ê[sž[˜»»~%€«ÁS=ÙÃ_p³[kÒvÔ™Ìï³å–µòNÛ^6žu–}Zs>9©óTþ ¦¼ï£I½êó1&6Ï€îíT˜–¶ŸgÞ1‰ —IAŸïã§âŒ)h´UôÑ«û±ewo c”¯Qe³·Í÷÷i=çNÄ€àÇ#3ó„e h+Ú\Ñ;À_1Z,Xe (Ío„ÈÓ‡Vì¿!¹¡J¤ÙÅ� iœ`ËàC„<‚Aº͹ôôýºéÙ»0ÓH… öƶތÌB°éBŸq„/’˜IÚŸRW}em&ÉlOOd»‡´ùÖ k‘‘± ; 5 Ï×­‘ÿþs¼dÉ•~•ñ@�Ge³uÏCð€´&äj$'˜æ‹p“Áé!¤ìÞÏD‘ñÿbÅ ‹~ΞîÊûï’mù”œ¨Ž‹n—*íð&‚:]óèÖÚ�‡î¤ÚK¯ñû_tk »R6þ¡/ Kýhi!åú:‘X s�¿ÐAÓX´zDmRw<}†ÓÐ]¦QYäðñíˆ�PÆ ]ÈbQé,Áí ;°åþ³ÔUS {ö!3€^,¾þ?æ ÊGæI ¬]ôª…ÏT¯°Ó|YknU¨"!ôÐo:ë�ÿ­¢ïP<ìtLÖ$—·¨·zd°¡4Ãé˜Ó²"1 6¸ãÑ9†7ÚiJˆ {�)sr#0Áì+Ëip(©$]—›Y¦ÌƤu„=Bºnä¶AhýÞÎ�ÆÆ3}„°%Çœ!gð§÷Çr‡žŒYâø„›ø$ÀÍÓ¹ðáKmˆé7ž<Χoó:ï),­N†OŸ¤ŠþöAáPHˆçûBÈÛÐÄ˳O«~ÚT‡ßŠ‘¹3Hì±+ÇðßÕS® !Õ1ÎQT¶¡\¬c÷*zýUú7qƒ°O“ëxÛâü‡… 1¿e«NwV¥, N&bÛê4ÑE�ó&!./Ì«JÉžù}Æ^^C™êaö1B},8\΃]72�(C�š’œV¨Ñ»J:S_^³¡¡Â§l"Pä’æ©¨a„ˆ7pgø«$ò±Ž )ŸÏ$ÎòDÁdð �ÅlhÐâ£í®Ùuà"i´jOðÃy+û�D�ŸùÏÂ3‹AB@²H ƒy¬Ð:Ý ±Å^0%³0J™È@-õ]Ý#–Éfdä 4´Öò V[0˜ �f®wÓ®K­šê%×�™ÎÎ9Z~¡¾­‚d9ÝFAÃ?j<¸í½P²I¼i”k×ÅöáºV±>ÍbÀðtúÀ;>‚™³ä ®vLWjPšÂBQ£�·¶=ÞØæ^ÊbW T6]ôÑ “Qa4ðÀ¨!t©Â‘¼<:Ÿš³§ªô�kú÷䥟ÊéY…/k,ëGò�‹ÇacZšt¤±Û“vB^ú±=yYĬ¶ö¸š^ØðEÜ@&J�ЧŸ½i¶¦¾zÈs…š´a‰ŽL�ÝP1ùéi¼L°ç¨°U¾Î¤èê6•”PzøªßëÉÓ›BVÞÏ 8�gÑÝZòË|Iæ[7‘ß©õ˜Èx]Þ»uI,µr·¥¦ýƆ%Sbû�“ï1F$ÕšÈJ›E¡«mJ†V+†/jÿ?úuáa¥æ9ßð ,�}%…¶Ó�þCðŽ ¤ˆKÒÀ{.Ïßã‹î˜Š#à0òwØn¢`­ �•ÒíȆöb…¢.ñ„~êZ ŽQ‰‡±{@D Ì¥——¡tÐÏ4^� ¿‰»þHU<œnbùÔ#û¶håö”‰Ì€œ×,«õ�í?Gq§ùVç›j.½®† îa¸åPoä¾s^nu&�9™±Jµ“´wðrÚ]¼v`S…æIz§´5ÎfοDÕÓ[�‚Õ9jå £w+ý³²Á>#zDH ¶}_ž«‡ØvJ¡â—ÇmÅRP/+ÞP ³ƒC“ŸJ—ª#ß¡ ]ÔRRK¤«ÄÜb9 ?¬‘±Þ€¿sîïLH]Cʤ¿=#§}-êŵ@I (ˆ!9ï_<ÒšËÜ™3n§éPãÅ:‹çcîs²‡ ¨�£d€Õˆ³šîFÏÁBXw …«ãÌýÀöüüî*_tO‘gJ¬6QŸœÛ¥8{(¼ò Gnû¾–G@€œ' gÖ˜¤ôDÕbÀÜ… Ã()@­ö ußÿ£õ™/ú£©€m`à4á@bí4ÃbpŠóó"KVÔ)�…£?v‹$hïƒ4}éÕߤP@¢ç¦'³€œßÁ¥·Öuõ®Ž±QYjKhç nj-ÿïMJi•sFL4`YŽËâ{ÿ}9­œ,YYN!ô‡‹ˆ‚:òZ¿,ó‹8Ë9º†ýȱ6N®5$÷dhÈ�tkB”�IjÈm‡¡£Bužö~l½zNÒ¿xGR5¯ë� g{Úòý²Ç˜zòÀFÌÂh´„ ¬,‚ôl"ÏÎW8=v2!„œ`Í(ÔŽG{3W:N'Ÿ{0îÿ|Ôr×YÔÉ#þXêý=l𲂤�æÞ±£S·ÏG0d¯XƒDBÈ£Aë"Ë´µº«fÓQŽ�£4Ý%ÜÌKô °Ÿ}=×ÄB2dj^zcûŸ Êë‰ö¡ˆ^ff8ÉA]DôQ¹#?m#Ùv;—´RŠ#‘]K¹‘ èzàˆ©]c·Œ&!£ë ÔWé1BCº–Û’9WžðR¬ gõ 2VúO^MJì´ÌâÔ°þî·º ‹êV ¯l¬åá|^ñ°èÚr³ób'ðAÁxèi“¹µWŠªJÌŸmæ±5Ù®Î׬ªZ0O§¬±šoLÂÅ^ ±oz¼—8�âU5^ãæ¤hß×[3¼ió½…<9TõÙÏüP± ¦t^ë(Ë^ö·H"i’‡8Š÷ÅÈÙó�°ÒÆá)6œLm®O<çKÁyU¡A¶cðÓ;‰_¹¢K­€�¨ ¢`ƒ7`qáÔñ¨Ÿ¨„Òû2DM9ÕÀeox>ôz˜û®Åcùö¡& ¦x ;)ez 7*ó¾Xe÷¤ÜÛçm;$,þc¼¯þüg»5È6Å[7D¬v\lLiâ»ÿÿ¹t˜°õ]8@¸#ð ¸Èã¶ÆÌëŒ�gÏθ.“ U´¬çí²¾µ–­D‹¤MÛxÕ åÿÞ×e¼wÛ‡V)?z&†j÷°00פ^”çO[ê§±.œºô T;óê-tÀ·§ô+™çµ€Ùú~r鸼ïñØúFâÁ[¢Ö²¡W㥤¿7‚3 ꡎ«Ó—+4^¾àê÷NЈ¥¹b�·îÚ·›x€Ç¤¢è "¶):²±² S`Î" É „kúwÌ�“7_e¢@œ•½+>;¹�çvç°Òà‰É½K”‡JŸ�V¸S¢M„5×ZÛÞÜowÂö^8BœH ã™Âçv猴·º¾¥Ÿd $xÌ|Uzüi9°¹J;©¥X3„ô.ÚŠTÿ@ñ\²çAï3tñGf×—ðwá kÄpVý®Ô:É«y8à�—_Ä븛²ª¥á?SèU—]J!0T\ƒÃÈ-ÒgÖV×YÁÿß=´†~�ÿ®-o‰ò[ˆl¼ÒtS1éf°“»n`=“Æ];^OÈ+=q’;)öC³VûTÖ�˜ý¬ô-/Õ,¦ÀÒŒá%¯Ü–hËß ñKÞgñ7†Žÿ9¾HØ| 7ù´žÜ夨„ÂþL^æ׿¡ÃÒûìf1|å@ÙÔ¢í‹Y[U 8ýÝÿŽ›(r¢¥�u(ô t3^ Ñdœ™5/D¡¯Ê…03Ä-\�AC„�zØDÑbþ`ãÊ�×C{ÈÿÑB¯�Á üûþô§ÿÇÇu�zsÒÐÃÕK·¤Á¬|¤ÄÜ7æàÄ»e§ Y£„¥ÍºRþÃúº„² ÖOf Ü¸ã6¨&7·Sé’ëÖD�ÂÓÌPÔüжÍR&úŸºÎýo‰S œ+)C©±×¶'.Ž÷�YÍ@ÉǰJÅã6’1°i‰ H™Œ³ÂúLYfÏ&™?œû¹…´ »ÆaBZkÎISTQwjþ¦a—"jE<ݤ¹_÷Ö>øÊ@G4çšÞ¯ ¬º2k”ÔÁî½+XÈ’'ÞúI\|Ýé„R�I9r9`=äH ©çD>„—bT5ŒêõQ7ã‘1S„ê–¾ð¬•òAçOæè&™¶1/‘�šúÉ |š_H˜ÿ®g´Ù™^ÊÞ”;¼ýtEeÀb´ÙýÀöîG)ñä¦:•’¡Y…æÓ“Z¬b# óEt(É2PççstÜb×?㥾ÚÑÂØ@Ì bîË.ù$WÚÇÙÝb ÉÍß«²Ž™FT6˜$û‘Dj_å²Bƒ>­ÅËLÿ¾ï–ÎÎQÁÔ÷Ó¾­sù É–Ü<�x‰Ô�»yÃâ¨QÌþiÂ&žJ÷ËŒ ÿ"©öŠª©¿†�Ùí%›óÊ´Cœ"„U3ÌsߟÛŽA„ßazÜ&5ö¿Ðà&a�¶L‘ýÔËW}ÙA,xpèB|?Þk×$}ŽÔÂbÄ™yêÈ)‘C[àjŠ„,Y‹”ÙoW7hƒ¯Nj�ý�W&ùt“‘Í�ü Ý+Cß×:Šø8| H/:Dš~�AŽ|ÇÀ [öwÞƒ¤< ;E=æÖ@ؼËåc¦›fJúˆìõ¤_ùÁ—õäW‡Ÿpº¤Á‰ðÞ¨Ïï’]É)HXž,ï#b–޾9Ú~£e,dš 4ÅPAþ7 ^lN6ž©�ÛÒz˜wîܤ¤Ê;ÍáèfЏytfˆÆ±maŸë¢RÃ[°(³¦,¥z…iÑýuÿ%±¸›mìÈüjô®œ8å›Ä]Dà ‰WMM} ºþ�ÿ·2Kô�e—yAè‰C‰FèN\!ÿ´¹nOü‡ä½Åò|^X•ž¯¼É¥*Qê¾ •=Ê^ÉÂ@,þl<ËSîEø±Dµ‰ÖqgäîôöN×S©;6Z­¬ÃÅšy€åhEÖ¨ˆ/»b—™ÕÙÏÇ+¡»ßE‡ƒÚÿI_?Ða>ôM àž?À_pð"Æ“šØ +hmt&Œ¹+Ò×’p{‘½”娑,áøFP� «Jå`k*ž§>¹®–åS­|=13Î5“ÈH5›yàc’)‡í±fV4ÏQÂ×l¾ÃX‡ê8Åo·ÞüÙÜzÜ Ÿ¦Ã4,…Öùo>ÿ-j8reŽÂóp°Æ�Ar6‡p%iç8‚{N¡¼�j °Ðm3¿v³»�@î$�/˜áÝ�Ü¢µ �Q³ÿVÞeÞM$ºIä?°´Ñ�iß›Y>vv*ÂW—›è*è×½ÕWb¯¢†Ö-ýØŽ0Æ¡‘5°µÌƒç•‚ŸÝ­ `­Ð{Æ’6ÌÓK³Y””o¼™AºDnM†©ƒ¥SÚÒj?Kª?òœ’›gÜý^hüÛ}€cN-;ð®s­ôÃßêÜYy¨�?5ð 9.Héîé ê�”5 ˆ‚«‰%Å_¯º�I*Ë4œ«\b<-åIÓøë~|™ˆÝ  ü–EÇ9ÐYJ[j‡\¼ûr®‹˜ýêDË¡™G”¼Õ'ïêå“ &¿Íˆº¾(ï¡I‚ûh"L¨£×¤t¿Õä¤zÜá4VbÁPÞpõn¯d—Íge¼’ÓÆ¨Z#¤– í�@]ÿÄ�F ±¿P V¨íjÅUíÀáR›%Ï6ùLòlËJÌ}{cjSç0)·f6xÀ™UÁH4<<]æ¼ÈÅË!ºY�ÙyMÀ*©Î|ˆzO"Kˆ×¦½Çù#.×ç�Î á*ÛvÀpt„&&u?z¬�¿´>ƒUŸ²o£¢Älýá2ǸY ƒ{O¾Ù¨,ÔiV÷ìè ¡HGí—hÜJø¡€,X@@î&3´nÉ›Õdñº{×úÚësãÄEp*†Cµ“®ùŠÈi™H™p7¶Šê¥vîrÛ@¬í~Ï>{í÷!øm¯Üg@Slæ*®7½Íþ<@te¾å®¼µêB7Á \fkwktβãrjör.Š�ü§ÔÜtNrÊÇrwú>¡Sè*h±—‘> ü¿�ò7;´ï©�íX<¨ýÿXJ1;V±õÀÙ{ \åK¬Õ³g9Ý&•V¨SŒ$Lþé~òTáØé\âÍ.ç†ä�'LœZÿWÒÀ-¥xŽÁéôdP /“×úÚœó•ŽÍ»¥�ñ/Q˜|ÀþÖ J+öÓ¾#²Ñ×C(G¾0©}‡r$ƒmÕH^Gœ»W&ºá$÷ãË÷¡ëY\¸Êäæ ,¢JT¨(3˽ðm<ʼ�YQÎ4°e¿�ë‘ p¡v‡5‡ < ²^4 q¶yEXQË`äÕÇvB«]b¶íÓ²p^”ºÆœºûC‘Ûä¹\è7®gÔðËÑ’‰S‹¢ÂÁÄ.<ëVþxí"Û+ÞÜ;õ¿éÓ¶Á°5À‡#;8ÚÂÆÜ'Œ˜ÔÄ_%–Qn0haÔˆæéMЈ6A¬4!Õ\ÔB”ª? †Ãs*AP�l¦ËJ«ˆëv £(ì{»¬ç „rß{úé䦸vô—kà–iœ×µÏúž8}»e?œ ‡¼øk1%çáÊÍq©Ë¡36=/îY–]À -ø A³ aL˜†;õ·X…hQxü2¡¬¯uö?ýœ#žÑÇÜé/Ÿ­Î EsPìÍá‡1b†:y.(õ³â¤$Œl]mËo;Hþ•óä¯9¤Þ×D.q£fäD–Üއƃ!¹±ú�å8¦®"GÝ*âÏX¥[Bª¸ú˪ü—’ÿ¤ï,ôãûì™ó»¶éh.Ó_b@³ppì9ï´rï€I‰É4­àûu‡Â£”6”ù\hL�Ÿ_€™D+~hr³Ä›Àþ´P{2†)û%nù˜kTQÄ™Ìpí2|1š�ÖþâìsQ¶•ßFÈS™«v/yXOîN[Í:M4[n'2ɳ[¿:?«„Žââ +„4Pˆ±´„aôœÀބߧ_I¢É#©5w1“6ꘫì×Ú"3K#ÞÔÞ9ó£›6î—·=„–ûà畨:Ö(²gœ¦±UÀ˜EŲÀ~N'šê¼kiبÑ=‘à^ƒx”·;†mh¡x¡ØÖ¢Eo0×4¥#p.Áo{ >ÄH„’‹Ìç–À( ‰Í%LÇþ%_2/£ã޲•)Ü_C¨¶Á[€è°Ý�Ô;nhñï¼ã»]œ‡›œ¬ÔlI‘Óç(ÁöÛC½…Õä l:�êØXݾ›v/×€áR. áåNÆ ÜÑIe½ÀqÜ›�ʽ—M~Ì›õöø†y7íæî´:ÉwPñq¦ ݉+°)Õ��¸ç;ÁQf)½ø^�åRée¸’û&·;a¼¤ï õH&°WLêðŸ��J™¶žŒ þL¶ªûJh%¼))¡ÌEç´ÛYrª’Ýñ«%5òAìGWåÄ dÅ$T€»L�½›‘®²¤Žuf™,H¿D¡Fi|C¤'pü5¥vQûÙ+•ç»gLÿ—¨úï-4`�EÜúJÿ¡HiûJáF9”]XªÊ4h½]ÀHÒ¶�ðV­€ç:OO¶ªY£š,ôúN6[æÌ0ei¯]‰"”ëŒG„S¾Íްÿz8ówº Vo „Ðð6!ûrÔ#)=QvuÇ…'1áãèÂ7ö…à·YOÑj¢'ÉyWy¢�Mç£s¬†#Çá¢êî² v:Š âø(ëø.jzÚ´”É:MÁú×e’dv¸øû ^JµËIB j6|F¼!ßXè$·îÇ'E=XÄãÝJíƒZÅ8¨wK’Ë;N¹¾œú& `´ãV×½Ü1ÊK|ºÑ4…ˆ6kC) ‰smT:ƒÛºeé‘\@l²ËPVæµg‚‚Î/²¨ä•·Ýî.ž‘�xjÞ0> gÿÂQŽCÖÕ˃UžÇ1™ÉìÒ¯SI)¢ª]Ûý<Äy›ò�ïVdÅ>9pú(ü‡CNYâ`X}͈XÝm M,&HVÑÚÜ Â=bSñ²È­{®à{«/¯'̓ÿ´ü×ZÚ-ôEø¬+UåìVvú~¹Õ?ìçÍ’Ñ´�v¼Ž[šä‘r?6¨^_7áH–�µJ_�„Ï�lŽyÔ¿ùLrÝ‘ˆ þ ªqü,Tóè}j5­›9x΃wx|Yçy¹0i´v7£‚.' L=ïã׳‰Î T’ÞC7FdÚ1{((Û7ê|æË‡.1>80g>fu‚��‘{þ™`!‰þåºÝYF¬òd‡ $*hèi~T™ñDò¤òÈ'&fÓ:”2Òìªí °Tüdt®û ÜßÍHF…ÜŽ9•5gä½bϤ]¢ÎÈÉrÄ2ÕŽú.¯²ã®å•˜džÁOÞQbæM`S2]‹¼%Á¢�”ÅG?vØ$¶uàâXü\i=�?´µÐAîLPgÓ®ÖÚ}Ծ€¡”Q–r™£hÊÕ*®ø)�`ø.¢(Û.çúf<°º‹Ü]Véyv��1Çá~©}±Õ]ÎÏö #g kØ9ƒêhw­é2&!f]/ǼTsÜÿˆ¨–ö³µL´mT—Eü˜1U„¿é uÛ¸k‹B%+{åï`fq�á!lÞM™Ú¨8üøÜ€¾íÝUTœä“<4Bž›2¢Âš�Ú).¹Lrj<$°ß¹3*zþØl êõ%çCá•T9õ¡X}ztˆÍ±©“Z!õPÇñµ°;{Þ6üµî¶<�Ø"²u�kûwË`oÒ=C‹ÓÁ,®måÇn„a›‰Mp®M‰²榡eWä]œÏÓ´*&(pËqä½=¯»6¶�Cð–ÁU¼ (¨á—oÐþ"u`=òæüJ E̺ QNÚ­k7[UÓ'gðRæK”è—ˆ£Ÿ*£�¦›e �ç­YzG³=JVŠ|O7›õõ{(6Þ�7FUi ,‚lÏß• ³êRüösœÚ6‡ë½èD·S�èð+]Ò8e¿“ª$³Èx0¯œ¿âyO�ÖÿÔç€�ªà›ïeü¤ÿË7e|é¯y_RK¢2µü/¹5þ!h�—£*Sð™èë7•-Ôl\^ß—‘ ¶Kk† â›’¥~Ç&B¹ŽSàÃ4‡ªÑÕ‰QXqäÀD l <øøûÚgÎiuþ !`¿ïÈósÑsÜòFjìÚQuœ¦‚þÀnå"ÓÛÝ`±³yüˆ"ª¦()×3¦*ª5Ø™)³“6KƵ>dzò¶g¦b´†KßÜ yˆŸîÙ×/Ýó6÷Å•`û¬G[�›ë Wo-óì�m޲�~z ¦%i8,�ɧ‚+^!µ®ÝÙ ×uåTˆž½ÃõÀéYÕé\4±¦©?¤bÃö>gòÈÊÇ¢ �f&þÓƒ¬ä’:ðsBLr\�ý·Ák ³<ìé?ˆ¾·LÅ'»�ü%eú"qû�(Æ,lG_xÖõ…™%äðÈlÒù7\ªO² Ã]8}ö#¡�1�ç™õ.�&”Tx•”\;Ñö¦âgŒŒKƒj‰+×+n¥­U6pØ1nrî3û’”�¿ß‚l‡k…€C³3Ý•ëÖîuÙí-®Ç.R¯9‚… ªDÇõ4„€Çm@Dû¶5_²ßaäzšx_Æ_0¤ŸWd"Õ(|db€+šm«3¾%Ekì»÷KñnCJtÿnþ.\ˆÿãì`:�&ñÿì_šæËP}¸‡æ3©¼×IÜáóËÂÎ4lš…Wq›#ЪêIp‚g0¥Žâ¤=¤÷šUvà fjø¯]‹˜=qù›Lœ¯ºû'¿œ³ÈW€ToÒiƒ¡GP"Gòr- ”G™uÉ’×L"qœ±•‘Ÿ$¢KjÙ댲a¼¾ ‡UTñqùû¤‚°�YüÆØk/hNå¹'Ö�¡±Ü¸¶'5IÚ�I&ü žI/ß³ ç²ý±°Me)-_,êVÈ ¦(jÛj½F¬ô¢Ú×nޱ®ÐI}Ÿïfd|ÉiD ,±Ž&à®…ÆÓªÿœ«´‚Ÿ¢wßP¦c‹×¨;+¾vÛ¾“°bÙ³�Ç›û½ê[E—¿);3—T~‰`ãÀ19´³";WgÁC«µ^Xêѱ]ù©¦W¼�ΓwãÛn²iœF~ÃÆ„–í*¡¶ªHïRu°ãx†¿AÉÉ€•Wýq‚ vyÿ¿̽½°ƒ…× G¿è쟦õâ-©­×È©<¥mðÖLÚ9§’÷ÍÃAø U¯6q÷ª·Á~Ò1%åEmƒu¥=åB„b¡îs4ÍéH»»�¥PÆJ¥ü‚w9�áa]q8ÁInÓÁ‚¯äÆ×|¢r¤ù¹¨ÒðÑ)é�9rTÅUÿ„±¢^ îþ eêRt„ÖSÖ¥*ÞB `."+§�ÞZ�Ÿs€‘$àÚaºMÑc|•kN~FKïÿ"Ä êÛB±›m{yÇ3©íß'ì;„‘;w?KÆf)÷ü=ÈÂiTöûXÜKÐÁ™ÚðIëÓ½l¶Gùåí$”ñû ʇkæH…ÁÔq'Ûtºˆo°”ö0G)ðNÑ ©®§X°#uõŸ·E(GσÊß•%…+òìFjºåû‰Ù¸oN«¥"À¥|Â(‚ ¤ŒÀϼü3—ûn=áq5­EpvBÓî(DQKHå&ãš©dy›MB_£:nŽh’9“n[Ó˜—®á5t‰0xKë�…¿¹«^yb��ú5¦ìÎvû-nú™“]p×µ€¼aÚ/¿WœÌ þ˜ºÃÀ]‡ R‹ÙÃÖ>Ó}é7]™öê'U¦8-[ù^Å4É0Ù6¸•˜M£RŸ­–ò }5{«ˆ¸�²à …g8`ô¸"5Ó%Û~ùfpŠ••Ôô[AMrN@ÎÞ9S§m²·‘}½¹rnŸâ¹Öº}‰ŠÚN²*�ÖUuIñaRÁÌÖWö‚þ�ŽAŒçÕõÛÈYEEhB…Ô” UK`²”2<> Ь\³¶˜FÒélXR~}x`£>Ö´ìÅr¿>B§uŽ("Ì~G²:äêzü ZfUãsbÈ“j6R@e­„\ŽNÊqkëü¤5¾‚‰ãqG)E‰Qav†;Fv¦ÒPB¿itcÿ*²tî5´@{ëðrö£ž±—ø¥k{Ô8š ½?~æ˜d9ÿÈC77íÜl0=¦x<0îGm³¾)‘gûcÚã7 ê·§U-HgXU_r-0kSÍ÷Q‘±>¸|‚€¿-‹ -¸{îò°òVtŠågc‹HÛgN¾©�HäJÃ,½1EWæ&Íø*Ë·Qä…œoÞˆrö-kP‘ݨš€Èªú>þA’¢Vz¤Ñ™ã%÷Ⱦȼ!é`¦+9Røp}NØâì¥p³d(Àxò;h^§íªA»Ú ßF@TÇJ#º‚ÉÔ°R<ååéíÜ&h„=¨àaê´8qjâ¼9 cG†ž5îòêj³ß‘K¡hŒOœò9(a�j†ðgLÇçlŸ¦²DæŽK£�yN…XA/´�éõÔC¬koŽs>¨‹ÝfÓ¼Kò,Pý:Á ¦@Þ¿•HÊó­áJ6@½þh(h§sæ`=Jð mÅžáÿ�QÒŸ·º�+f‹ Ù™#AÕ“GŸ †S»�†io/ETÇŽD¹Å! 5�+\ÒÄí±1?¿¯d懢]Vò‰X[c6ýènø-�ät+ÙC¢ãšàA4!ñÎ]ÅQlµ?ýE‚,Žd$•ÕȈÆþT¡ ²©V´Åâm—p/FסöE© TpÔ|ÙG”h]p°Fñ¤â%!‹Ê‚·P¾W²NŸ@dÛåƒó(ÆÎ!×Éü‚ÖSgn �#îÈnû@ñ«M)`”üS–ÁîÌü&hÒI“špŠòÏâ­WQ?w‘`ÿép?A½Ö�Q+ßžÞ'3zâ¸{bíÿ—™@–à+ü vPU &ïù¡Û×[RÝ p”W¹Ã…Û§AòÑ;ßÍ€£öé ‰;¸’kr3FMGk:¯Ô¦ö ­t­Õ={F®°A2jõ/€É|�åHpÀxIñ©c£Q0Žá¢�,¶� <줛Þpuöc$ž-j’Ý鳨oPWåꇖK¸ ¤�Të�îLÖ[ñ³ñ”ªÿ¹ÜiWP) šÍœ›?/¡bÖ·I¸k]?«G�+�ð�ôªçðß Éˆ–v ê[ÊŽ¹I†Æÿ:6EêÀWMç¶•p㯘Žëv€ü”Àž�§`‚•±¡1Y,’Éɰšúõ¿þ,›p×KÕ ¿‰Z²ÌóYî¿ÃA,8Ít_ü0­±­š¡Ãþæ/èÀ*¹ñ îš UpŠÌ]|šMA¥®»2= ׫yñà¨�±#ù¬žÀw³ ³PjÞ˜ÓÕë_zŒ�­ŽdP>„‡[9ÊpÄ“8Ä�ÒüyµVRÈK>:Q”wCéô‡¿‹Gq’ŽhiYMjÿkÿtwà€¢ºae¥@7Bª‡ Í2€É¡u B¯ìU9ˆe `‚be1I/[ÏÙÒ”à]ß<úOßGai ~ø‚áî÷É?$?§E«KÍË$ÐÕZ�"OÔÛ~Ñ`?З`./F“… ô§3ôþ›&‚ðúlÇã¢Lž+²MG)Zè à)sP¼Gåñýfª}0VkÁð~5CºœÛ|"Iš¥V¯ÆYƒ¯ÃGN êŠè$ªàíà/ˆØÜ“ÝÜkûqJ ÈÁívý‡l‹í#Ïö£Û:� ED¹zÖBzSq1T„æKj}(­ Îö¦¦N�9ÆY‚Yƒ÷î�5í´:=Úbby‚1$×n5£Ýí³‹ôJoW+ÍcùQHÅðç½+hÁ¤ßøE Mðr¸Û¥Áœ9ùs�5Za´Y¢]p€"3³I.¾v½«Oe™¤ÌgêôåßæŸ}ÁŠ7�[m“Ó%ôy5ú3vöͱbgú¡Mót òmÃ8·ò#×µ¸¹‘â(¬‡<Œ[•î¼bDÓ¯¹ÂÍóñ®X³ XdÓ©ŽÑ7!•°[ø³,›NiAð~¼�7ZW$‚�¦HãGEÐë�‹b…„yÇÖ×Ê䣹�É= tŒL¾ntÛ…4 ÞÃ"1aÜ’ŽQZµæñLÙ•Šîõli¡ŽX^ÑíïÚ­ ’Jš»Âp[ÂÍJáÉZBƒÁ¾Äð"z†€­•‚k,ôYó3?MÞéžå±¤�,1l7 »s¢ë]zfA�bÔ¼P$( ÅcòGQ¯t Žät®Df5ï3Ó}B\ŠÁc_(LUÿ©¢ô�'Ó‡fYP�RYh£>´¸A°Yp¹4¸Çîõñ’ØYØæ—ŠW ˆ5"¸Íá=¡â�—¨å*t¾WÆöŸì@w&qÈ-°MyÃâ�]v{bïžK‹¼S¿IN‡Å6ªÅÁ™[ÌàÖ“C.Ä«²C¾Æ%þ”ý Ÿ…)ÖÉ“=Ì5éc—Ÿ÷²¤cÊå™8 En劼îF$ÜÂã<'Öò­�Çz†¼?· ÃŽLÉ%°(Ë�ÓùÈ“û´�_‰a¦Î�]¨Ñ_Õ,~tÅfcrýR–ßJ®ÿ‡[à#pFN¹M¿š-ìÖŒ¢w½C ô0¢)BýN2zÌØ�‡åÉå̧3Î ñ¹‘¢À,¾U†vá‘Ç)V⋽4Aø^ËZ MäÄÒ ª6¶b• h£ä: YR˜¿Ú!:I?|› Ù§�&–Äb›®d¸Î#î¹p_fè—…¹í¬çEH®¹ö¡÷¸7"šF˜;ú–Ú¸Õ¹fÒÖï>a§Î‹gôf’H¶cvÂ�.õÉ,ÀÎ-ÒÑ. B“8ÔÜ>« Ô d��È­vM’è[“ @DkÿÇ%�e‰½µ8ƒÖ”2[5Lîgs¤-éö9hWÄ$»~b#~…ë¸ÓÙJó°lcg¼9ïÄ;϶=ù{81ó'&±^{“'{U·‰=ñˆ™¶+Gà~›Y+Ö‰$�Ki=}>Ó�Ì£žÞW>…Â(Ùñ°D[ªì€ß“-=0ˆËŸ¸ ÒâG:P(ß`a.=�‰Pe¼æ]K2® ¤¦ë{lŽÌ/¢tµ¬EŸ%,�‹‘tøòí!ïQÿ¸P;ô¿§ j¶F¥óϱO·ÀÝ7Am…Ãþ{ ÞVÍPúŸtÈ\i ÇR'>Vß… á|«X›_´ÔÊMMÑóÂ×·ã(ô—ò‘û$µîK'I¨:}š<0¡¦ò­,«Å-ûÛF=ñ1?›€fç’œ/½³*0»òñáLßy”ýa†‹Çéáö�f<ÀÒNo”L4Qb€\Sè-[ŠA‚0$%’¨LÿÈǽ£Œë<†…BNyMñlNœ®>n+8EÑ-„Ôî-KÚÏÌc1¹âB(že§ë\µ%…°=Øð&>E~[C`5e«j¥:Ʈ־ÖVRâc}Ìj±Ï®æF¹Û&�’&=‚"ùW�£ä–þ÷Æ©7j±|ÖÈúûô ­kÕøaÎ…í$òĶ™ˆ d `âÓ¾„ýl\éaëuú]ñÏÏÜ'Þ­AR&ò[K6:ÃGȘÑw–EÍàL.£rÛê�ßÿd¡vŽ‚cpõh»‚–<ª9h¥¾ P;¸›óÚ }SU?·Ô�>;WøÞ*Íñ�]ÎK¿Xï”÷«#¸¼‚8ü2B=è?©®ðÇTãÒ²­Ë Ý@á(žwSzàÖ¨ r ƒÓŸ¯º!âלà$=ˆD6;Fßú¡£'$‚-o7y¨pïpPæ»ÏÙW§„ NoGÕö—èXIE½¡Kì¡×ùTähË�TyA¥Ä¢lÖœÚL¸·(JU~cŸü�ãŠ_cN¨Uµ³F´³t» »�|Òûò3\E¡Ö²@.p½`]Èʵj„ñ&„œÔ¿™ñÔ55±T27ÊF¼ø\¼ºª! <Ì^mÁöÚCn L*ÎY Jø0û¥‰O±À’ÝEÿ°Ö¤ý{Ëm]!/“Áôdl°~Z¸¨æQ:ãu@¡»Ÿ¥ßušL¢Ï×tyé±ùüv­ýöç¦A_�×\U?a>±¹ho8ÏaO‹m§É à=œ‘>Ø">sR?† ZÑÐhSt�/³~1öÁ9zíÚšÜpÓT¬GîFT¯Ú�-®Ä¶�oßçÁÐ?$¯¡m](ÌÉ¡FdP½R¿KgÙ©’’¨²ûËÖJ&uQwO@š÷.„àn0EÌVP,�XÂù÷ÓxU<)çæãŸ�½‚ë�ý¼’ÈßXWL“x­^? …ÝJ¥•ZÌzbíg=BEÄv¢#�WL[ÎAŠM ˜6á•×`þ³øÊ«£‰~atr¼ZJã Åõ~eZ«Þü":â_Éú¦®Çiieëà\Üu…­÷QLagÏi�V‹2( d‡q#gª§Ê峯žE 'ãléõ—¸’`¬½þ±R�q»µÜ�ÖìaHë¶ÑÁöˆ�ÌWù*ùv�œÄ„…Sà_™²ßüwm‡ERfL –‚?½U6ñØÆê['¿o ÄKpÂ…9±bR;3'Ø“ ÞÇ T%’µ¯ìÖRÿ¼y™„p'´BlâÁ­«MqÑ|LLvtûçAåPÛ^؆`HLJÉ_kgôZC‡è1SÂ4µ’Ë>ÑcAQκ)G íkºT¬º~–>“ù‡Qnɤ9Ü Ñ ¨ØLÕ +s1æ@ œ9&©áÀ%¸ÌËØ<’dö¼,û0DÍD¯ô(Î_rn› p•OúËÄ£µ§¸¼=lwÅá$àÅ8ÿ!Ä÷_‡¡¶Lûõµ(ì·ð$4µ… ]ÉÔ†Ïrx›¸if@5âQ9_Q©!y_jc×NÌ—Öj€T™£m³ YàÞ¹S  ºb$ø²Ëyœø¼¿¢Ÿ�þ”ˆ™¡s  ä ”¸þm{Ê!q/'ëY»úñÿm·Ä#¬�P5F•Qµqáó)-¶Õ>]±Ùk¨l�—W ÓnÚG �¢—Pù)Öyøq´âéMz zPÁP¨XìVâ Ì%°:tkÃô ~d6£Án`©÷ö´2’ñ-ܾ܊²Ù,Õ.Ô='�G;¿T ü÷*¯¸„U�ǵ »?„¬Ñ±�_½§´X^ žÉ�j™@íDéª$¬êˆ÷Ÿùl‹;š } Mæ@ Tš¯ŒáµÔRæ²khú·p`µª~„ÏäÖƒ=#2“!üýÚ”Ñùf^³˜•ó’Þ³(©³d‹Ùî#Ó&öž_#lî]ÊIº¹d¢÷ª‹KõØnq6ÏŸ?Ç|×-n@$iƒ!+ñp¾î»#ý» xI\ÖK�h¼œ@L®cnH2í´®¸7®Œ—Á—üRâkZ/]Óôzˆ —¶¬ò>Nù_¢”s‘tLõA>–Ý ÁòÄÀíV¬¨LhHzì†S€+½ Ï3‰æDU¤)ùRÖiÄþÍИn¥E‹g�X¡ýI¡Ó3J‘f=mmð­(–ÓÚJà`6士k ÜýÜÔ‡²ŸÁö6kBÿjvu¯\Û˜ zpÏÄ6^w¿A£¯!—6QáEžy�Ê‚&¾ºÒ—�¿<ý,\¼êS8Øú(`œR¸ôÄüÊc!îú¡2°Îî1ê>ã‘pÂjêÃŽ†*Ô©nÂy:¶±QŒ-Y9ý&'§P‹¶øvNˆz6«&�îά1³~Æ FPR±Mª!nV…‰ùe3²Â?„Õ™ǧú.™Gƒ„íÒ)(í¼�Tß´:¡"˜N—A£ðWF˜íéÒ½tù¨Fbù­êU¯². §^²Hq‚X`UKô\;ª¯¥©êÇVìö¦dý[œìÇ¾Š™à1ûÃ0Œ‰sÝùŠü&75éGW=:Çeý‹þâpP‹LpËå‹ä¥Ø!ói«¨Ø|�}É êÕÀA¡+0‡7T!Æ„õ×AŸ{šà™™“’9*yƤr–ž¶>n‡¨#¾¸Ý;rî°ËÃR\‚d•AízYÑ@,¥qOä’•o~‘Z®zRé飞xõÃюᯰk»ºðÚªII'GƒçO½‘Iýƒ$žàÐGùŒ,«Êa‰‰ÚIÌÔ<Âg»±±)¦Æ„EÒÐ@o32:wé¸<”(ÌúòB=àèä÷®ZÕ/ÛÆZ)¯=]ë²üB 7104:0O‘É Ê\›MâdéLoFˆ¬Hvî_}l¹”ú�œ\©ä3öq’ÅD5×kH îÐZ¿+…ÖôJô”–ÇìSÈMfÔÒ&�}.3âLÙÑëÀÛ|„«7p8Eå±L§B—CˆÓ–ꘛ©“ˆ ¤M5ñ,!n%¢�Á Ý£êÇ�¤`°cqí�Påqä €h;]�Ÿ™g´ûÝõP¡m`~U¡1R‰ù*¶GgÀ•Í›E¢—ºõý‘½ßûÌm)#ǽ/d¿;ÍÅ­šZýÿ(L¶ÝÕΊ3¶—±tÖÁEüïä�ºµ¬»°»é ¢’ZÐýXb[¹·ž\ެ?þŽ•†ôÌLÌ+^æxæl¹�¯£|Y·‚ÓúÖSÄ$ÛT¬åGŸÛ3îÚ�*%EFYê(ýŒÀìr¡÷/oc“pî&š)š"Fõr?6Áñ=Ò„ciŒl�ï ñJÕ>d2]˜¨¿Ìj{xbÖXh¿;€GÈ ÃVÖ"s!g»>#.±¤JÄŽé¹R©jnt¿U´sjšËÑÛ+j-*¹!¹,«£Ig1V Í€·!å¥QH~É+ÀS,¸ý4Öþ™‡‚E£Ëf½~æU`‘ñ‚�z°K{5cFÖÜ~Ÿê& ”—A²Ö�çŽÏ/\ù1. –9g¨àr½"·W­¹ìŒÙiÒ䆶•»V$˜¨¨UåV¦°{{;2N›}Y7WÒ–Îÿ>kà'3.k²òã�VX¨ËWoI®å’EÊœß$4†ýrG¹ÜM©ìvâõä[LýÈ"¥qÒ#œÑ½e (¾;ZKâÿ8mž4Z£ç-ÇÚXð¢{h¼Ã7¼‡(ÿ5‡¤èŸ·Y ®´=qîÏÞ23é¿ è©*?{[‘P$ûœŠª­iƒS»©p{ÖÖlßýo©¸�Êê2¼`ùž;ì ïÂ��íXj s@…ÚJèd¶ÙÈŒÎÓ­äâQå±6@6¥¥Û@í�:À‡c�¾ÈHÇÉ“ç„ ,BÖ âËŒ¦Ì¥‚^q%u›§­¾* ÷.‹=úˆ8}Ïi½Ä´~H|7²á$§ÕÁVnˬ‹§‹R”²÷&E©ÂZé Óà|‡b´¡8ß¹b¼¸n›\ 5{G \�{|cVò$¾òÙ.+è4d?"$Zqè󽦟l’.% _ÿr¿¾‰z‡Sm¸ß{޹’{F.�ânyÚ úºHŽ.#¼°�NÕ\¯¡…Ã|LŒ†âÅ#Hèû°J_SÁÌ=;n'ŠIbž_�?šßc(7ÒÃyáÚΞÄù¥u¢·hø9˜:Ò=a¸¯yú/×¼ôòï±nô|Ô\÷pBæ#ªIr“ñ.ÜBC@Æ*§UMw8UDãÓ!­b‡àWQ�ü™%Ú†Œ‡4y‹Íõ@“¸Åƒ¹¿Ù’øÍ\6,|<Ð[ü'¿Ê׸qìþ©{TÀàŽqÍì0{ÄÌÈȽ�^Ö&'^°ç¬´E”Ô,ÊË^Þr€<`3Õû•ÙùR“tYdÈÊ©Gë¡5=-ëídµÙ—cúœV2#¡þ‡«JZ¼Ö]#¼Iæ«ÔûzìüA8]ÏXnÄÎ�ÉÈøÑ&¡©¢ÒÆ,DK óEÓ0µåÔyn¶õ³áCËçÖÝNw6g¢Ž�`t‚Æ}|Gˆ4á’kÊ‘¿!Ít ]f«íšÖHY  3!›‰k"ß ¤2o€g”�Öî²MRÌ�*´å s°ÿÆ„èû¾õ… ‹ÇÆÀ½'£CK0ÔÓ3ÂÄRa¸ŒæZZà„Ì´m ïéft,ƒv‹%o#Ï»ü3´ Ýí�c‡D�’”{�™#z #ÙB´¢ð67;QPE ^°[­ìnj„ÉëŸÓݯÿ´¹n×¼caùѳËûJl…“0&'2™ü¸È»æ¯)É«éó'�Ècž“‹Aæ†ÌñË•S-z vä 9IíRñË-Œe›¾vOC±€6Ì[ìxñÔïV X�ŽÁDÀ9ÅwÙ¾BçÝ•Æpj¢ú~ÉIäf® ÝŸÞ7ªìªPÊŒ¨|¨FÚŽ+úr¥åö^H­pÖm:g 4½¹â�£Ôá�³‡¡UÖͬ �‡ü³Ïr'�[y¿<hÒ¿~½•N ŸûbõP?×W-ÉäS®PH^ø1_~‹Õ<ò ÈŸ©±í´ŽéN�Pë ’Ä4šã ~¯�-3#r#| paXxš$t«Êõ‰$5A(î�Øw’¿´ Úû_ÅÚÿbç{ )N·\'óâoóƒý qob2 qDîÙz�è§üªšóÚŸ2ÏAg˜gcìùS`ø %Q…PXŽ)Hq˜èíî½>`w™BŸº 3… Ó×¼ÅÚNÔ§Vã/ðÌ•ŒÁ‘��è ¤Ô½8§3 a¨”õîóâo�ÏHÛž ¼¢¿¹om=[·�±þŒEZåJì9¦s`ÎÔé�BŽŽÇ'ÜU£€6?Y1»7›—ÿ�ÄœÆÉ·'óÍið§Û8%jKÞp-~Ú£‚v¿À«¨õd}S^Xœ±Ùéeî0Ù^–:|2é±LxÕ~v¯NWbÉÕ…@o¾ÂX¿vË¥çu}‡J@!AuÞ‡ž&I/ÝKêD ¦x Qÿo¾%ýÚ1_!=ïO<6­M(š^+e¢ ¬`Û©uPó]C¾#h‚‡è|І7Å– ô{�ÌΤŽ}¿±„/“Lm.øü‹„§¡UëãÔbæ Ã�ª% QQˆø =¬ÌçÎ7Ý%ÔæâÒ‚C¡Äºç‰ˆÌã5ù¢Þ{´\ªP¶ÁG)–5g[i1Â: >ûÄðôK�jt`~ÍufûZ ÆbÍK³XUtmF©á3^pLDZTÜîÀöçÓŲ°^¢`O¹Sî3ïÏ#ØÌ+TÉï]_c‡Zâ?X®™ˆ¤À(T4J–o¶§´ŒÊÒÆê…²ÿùOBë:5ØâŒ`¼Oyü¢Í¢D;´eóç=bØršÔSç-훎$�÷ÁVzÿj­‘ í$–!ñ•ñý$˜à¾Ù¿D~qnÉ™6Â*ÅQ¹?6Lk½’Šy'n‚Šm†‰‹ÆÅ÷Aý_AFØ5G×îEŠÔoæÒHÍ7›žï<š-‹3®F”¿áƒÖ…¥ÔÅ5uÚõ 1s——MX$9ÔÈ8˜37æêvÕ× î eãÀz(^ß`7Ê€Î.,-®é"�Ø$Ñîüú([àY¸ÜN&Úùö†úƒ°´k”O¼ºr=ÚÙ^8‹s¸ŸXê, ñêšÿ LpÅxÈ�`%|m—ÜÊä†QêÌר¶ÔÎ£ÏØJ(’Wˆ+ ½Šô„²è*=Ñî9�“õãE£ré�ÑD– æ¸-p!úþ¹®µ¸xNÚ�š ÒŽWZE�¾2˜1ó$tóBLï³×é?�“Ú„­•û\¶_“]w£E0° ¥,(^üóIÔ�A,æ3NCÐSJŠëš�ÚÞ„?9¢Q‚Ûæú”ŸÔ.A¶Ý§¨×¾®j^£OoV>ì¬vÄîñ‡ý¯Ùhcºý>‹$MÕÙ2ï–W 1«”»ZdeOµu�ö /ÑZç~&iD„�]�±Ý}BëCJÚ�¢2‘´ ‹†óþ?kvNÃ1gj׆ÇÜØ›e x¡ö˜@‹¬EI˜)>Á™ú�4)á(yÄ”ù”š l “JÙ¹A”V8Âõ»Ð¶Š–%G-¯<ó’*% — ±ØHfÕ1 7ñ|õöè%icCeÎÈÍB‘ã‹è¦‰ü†>j¹©ô›Â`xÊ‚ %v•@Óúß0+)Z¸œý„ßGÄ~OЂ� Ê(O'`·úe0ƒNnüvNCú–íÙƒ$[àà h(Ý'ŒÅËJ[­enÃÐ%dH([#‰/ì¸0ù£XèdŽÝnÎÏmõGÏÑC̨&’Nô Ç¿d;Q�½¥úšj@Å•bùT�oÆéªv™øû‚…8á:‚Ät®œŸ~òƒ>�Jà ‚8çG¸Bî¤b^…q¾ß6v/ž5q´Ûè—ÌZ� ³ŒÉMYfÒ lÎË“8‰y%+Ä_¥1ä´á˜ëí H¦ü¾g[.¤c$M¹–.�Ç•¤$䜑8¬aÁË„�,7’;ö‚¬�Ðýp¢¹›7LŠÆ�Ö`ßpÊË‹mT¿åê©�øòYc>ÓIå÷éÍ_È¢¡¦Á�Ta©üÍxk8wÖ­+Z­'½G/ƒHn{.ŠÒ&’¢3J¼}? S×µ&dç¼~s”L#uF>X$ŒS/(pÎyÛúÄ8܃饗jkV�K =!ŸJ+ŽÕ0Å÷g„`ÑÞœ¸÷Ë­•± ?Wië‡6£»IîÞ;¼ì›� øã­ý<ïZq2×·’"õŸn? ÞÔ½è› e>>74ñp_žFµ�Òe=YPm[Åçâfóí,Åaþ»ò)�wÍe±L¦�ˆÓ£r�÷Œ*4YsÊ�¦ýÐh)Ô�j-ðoÞ:íNc/ß áôèIL bç–»Å0\Í�_[-ט–"YÉ÷ÌÀŽˆÚ«ÞÁ ìºÄÞžˆ7SJ8�IÆ[Êþnpä£x«*%ÍTÌÒ�¸¤¢»ÖiD‰’q£›sõz `ËMi$üSDZ? —tº¾sü�`íg2Ý­{¹UËBwÑOw‹NóWó `´áêG²ß„æ?£ûˆ—½n»@¡ºâ…jzü³ù8ˉr2+izD ^„ÉHi~ÒU€êpd‚)®R”W¸”á´ÅŸ“Ðg¦ÓÊÖ2%…Ö¨w8Îò�2^€CŸ5]ö€lŠõÁ0´¾;˜!4ÿ¬ê9"©äćÖç�"£Â~ä~ì÷a2ü)d(%ù²=¤±Æ=åÜÏŸ+K}Ûƒø™ÂTXaž?×Lô9ö ^—™CÇøÄF#`AOÌ Êb66 é0ŠÃÉÜ´lcåW*û¡ÏDDßäðŸ`Ê•“¬‡Ñ׉†ÿåë Ör�¿7Ž}œžï M†rôY§Ém]tÐ¥ßk% iÍX úp(ÖQ¨Á½i‚ øÝ«M¯…Ld«O8qûQ¥0m¥¿�ý­‰¼ïêb¼¨wnB»Á ˆb­Ä’‚OJ®ô( dZ½L:Žxàü©›°§R¾Sƒ€”=ý4~^>��ï.‹bä�5)®Ä ›¡ý@]¦Å®³påªL•¼ðØ#EðÚ)ÃéÜ¿ â€Å%¦.÷¿|ù‰^µ¿tg’i|\`m•JxÖR1,rk‰¼Dç©�É.>l˜Fñl÷=¶ÆATçB`dˆm‰õ€!^ƒÚ=•Ï‹iÁël² ý‚í9¨«kÁVåÐ.Æm}¬>7§/R âh¡™C½Ã^Ïûò;;þEi›I¨D/0¹  ßüÂÞÍuån10�»ÉÔÄu P÷  ºÁ_Cru�»¡/§é­ Šc€8ƒ¼ È,~p*y”cë1øØùè¸:tæÀcî° COdSÛj (Q–¾¦ø…§—“cbívÿŸÔ©îS ú,óù~±é<¿…Gøe¨Î50Só/"¹èÁ††•µÎÐRñŽÌncÍòÊ©Zaˆ\%à½é뾟€ÓÁê ôÝfOýF•h:EoÇ¿­ TCÃ×zÇf¶bpÄöèvAYÝñ¾ûÂ!˶¯•Í‚Ð}#F;´�>_k�~øR¨Fµ‘›Ê¹JS�^A�Të}°˜ßQÉ^à3L–-, ön‹IH®À›œ#f#±QAæõe¿ˆÔ«Èn:²˜}pïô�,V˜J˜�kPÔ£ƒ�Êûùà¤nGÆåm¬ÚÚÞye?vßmÕY™N¹|(Eu*¯‹a“ùÀkÏl %Cš·ž#ˆC`³åÜÜ Í£f;]¼(ØcbѶ·4eέ�G–Fº�E…(‰eå'nF¬ÎAU†’§í¦åè®ßý£ -}Jè´~H]�T�çšð¤¾+ý%J§ ¼c�…ëÃ-Å~=òñ,›aþ8,£ÿ@$O VÙ´é·�<çÏz‹3'8f(OÝtÜÉÖØÜ)©‡gÍ óN�‰XÒ/7‚m§qA!s´iÌ÷a¶ŠåúPgšf¬5õˆZ tqñ"?ÃRFI\s¾Ò•ïO¨ákíH1ª§8ADåÕ&£Bî_»ôìÎ_'Ö6*«9ßJ's,æêîÕ€.î8üdÉ H24æ¸h¥§èa‰ŸÖ6{�Wö$c0i#r`¹Õ�UI{·J¸äõ˜ÛdzՊ}cIs/¡e°ïf’pI›©aÎÅ8�Žý^Ÿ,ïA‰º™"Eb·‡ûß’+Ή˜»MÞÄÎP­X{kßËDß‹M«~Y¼¾Y.CÎö^ÐÌÚFhÙ(§˜)õØU)gp‰uŠ¡Ó�AŠõ¸]$ÓV¡G_ü�;þ62Ä©Ïýrö;±0&5`=ðþ†íç¯ð 蔩+8èânCk™HÏ ·Jàè#§4wîì«jhUJ³|(¡°†F4E•É «ÕB41Ú”>@Üä¬Þz—TDðÅ™qЋC*ûˆ�Ù­¾`S˜T¶5°ÑÛÀ½·cv\u•lO¹–÷ùpÉ骔ž€`P]©¬¢yf�/Pã”i Ýáß«gQ„v¦T°Ù¹ŸPƒ¯n¼nìNì½l,;cBW+qÏʵÒÔßóÏ;ŽŸOÜ5—ªbô¼¾õ—?K(†ÜXñׄu}9Nî4}�@­e§­à#L ¯’€õ Ãç Lûð§ÅboèÀg–ÚîˆÃ«©bÞ9ÄnAU–±– ùý”›é¾©pYÕ3çP“4íûÞ*�tf¯ü²LÉh 7½¹ó°“@MHÀÅÍÈ ñ|§:»i7*EÿbZ1Y¸9ý:f#}Øcg©žï0v·wøöCȼû”´Õ'Gq  ’ñßþÓÿÙ�¸ -ûám e32:Ž1Óž%Æ3ghéx&ȩø }‹„fD(ÌÆ416:]há#öln�¼L‚�üÌ‘˜¤%�å\&»£÷ ˆ§S= Í\®×n„DÞk&"¸ð�¥ ‘Å £¢% F^Éúü5‰^‘lyFNÜKÁT•Ù1UÝ‚‡ 茎W‹´ eür�l *«µ^¶õFŠ‹>eõŸ‚ÈÀÙMZòZfø‰€ è%ß,r±†t·–GYeC_ ­¹~îŠÇÚŸÅŸ¶ÿæOÊMÏ´½hÀӀϷxÑth¾‹ŒïM½oÑv’+VK d¬Ô2c¤•AÇ«“îƒ}Xü5:�£kÏRÙ!7I-êe-Ö¹%¢¹@ÈM Ó*.nÀqVwý1:•ŽN@ªv§Xà�–S…ïóN¢•z—éN™!÷X`ÃÊÖ¡”ÙS2bò†à.µ—›“}cŒ*°r-®Ç؈M’È“)®ª •i‚€d@†Š—ðúkê©#O|"ý8œ_W[hÂ�\}`x-oöÆnnšÃ6bÚ¡X·TãÍ‹éB€Ûëä¯_»[ÉAS3ЫO…5ÑÒKÇ32:Ç©jÁ ¼2ƒò�aÄçXÿ€:³¥ÁšÒ@µš3776:íš’ÍVË[ÈF‡ü·£³‹øÊº¿+å®Ì€zÇó~$"_ú==„Ý.öBÉô‹×Åœý¼³9l¬ÁÚUE.‚û4m˜u· n>Û]AUxä2!áóÉŸ´o˜º.† 4�NÖ�ZíIî yÚ)¸|rq ÞX(f"Â!E%<Ì/0tÂÜÇy¢Ø ˆhÅ‘ô¹Ò›$ºãûk9󯜹l”Ed‹D�¿Ó”®övbØe;9³³‡™Ê�µo°80ÿ}L!‡¿¨ÉÉÃîu¹¨#¼S¼‘‰JÎeË-_ò�jÝ`æØ�lwqJtïshV`4R6‘¸rw‹žðód­ƒ§× pNôryxb”Зe0tÀƒþ§7É:>‚%ŒãçÚËí?¥´X E%“OW#‹éX4ŸçÛcý¬ß+†�¥ƒ‘ÚÀÁ£²õÆéŠß¶n}àâÍn›(Ýߟ\{Àï�»V«¯”Ä,ËøÕãsäžÆ†å üú·¥E�ÇÐA1‹û“!*y@ (TnÒïÕ�UN¡ÔvùŽžqÿ#î½nz×#töÚ™K€¶z•Š ¯oL‰uvWÛ�/`°iõ/¬¿Ü©EµMOg´ªä(�þ?rt`ðãOéÙ.WÌØ˜âgªI›—E­Ë»8š˜ý¨C†ýs®Ú°w“Ä'Á6ÃbÇ×%ZWÖýçîý'ýak¢Ö+Y51Ÿ�Äró!Ülý‚O¡HH1fÈ» Þ»9¹í+’Ó»ü®0´ËÑçd šSòtLÜ}’'έzf’%µO²UkT0ì7Ô°àâÁe×`Úf¡fÒˆÞï©‹n$5&|/r`£¸æ¡îŠ»"ì1MSàíþ˜w“©$XŽVä³à›'þ ü0ZûN­×,©ç´G­3x]ñ­OðÃ’<¦§ù‚ÒNïÆvÚã—+)Œº<ïBϞדD›oqä„¥Äu\8 £=m¹´4ktt?ÓŒ >iÔZm†PW ZNƒAÝ¡oí�Z IgGU�”K×f~�ÄžZ~CôÊŒf®öþÄñà ÷âó”Ä{Çý¼ °ì~Zê7èLdaÃÒ7ÏsáúPŽ·ï1ßI?Ja`ïûgÐÐ͇îè6�ÖÀ\…!SÕÕdDo”2kÂŒ›h³l†Í»%ͨéöéqßê¾wA¼ƒÓÛjØ€ÉCß�`}ŽéíÌ‘†BEs3»§7_²W˜]v->Š2eƒŽÉÂ耨 Bªœ �Ð;(t¼`;ÆY2%át, tÎ¥uƒ×Ræ�Ttðe&¶{IiKÝF=Æë4Ð^ôšLeÀiâ eqì�x®‹#ù f±f)‹T‘D­Ý/N)@¥ ’œw#'P+"ÞΧÎYò>"’î¡2èø`¬4sôj´Ú/Ûªß23¼w‚Í!R"™}œÜô´Ã×Qt¹�|È»PŠgƒì¢¼·³D;Ð*ú@Ï6Š…¸™ç SÂÏ*‰­kæ\¥¿˜ŽÐkûY¬9UKò� veNß°Û7q.ez]ÔVœX×g �GÛb´V•›o0 O�÷¾ áÆ‡Ù¨�†~1±ÎÖV|: —ÐJÒÐó K`ëEmúå¶YØ|N`Äz“!^e$Ï5iŒ¡�² þ~K)ÈôKˆ{?–;ÄÇXÜÚ-¿”€ãè' ¢>ä”gùQFó釸B¥GOM=0€þ€ŒÆ�Š ™EGÖ¼Ú¸áÌþjjŸYtõr“·‡ðµžbUð#L@¯TÀ÷?`È—$v ¾HÒpº+4¨blKË1CP25fw}‹aGz’O•ùaÚðbÿð/¥$åˆï˃üXãž•BpE³„óW¹ @gÂ^ ,xœ±‡vg‰@IÛôm¦~G˜-PŸZëß* .ËÉ•–~U’ËépÔäÙ¾Yþ½(M;% 2cFû®Rq¢N&3ˆ´EEïÛ u¾[X¯u�üWÆe˜­Ê9ìçÄ܈¥ð±{ :|è�Ž­©�F¾W¢à›†„ {)z�Gjmíï§0A5|w`°ÖïºÌd¡|ËÐý‹šÑt9_Úò(cEÄÓ÷¡×<\Õê «Š±úØÞ‰ò¦À €÷zº€Â…q)I­OøðU‰|u2®²' c½Ÿ`ž¹:¿ß‚BùCäa'cš p|Ÿ‹¼j·ŒÆ*᪭¥û‹0}“™_}É´KŸFyÌÉêвH„ßßGÆ›pQ‘¦„îG—¸Òtç&ªƒ� ×î¥ÆAM@I]'0–iL�+2~ ´F¨ ÚÎ9ûà§njZv�?õnÎsü†ú"„ ëÄdùeßµ¿;]dPÇfêºö¶Ž*(ât¿¢pàà#ùVxê¦ ÙÜáu³­‹•5[O õãԮߌ¸$ý ¨Ê×s)\ðÜQy•G³»ó @ 7À“‰çØŒ„±)¸Œó  27 ëß°"ìÆr.ywd¥›ý¸÷·3oÓ~òŧÐÌX÷�'°´É~’RÝ"ìÅ«Òçyø:ã�†£`a·¥0²±  ]#…cî¡dRR�4 ¥#$ú •D•0ùC_áŽüv 󆇨êHP·ùò`쉞FµÖ汩Øk×^Cˆ¹D¡¼ç<'Jw)PwßèEú&µê@Ž©j„õðªâ�â.È7c¢)ìó_ßD.¯= ŠÝ؈ßÒYÕ¾äu÷f…mé²7>ŽyfN®Ùƒ0…¶:q}S”ðZi¤œ¹jíyΰ HZ ¶B=”Ñ$b6>÷; °¸ŠÓÜ&³è^Ð7Wköµ.–^>ÿ„Ý`€XÚÆ‰“(/5Ñá.sµ°!$ gññ´LÐKÕÑ;:b<� ‹¯» �@‘>3�¼Q„žCßY×Á·�jdÙÿ…E¹¿G(sÎ+dæD™bg#ßU?Á='¨Ê~¹°N�ú²ö5ñbž¸n¦ ‘»§î�sDÏ$;Ö« ÈÛôñô+@yÔ*ð€—&© ¬ŽÅ$EO=¨¢çþÌŸcõ¸T~Ÿ¯:¨ƒ!™›J„œ‘h^!€¨.1íw8û¿OXºi=á ±p¦ùsbu_2ü˜ õ\8 cùƒs­VÞŸ œFJ�¾ƒdpmžó’ñO3 ¨±ø,­ÒÞíßáw;¹‹JÀF?éZûŒ—xB¨/;âkEI‰ÀX±^npYgd~a}è)è"MššÜL{Íê̯£ü';›&L5O½"Ža°š¼ÅCö½ßo1’{Ãk‹�ªÖmãèç³™#„Q.ˆßó]±Õ}.^Ý{vÍͱ\%\BQ1¦!æžwjP³ ‡Üe+¾M'è^¬±�ÆíµX§xÓÁâ¢Àæn’"””ý@àòÙ–ü-œ†¿4wº² �¾mué]H¯™·C¯ÁŸ~áµU“ ’óIû\Aò¼5] öhÌñ’«à n½;I‘\“ËÅ}§wIa(Q€NdŠßŒON'‰I' \Ü1G4=L¶ÍîHù/2âæ­B=N aä9+ª ¶C¦,¥í«]réä�Ó‘p�-£�`´g‹ŒüÒ +º‘,Îs|l_ðb�^#ή}Ëqì‡ú!¼�Öw%ª‚xêÿ°Ãuµ^0n—­7çHŸ<�d9Ä›1Äz’lqÔ8>ñbÿ³|jZöj‹Tó¬\Í•³&nBÉ=Ú½Prp,d‚CýYŠSdð©¥Sg5ñ#Ê~è«üÚ®¥À£ ‰¾#{_—ÈýÚ°u ,‘ÚWå€2~ï,kÉ%k»Ð¢4„N3·Ëü{ÌH;ªá ˆ&Ä…\§$„Úz”ᲃP“Yò+ºßep\?À¶çíW‘ mÔùP zy|:¸m“! é»™ÞI:Çë,©*ž!ؤ�hW'©žÚÒÛ«ªpJ¦�‘ùsfoÌÑjñ´x ù¬!ò„pbÐØû«µD8˜g¼v‰“(r/2 �5È×”øF.ü[�HÍ~úÕÜgù¥úˆB¢‰]ù±â …rÓ}ä*(ûuÅ€R{ë9*32:ÍDç<�’úžÓ"ùF¥ÞPŸŽwO£ä÷£Ã·… …Q1632:49¤;ˆùålÆŸ¾ÀN=±òÖÁ�x^查ˆÞŽCÀ[’ì. hyZ½²<ãmRö�ñæay+ôí�z|¤»áÈ#£6ãA[$›ÂFDÿ2hžAÕTYßÓÛ®<žŸÕÀ c¾ÎÑìíõ9?›Öê9о²v… Ûs5,4ÍxUÏfkÃ%0AI&rŠfÐ dy½µŽr�jMÏYØ`íº[¾ÛŸë/²IBŒ²¦+øfmÁ~š2ðPr%¸}î>]é½Þ¦þq‰ÉY°(>×ü#x jrž•¹iÄMj§è[\ñ(+š°©Ô•á—¸N ¢€�Ûm¼õ ”°Æ‰Æ(Š£U¶``ØÒÍp½j^(½ Oq2à�»¦Cvœ$ê¶¢®G°F×[M&19³fE/£·Ÿðþ^Cƒ™ 9U;7*<ˆª°?fŸ…Ó˜M�I…Õ`ÒXë¬^H«DQEµˆ©ªww"»{« TehÜ 4AANÈ‚_jù3hf�×ç¤Õ͆?55ãU`8{RÄÚN éi;¯ÔÏWeµÒfÃj<¹ QFÂÀ¡Dൃ ‚f¤Kx(_3´E]r÷¿Û0î×t ÿºwbð�I{ÕÉsö÷L•‚+îf72r‰ ÖËG¦bigðÄA5L8«!RÎn‰£Ë]âküŸxzìJHÞ1gmAcˆ1Yr#ÒS Ëd wô¼­üqL î o¯¶��²p¡xlú<»Ù$.!ÏåF°˃°ÞóÂèðj²PKêwqžPx…Ô“ÑÒ@À¨¡Ï€o ·?$MÒïæÍÅÒ¤±vöU…`RµRÕ Yv¢Q¦ÞWMvGd™¹’˜ {UŽEÞB‡â1³d ¤2œ‘Òf4‘Áu�Ix�©ñx³žmÑÞpKäÂã“lÍ/s`’P µú*‹ïc…j°¶£þìEhDŸÝê Ilk4¿çx"ß�·òV®ÜÜž–pñÚK×%r6~]é–Æe뛄m 6P6À6}?�í"1jÿå‰2EaµÚÊ´„¾ÏóÀ!5?NðøàEÅ×V+REžV2s>iB(DZû‡ÀÕ¾Nx–Åè‰Á¯tïKOH¼Ï†ää1®ËÜZ¸ÇŠ#lT+íf�—g†þÅ /|¬, à�¨Ž¦‹}“ê’¶ñÛERql—�pîQ¶JÏà}|£uxbw¥›�´4r¼EÀŠA+Æ(7/²dO+×–ó3ÈSbœÏ¦GÁ×¼ZÄŠ_6®ÎNõ$'R%ÔÍØ8¢i;hò^¿n&ZsËLÎý·È®æS÷dì)cÐkí¯¬Qœ 4üä!“KðÜûrÎ5º Ïܱ?ˆ]{„aq¥¥áïƒGU|;ü�ÒÜè˜UJ mòèL0©ÉðöháŽ9@Á¡Æòùå‹(1¡%ív… Œ2®e¿¥í#—jÍMµå)£,Ò‡Ï|˜;XK-\Wl¶r¯™÷7wwžYÑæ vOT¼á½ŽH¼rÉ—üQ7ÍÜE0×Íó”!x‚ᕪÁy‡ó½ä×ÈÝ�Ôã` 4©ÝWKð‚aC놤DäÙéþ©ÅR¸ÓÌb|ÊbQˆ[…d9ºËø�Ч,cHÙ2<¾å¬jV#µajÞŠ,fœ1?ž(‡üÈCEl1ŸÍ»,ÌJ¬ˆ‡ÒÚP®%Òi‘»êµ eP,B¶D šFâ£Qv¨RÙ´‡éšqT½}¬Š]lןŠeh · tâ ÙoÅ8¸WóCÄ´�ÐÖé@Fg™5`ThˆÄ+O4FY—oàã°(SŽ®È¢ •=Q­ª{ü%¿Žù¯Ž~à †(æ,yôE ÑÊk•f9ÍdQNª×÷+–'qã´îµý­ÁY0qMtãá�"T@Ÿó?é{ERQU(@€~—Íäé îׄ•õ‘‡"ý§ìHìmBÐ+�ªÑ`‘&÷ú'[Hx�•dGMG·¡ �“H@¯°7Ô?é#P†k®;‘ƒ¹R]!\þ“ÆeøBy1› æüÝíbzªg½¨Èi}Û—©¥t¿ñ32:Ï©OGyjéθÃn—HCPC’’ºK"ù7×-Uõü2752:]4¥ÑåtÄ8þ7ÜTÆzl:¿ßTã G×Þ®e3†3·‰ò-V1DþÆ."«/"D È÷‰h«M§„UÝÙK\Ÿ‘ŽJ‹ãS/ž©úâI…”œÁoŰ¼˜�ìF#²ÊýØx{®J‘ÑeRîÌTÈg[cUxŽ‚k.µ¨÷ÏléIŽ©úÊ[ÜGç‹3³ •w×–)Øæˆa½'OÒ½Š©†wZÄøÂùª{u[­w ²áÿõ¹ÐSpj{OdÒÃ�¼îqÃM¡ëõý^ùh�‹³& _ÃeLB&éúäp?¹y[XúÓu~©{綼Ĺ3Ö�N SÉâæ ù!³¶á°ëÂc¶‰pL1’�]>H�3O¦Ç?npUà¥A‰æÀ§ÉƨQEHéÑ®ç¼Ç±Ü"ÙD¼ ýdÔ²�E¤3T:ž·2…Ôüÿ8ëå Òg™r— wÄ>éœÖ�àóK¹åꌈ­Ì<æÔ¸ï¯W“ ;¡8>î‰ÊßÓP†Ò½w!ìËvg`sz¬MŸã3D“ú#09Ww ²%xb„\ŸÙªÃB3–cåëÙñ‰Î:þ—“+·6Fe ŸÛd ��ã{–�h€?÷Ö>vu“œ¼Œ �Ô Tƒh�‰ñ 6¯wZƒ%�r¿~<¿¸Ö¯�iôàÓì"JLʰ) GH@O»ƒ—.3-w·ªÑ͆÷Ïñ€í,¿R])@0±ñ®E4"a�¥9w|]“e†WÉÇl²: Ñ-)¸w6ØÁ¬¨t¦Bî¹ jƒ}?úˆÛ¯{Iùìƒ-³;ŸÚð:�ÑÝØCD/•Ëüsw9ýåùÏŠYSDÓ(¹ôᙳó6ÊÖ%É'`¥:`d­B’–'WM6 Œ±{òßáh{ºW^Á[»ëŒ.hÀMPÇEOÖ9–Œ&ELÕ-¼4¬] ¡Ai6î†ÒYþ±g‰ÓàÒÕ«÷«r䪩ÊP5É,±ØÖZ(† ÚnÕ: ÒBÏÚ[i³—,žádë?gaNhnVß(‰Fò�âNðéøá ècîóúAß3E¨ þ÷�ðyÙž_%µ®·Ïý»àTêΖm¹fôv�To×§c8ÔŠÈÏ~¥´W°.iâ½gnÍö¡± ö|´8ðOn˜uÁŽ é6Ç9‚”;HŒÎy_?á¨tÌ–Šú½b%ôöp"!A€* ²z/sTF½Þšy ©‚�¼C�«iNISÞè‚¿Ž¦Y�€‡÷eãp?wÈ6Ö\Íû¡¾TZ^PH«20ˆŸ´Þ{>_økŒµ‰‚€ÔL0P“Ú3�ÕÇ(Ø6E¬”„Y+E%Ô¾�è3»öucýC6€mՈϥyµðîàÈûÐÞ�–=2 K&€¶ÅÉãSF¬0<ýHŽ4|ý¬{DD­›m“…¾{¯V¹ŠJ²4ü_àtíf/",Ý㲇R¬Î�uEåËîSrà§¡ ôAH;ísý�í¤Éþ¬f¹Ð•âÇ{BÛà�zt¨•LûŠ?D´-ãò]ÍA8ùuˆ*Ö…Žú#IBž$Åþzpõ§µÀãÛÔpE0Ÿhv™öLhÎ,ÍŸÁPþá#”µL}8 SÁs¤3oç?Ç‹fMƤ«èÎn¦� ráác'^ÆœG.”II­âùJe7o©öf ׉žÚ'�†~à<ÇÞÊ‘s­A.T¥fï°î*w+í—ó‚Ÿ�q1šsÊÁ5æ6CÈ�ñkÇGÁ4žpò«7¦<¸à£Ø<�$EèÈÔŃ"ï3e5A{ë1¡á€'K†p$Ð3ÝѯúLå�ø0&¸:Ò�9ÃÎóæ>¿ ís‚ñûäq§üØF®Õ<Lú0º�Ïøœï1Ëç#8ÇQ}AvÖÏôQ‹ŽØ ‰J¯y—ëÃcÐ7�ºP¢q]3¡ì†ëzýÅ40ëô4VOµÒLÅäÝklW•Dšt;ÞG}h’¶Œ�NɬÞÑHãÿêåÿMõºŒ@ +ÝûÇ Ú�l¬M­fGet_&>iwŸµ N$ó*ü8ÿjî.xnE¬¿w]zP,»åf—t²îï!°î�¦~x‚üÁ5½á‹ÌUDþGÊ_p”|§zKñ{€(IÆwŒšÒÇêûÔ°„:ˆCøÌ.­—ô:z@r%ÁÊ)W A_é‘EÙm®ÛCk߸Gè-9¾߇ø÷aAD�}@Šôà“-9t! €KÃP`!=÷=냇̊>å°3s8ÿËÙºžâ~Ú>¯XÕ@VÏ �ñà�"2›…í¿,•=_¾Uo=žoÔ Ù'a)xß³Z‚�ñÞÚŤðÔJjçœÌ.Ê–4Ý»M „Îu÷â  Îw°O‚¥5N9Ô`†É6?;NõÙ��b*˜ó¿+¯Ø'¸3B¡{“ê Eã¥:aȤ6–æ§ÝsòƒzB¿äµøA>8Ò{q·Cñ‚jÿ¡d‚;Z¥ýâ`,×róº8ãÓr—“„eKÊ~‘ 1�:„’‘»¹¤¼ðGJlA&•dô8’ë øXBüÑD£OêS 9B�ÍŒPÜ%È? �¤:4ÂÝ �Àz T3•qFRó|Ù|MyÌ<•cÊ‘!îSŠa` hzê¸ –Ÿ íG=«Ë‘HM;ü?¨¡cZ¹±®HpÉØv•d¬JL�áQ<ÚÖ3,ÇÅx&j÷Óú&X(ŸªÕ�â𣹫€™H„Õ¥ * d²¥ü»Z™+Bzë¼Äߌ‚oPHËvÏ„ˆ�†4[©b²£Á¦`ýëD7'¶Ó¹ä2h,J� “©^ š9ÇY8�ë > ŽÁÜ�’R©ïÉbŒÑ­HýYëO ÐZôˆÜW* n|â‰iˆæ¸¬k°å6ûO¶èç=A§_JÌrŠÏd32:$ÉÄë&=UÝ„í,ü6o`©”éÈ Ö—œ‚.ÔQ®1280:í‚å�ÖÄ�¿B•›Àh“Ž'ÁÙgß3<ûÿjGhuhŽzb!0/êFrëàVÆ÷˜v„i S!¿¢ý„De­Æ±Àèh‰½¤ �â?�WÚ3f t) ‚ån”°É¶öÅvÍK1Sa\õ¹FÎÚ�Ï0“ –~{ξ™XÓï‚å{‘}Ø¢§†žÔºC ²aAã‰,é+Œ?Á/\Í‹•Ör[(¿$~”NÇoÿ[Zâ«ú¨ä³þHœ·¸P;&e­ )�ø¥Ôóìô[±†TÅÑøþý©²Â4©zÛ¦$[bÔÿLtA°êöí(…Šýaù¿õÊû…;s@"Âļ÷\´ÊÛ±`i§l#°íãx¶¢ß¯F^àï·¦‡)OYOqë>aÿÏ€Êÿ÷¹¤¸¿-¨‚~bh‡†½&Œ¦â_¥|Ó+,ÍW[€Y=‘HHkà€Ö¬-Xüˆ”^BÂ=ß³AÒšÇJÌÚ˜µÒ�Êšrë'E�:"†l^Y ¢ò8-‚ÄœÍ?ië`�ü¿¥DÜ»Â)›†ANíÖ3Æ 9®Ñ“ÕŠ”®¼ÜaIq$ÝÝ%¦•'¹¯ÞöU¡¾.ÀçØIâiëpáÀUŽäøéÀŽÈܹҬtîB7!Ö-`M$_«+k·hZ18§GŸè×±1o.´ Z/Y9z?ˆå�¦¾Æ®dÌ v[Z'fP6gvqm:È/ÅΜ}‡ €ì®û7¸\•GBŸ«Hë™Ö ŸÕ�¿ÿÚ�å´pÐ({ÓmtïÅS¥‹â1SÎ�táuŒ º}ZH\kª #ir1D (÷ñâøtYÔCõ¨Çz¾lË›”h™Q Kɼ ÔÚ=†Ä?Ø>zÉWÿ¹Ë2EøKB-êÆ�ÇA]¸,‹Å¹¦ýIåPúTæC—d,PÈTÒ¹´·³>/Énä½VŒóÔ”RÄ|¦ÕÔ+[†}KRj\HÈEu^˜ÈµÉ�´¥“‰v¶ò~K;Ì&¾óÒjÔ§1£I –Xm+i‘17ZßU(`4i¯+Kr�äΓL(ëî<ܧ—9}`çnf �{»Ý“c 9�X¾ÔÚ‰=ö�¡Xôåbc¬‘s ά¹½Ë¯ÂØÎkP%FE;ee15:asd-rupture.mp4d0:d6:lengthi263671468e11:pieces root32:è=ƒ9à ø›P²K×Õ†.=z³ËÕ5µ×´fÔ,ee51:cncd_fairlight-ceasefire_(all_falls_down)-1080p.mp4d0:d6:lengthi342230630e11:pieces root32:i}›SÃa…†~]ñ]º*~�ýÚlšíꪃ¡ÖD‰‘šee54:crionics & silents - hardwired (1991, hpad, divx5).avid0:d6:lengthi135000064e11:pieces root32:-�x�YÈdiVK°ñ‚¨9É„±ÊÅ÷Úž ��õß~ee17:elevated_4000.avid0:d6:lengthi113274880e11:pieces root32:]™ «§Xv&o^]Í«(‰‰k‹¤K ?:&i`ÝÑŠ�\‘ee32:luma - mercury _ 64k _ Final.mp4d0:d6:lengthi183814243e11:pieces root32:bãi 4Ü�¸LÐ >cö(f‹N¥OÝ»ww¹Ÿ=Õee10:readme.txtd0:d6:lengthi61e11:pieces root32:;‡Ý¯W_ÿÉÈ ã�mâõ§î%ŒAAUˆ¤ñú ‘ mÀe>ô¨£ëà7 úË*íÉò¶–Í©±‚°º¯é7·ÀÓ^õêô&la´vsË¿ªIZÍú|ã"ÊèÈýmŒ —¶‚z©“œJÞ·>YPÜ@Iø`x{ïNväjApzF«�^² @Ó©ÏdØNä¤L�z4²\O7ùvÀàÔËp�Û•æqNó¦�ã Èï_ªâ`ùê¨Ñ91A¯R¦€éÁßÀÂ\Robj`$ eÀð¦Ä'«½Òaø#ïvk‘gFABþC%XOÔn —+4|U�°³›g�8¬îÐ'JÇå€[2Ó“Hç�}�—Bneo@$H&.WA«åm͹Áá©Í�…rx‹qÿ¡2«‘=(ت“Sž{åß4'o¸@*  êI×a'î«÷㮎B‘9j2«O6@èS®jÞš…¾ŸEBem=±GXS qQ�¬¯æ�=œ«}nALñtû5Ó¼7òó3‹ëMgŠ |àõðvgQK-nšå�÷pŒÇÔËOUDGûÈRwÓ²¨'^”’WÂKØôæÖíh©`”†(Åî­h€E(/V%Ïõ/…Û P h€J³òÙßy®Žàc¤;¤(‡­ðé¬z5­œÑ˜þt79^ÁâÇÞ2®dß#7pü°À$Îö±U¥‚RPO~-Ì¢\G©ØüÆÐ¿Õ�¯<Á¹µÿÂ>ºt£ˆk¸y_@wCS *6.¦×ä_f:·5´:;/©Ê`;ìâ왊Év6Ÿa¢eûI; ù²N�Yête“H¾U?¶ !E€zÁ³z$²ßf™'A·Fh*GM(32:M†´!Ä·5ÒŽxÈ9™…ãü»>½Ë¯ÂØÎkP%FE;1088:,Ÿ$Äå¸|´ü~/«xóįÃÃÏðö}òúuu×Cên¶t>柇£e‹Fßq©Ôõ{ Ý®6ûz¡‡ˆÆ]œžÀÈĦ’ÕäøìSIˆÖ=�šœØSÐð8íÐì);*Ì€ô.’k‘)³>ÕånMP¾ê vkÔâÕþÓ1÷´€Â]Ô›_i)RØ Ä Éæ½¯ t߯Ÿ‘ÁËT‚Ln‰%„ÎHÎË�/Ô0€I‡¾óÔÔ¸ëZÑÙ}?0ó£òƒZpø)¡›#ÍVÛÃ*@ÃTùÿ1¯Û½ éïçë4÷5uPt! 9N˜îZœCiL!ûëy­˜öE=9Ýqs‡ë ä”Tþlî/óW*O)èz3”§Pzî  iF4Q¾•9?©�Е+Ô 4YìÓ/Âçú5“­ÙÁWd çnq�´¶+mè_0K;Q¨5GÉbV3ùò_&àLÐí`®éÚRâÙáÒD¦«ju÷Êþ®•ºYÃvÿ´Àðrå»'€p¬‘`('µâìœq¬>ˇ¸öðº˜À…2Bóûx»÷‚ñ…‡„r8D{J'°P´DíæJÙœ�÷º�æ¯å«òì½áYgW:”/¥ë¾ŸË‚àÀ –iû½ÿY5—Jâ­ws�?:,/$òÖî¸U²×ѾddÊIºóæÓ×ÜL8þ–Ýmƒbõk¹:cÃÍšã.,7*Ä.ap¾Z[ë]«mð³Yä_™J8ç8œÙ#Ã)F+i7:‘œ¶ÕܯX]h*T^ÁnÃRa}úý…‘•â·‹óGy†•þ7Ø€•±~kXÊü©'°„©Xn¿§úH-SÙמ¡Ú†ÁÈ[2ÿØšƒô׉(é•) ¡©x �CD½¼ëE_>ŠÕ�®áÉaÚ´öÞ" üm�y8öQ�áïs½eZª�ZÁÅý¢:Wgîî:å.«ÒK}bZ^m�‘ÿP í¯|†ÊÁ¢UlïZ>„ïÛ‡X^CÔ¨~íµÍKˆRë ú*+÷,¶v�Œ-aár˜Ÿ¦0ê`ûQ4UR=�nñ™½(²xÆ¡J•z](x¹>¶Ó® ýE=±?|Ü‹òÖs7Ê×BDvû¿!›%ƒ£9�씚봡çr�·¿ÚIAr`1à<ô+˜or¾5Z_}Ž3¥êA#ϰEÍÑvîB_ñ Íë ’¶´WtôÆÒǯÉÍ»ßú4}ZHèŠ{>³«Lî8èú™õ/°6h–}».?‹q–ªd#¶ „R"}ö4n�LGËâÊÁ¡ªøPÒô°já<ÂJôëpU¼ÃE"z`‘+óÄ �wº6üÞ I'k¹Qšž XÊ…y¿ÁɯQ�^ç0ˆÉaN¥L(ÆÄǪƒ(í°ÅO ÏÛ.²Œ½{*Ðõä &ó32:]™ «§Xv&o^]Í«(‰‰k‹¤K ?:&i`ÝÑŠ�\‘896:vï>žÜb?}v1 ÷J¤ÚåñΙ w­åsVÉÝdæíDÏŠ`ûãÃ@¾¦Áp £+Âe™vÊ~É¢áûõš.þ—1\ùaž¨[ �ý �b½Î0Ñ([…Zã§ô»aª€8§�š˜ÛP¨#R¯{ö† Éí×þÔ-©êÍ4œ'‘A^€_ÝÅ>a°» ‹ô ƒÞÖ6íåréKS|^ÜÚZmÝ6bÐlÃU³òû€h³Z^2Ÿûþê�ÏÍ{�ÉO¨îhb¯4Œ¹ËF² ŽáÞY-s¢å¼�¹kÅèlѵÚ aó,»¶îjGŽ“ŸWùx�€/3e2º÷Ä™1÷÷]3³pÌÝhùXáÒ@X�¤Àà—Î,u>²½S ¡-�"|Ã�úE™w|êºÝ¿ )6€6„ÓwéöŠ bö³D§jPzî ÇÐ�bÔéJIKŒ…�—nLZyA#`` ¹.øo>Ñn4ÄÚËÄQK~¬hmúøÆ‹Ac³Ø¶/¯"4ù­ná›�œ“y޾ïjÓ¸ÆÉá|KTÄæT³6K2GA©Ûš‰b”2ôöHs™ Ô�!²xbMšk@—ƒs%ûì“(Øò�x‘]Ûlj j9©6þÙYw\­f’@ ¬1ŽvÎã[UûèPu‹]¢P+Û’ø¯P/3ó¢Zö-´‹óÐi…†\´µ]k­æð¸IéX~2h ¹qÅ“ÿÒ‰¨×0îÛi­/¶q<_?8­_ ôtnë7¢ùºÏcí¡³fìØ�À³/êqDãêghiáy,©,?»óóGoš9ÒÓdh ¶‘Ï<ÍhеªŒe�:!1±Üc@gLèÊ%˜-«Ø^óC[„]êr\y»Ëíò“Õ$dá¶HvFIèÄ`"'aXè†é9—™çÉ/–Û�ˆÍLñÀgâU,ò×32:bãi 4Ü�¸LÐ >cö(f‹N¥OÝ»ww¹Ÿ=Õ1408:rÝðî¤q&!¸HS±:�[eû‡æÒg8-‹á¹CÊÙ¡± wgx�9/ê ѧ}�‹;vú0Ž8Gª^ $¢4ðÕÀ�Ä4cÄ©<�ÕÝæ»�¹k7�nå�è»àIÈ Vì}q.WÌÂ$ÇŽS¤Ò;!ŠG£¶çìˆà õÊ••èÆR~&&£V²r¹>�Þ® ]ÐÒòÖ>ýÈ¢«ófÂ]‘`%ôj;ùªyýË+–}É+Nµö|‘+ÇmMß[…‡Y™Ú²Ýî3"ßX)Ü>IJÿ&·çý‰ýŽNej,ö{9mèaÛÔ©°$Pܼ5Òú+h.s.WR耹ݯâ‚y†Ntª ÑB„F;)4‚í÷ZÈ€I* ƒ‚°Y6Jœ|º÷Ñ?¢!cñÍxŒ#Y™'“¾nÂ�Ž�ûﬥ”©¼g¡ývîi¨ ›öL(ÆöCq —Â^ &¶r­� ¯¯¸¹y(Á0´¾áv$¸­XC�˜óŽ%¤ÀhÍCU¤,¿·j][wjš{™+šU–SÒ~lÒ¯Dša’L¶X‡~MV�ã¸ãbÎ�g.’(IÜØ±È¸p‚ŒL/VwåôHkéfîœ÷ÿ‡í[€jûÿ¶-µ¦tCï·l;=1U:˜,¥îòAaF î2oFûŽyæ™Õlµ‹˜;„Ý¡ÏðŒ¿<ü^éx,*Ò¾8Ð IâN¿äæRñº™ä¶FÇË«^©FذR**S­õpúú®UN´9°¡£´@‚=YbŒïºO“ß+æÁ&t£Æs{Pwl†3|Ppnî³ým%F5»ñJo4Á©èû”0�ˆ\gàüª“ fràRá¥íuôTöü/Ó®bê€-„ëv± ¬rkÕ¢`mKʘM6çXÐó=2$Ò)÷WEÎ…Ñ þf§EŠÐÁûå‘{�ÖëOR¡Ù^zwZ¨!.üÑ: Ä¿-¢0C"â[(;-X·jI•"Øþ]4¼±´¶N‘d®FÄ2ª-¡‚q`/‘’Ns2rõvÛÔk£åKÓª•ìú‰ÒÁñîlê¨�:Êýï÷™Z'Ôí´Å'8 ºJþª­SJŠ&N³eXxvõóÌBFÆõOKŠ`éܺ~éL Ko_Y;B;@ÌHx£ƒ| ’Úℼ®:VëJ�‡zæéÛ׈¦Ÿ›|€p˜õ cî#ý[¼¦ "ù~ÀàFŽJþÙ6ô@N™Ô1¹E6Õ?TÏ9¶Zv1‚Øgx«ç5í̑†QÒÆisDúͪ®ô²ÆÜF^>AÚʨ>QˆO õùÁ­ "tŸ]½�Ô1× zˆÉî�—Q¤ç}áÆlr&p]nsE©ÛÄHFû§»5¬>¸„ˆÁÎh(ÍÖJp(FLU¼Êjƒ=Fø£n#t'½û8*IIõèÎVöM²�†o[�OÔž‘ž2ŠÅ—–·Ž¢*1¾–�èÚ3ôý¡š ¡@ÊÇüêLâØVèóeJzq¤c8Q‡„�§�Ôw¨ßÆÁiÛqõJæ@[ÖD‡üž8§Q'ë–©1ü‰r9swL1ÆXŸð@8œD(¡‹!ü¯C\è‰Öè̘¶Ølà;ê�ë}4 hòªñêOÐ{± Oæ8õ÷ÒçÖlaÖ MÅD@¨ÇÑßœº‡ ‘>u�v×kº½ÂÏú+5Š¡=hãðHÆ’É_~à‰³Î#Ã,rœH6ìEpM (ÆE‘ÄF¢I:i ±¸DÈ47ÓnÍãbœ}jmÚ™ß(í!T–kUþ”¤!'=m²��BºMòA‹8±žì@¡ õP¤»32:i}›SÃa…†~]ñ]º*~�ýÚlšíꪃ¡ÖD‰‘š2624:ä¬c#†'¹H¢ù�÷l ¼ì‘Þ‹¶¨ΫÊÅ?l$�Ë#5[UDz

—çû 702zm•dM=ÕJE¹J뿇~©(Áwwì\°�TEiê’åÃö† mGé`’†DdñMŃ7 Ñr-ºC¢�'ÑÊoèç`­›Â)ÓÅâpÒ;bbï¤ô‰w¨’¨ R•–s°Håø”7SwšÓð”ѺŒÉ¹Í4œ ª¡¤i §†x&Â7çŠX›Ulá2üQ‰âé%µÉµ£g`ËËÝJõÐzÏD¹A±•7/2™h[hL;`iA‘šh}/dBaç¹5øÒ~UÀa=½Ç5’…±{íU[–ö¡ÌPœA‡í™°º‘X£½RxÜ‚D£ëGQG=�øSñÈ�«êZWÙðDvÑ *+¯e§< ëó'6'L›¹Ç¸€js/„Jio›Î8à6Ÿ€ø?,“2ÆøL'qˆþm™F‰ Ñ,½Î»IBéHÖ{£4u�«116]S.K-`µø‰ØQ;y‚Ÿa'Þ1½é]æ­ë\£H eÉ™¨W…±© Û~;Ïà"ž¢ ð¥6À°‚¬¯oýœqˆæ(½e88zýa= I@�'Ê=þQIˆKg¤%X^ÒäaõŽg0Òoēł·¦;•/Û•OsÈ]q0�²çàÈÿs—Â'm€÷ɯô@ƒ¡=bòPaË›Ød{Ê8S#ïL�þóÂv[¤ÜÈžt›»}•w½![4å *zzÊt?9ĸRÛØ;>j“YS�-Q¯ûìx¢1†û™ÿÿùi´Dô“ÄrX~"ä84‹øð>¡,8Uoôž’&Ô UÄÖÅÂeŒÑå¯,/ŽM«8':R52vÌ c°ùÁë³¢Tgx2C6Ù²$xµ�kñÕHø6)„{´è?³6ž¡—\´oP`krKm�&ˆ>"1»ô~ŽáýÜïq•~�°z¥æ=�/_[ÛŸZ ã=¸©—þè^*FÐ/¡ó&µÁýê ™ø¡Ã\–‹þ3L¶T“”ªD±y]©–�8:ŒV3ÓçÂzCn°×ð4Î¥�©qG«-á‡2¦ýÑ]§:�ß6¯vý¼½’p‚<¾Àò/© –{Ë%ûØŒ‹fãZ© 1:†À’ôsˆè³{Ž}ú'’F„¶É dWR££t®²Ë™ºSpœ¹¨‰YþTv:ä~‘Â&�8Mçø5xg 5ùàÈÞ\(6Z� ³váá†NÒˆã^bpAßñõ·Å‡æø÷ü &Bèá"¯‰?+ï§½%™£ø‹¼jÌ"-Z`2§©J—ŸC6�f-ÚÉÏc“ãÇ’¾ÿCÖ·wž½ËÆþ8TB˜õ¥¨ªsµæÚË;þ柽ÏlCY^Ë™xŠ[-ù¨Ê}«ë€GŒ˜ÉÃä¸~¤ß‡ù]áÈõ²¾Òl£[’´fF½ßεsÃy<.ŽÅÝ[hùˆÒgú1¯6L„iYçSŠŠ:ãiŠL­¥}FÈNB£vT„ˆkp;³Qó5^� @V‘Go¯5Õäøß¦Ë˜/û1n¼®Óô °ëâöº|‰D¸»:§Á]å¡O\,ã�l�ŠÌIÐqï)õ"f±žM†k%£]%_+›¬ ¾0*ÞGÚ:ÎöZ¹�:R—h«:'Á~×ßÓ¨¯Ð¢�¶õÌÛÏ¡i»K9®L`$êD°ÄÄ{¡5 ´¢å>ÿYÌA]UI›ÍY‰mMHaqº‘%)‹a<1q&þ~‚WGHÏ^}Që’‚P‚Ôåõ]V|G&&ïØ� ™|r䤕Öé||!²ŧ)K›;$§Jè¤/õÚ-j€7íÌ|KÚ¢<ál$ž«Øñyì@øt—Çž 褼¥m£g'>„‰~› ôeɶÒXK‹P -:…/ÜlxlÖÇTF]ü±[¹pS–­c6},Y ì‰(qñI¥brqnXgeÔôs^H3ÀztN„þwÃ}íJ$ñ3=wïlc ºŠÈˆ´/Öß`ªLŒÑ¾Ï—³ÝWÍ$Èö$f~f´›÷Š÷Ńz+Í!»êJ›ÿ�‘·w2â ÍņC¿_¬|6Dt¿ÑbŒžï±ýêÞßóÂ]Å”>ÿ×püá Lãà°!ØûõÇè ï'7¦cFëi‚ˆ.�ÏÖT¬¬Ãµ¯bß´Ÿ ¯áÜÕ¿èóöb'Ö¶TK;Xø~Kòtœ?Ž,éú÷q ŽÂ¿Ê�(á�$�.‰ÀÎň�[4Ç©§“ÚÑî+å&7�äæ1~í,Fö嘣»t�@Ê|k6²ˆ_ëæeúëIg„½ãõ‚O¦Y+§¦‰UßÜ3ÇxÒÇ‹¹¾¿-ð4¡©~ I~%Û÷L ÎUŸïª®ßÍÖû"Fã绫ûú–ÌÎì"þÁÒý-Œ(`å]Ìþõ +´ òÑ!ýi}rŽ%�·ÿº¦##Jtp¶&ÄG>à Ïë #}l‘x xW•ªÉ4(wL—ŠÔ!çF,eC!DLg#¾ÓãXvi �<ËDPð˜#ìW1Ë©ú$ôÎJf·TY5sèÝo ¥ð< ÷Ôt&žŒÿÜ/ËËœÈþØ$ìTÃtZä0ãÎ2u0ªéîäm•T|ç/ÚTW-®¨üÕTv!6,³¥Å\Ø–»n¿6�^\þªlV~Þg6ù=Éå¹åŸ_!!ŸÚ‹±ÎÄl–ä ”¹¥/§ÂbžyÌÊõ¹Ò˜#uÖkYEÍ¿.§ò*ehŸûäc¡’»¨��kBVìÿ�ôÅ-SÏ#9õÛI¿vŽóDÞÈÿÑ>è»*m€›ÐUÚ?­„5~åÚ"«|í¯U|óU9�¢ øçïgJnÖ”'ƒyê¤Ð¬P±Ë§‰}ë2£!J«RH*ï¬Áߟ�Ë'Èî<<7IÅ �K ˆÇ…Iâ7bô!Á7uÞK‰Çš-&Ês¼ÞØÝI˜-?EÌóœìi2áš¹®k�l ”©?]ûÃíý„" nއÝf§š ÉéÞ¡sH^ìCµŒæ˜ê‘ŒRzœ6“ôæÂ›]'ÐŒg»M�I³‘ºUK�Ûª Ow–]·_È…Þß !.C³÷Æ8a9¼(:ÒT@ –U�¶Ôd層�œyM"xÆ;M[©óÞ‡ÉzGU=öçr`³åÍ2˜¿+x'ÆD3¦ÝîpþÇíʉãhžQ/1©OuZX�“ñ¸bZ£lÀ~nV3Úé WCÚÈP8,Ïù>ú1ÕÓ‚0Ÿ(5ƒú~~ l&À|EyÅà<üÑüF; ïf•¦À32:�ý?ìÅÆÃ�°Vé|s½{±Â!F4Àþ$êù224:·³©Wè ¶(ÐÔŸuiWÚŽ{§¸Û7~¥ñ—áHLÉa§4½¸¥î n†�/¢a˜p­­¥È×úÀOÅ pYªÖ•#$J„\Á,RV‰�dhå'TD×:ñLø$þ\eœp™7§ˆfº·½§V*ŽŒç~q~Ä»Þ g2K�–…•òï-,R�þ±äË€”Å“]ó‰§ÇñùRƒpªIOC™5~äãJ.³js� €;Xï«““bV1Iiü?C‡Y®Îú¸kâtåyì¬æµ_(SeQœ 5c2ˆ¤È32:Ž1Óž%Æ3ghéx&ȩø }‹„fD(ÌÆ64:é]Ø[»Ë5 GETÿ½ðC ƒÞrq¯Œaíì”ÿk†Z]Y„FLò‹->63M=Ê5OkØc“�]N!œ32:£ÑHŸNAµÞÕËH"뢂ª@`e9Œ�c=€¥N/Ç¥C~²k)TÛÄ=�¾ÑD¶&'�Õ}Þ/3o×ÿHŠo}õ»Ñ:ÓP®R+Ö´�†—åd²ÛÓãð”‹lpÄ–±ë9VɧQqÏ+µÍZ%B4Û¶æºPÿK9G¾‚7�x=fš>}=JõäZࣂ7BByê÷�¦àã¡�«÷.|5€ÐËjWˆ°ù<ñöa•‚´{l¢® ŠÍ&¥\^Ë#©ú ^]Žy¥KN‘^¥&5c†¸T§�éý©ýѰ~éݘ\k>ž?ÃO,*ìÿèÐØ&–Wt=Ý]IÈâ% ƾÆ0â·Ÿp@U×{¯ÚÌÞ'ªÄ–8˜Èiæçä6GÊôÄÍåîvªuÀAË…'(Ö¡Q®°7!Ž»ô«§#þ÷Yâ}1¤ñÚéd7àÀ¢¡5©µ)Œ®(rÃï­* SZ*9MK×x°—ÔŽÏ–ôéÑHöýR¼i €f­<3Ç4 vÔ Ž†M?gÇÚ3îb”¨"mlàÉ,ž·uo’ØßÚ®‡˜jœ…�·wÊÊì[ò¥7Ì9ÄG¡-hO©~½²ê`l3=Yå/ö—332:è=ƒ9à ø›P²K×Õ†.=z³ËÕ5µ×´fÔ,2016:ß;â �Ú]B½ù™ád;�|›Ám�lpRñ‰ ýÿ¡¬Eß°i[T>³ ‚FÚyA-€e°v¥™n‘¨§�ç{·[@Ùâ›k>C@kÚRùí¬”?†¹ÐuíDÕ¯$âðf¡ï ­¨ùl5—ÈH~ïHYë~)�C«ÔYÎ]2p°óÙ;�´�´¨@Wãþ´Øjb¦`&Zû(Åtãeõᦿñpqypßx(¼Ê*�^å®]F ¯Gôß«¶YÐ�¸gù‘ÿîøz�Œ*è‰Æ'-ŒM ýaM{‹ù_sÜJggIió³.KñS�=VÀ‰@²³q̇ÏnvB�:ù»%�JV|v½uþÐÑ/¢=A¿£\ ÿž;Û=¨e¨ÛîeM‹(˜Ø›`å…ê¿Bÿ8Ñ< ‡ ͸›±M¹“¬yŽõÂEy,}$þ*’›JÀs=7O|âpÒ?Ã!kkf¹ -é Pgæ:ÿöb²—hGq‹àˆcù^ŸC£ÁˆÛ4WÇz$óš§;·Be.{EZ S½LØ)Â$[=C1žÜÅVx½´ÌÐiC42ô´ÛgÇ}˜‹TÍ+Sê@KÌdí·ô1àœŠCS3ùp-¹ÏÐÞðÞsÖØ£Î~Ó±OÆRM?ËÛ§�0}ÝÑ$Ê2aïç]O`@Ræ««(‹ß¶wó3Dçú½Þ\YºÀB«­]˜wI™%bf'¸�€„øÃh ³%LäÐÛÜ•–vœD9A|ªva{OAß$}]Ö›:ê IË.éîIJÕ³ü *?vÍ"ÿƒ¯0þ“ëÃ{ ‡Kt¶�¿Ôky{}•ØZ ‰ãö3Œ[ƼEB• 8÷Ó²SKÍd2×ÉðY<='ß±P‡m.>×À2LSÃyŸ ê´ªÔ_�E ”Bu%|¦‚²¿}›×xuV¬˜ê/ :ôZ¼n“ç¼Ls@#y—Ìï˜J¼ê铚À=ãæv-ïKþ*2 f‰l¶¿åƒúýú:cÉk:Ü òN¶á-0ªÎuÖ¿Cw†9‚_�œkS \/ŒûõÅFÿûBò%40›—³Íl#¯‘æí²|ö� \ºœòïT€ƒ üÖž¥iw€ê¼®å§‡nÞg·îV¬�nÌo€XþO<)ôf2´½Àò/‘ÿ6(‰à;!Þ‹\—…îA�ÄÎILÝúQ¡|MÈ’Éó¦!ÿ²ôoJiÎ͸-nuQ.U0þÎ ØÎ˜íÀ}Rêþ ’ -JÞêä*Ë¢îå:°*†ÿé´ë�jØY×qMn·UŒD–Þ œ¬�QÂþB· l“˜‹C%¸–ú*úíGÍ^ëRPV�hd<Ä/뉆»_‘—ˆ|�ùk¿IÇ#ëÑàÌ Úê5;Ó†oWÓêý‹÷iÖ×e/+¸8WäD|óÝŽ\æüû%…h).M|É4u,» f_�)Q›+°¿xd:?�IÛR³MÃk$ڨΖû„•¥©F‰Î•¸„íl kØë�;$µë\¯]³tpãe¢eÄ&¡„t¸‡ uL'íkd×X[ðþ£˜Ò÷7 ¯rQ’ EÌ7 ¾ä¶æ·M«óþIóW¬uå,…aØ“£œ–Ã/"Û½·'3ÐØÕÀʧEéLÀmç&œ(ÌqøØ9¤±R¨<ÒNüæ…ÅfY-½{~5°ÚY)éã�âê(£E¸ùj%;ð Þ@­ì/Ô@æ6}Ó ïÄ0ù�tD/ßfME0 eY=éÜ/\“&×5Ǿ€Ãè,„3  r^ô·2 €•÷2™ƒ)ËH§†øNb¾™‡M…™(Ý7Íj9 êœ 7KT¯BñY“þÚò²‰ ØRÁ.èWCû=NØEεò ÛEÍ—õÉãϳ›5¨;_át6öMÍ÷àê=io@º”†ë|¯– øFdiiîåΘHÉ´žÎõ'Ú=Ä#B%íNª¥æNhöÅo²‚ײ×O*7¦G�ƒUòhPA5.gi*uüewš¶S—1q+H²é"müû€À0­V±ø-ov9õrÓµÀSönÍØw ºìÏÆUÞTýwô±Ïk“äÑN(Ö�3*cðpQ/½š*=qP›(jB:eRôœK#ï6Ì)¤…™•tèÔÝOñ�³öžäÅ’D"sÇ+Y•ˆ6S³)�hÚ,Jad4¨ÞÃv�0’ú•O)>M´Ò ‘\ެ ê0dúGÅWÓÈëA:y1 Êþ¿uÆO=Oö/;ÖÛvB™ßö ®»Q×ôý9ú32:ë?Þ_θí¢UÛ/!Š ì¬•quÍTJƒ°(eÔDz‘1760:ôÓŒ¿el™ó;€0�ÿÚï,áoråK¾röõõÄ+ùþÅñ[ ¦Yû/Œr™cáí˜ía„¶�»ÔŒÔãˆÖjµ¨ :™æ?o[b?pØøï“ÅÏôlãÇ •ÊAZº`å<³ÂÒ,“yA: R-çóÅáh*7±pÐåQjè|;DòKÚ'õ²Cßæ‹ó{UôŸš^¢¾dÙŽÕ‰øïk`ÆoÖ&tËlÖºŸrYÄ9b£Ó$væ39ûý1Ö~Xƒ(€D‡‚uÂQ‰ØY\P‰0ßn+Ú•±íÓ70Íj X%ùêèâ¢zx!ð¾Eî�ÂDÜEŒw‘ÍM{O%³‹5@è" ˆPr��δ#õû®4opƒ�uÑzHêhSíà ´¹‰ ˆ€ªKšb\eiÅ£ÜúÓ2xèCœÂØßyPˆˆäÏ}úk4†¢ÉBíŸî’©‚7B¾  Æ<¾7Þnmf’}lÛöҵσ@Ïð{�N¾÷-¤.åâ•ñ@s a]õ‚AÒ±oi>†ËlèBh|+Œ]s®ËÄ,šþ6Ö‡¨ŠË‹òð^dˆ÷¼´Šf¼¼~ZDU¹t|}]mø8å ¤Á¾…9%ò�àK‘k ´ýr1‹/àádÆmÊÊmæ+ßwŒÐb’h4âÂÏ´‘4§ºš>AÞ+¼¾%†´/áå`Ö½…Îd]䦒™1¹9^óyPÅïéh€åÄ—6–f9_dr߯ZM‚ÿR÷7ën¨óœ6Ià‹oƒ÷WªZÀji/ëåÛxo÷ìqÏS:�¥Æ–�§‚×±ÆW—½0�Ú Ed‹Ôò…ñöïIzð*»ïšz7¶ðQDivN%õÄ#»áX1èÖ!´â±lq/<†°Öl½bÃuCÊ”î!Ž~Ÿ”ÇXHˆð.ÙÊp uG­Ô«º šwøV›{§;Î�Ðuc:qˆ¦Jv#",×MI½Ýâ]‹Õô_A‘u^3™^Û^ÿ×rAPMKäÛÄ%\´êìòÊŒÒ$¯ß•ZÏ$¼‰ñ½O�PJx‘]í¥H GÑ(·ì<‚ùBôwéœ$£É} sÉH¸ÍyÂù fª'Q[ #xprá!û�W£®ˆ¹ÊF¬?J>âÕaâsú“öm6 IóreEv$"QùÖQôîï’N(âü–ÕÞ)ñ„ Eî6µ_©?Æ 3Ç4ùÛ BIžXmN·�Êúôaߣ5OÂ.˜¾ýXäóu0[HA¶² .Uw/±D*ÔC¿.-2!9†âwP�@ÙÜ@y1œ ¤\©U*ô9+¯hyþ‡å–€íÞÇ´ýyö/(èÌA×ò:>�ßëÿóʶþ¿Ö <”ÉO:ÒâªCˆéØnÎÒ[ìÍñ �Ù9 ³‰ùàÐR“ô¤É눖úåç‰(rªi‚3·[‹V�À�”Ý:‡³Ûß|¨x*¬Î`Ä«U#šù©Û°¹]_AÝÌj êÈÆäÄŠ¥ãªæ-^ævÚ5ö°&…H£ÔkŠÚ.—Û&�Tµ[zEÏ9%å4ìÂÝþ§ýŠH@ciGÉ,­ðÚéY®™ôî…yóñOcºÄÃôY"h>«l©;kîjÂ[÷ÛÆ»å$þæ¿÷£ª€?ôÍ¾Ž½“ *ÂmN`àF/Ü}H€qÙI\Ó5¯;mÂS}_L6ŽŽÔHBê-sf±ø”@ä~ùÖišÄ2þW¬)Ê�MØ ,µ$]–|v,Tgó=Œ ñsÆ¡°ÊØÏü·LÒ•ü:Á8ó×ÕŸ³k5{¥'«›þ� 1oWÏS{Ód`ñÛ1²•c‡(x.¶èŠÅþðseÀƒ{ÃÔªㄵS^‹ .`ƒJnºŠÝuL¿CUô´ä­ “ôøH/�â;1ÑÊ>Lo¹wYFÊq«…ª¦?þ J'ù‚‡7] à *%Z'd!oÝA0=PçÐZHQ½Ý³U>@E­¸‰ VZ‹åv©÷èsX¾Ki~ÎDHg—hnÈ·ÚÞ5k¸w5ôË¿K öÂÍ̆Á˜IРƒ_o‹éôè%*8¦’Àð%þ³¯Ä^ô‘vÁÈöÞ;JiË\2U1.j‡?‚ÔŹ -ã^].)ý:™c»“úÙè�ÆÁ¥Ê±J$Êß�ÔÅÈ’;q˜^ÂÐ"rõ ã/ú­[N5à‰}úþ¼±ßÓO6K áOEм-E#ª¨¹ÕÓ,j�îÂ'ül«�YëÝYˆ�0\xZ¤�ŸŒ pÅÎÑ ¸GYBì>œO¹Ã³;쟧@± vQŠô˜« ’r`Ûj;¬Š"­.þ/,8 Z{ç ™kPd?«=±`1ô¨Ï¥…c[ ÕQÂxµ@³!ä(om„ç×7µ`r^Ú‚$�' "ÙMá (À;’èbT.,v>]%¸ß".¡Àê�еëEò¦ñå1cAÅF�„  U”O´AKU±è×ÉGõÿ}(Y„ÓÇp²;P:Q#@¤Vcj£4º½Zšc—Ôj<Ü'ÐQö™�ªKyöñk¤…Jz� µÃÌ^Þ?¥:Q«â�Q,žõ Ú±œ€ÌîÅ[ß&õrì†S|‘Öuèmꊯùƒ®÷º¯bèk~í7Jäøp@áRTŠó¤Õc1êŒ[PJF ÊZ"sw­â2-\ ]�Ò®‹A‹‰&ÿ§èýGË–õè^†‘Ⱦʃˆ›d•×�ÁÈTô|9¦M¶‚wo¶o²-Qû[‡íîŸOÞ‡ ª%»öû~¡^â ç[6ÚOÝ`ÛÀ…ö#ÃF�”ÔÇZj‚S.ŽþÊÞ×™©µ¤—µ�ÿ¨î8µÙªæ {lÜ\áÂ�ãÔúl&in#ú˜µ¿˜.1©l±ý5Ý׌XÂØ=ÊeÐàØºå²�z’¾aöY>þöÔ%ˆˆôÜA*ÀõEg#§äùÌ *´–dæË×)F�-dÐTDÂtí„å©-E~n¢ÞL³Èe�°kZ}@èMfó*ßNYœ©7Æuw‘ˆI$$E9ÈÍ›¬%ž49’XþÎÛDÖ'�²ì_@ºø�ì'°g~ÓÎJ(EF沈xöP÷¼E–¿›A£Ùð3“×_ºUhÖ/�–q¶zxM{F€”[ò}}Ä5sÔ  ëO-;ŠÞŒqMökÃ(j<¬Êõt²‡²eÓè{Ï‹ëxïN{ö•^æ90áòè¦#²6 «-39*vÊ–â SPMBÇ@‚Ý®¨šxúFFþ;öò|çÙNU¾Ò·Ø ²€N¹!Yw¢vñŸ}Ö¢lgs r'É3­�ÇP�ÃYĉ9-DyQpAey‹ûe‚§™°À¸úS3”²gÀ™>´ IÑÖ½ˆún3ªfJ];“¥€iŸ_”—½¼ô­Ó;´‘…jЉ§–Zˆbƒ×�™#y{¿F´bcb"ß]#ãhE!gø˜£CXp.¼‘¢c)ª�sŠÝà V!ß½cº”É…³À#?ÊDU`h_®¥ïÓθ± ,½yðmTñ.Âð'‡ÉáÕÏ6Á€m�û¿™Ý�Æ~}õ�Z$ˆàÇã�ï’¸9äÁÏ� .¨D&þ²G’ £rM�˜«ÝÈ=¸ñdûëÐ.=ÏŒK‹Àœû±æ²° Ææžï$¸÷^bãdzΩ5M°Ûê¬Ôô‚z-sCAÉÀމݸ�˜¤ÝЙnÔ�ëß8Í<¬7’ Ëz[Ða#« 3ÇÉ À3½58ÇÓ;r}zè]mÂoŽïøi£œñ Bœ§Ä)Š‘`½Üd{îÌØ`mu$%\=U@aå%–øã böwóµãšãH4©M¿ƒ’Tè)œ&ƒecÉ,Åb8óÂ;¿' çø`”h^i@FÉt1—D­J?´û|ù‹i“S»+¿b@;/Žç4 -u�‘Õ ³÷hD«×fs½!È'¾]Ú]'—-P¡| wÁëŸK;cQ^K7(ëÒˆE�#iøQÒ¼°*5U‡öÕ–×àÏã~Czr†Û¨]²y˜ îÌ9JµfÌ×ÏsŽƒFZ0¡ëÝøœaÜhb¾ã¾<ϧgΪþ°YeˆŒ²Ç1ºãØü&�¦‹�"Ð&o°{3zJZ‘´!ÚQŠŠ‡džÀÅ@� Â+M~ëefnš4�3)XGñï:|Š4ú7Œ¥ÔáUÉza‹è"=NlöˆùØI­[Tâu?L…¿á›®p´ˆéqŒ—^ÿŒþøg˜|**ûɟ㼎spš6®�ý­Å]Ç]B&²Vµ´�Rrí°.Q}æ—Epß>ç4•Äz§‘’B¡à ß7¹‰A× †ŽºâÏâçÈr)´k/.ãÚQê'êì‰!?*�‹KŸyŸèæåþ ðÓB¦¾œ7ᄘ%y2ŒlÓ8SÿDäw/¢{қà ‰Ñ"ºJ6cq$Ým7ó•Þ”óÑê€ÿ rü—%[©òŠ �¤²–ŒaÓÚ¹Ó&‚�øêœùØš•"�©º$ÔÂ×á/$CùÌ nÓf«÷¬�µuï‚÷lók2¡7à[Eˆc;‘+ÙàM�¸ÆÔbw¿«.ሧ(|䣲x·¦#aæÇÏ®ñêÖ*Õx)Õ˜$ÑSqsìKö# ·7jêž];!��í·t¨Ïw Ñ—˜¡Â_;"SÞF½�j½Î¹“Ž œŸ¢( »M'Üì_…iÞ632&��-«‡›�ÛÄ©@… å­u1ÄPÒ¤1l">oûÜëd ¼æ½‰³^tÜó&sb™ø�¹Ø³Ó‹3|Ž××,�üFŸo‚_K [à˜¹‹Íè嬰ælÁõGÔ­;×A0Ò2×@úV4|ùŠ,Ÿ &‰­ZÏr—]r•¹‘Oë’ˆjOcdÚòPMÙGtÇ Û%lÖÒ:&>˜¼Ÿ„„kA V VÓØ§‰íÞ:°ožÈí(ú9¼Ñ+Ö_\»ÂÈ–…Üöo$ÊIàÜæn‰òE±¢Ã¸áº;SàþC¶ïœ=�ÔCôëK¶Wœ‰|ãÂvÇž_‡•†#Ú¯BvÑæ<ÓO�T õþ´Õm^kÛV¡|çìŠBh¹ ˜Ø¨ÚuC(Gldû²ˆÒ~†°)±(ˆ“¥¼ Þº\,èÇ+û‹‡é}ª¯V;RñÈ^1¯É&M# À"ˆ;!_‚‹÷¤WgVxV íôâ�ùÁ¼�XN”ÔÚ~ùˆ­Æë”ì~ä¾÷Œ\è4ô(rᇑ‰¿’è>·c›Ê¨þl õf‡hyùb~ù“ûŒ–mjnúÑæWÝD%DáE…Åjò@ÄFkï>§Gr¥õ[üpã“Å2c®9�ÜÞ\,:¯<Ÿ�,((Œ©¯â#—Æ8†ù�áÏ—ÍÑ‘Ç0ºÙ´�Tž³a¬è?±›þœ-ïÞ¤ÜàÏ|Û§"[«Ôö ^ÍÓu³vßa¶Ÿ«½úÐ\·”«Ø>ãæ¯M°¤]Gsù ºÜîB�SRigÞsòÇBpWÎ~΀r Æ-§[¨ÿN«<¹@­¯OnqІÅ?bFs`R‹ù“;¯Ž�,Zþ3t öè�æUðúpF“…îlË×Å]ì 2Œ%}Ñ·«CX¦.µ‰ûh{û#˜ LvY¯Ž5tÛ‚µG`G]o~³¹/£D…ðx¢Ä(½&`¯U‹HáÓQ­ ¶ ÷ÅýMp"¬Þª#Sô~bôrbN]¡£®îÄÈÕ,>lÆ{­ã›³Áuqû´ ´NÄÛšqë”Êe’ƒ½ËÕu×ûÉïšd“•µæf[¦éö£’v‰Ÿ‚<–€kS­DC¡H¤òšÌ¹O �W²¢›:µÚR‘&·Lí¦P¯Ïvf¼ŽE¶ûÆ=¬#{¨7´ f�–79ÿ8QJ5G°†}óOÈ-°ô»1�5r¢r’ì\DðÈH^q+JL Í3küMûµ†[b ä.þbõã0¬Ø7}WŸI£†žgˆQZIÈiPèÞ3orhð.p1}…~Å Õr‚f¸†#ðwÆÈ+ÜF Crç:ñò‚>Æ ÿ“è}ñ¹x�9¤—«*âÄ8€-Q`¨d!v@xíèp½[:ÆÀ…º¨“tPܲ ñÈ×oSôEq»aéìô¾SX™Æÿ[r‘éËdô�Ú*€ÉÛ {�ÐŽëßµøâÒs¯lJðÜ"•ØqˆÅSî[ÿy*!-£0&ý¯{s(ö55ÄP"Ú|.�.#§ÚZRŒnƒ•KØ^¥»X¬™ÈíK,]ÿMÈ&çC#ÎQøw\å˃¢‚¨å‚)>m·[<ÊçSÎ…feŠì[ròg‡ž*“x�;°ëÀ‰ŠPõnªò­e6ˆª"y~ž°óþ‰…|ÙØ§ôhj;›òü[W¤?2D��Õ�ЇóôÀ’_—·x ØI…õ�’"€@TX‰]„ñn09›$¿q�øó”ò7h¤`öÉNk’Ñ)g ŠõaÂÏÀv«…ì«d®ã%Vk&¤I?l£ @‚yÎ6Ô¯ždî°=/¿Å\§¢ô‰?Y�|G†Y(óAnêàŒïØx‹+$Ó»&š€„uzŒÄ]ê—šn2z@óØZ,w�w¹•ˆÐÈýUKƵ´cR;¹'&øT_Ñð ÌM|¸([Z²å’éÿ .`„ÃIU_ÃaÐå<2í¶¦PÍ1m%L){û˜†‰ÅtS] úí€oH.ä'3“!Ǣδ6µÀ£J­ü‰y!«�ž µØôs`cbÁ`]bÒTYô„#!(ßyï�Ûy ¹Z€sÝ.•—`#XB@±×¶^êIÄ– H�hƒðV×5¹:ökO‰Ãó´{ÆÈ¶êáîb/άê>Ï D¤ëçA[U5V¾Òn†Ò4¥Hu”¤ÓZéVîUÍUCâ';y"’uØ—á×∻OØä iHú‹¿•“rAö¦f¾\ý C»Vâ¡úÌàÚ”ª¡3ìø§¢VÒ¥Àµèlœõ ]#%Âw˜Ý]Q¾‹íð§dF"7޳&:tlz ÚX_ºûeуùë�Fµ©RÿõòBðÝÿïjÍäA·ó�ÈÐJÅžó•<Æþ ×s?ß=$E€ª¤â ûŒR;€4ta)“üü�<ÿ­êF¸ÑÈ·Èîrê§ÚŠ‘%¸nñoÕO¿‡»C�ð‘EA¼¶ŽÃ»z™ "ž«=9†1Pçqb3ªçµhžÛ(BëKhìI ’ô(yö�ù‚–h v³]?éÆ ºÕd;Qó-¸ŸJÆ{,³Ðiy=é`b(w€GÒPôPáÕÙ˜uY©!Ó8àáÑ‚V±°ñÚ\¿þÕð�Z”ƒ0Ø[ þÓ›¸©…Ù±Ÿ@#–|ÚJT=ÔÚàz©qÓ!ËM%Œ6ã ÜaÜÈs 5{±&é³0U)53…ŒM˜Óáó€M:\»ã ºÄAÎhÐqIhQ¼ÍDæˆ7ò/Ãï?­ùù#ãYÔ�½ÎÏ‘5almdìš`‰Ò¼@T`à\åдÀ|)_M`”›k*EãID¸up|gÉ:EïÃwúí³Nø¥E/0ëšrÝ›ýµ1,ur@)j2ùÉ{ŒÔ3«ÞŶ䯟¯]oFʹZråµ7}K÷¸´ÿÔ‡‚¸“�Ö½‚ðÁD`w�%Üþbcbʬ”è}H_ö‘P,6¢×6_úœ×듾LuzˆÏïÛ£œf1œÙ˜šûöôóÌ�®Ä÷æû„ §BáÿÖØúÅ ÛnH!×X‹VÎX_ßgÝÃ+ó hŸa4ê3¿VP+Ç•dâÈ Ÿ¥[` w§É±8ÙøtïïR®CÉɽLh*ŽjáñŽRŒ/ÒWQ†)‚B\NŒ÷vp1e¦†Ñ3±,T3‘$ر(&&|‹h{—­+=¥;å̈fÓi6 L ‘\džGÔçë{8Ütjj੸Wï©�Jþ :÷ŠÌö0 Pç }A<¡0Ûœˆ›¤\P‚t•‘1þ–�øâ‘ Cf¬¼öêYš`cc\â8Û³¼2äøè‚w=n¹?Π›<^¦¬…>ƒÆ«;ð´[¾ '’ã!„~á¸_ÇŒþêÚITÒܺ½|b°Ê‘DñöUÕ· º¼/g¿›ã�*ûŽê-NË Ü k/�}â„€d;A „F ¤J¾2T @+šI‘ê¨DCyûn¨td5xášAÕÊ�âž=òµt0†ÕÔí¼ÐÓ+#¯hÛ�gL“iuúéâ…zéï�ËÍ­ìÄ®’¥NÐ<«àPQ–hãTûVq£=°ž¤ï³ñÈ 3/IŽõö¹ÁÏ× n 1�.`±ùãW”¨˜ûÀHš|©‘¥sË¿Ÿ3åíÁµSüöî'ï~«ï*¶ün<[­<}‹ž$W|ªSSOóÌ‹‡ÄåÉX ]â~_nâÀv§D¡\ñ«ì"ø„w5˜¸I¡NÞUª$#KÞÁh*G® eG@¹˜ ØÂ=ÎÚà/åKÕ€Y¦Çœ7dô^èC¢”g¢Ð¦ø*ÁÙM�:§¼>8ÂT÷Ÿlþc€÷òn˜x½uû?¯>´jF²üAû|}ÿ\ô²Ë*øGL}Ýø¬ü6#jÇuX’ì·~+íýz™Ru ’éB X4Œs¼öÚD>'3ˆJÚ!EÝAÖ  fØß]�Çlÿ8ùÖ{W¸SqÛM;ÓçœÊ@ˆi —s>K¸AÆv}ŸÝâ¤Ìó$¹Ýh`6E&Ewô“X„}ïÿ(±ûû+vNc+¶¢1ái†± rèp®…Æ^²´z“w_8ù┚áJÈ~Ç‚2U,/Í&¤`ÕpL$Þ€½ªù“ŠD¹” 8ØÞÂ’У�²Å�=ô¡ÖnÁ7#\¢¾ãTsBˆZÇ(eÌFü˵ôcòÈg$¹)h6 AÒݰ«;ïÐt"¦�ã¾}M 'F{.1õÓzÓŽºßÿ/\¤Õ‡«›þó Lr´®vVkªhJ‘DZ�¡$ µ+%€Ø«Uz¼>”ûa;… ªx”pœ‚ÛbÁòÀ!ü T2�¦à÷åf˜pŸÅ&¾âZ¡#§g�”X¿o!�êÕŸ1ÙÜ‹  —&XŒšÆ 8Ø_ÌUž€ZòØØægLÄ0nÞ7óº3•]Ÿ§`Õ{n9# [›œp*{H£ öþª›vÈûŽ�ÏàÂz¼C!³ððü¯1P�ì&Í NŠÔyvÄ*ä^J›�Óÿ›´�~#¹%ìGŽÅ˜óÌÛ§êÿ©¾6dlS°éÉŹ'¼–hßÞ•¦U÷kâsL¹_ û#�<ޱ›3%VKu”é›Òì,ÈCr5ÊEtêП?+Ž�KVwçÛ6™‰¯�Åq;h®†Åð,t¶¥ˆkdÏá½argü@På‰0ßÉ�v0H›,IMº…ì É, ê -¯ž‘�ä-t‘è½û2’Ñgn㲈uœ sÞ5SSY½ÞÚ¡°‚O¦t9dÝk•*’â�gôÑŽ~r°cƒ0Ü f(EzRazŸDÆ[ž-2Ø>N{õ1œQ³,Mæo‹]Ϋ¼ôÑr™/÷x%J(N«Y¼wÄãÜ FþÇ¡_TbTRä²–^Üç�1”š-�k%è8Ð?ë2„Ç^¾ð¤ûZz¥ÝÌýÆzIÓ¦’´šû)òÁ÷6+$-»Êl³x!‘,:줄;£1ŽH®´ï„uˆ±r/bT§®;› | œoó>qߦO‚%˦ÊQhI\2“íÂb_VŒz~Q÷ÄG™;�šÃÑ5š‚ “² :jý;Pä'{~Wòð³õÕ+üßqqCå£a»#,*t+Ã5ì¬&OCH¡[×`‹]bç%B¾4r”�kðís¶NéqÔ„† D.7Ë&f·ýþÏþ�“§×˜Ï�¥‘VQâ» �"p¢×ÛNÖú¨Ócôíw©c?yµ�`%-†\à ɠÍx-Ì9É{w�ik�-A Èg¹–ð63 ùï}Fq¥�Â) mL yžábWÍ_lþ \Ù{ÕÆŒ²üɺx9(2Ò¶ÄšÐÄ4“å½ LpÀx4 ÄB;q‡�b¢´»á䲟RZFas*s¬�‹¬VB4ÿ¦�˜3·[äY�r‡P|ðÍ¿M´žs‚E–Ä™6ÅzŠÎŒf;‡z›Acw{?;Ó–#Áé2!³ãX88MáðUŒÒµE C‡öŒQH“ý'H3—S¦[û+�é‘›M‹{ø±§lªŒy�ÂZFö²ð.z-UŽçm­tHî"N9rrÈ>>õÍ7RœdN1Ø™Hm@ÈÖÕ®ÓÿwÄË©Àì×U# ÜîK~ 7�CÖ"²¾ i+u›£¸U½9‚5Á2×&„æÐ¯ñwë²E|§jN‘=LÇ„ ã”ê+¡Ñ8Û¿ü.(QÖÅá±Ïèr“¼šG>ÅW|äÜ´)o9ŽÞE�a�]½ï¨h�¢‹¿Àøl,ƒØ=m wþ¢mñ�ŸXkãö?HÌÑzr*S;Áº¶Œ¶g&ËIÆNs9wm= Ú‚?YÎÅvÄ]CmšÁÖðÂs¿«9åDÒâ2ÙEÂ!³Ã…Ò˜?ìr¤^ד0§›�ýäXÐh°Ôòð¼?Ñ@Á—cÑcÆíÅ÷¹Gó„-¿1\ëdR¯vB½…^ r~EšÙäõSœ ÖÏRýÞ¡2  W\÷¾Û<Š˜ŒAGÖM}¦<äO⣀NAÙÚ4¨ÍÒÚk™kBóÓØVkà=9 HÆwÑ„8­O@ •âE&Íš¨ÆåUI>Ëš[Â6ð»ÀC�ñ´kÄ(M?¬_Ä ÷Rý_ÛX¸e_íÀš‹Ðžžúmõ˜ˆý¯-qzdËX„oÑbÞøé#ÄCƒ#¥£3¾í;H¸y ÝrñoU­m? Ÿ‡´:ýž).æÀ«%¦*VÕÓgêÌÎYPäj«Ý¸Ð½b-IoAöÈ o̬è\Co¥vŠ^+ªýM¤ºß™;gž~7Ñß¾Ù‹Ì–"¸yü¶É›÷¯Y*²"PSà (åŸÜe}ß”Ò1–ˆ:EÄàÈ£ ‹Á‡ã›�'–o' d .h»Œ°Ñ¸d³ÆXàØûü @šæeλŸÑ,ð�¸’\{Ó~| ý¤H k©Âã²ø¶¨ ¢û5¾�,=Ãuñ[ Kî¢ÌqÂ6öŒ6�løH�è A˜(ÔNÀ8•o3ZZQjêÿ±’ÖÈc‡�3ŒÑò‘, É\}Åá©’“sV|%.Àm° R ÙË„,Iv kQ�Þ´3ÉÒæ…ÑØtÍØhÉEo°¿lWptÕκˆ §°L1söå ;ôѧâz˜E::#µ!ü‰,W™ú˜p6ÀÕ>µ\ÅÅp%³D^ÓíÈ8X�@O²RÊ~‚1]¨ÀÄ×lÎAÌF èÞ³³77˜�⨤þ$¯a©=‚j Œˆÿ˜ŒÐËF,¯ /®êÚ5sEw{¯¡ÿÄ} N�Ý9çeµH,Ò‡¯¢HƒJ¢¥Ük:…qHÇï€'»å9ˆãƵÄíB¯rx¦Yžð”TŒ ž*t"ÈgåÆcÂFÌÊbj£™õ!N\6e@Å?ËM¶(z`Ðz2ãûÔßTéTIŠž½N·½µ%?ŒXÅÃq¢ÚZÊ)G�<Ó6Ä�Ë�mùú"FpÅáñìyvx'ôÏ@*ðÝiÌf¨Æ¤}•=)M}¬j°S¡B]ßÝ'’—ëM\Æ‚á ¼ò8R:GÝL ÌÓªumVM†˜q²#ަñÉp<4^jKØÐ­+}Õþ_î°•D5eý^§šJÎp!”5ëÐÈ/g›O°x4ÊøD":¿‹†U@÷kB\åÔÒô×ï?¯~ù¼Æs+¬*–óN°Ô¾ ®]Î÷WñtåÊ×&CékŒh"ª¯s 0¡â÷?"·<º@}AIøqïä³[ŠùüÓ-Kûä¶«ì-7á�/*wœIŠàÀ»øþ‹à>À¼†=îðÑòО̮÷­”Ðj hÁU/k~Rö_‹Xrͪdê0°’® ´~±ó¥CÅfÖ»É2U±­I �ìÁ;µ´ç›ÐVŒ-ûßÁ£�¿•=û"b§¯9þ›¦Jî_?u:Ìœ�cާ,B”2sk•·ê¸ ­cͰ…EÜl �œ*ËóËÞÿ…Ú7N(®Ž’üh,çhù"•¹ñÍsRÑcÔ‹â{-KqF�ŒBLa’OO׌ <ÚÎlÄ+—½]‘]"R+óñ™x¾MrÛºžCt%&ˆKä³ÔüÜ.ºðßiãÁ�S8v*vbÒC—ÖVR'°• !áôÈ¥ƒÔÊô.‡Å™ñµfe-·÷ÖizÙÜŒ¦…)�D÷±¬’Vò0ӄdžÝí3Ó”�uG4K)û4øjIEó¸ó(Èi} ÉuÄ{Ö7Ýi,8³iÂ˾¼‹ÜšÄ÷cø#¶i»üƒv£äpeÛ9ó� !Cج`Ëm&�›ÏTb•Ÿ˜éÇÄ%`£ªØ~?Z¡óÙ§Ûe—iF’º^Þ¨„D£æº/ýÁ%Óæƒ®v¹?”ýS™`‹t§^&”¡ÅÜJËQ9M¤“°eÄâÜêÕ=Vm%ÇtV·ÞØùÆU¬ ˜NQãóx~S%ˆ’�g¯�É�ÖEÚõU³µr{'¥×•8©ã +–4»�5«^ý W:`ˆS¡ñKçPÓÑ­»á$­/â´‘ÌÛô•ÔÑe·Â½*° {£k7<”dêŽÛî8ÆãûBÃ2=Òn‰”Šx•ù�å!¥#Jf4~Ö©öC†Pq#M§³�cŸ;úÁÂP5Ý×ÓѧN0q<§ @EP˜Ã€”zÚ°ôDI‚ûüæþ…Âò• JîëQaËdéÁºe¼v˜è±\'+šì2¯ksÂwhÈ7”7ŸÏõWs²H°«Ùü~ >@¥º[V›®P@!yT‚d‡a �Ò‹¶JhÏÀˆ§Í«Ãg÷}«¸0ГÅ|\'Š‚©Ù—Œ¸Ì•¸åäuεBpYóä ž�â˜]¥ƒ•äM¾±v��й!O ç£URE�()š";0]giHzÉHÕ{ÃûCóº áláGašt\‰$ _TxmÀ“Æ ˜6l~¥)]zÄäaëÃt±Ù2ß÷3PÉœq‹—êhj* 2—ûqå§>�‘Œ ©‡ÂÌ=&œÅÜ×uµáR'çãŸÝ¡u¬8È,I­;x•¦1w±žs‹÷áyú3²Zˆ—çÉÝ`,dÆ”ÛöT'Úo ��k)»¯ö|ÄFª ›k’ØòAÄ`T 36àý^ ¿Cÿ oáIøW¤�ÝbÀ4SΠG{–±˜óC[K ^‚Ê.c8Þº…«_¾+ò#SàŸxÒVÏ9ÜÉú‹=!ÊË%ùþ¤¦ôkdÀŠhØisbpr.—‰,›£pýW± •\Z} Ëzæp>Ì@&Ã.£`Zçúˆð<ÈYêÔú¤)“Ïp kFšµV2?ßþÒ¬[ö#?E'ËtqâR²�°ñ®È!4÷½¸è¬¿¶ÞÍy6ÚKÔ‚€>LÑâöiy#ü:WrsÆ>2§J‘ÚÉÎÂ[ìöÜ õó šõÉÈù˜¼ÃxG0Ûögù“mÚèãG}χÿi/埜È×Zmr×D[A@†ªFŸC“Ã~= Û PÄî.(B½¶Š{ÑÙž»Û�3ן`ŒÈKÒî$÷~ý²ÉBÑÌŸ¼Hl 00ÌàVîÒ;³dúÆ]!gô–$²Ã­ôA�?½> ïD2FõÀ” 0ô=w�\ÄæºZ­·_g4/ß;õ…äUfØ_�…  ödœ0Y|ÐÐ’�ƒ{`ÃJ€�øçŸ®¡æn¬ãNS€¶”*ìÎÙH HÕ>m¬¶2œŽIï@›ó[F ™ =¤VN¡'dÜW¸μ8 à¸x;ÒRû‰‘~>;B÷sgp\ÈÔs%ç³±d·%÷acIjø›¬*S~G2r1( [‚¦Œ®9‡ÍŒöY%ç¥ |ÀœëˆáEl/Þ%žzã*© ,�A_£K2IK‡êöŠȺ÷¯Ø7>]"j�…¨Š§æÏìm1 «.úÒW5>ÿöJÎjèzd;}Â×fQgÓƒôo4�övÇÚá­€C{RøI§›ÔÆöàšU9p莸Y(µä»K󷻸éy ¢öxUÈwp—„N@ò²÷“þ ô@'~ø}éÜà¡«d!“Z&V“ôáz[M šjÞ.¤¹~½hßNw”bHÿûŒ�ù¯£Í${ËE>+Ï…äNTj÷ >\ÁS�{ž'°ö'›¼¶ú¨•1>î› %ÌÌ›ù¿>â1táñ‡Þ½Ëp]„SZM7! q^7Ë(¬Êà`™Ÿµ¿;¹äÏwc6Ü]¨©ZðrFܸa}¡Ù[€P‚ä;³Ïº†+†Žó0®ROhíù—7é§#1Ûø­QGµÏ»2)„Œî°]ÿJKµû¡u –Úûº/äÊbìÓWSD]ZΖ•óèµKP›exo˜] 'Oµ¡ó¼M�u»Ïàå„o¿TœChΦXL‘ïgcPawÿ }s`£´GƒÍ#ƒn ¬žtµÓŠX&µŸuŸ¼\K™õ§÷üH(ÌHsß‹Ðê­Í7Ò¢ÂGCt\¦Þh#ˆ kå7 ·ÒYVHíã¡zφF%ð±y!r4q0™Ÿátñ×6ƒÛô�EcF<ÀHZQƒ)£ç´yŸ"ª®m,P‘¾ b‹Öìu‘Eiiö†{ˆÀQ+1¤ZðÝs&»UòV–�î©Tñ4Ì óÖ&ÉKèN ר·#¼^÷q �°ÿ‚¯Á•,¾ƒ©¨_Ž %ÌöCš‹(3"¹ä«‚Û576ë*x=À@­Ï˜‘©ÁZߢçö»õ_fh“Np,ï呬ÊùÌI 9é-L2ù¦ÑÐÇ(á´ ²@íl¦Ñ–0…®R ¨¸F$æyî?`“ܿҕ@ì�/wCÑÆÚ¼8g¤¡·ù9õG¶´à"S`™×ª!z„î/§>óãäYJB/~¦ƒ5WyûBê_Q”°Ét!QeS™û®¾µ†}V§�€¹¶LV^_„EÑàÏÇã}øSóÄÕûÓpOÆÿ†UÖ¨ù’Ÿæ·vxœû7`ŸwÕ�ˆÓLå_?ðû˜oc#õìÜ'Ôì�¼–a�ʽ¿ø³‘1uê‰Ö¦(èîÓVÃ&t|ö’Ëà–xÑö'öŸge†¼'TiúÊA‹ÞfNTéü׆,ä# á?Õ'GM38u8Ú£1¼¶™"©�¹xòb+ßù<ê—› ©Å:÷ó²ãà¹z—ÛP íóícÓŒðê'©Œ˜‚!Õ½??ð›¥=®Cÿ:÷Y¶=ü»ÐÎwUÙB¿÷U¸®:¿zŸ|qk–9ïƒíxÂpC¦ƒl+2Ea;r¢Æó€óÎl^}³21�¾™¹Šàç’�§ö0 !Å9¹•ÿÎ>FJkÁ7SÓ‹Û�%¤­«<ô�|Ÿxƒz:-³¿¾¿“á寭ÅÉðz{ŠE¤Þ…t¿KÔ_üŠ€ÍΉæÛÅR\†®“mà˜Ôo ׺ò”þ RŤødˆDp£$pRKÈþge{«â/½­‘|‰•¥psÚQ«•È S¿4ÂÍÍõc@|5ÈáS(«ÑSç¬ÕÌ^­ÎÊT�;=0R4|MÇÐýÍ2ƒÛ€�pSó[îð.챕Z)äÄþ•¡L6?N¶–·é‹ðå9Ýz”Ö³™ýØú-ò¬f'µsªš¢Sõ�Ã:è°?2Ùl‹Ÿ€Ü*zµí\o›.ò‘y"Þ§¹�\æ~]s®:3Á èÜÍ¡åÚÔB‰ ‹3&-ÚÞÜ‚ ¯ö”l¢<@:»3§ÉÂý²µÞQ«BP I¨�ÏnÕ†0Š7Úä$Ô¤P´Ç3<Šx°v´GÓ¤p!8:J­D%¡ÅV=’Ì·™`‹lï¸ÂÄÁmϤ\Ò71 0ì\uûb£ï ü“Lzíæì—?:Öi,v¼\"¶ÂÄmE,ì@wš:NçöвcòmW꿇së#ªž{ù¸†ØyÑy�T?ñ·£;J'ÒQ´\.Ñp³'ˆ„Ÿ÷_ð`´Ë†4V §Rò®$—�;‚|CmÿΘÅ›ÙHQÆ{Eaµev�' 1üo8©^p‡7D¨sUV3/A=5lt´+TIÇ“áxî—`ˆG'�)VEDqt!‚åÔgR”ó¿wÄ+;´+…I7zä·'EWhY±>å·$šn¥Õôh݄جŽ!•§Ì…‘WÍq{ó&9-Ž":v¢E¶q+�V®]ïA�ziiÐc0Š3ˆc½‚A•=Ð[3Å…�¥â�¦ÏnYQ×»qHÛ”¹¯íò¿ê{çÑåÌ"ÀÙ=WÆÉ_íÕØø¹  ÚãÇC²¶\ƒ² ”ÑXq¢>�n�=@Mºbà›È#hÊ�b¨ƒ ú÷7×»ã@r+àÇ=™�s-r_‹Ð)�!cؤÊÕµ[„ï’"&Jô°oçIÁÓ%Kˆ·.IcÃ>Pì÷ö46g¦æÏÉI0-àZœ“¤ü¤pU0½‡›�¾€ð |÷§Å�¹Re+‘!3J–kÊ;܈Ó.âÜ^ˆA,F|ŠuóMÕ-�Ÿª,\m!ÄÖ1Ö"U5&Ö~!«¨ Üç]ˆHB-:�s­“KPl[¸-§tȘ�±VžÀ ÿͧ‚Á6+†ƒÿ+5³*‡ŒgTq€“‘N@øÌMK࿞:¾/2%òaQ:yCbÁ~3ІR­lÌ”>=oLètô•T %EnÛƒ`ć{+¶X…§^]б>À{èé…€jš�­ü7Öýß´)äI|( ìüóLCÜÓZM)Þø–_AÆ…j- fRJÀ× /l!¤dŽ/&®dïKG~|®žžb¡ÉÓ=X¯lBêÃb[;!ñP£ÿ°°ùúŠÆ7¥ix¶ÂGgeb¬‹í§Ü]Æz˜Ž€W½|µ¯ÆŸŠç™Aî¢*:�¼Z�yPK ï·–ʱG˜|PÌT}&ôš8¤h]*¡ì”ˆØÄè"L@µ�m™ˆ$5 I�>ðC<ý7$fŸÜ¦‚’<¥p-r)Tfwª˜KoÅr“—hâ«\ÚV…�ªÆ!?‡¤F„ vºïýë{ ì×­E:•ïƒè ¿ßõ+åi\@ožæ UæwÈÜ�V½v}™¡—5S€¸Ëí~[u‚=Õxß½TØP� ëï7c ÖjF –‚]à*ÅZê§2�+¡–-¢ÒÝëN™µËžä@"�ä0�ÔØ?7->úv³ $<Öˆ2Òô@ŸXêåž×TC£õ¢+r5ŽhH€¿ÓšÁÕÊH 1ËiÄ$ЏŽoùý·ÍUŶ‚½I? ¦çú‰ŸÆ þ§[å2¯¡f¸ºz*\©'ñ…wJ0¢: «cmî=‘�’KÈý[„F¿<Õ·¬šä4˜ã©7A„Ô`(%0%ù–�ªß“J‘7…c8aO3©Š zÃ\ˆãëôW,Æ'2ÒD˜AÎ*ü™½DsîbPã“̵ZeíÆ‘­ÐÓc y(*¸››p‰�?K‘z£è‹Yk«f®Xò�|Bà®Ú6‚ˆmÕ¼cd±½�=Ž‚'=t<À V">šYþtƒèW¿¶(_ÛJCn~®m›ªÕ¤eyJ|Õ±¸x³'MÂâVwt÷ŠÌRnÜýK—¾­@ýJ�±Mr9¨;¿Ã)„š Ù»a˜JÈa£ Ó*»Ýù䟖\D[¨œ3uA¨²ÞCγ8ÕN~­ K¢óò¬â¼ò�ÒƒšqFÆ÷ÿ°9µÀÉ æ»]e§ðk ôcZ ÇR¾·ó3®ÛèÂe¯‡áüdy^_~­Ú˜m2ÆÔ6yjBZnm“Õ¢glRk�´CŸ{ÉU…ÞZ,/êo‡�OÖ~œr¤N‚ÐH§þy ‹ÛÀP-XȽٽ'þþ¹)£K1é�O_s„iÅ'�1~f*Zeܘ.p%¥8? 9o¥j/ð±èn?EEBPÜì�çßhžúãîâÏ+N�<4±í>~�,¥‚1o„ Ý‘`8Ÿ3ˆ=ïGÛò{9²ÒäPxß¹B5µ\X ÔB"ÖY°NIì.zSöKBÜ%Âó79TL*ËE©AÙUð1ÝK jØøf0L’§—ñÚ˜ï2ðõ«v@_š‹q7†Ü©“…ɳ MfÛü`=Z$­RU>Àé[ �kz´ié5ãÏÞÔÆ¬Yì0%L kmtÀ~¶ªÊ ¸×8Ô:$ÄØH–°®½¦”?Ð$z—®™ƒP~4iS3‰f~�˜æeZãm8iØŒärd@·è¿,!I¦Žh2±e{ïg•0Õ õBš!Þ5Lœ' YùÍéÚÝÜoEr÷�]{c+øë]2�ì à$Èîq°ßì”ÃBF­‡òyæYÈ[!�”ûôéŸ"_„M,–áüPM*°”þû§îõÞ›Éÿ_F¥4ÝÓµãLdV™ 0å WOßbN'6E@Û&¢¬Qr'ºÀ<¨µêjبÀ)rZ‡ƒfèCäaì�}~K�‡_Qs«6*ó©&,¹uU‡;ç Ú�¢ÎØ5‚û$ÖæÌ´c/k|Tw©‡Œ“æG…Õ}@§¸×€‘�@–äP;~ׇñ�˜ Ï¿QߨE‘ ­Ê«C:V'ƒüôÔ¼>Üt‡®=ïךQ»DÔñsN û›1µV ç�nî¸`œý”SOxœmÃìb%Éá7AŒ�ÿ¼B“KЈØ¡–(ß¡Íi¯*Éå|úâ�‡Ù´°¿ìDÜýÕ¹eÇT!ÿ›¢ÝÔ÷Û½'μk ?ƒ¸VÈ·oó“À#ûÕz.œ%¢mãÀÖ¦?ÖÂ["Õf©[�“Hkʯù«?d¾W{yÔsëÎZÿ-8€�×Z>mÆÖÙÐW”¾àg(!¾ú>gí—%)¼uPlιM\Œš#ñÈDN“*]sóié·Ìi%°ë¾ˆÿ��Æ&UÚÃy¨¢ Ló%ÄW “é×(Z)D”ä©Cóç7”ئ[¡%œ<Aç"#‘ á0Þ[Ü9¼°WÕ¡imMÿ"Ëã¢?iÔÍApõc#d}íS)GîD8¯b‰°6ƒ§Å“©`k�‘/oÕ˜Úújšˆ¡ˆàòÅPd¢·èÅ\î4ó[aŠ5—‡%£•DЕý+ÛËÏøWþúQ‰ºÁddÛ†·Ý®ìKF.ú�)vœˆ–r™Áâ»ì òИw@·­ÄQ�«ÃÃEO*>' ÜHƒw.idzÒæÒ@D\cìDJ¾¸¸Yî)Ü@l"•ÀùmÎû'寭Å¡qþaÎŒÃàä, [gºb)úCÆCR§ØíÙ¤›H€tEŸ†¾?à;å™[LÜà_PŽCêcvóéTýI2.öŸx9óŒuG67xV"7iCŠüyô� ø~¥Ì­®kîdzPz�9ì¦Õ€u© ±ˆ{(C„ëe„æwìHýÕà/˜ë¬Ñà‘…Ä[ÛkؑβÉÇX��±"ÿXÙP=ryÀ#÷¢V¸7ãÔ³p7÷‚¿p"çUêÛó1�«Ô>˜>TØ+ljZqL<ìEÊ“«ÙÀe&úb9�už¶Áàãš¾X†¡,fÏŠª Íe�ÕÉžX:_�>ƒ¤kÝQÎKÆ”&9_!Ëûï?õ £KÙJ;�ÛüÕ;&–¢c”…ùy¬0¿6lŽ o�w‘nÕ1è]5ÿ¿9>£4äŒìU£ŽÈxç®ç‘NŠ}Xº{Z²ñØ‹þ¤Ãµþ!€ß>Gçwt¾í_§ÿÑ–Oã÷ÙuUç!­I%rL›ËºT\i)®$d'Ÿ¦9nñfô›fqpG‰aò$Zd7î['Ú+ a¨‘¼ì\þìø€¬QhþG$–¶V2s´ñ—„:S@lã–ä¥qäj9 ¢l±Å¯ön¹L ÎJ¹ÖiBɸŒtЈÕe"%klGvnÐåâµÊÙléMÜ&?åÿ³:Ö~˜Ëx$ãRZ@¦V(˜ûDsÎ÷·jHåµã·¼æ`àó­2rä ;Ò`ýèÒЊâZa¢þyý¼ÌÁS­ï[¿ºáMõ�åäù)0“1®éË¥žâxýxµ¯Pž"1·ºÎð8›ÌÏ8»ámŠùY†ñ|‚ŽôhFoZ™©|÷Öè¨j×)u>I|”7«fM=´pO,jæ,Xµ ‘[©ðÝ—¡š€EÃ$gC[«Ÿ´æÏ\k4\Ŭ ŠºÁRQ¿÷’¸Ã*íd‘ìAºýÙF¿�Æn Ëç}nà m¦ÿØ·|eîùvûݯšJySÚ:~´gDˆm†‡h9·¨¨a¯ù.Hú&ƒLÃÀ%@ä{";9Ô WmÌ:Òu®ì|.FøìÑ<™´• Ö9CÆÄЩh˜D€Ÿ¶ò±j.ÿ“.ìpo}·-dÅ�$¬’`i9ÂÖ11óƒ*ÊJƒ~éJ¯Öþøa–I™7uý— ±3[_`Ð3°VúчÃ_ï]Õsdâlšºù]opôÓ¯ôrá©‚´ñúѸeˆðp 5Ú^aòW¬òù|Žo�Éøtsmœí•Ü�ÔèÊ ê5B¤HÕÐ6h6½)WØß«vóÑ‘î×ÂeR¯Gþì çM‘h³îÃE¸Ù\(6¿E=:Ò¿sT;®$Q¶I5<ºK(˜„’½§býOdfªË çÊk#ÕsQd¡V¦q§²*9�®?ÜšHÁ‡”à´tm¯#éÝ[5[DòZ¿…Í·Œ�hšÁã±Ä=™ä8kÏÆÚ|…��b³äÉ­DX¤v¦�õhä§•É"¹"+´BÁÞ\W3—{ Q¦k*"-X*£+á�ãG¹'ù –»äà_|õ¤øg‹žÝØ€'-�Ãgbò"[aÞå÷ zë‡Öº»‘‰"„„JÆ/¨hQÃ’ ÁèƒQø•îWág„']bV8»CàšÎ¹vº©?$² &Ùצ!ÛV×´B‡4gä#&FZÒrpPÎTŽÂ–,ï«w¥…ÜkŒ·`Ó Å•jžˆR2±¢¯Ñ‰&}4œÿ´g6pfïhãr@]÷Ÿ'®x¯m’Zÿ~RVcÈ*µjòžÌ61Ó”d/Ôéé�Qï¸îa—gëà§J9N7ÕÓ—8£Žò1“狦p—ÅC½½9k`àÔ\Ó£Okþº+‹6ÍqN y*—^ŽP³ÏbÒØùÓ6~d¯�¹/}ÔQ¿«€©€uµ]%ÞÃ-®`‹ƒp�<é:—èë$%BaŠLÏu…¾¶PÏIÃí=vÓ.÷¬AÇ]˜ÙÛh~÷·ó®"/ܾ@†>¦!óÔyvêG¦@ ›› 3¡X 7ŠHÞÍŒâS&Q»=šßû¾Þl�§¯ÒåÇwäe,c-‰~Ýlïí ²>À@û�êöñãÑYœkž‚�^ö¥àÔ×VÅJMtæú@¬%̓_§j‚g�ì>âû…|ÕÀf6@r²=€Õ*üÚ0dðu”Û˜Í%µ˜T6ƒ"PU %ÄàNÎF�gÌ€7Jൃl` {óšF4=»…ìLfU«t³×WöØn´Om`Ña™Ú¸B‘Ùq¦Ép°\­NE«ˆ2€³±8s5wæ�`œ»0Q“6*ZŸ–�zþiŠØdÐ�ìc›�3Œ“ÅÚr¤E•¬Á9ýô LŽLžû²ØXø‰œa }ª3óÀ¦Ø(Hè}9ùGd%È1væÁT¦9n¡ðf_¾ ³‡tsá¢R½VÞcÌwÕG¿]âÆT�±ZC ÚÏÎоWäÔ†Íut87Žf©Y¥™ÉW0³›\ÊŒ!° ‚gƒÞ‘fGvt™Ù¯1ë-]C�”2P·ê×¾}뱪ìúÝ êÌ8ñ—;&®àNq™¸åw||^RUŸ7´_)\òE*ÂiaABÉ Û�ÎÄÇøÓZîo5É—àX¨ƒgkþûǪâÀ™ÀúØŒ�t§2Ñb€ÜU™2^°í…�åñÒ³g�J~eÆ‚ö,:8j¹�2¢¿ 8›gc‡îúû@®e¸/Ó�éM¡4$µÖ¦?,ììä°DÂjê&ºÖǃ�áuÊê-p¦ZQ®u Þ¶Nð@Œ >ГfM¬ÞÞ¼„ÞÝRǺnâG‘¨IOc¤Ð‡·×+XÙÔží>ËŽÿ:´H¸Á*¨Xõ=_áìOßÊZcLºÃ‚£ „W’Ÿ^×w©˜AÍ¢Sµ” Ú)qütú˜ɲ".#�”{Í‚i[Ó 1K"þ–¾ªÈYO ®Õ‡�RÃÃH°ùÒa’(y@ž£•æ�6ê-»Ò‰)þwòÃoáü1PH³Ho§/W+¾Õ …CeæoŽãiƒ•ÑF3…Ó%¬'éd ×­NIF>%áÇF’¢Ò¨�€ƒX~Zl1è•l‘×-­Îw égEH++ 2›Ì–dæ—Y`M:dèÀ¹qíÀÿ*Çš�ïúPDðRtC‰�‚ ƒVú½|ý5'Úz!Ç3M²×.Õ`w—w,ål§Ù:Ü*;éì ¡Á¡u‹$t*2@¤sˆŒjÄ6߭м¢€ÃÓSå>!Šþ»¶Š,Ž«wkýÍzëÓ £Q7ö°Z˜ÙvàÀöè³c¹D¼ «uGŠóh™bEørVxÙ$tGƒºÏ!²d mñÎ+mÆG¡_0)‹ 0˜2Ò'ZÊe*O’³›’0ösäF�ÿÕc-ÇAÍoû©]Ô¯^¦e�œõc LÛ·D Öìßê;ŽŒÿ쵞=NîV7¿zÉþõ–ÆÓ¹ŽÆ5 ¯ÀVh è–+ìÄ•¾Üçž–õ0ŠíGß±ì÷¸·ÓU•L&GµرÑÃ^!ãò2M¢Œ §üÇŒ®D"ÏíJn¢Ù�àd«ÀNgQ… 36÷ |„<&Qÿm>(ð#Ð\¯½0sY�r=I¹2 ø…ç² ð^q-ßùÁ9´¿W[\Ç– ÀÕÔGA+îËïü{Ëú̇š�±’úï‹BJC5I Y˜àÆÖ|ÈIÑŸ—(Sæ1жži£³ÿ*9ö»óòùj!]h’‡ž¢‰z9vÈn«A<¯ŽÉ®0ª>µïfƽíË–š$‰ºŒ›æÖ|¯…iݧY©—µÌÛt™aNÇ=ÝÜé&œ:Z˜?§ç+{LS'h³N~¶aí mõ³ž[ Àª¢­¬öу² ½a9×Iqº­§"êÁ¼]YóÈ`sB$§N»—ŸBåà¬~¾3Ѩ_¸Ÿíužz×Ô‰ò@@ÿ##/àÏk-ú+°§eç9Fë³t>:éì\ô“Y§©šPeSX7\¼�Ëìõ[Ü×Ö�¸œ¤(‰´ó'˜//LI÷üÙ±+w?Ôïñ©]ÙVÉ£}õtS­pe=c=œ-sjpÚˆ2I¡HLsF+(o*7@ƒ'¶û{u(�_µ pÅuèõ9o97\m]ˆ‘±€� Õ× ¼¼üY‰UâmH›€ÄÛÏ$OšÎðßf UU ‰WP€Ê6J ç  8 QJ‚‘Ä]±ÈäX{R-±³µÕù:ë‹‹Ý#®{×Àš{ø¬T;EO X6¦‘™‹ãMÔ|>—±ô,_vý`YX†i £UQìÑ&ùG8“B«DÑae],SÌHÄòV£GHÖ'¢a�ôZ© ü;V¾Cƒ Ïþrýõ¹Ùæ_"h’QffÇÁû*�ý9ô”°§v£•¬ûMÆÂ8RK2íoC´½ Ĉz¼�x™JU˘ã }�ÿ¸íÅ¥ÿE,ãÔÊðo.QÐúÈ¥ÒŠß–M!ñÂBÅ;ÎB£+Æ_Âüsƒ;£Ì ›Šnºz/› â¥'=4#”dœ3ø�Ò9ÃPî=¹JÎ=CNŒÞT1‰(I”ÎFu»6ß¾šW áô8¢á]= kÕQ)§p}ç•ÖûŠîò…D³oó[«�ÓMO6µ P‘î£A?­ Ðâß«l‘%:Û3:€»²Án­ŒÛ¶�µQôv¤»¯zË£Ÿ*´*11B ¡�™nqò‹}z—»ºÀ¦M°ñÂWX¾,\CHF´¿»�²6¤œÈ ¬“Ð /Àyì§u%ð]ÔªC€vG: £6»—…�ª�)ý”0Dêk6oÙ~…ÒEêŽû­t6Î6èrëZåHº§›Þ›Ç£ëÂÿsüU„u4ÓŠá^hŸ*„N¿½��c'¸,ZeÓLËaÃÆ+žò ƒ®µx •Wya‚ýS4¶�ïô‚Âe::µ¼!+6u]9 %°³ùgÂÑ­õ޾óÖw¼gÿ¦·ÆÓ؈)ÖHê" o¥Èa×Ýß{"˜R@ñÙ *]²uKÀ"ˆÏÖ²ŠEñ®×Dh¡Ùûvf—<ÎR)Š3nÉÖ–¼ML½út6BXðª5‹ÂY±™µ ÐÈ@¿R®¬<ÃÁ�#³(ßÇ­¹Rªþ5çç@ÿÏ 6ø¿šå I⥬/°¨«G *©åh‚* ûÿ°K•†ê5#óŽV²œD�:ŽÎ.±_K˜B4h8�±±Ø¹ƒIɲI�t�JÁInBó}�©*àÊhî@^Ì&VpœÉw Ìá=Øt<%@–.l‡áái›®T ‘šÛ™ÂH­ Õ ÏÔå3b;eå§ÊðÏ~˜’,ç(˜Uo<Ÿ7Ì–,õÙ¤ŠEìØl?bckBsBó¬9'ñ”ðG`Ëè}Xêãå[ò�¯úSvÄþøª’WöÝì´ûDÛ.Érnãz¶šH±Jo·/Í0PÛf(TìÜÎ:ÑÿiˆJŽd@Á'(o¶Å°RH‹{Rz¼²›uAñHmTdàïc?!\쬈 À yB2)žä[1qdÃë*þ¼á «pÆ…P$¢‡ h„ëeJ¢{*朋ÞwU“áH]Úã2”¤¥›o‰E«1ÖìêÂp ÛP ¬ê§ÜFYµoÿn ë„zlU[ƒºHö×® ­Ú:s­õgù&r·,ïÔÜ}�˜µNb¨O2ЃÅù\ÁÖƒ“‡6ÉHƒÝƒÙæKøø·½«›Fcyõ>ÁT†sÐÄñ‹opKŽžzSÓÝ–5iÐ%ÍÖgzuóf7<[Õ&28xWÒ¸ø¦Ð®_šskÏ|îh÷+™€è±¥’€<4;—»ó%ЧWñ)ÛP k[Öî#2¿~Zc�_FÅE†¬wxq»§‚þïØƒN­Òº7uÕfÒºJù·7ÈÞÒ¼�Õ‘=:Ê€$ÍìþïÍüÛœ™’ÍÏq@Ð|Õ˜å"óÚ_õ¤*Î —#¾®6ÆAìaÆ/µWO™ØWøÄ�j.°“)Wš²Úí *&jª#ØCçJª�‰n&oÕÑ£g˜3€¯†Ç$²ð»2J4®ó¦+¥P ŠãJ£Ý^WÜ4™ÝTc@²ñ­ܸ’ÈûMÆ‹k.æÍÜ2¸±*FЭžÒí�,üM7­€Ð‰i0$×Ñj·zG™p>øJ›;ÿîzL®cµëRýó ¦tšYÂTf/Z7!–úˆm¼&ëo¿^#6•![ D©èà<°Šº;D·Ëýœ Í”K®|×;/{þÒ%Œ¯zׯa­3rÙ�œ–‡Þ�Í Sæ¦;ßP¡a*~îPžSë nкQµ„%ݤþN�`¯(ôQâ­þØ7X‹‘RÑ~"ò]¦ÔIÚŸÖ“©lÞKÇãþʤw€œb­s£ebªã%¯Ù9ɃfÓgÆÄa×:ÌòLRÆ V~?¿ÿgâÉë¥]y=ã·÷3¬{•Çq¿–¾·gtžT›3F›;†ŠÚE]´“Œ„�ö•Bhߵ⼡à ^uˆ~? wt“¡�"r)�ã³@`?Ú ¤ꬡúÃySn'ÞOGÿ''vþÑc'3ÍBñÆ[ X#v£__&ri+‰Y†ì*Ë6•îù]´ nÓ¥AÙFó”¾ä¬',ixYéÿègY°ÕI³”¤tÓs¼%M²:ôyzzŸWº :¤ôE;ÛÈÍ5ø2÷¸´ô2HL÷J÷H®2" |g—Œ¤dÖ§�Šße—€wr &¿t>ÇhÍeN@®'x"ñÍpÃ'Е¨\é  ÅP‹-š)¢VÜÈ5‹¶ŒD ~P’Ùj‡ïŒø£Ø 6m×Z³%ï¥í¥¡ªÆòD#ñPÑxÓèi�[H…ÕáRÇÝRªÿ|ØAWº2Ôø_j¼ � (¯Ã];,‹$™È„&áÀI¡;T÷¥F�]Ÿ4 �홼î�Šñ5£/tǂȽ 1ɘËä’CíŽðò;_ /ª ËMmDXRk´Å  �Î<~qŠð;‰áã¡õ¶[¯ã„÷ú†[³V«@×q–qÞ¾ÁÂöEäØïaÁ³²fé €‹Ë £^æ³Ð#O¤_*ÊàÏÞ,vï@'·¸â–”ä9´MIëRÄè'ºÄ|’à$È ÛQcËuTó\Û ´dØ">±’d¾Fèäâ’ó:*!Ù[÷�eå�g2uDNâ ØNEEd£YX0¿çvÒµäý}©¸»ŽögøD© á‘´m Šùp1¶RÁ;êý²o¿!B¾ �ŸócÚ èIɲí°âI½›'ô÷…»\˜Î @UR ¤S)9ʼn-Î@.Ù MÝP°\s6ˆØJÚFðN”…Vp‚£?êß«þ=Ò¹ ÐyqÇSH&-§Ùô"âò�¤¦–Ñþ§_´‡ ŒZèD_…‰Þœ9>ÞšÈ÷eS0jþËѪœ+Ž zãJYñË®�ïVÑ3”\D‡&­ÝR}Æ~ÇxN:5–Pf†—„˜„L[¸²ŒìÎéªZ?5ÄIÖ¦ð…NG[&QŽN$ût>H°Ëvm/#êÔ’(C£ÝŽÜv8-]O‡rßÁDñ¦–Ýi_$³¾_W ”µsó?¶ |ÂļihHä¨ �Ê€H.Iô> ŠÜi°�¨Ìxb~˜ƒSÿ2zÀÖ “Ž LËÆ·á•Ý|²+4&Êqî“e'¶ìΗZI…¥D›è%jÕ°yæP«Û<‘’úlܼFL+BÙ*„Yýo]IAENé¥ÀNiàÊ·ONKí�˜Ø�î–Ö”ïñŽÄÈÌ0éZËô¿¤ êR­í»âè©d²Þk@J±žbO礊;.H·÷ؤ1ª°}$(}�Ë=xÎØB®ü9”iyùM)M«$î‘fm¸ T»ÿ˜è|¼ÉÞÓÒ [Hó¥Ü¡h•!å›ý˜âvTÃUÙ;%�„ü{ _úX™íÚNþ¿õÊ…³ÈŸ8!þºÞpÌžQ¸º%ÉÜç5P•sçh7J�öñ ¹•Ç2”.¤­�󾪋Šs‚Çh}h"C�0kþ·¼G×—_L8…m=¼©oÛÖeý®aš3]áÁæH{:Lé%¯&ÅyÜqÀ2| EmÄøÁnÒÏi:âœÐºoúšä«ïõñ ÌÅx1§L¸õ¹õKo>í#9FmÌ ÌæÐ‹f¤ò &ÃÈüÁô—¯ö›¬LêDE‹˜z5£y«MöÓmºÒ†Ú«Ð6¦Òõãˬu„!Oe‚+C“‹Ü}×;bõ&E`däAû¤}F Ò¹‚’9"v ¬õù .E,© ¼aÃÃhMG×÷|žN4£î]xzŒ«3<Äl`�ADM]üfíê›°ÑìÚµ1 bx:©?ÕT4Z ÀxÉK€\åYÁ»"ÄdŸ�k)¢zlc¿Z¸Ž€zýez}7F·®QÄy¾ÃËð×™H§ã´ëKo¦^4—¯ªœ3CE]øMªÇÉôà¼LÙž)-_ø‡¤è‡—Ô ý Ë€3ÕMÂ@?œzïHYãtuN”‹nô8ÑI…d«îÙ"à¦Í©¿Èí“ Ý%ʳS�ù "_ k*2?'³Öž”°}ÉÆèñˆãƒ¿å&ÍÓ'D™%‹\óPW'.••Èá¾çÓoi: ;4Ósð`/±èU{‡ùCX¤‰:5ŽŽ¦{Ôˆ•)*éÉø‡zò *=%G„ÕB æ�¥¼oÊÿÙ¥º˜àŠEwWõ\e¥+ƒå›j›qé.�ã½–Þý€Û¸¼/àîÚdšÓErÇæ7\tÑGbÐxn6Mõ´Â©GäÕù’†ë?H BÈ9 )§djŸÅ’I ¨j9òz²ÓC*Ä‘F€·gtæ 6-‹ž7ñuƒA’œb²«uDf~B|ÀêŸú;QâïfæøyE59)9"ÓÞÉ÷ ȉÛ£˜L�'Ey÷“”ó)¢Æ¤\�ÛƒÅÇ�|Eÿœ@R~¦Í|D|!ö˜Æhê#ÕGÎÜS†™$@ȸ8;ŠÅùÉ“³q[ùûì+Gâ¼DµI¢ê3r£<ùgë{5ØfïZ¤Dߤû�Ék›œ´/ñŠ^.yÉLù_…xÈÑ•T){OòH†8©¼‚*=wq ã}AÉ?yw33ßö (�mˬw³Y*k�ƒAì[)3L¸q´o¡bef^äõƒØ÷Îèô�—tÌÔ©«µƒoï]__§}+µVI±¿çÖÂñŒ­IǺ!Ì} ™ø�Û¨Y¢Zg8ZWµ+»²‹ÌÜ-)ÔRÚÿM2·Ÿ÷þ',6¤ŸVBa‹Œ½+ãÚÞ‚Çì]Þ´K¹LËB« ÇØo�“)Îù�é:âçðùSüБwì`Eÿ€á#ÜM­šõŽ.ËØ¡^À¯Ôý:»¥Î †2 «½²çr¸ê eV‚×§ p‡uÕ¶ˆõjE—åÖ”¯‡»PùÁ̉î$S r.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§.§èWYÇôÂTÔÙÃ>ô�äY§ô®â+ˆi�Š>a…¹S†ÖƒÐ¥eetremotesf-2.10.1/src/test-torrents/enwiki-20231220-pages-articles-multistream.xml.bz2.torrent000066400000000000000000015176721523411710200316040ustar00rootroot00000000000000d8:announce43:http://tracker.opentrackr.org:1337/announce13:announce-listll43:http://tracker.opentrackr.org:1337/announceel33:udp://tracker.opentrackr.org:1337el44:udp://tracker.openbittorrent.com:80/announceel37:http://fosstorrents.com:6969/announceel36:udp://fosstorrents.com:6969/announceee10:created by13:mktorrent 1.113:creation datei1704190324e4:infod6:lengthi22711545577e4:name50:enwiki-20231220-pages-articles-multistream.xml.bz212:piece lengthi1048576e6:pieces433200:jœDü"w†ð"‹nKdñ^œJ¼1î5Æâ…°}’”å¢àéŽSwbÍbKÌŽ[¸|Ò„Ô<„ .·dIÿF?܈´$_�ñÌ<{åË*ô¼lOÖ–�C˜ð„²cI%ÅV¾ò/ÝÛ\ÅT¾ ˆ¼A¬Ów+Û#%Ÿ’«Jma9ü�ùe8†I|!’É1QídŠ…§åÝÚAip¯h»Nå4šŠ2¤Ö]7Ièuøüÿ‡…€O¢ÿ¡ªµeÏq‰¼8�!ˆ˜ ¡¨¢|OAúkFÂ:—t]ÇMA>?e¢!kbg?–0Vø±±°Vìg¬î  ­¶Î±$†ªÍ·”ÏÆ�ò6P�ÆO)^/Ÿb-A^ ¿Ÿ6è¬`ŸýëÞ`Lñ‹CF4·*-ê;Õ½N)âYj" µ´º£º;ËàêÇ`(k€ä�%+f/Ø­�`ûèõRãÐÄS5/ŸfÙ@ ¿­ÇÅD:‰zðLXRf®ç Föò9EÔf¹ðw˜-¢)¡‡Æbz^V‹Ûz{¨—ÜOUŒÞÖw!<ë ›SEG‚h�|�„�qN EÛ2N 2-!OrñmCe…ñɾür°cI‰ñN�¼JáN�×´IÛAM¶À/Nƒi‚E©Ý¶H�ÔˆdÉhBðWŒÃdyÓgƒÉ%Ë*,üÈ”%’ €_¤Þ�y[Spz_u m£=.6ó&˜v阎«@Çܲ—u¿¨XLТÖéêjz9°./ö¾>(Îe?Œõþ6]ŸhðåÍã×WI€ˆ§Z˜Æ_6'©"ú˜gÕñéäÀoÖ"FUtü©R{çŽõ‘Æã¾ÆDK¨Uz!„Ø:‹ÊCI/J˜¥üÒ·§ñ½ „-Mù݇ÆÛ(ÝØøÕ9¯<åyK¹€ ™µE²1^qkð®4Ó”fÅç©î¨î0Cbâû+ËÀÈÓäœÅÙï8Âì��HfýçÚcXÉã&‹àAÞ§înÛõ .!z+Hm `ž©_>íF‹Ð7d%GXQ`ÚG‚T·úÁ'NEÿ’ùCèΗÓ[ßugkÊD«ROŒÙ_.œ„v[q‹@BœD-׈}E»YØCñ#  �n¥%qÛsæ�Ø{Ëί‰ç>é€"Ý7^ŠC‚Ûéft“æ†Î)âˆ'ÇöZúÓÿ;3ʦªß µ§5‹ÏUÏSõ>²g;Ù�_Ø{7¹³ÁDîZ³50½×-^´@«KId©Ñi‹JÐCàx·!NÛ+Í}=�óué­GHQÆ]Ý�ŽºsI8yóQªiÕ\FºC÷Ë3fàf¹ëÊÚ=Sé1Py,‹jDçŽünÒ=½ñÊÿO)cã� щyßKõ$Á uÒ¦››_ÓNO¡ Xë_+ZdÌ;‰¿vÿA=ùV,XÔcÄl¥œj¨ÞdwWµùÁx=‘<©³3ôÅ�ä…påë;–u>ZßB•l«îÉñ}“å‡�1i64áJÜ9õ~¯E.µ'H±±<ÖVçÄ jAÁKp/t’žÍ¼L‰ûB`Ë%ò8ö�X[@̇6ƒ ÖmZ¸�Ø.Ù‹iO¥�Ü{·o¶âå霗ç+{´ÞAxv˜æ3à’Û!g®�2mõ§Rû�×¢s¹�üFƒqük)uÃÏnÜÎ3‡ H=sø&Éì°2fÁÞËŦ—F¥Z÷q‘ÛcùYPüµŸ7mÂ$¹�šŠ¼]: X=”À1¾Ü}7ÍYø 21ˆk«˜&¢Ù,ÿÝß6bˇQÏ…óéÀíÿ:BSâyʰ¬¶M¦<.„wÁýö‰MõvHÚ¼2“`¥-=ÀöÒŽeÆ‚íÇÆ�ÄT¹®\vÑá]Á�Ù¤çë`úOçU9ü*Ð1-�Ý7ÇbqÕ ý¯d¶~áEÍúãªÄø™�ÖÒ®m#"E±¯Mý¦‡Té§Ë¡79ËíС ” N‡ìÉ´} oEt|Ú¯”cµQv䋸¥ãbžƒ2Å­²¢ÔY2¦»Ã¬ªCõƒØÓ�pº4ÏÄ›äÔ«m®�%Îzÿ ëäg�´DÞ/„˜Ð¯q•g)òÕÇ—Ïn˜!¤ÉŸ£ìéWølxÛ«¸õ êë^OÊÃð3¡BD¯èùԳƄ¤x6“Œ¼{Œ!oTdÓ¤i,m¤�ÓƒG<Ðù«ç.µ�â›zê[Žl�`gk˜/ë_á^N)C©Í_+ Íû»K7q¼ˆ>¦#äsDm8U:áÜÕ>ŠA>•9•ïRC/‰Nânp %Ñ×?þùOð‚<.<òŒÁ3ôð�=ø��–þ•8_§!%]›º±AÛ×Ö�}àž:(A�eèY^Ž\Š[p‘Êyï&mûävy¹på?àÌY&dA¤AJµGj@ IøE>œe\žBâ¶Îgìõ RéÈÄ'#¿x?BlÕ HñON¶Ìïô„ . ÐÅݦ-âásÍnNªá<ùWÿ*»I}Baûlé”ÛI.|fbqéÛHù€•NØxPª[Ì´qó1M*Á‡_¼Ûß¾Üáí ‚œ|Ýb€ �u-w{T½á�z %h娌¦Yã4ÔJ‘ˆ8‘:ð áç_¶øhw$�Ww¥Ùq’Œ=Ñ_é–Dëømxû.„ iÀ•éââ“Cö�Ú&³Õ: Vž‹p)hHšmtΣÎF�Ù Agc�,!sX «ˆ‚„DîãœË~¬‡æulsò´ÂßzTsƒGibá.ÁæÇ;Û Hйââß§¶ZÖáÏ6¨ÝÖÁ�a¬ü9 Ïg�ôy²kv�æÄJ@‚ad…[8|';1E* Ö„Ö¹¥¦P_ Éÿ†ðOtyÀ`u ½q÷¬~?¿|s:åÂc~x6Ñ(–yäe“åÛ–tÂr´õM ?Þ+5#ª¼o‡ÃSÃ÷º¬IuïCÛ6oÉéAwó–âx”n ö“®Ý#@R¾­ë ŒÑÄ4)�ç”Âp\7‡Rß,ËÏ‹8B(„,­€©Aü¬dïZMÕ~�LÙŽ³’xèã3 ÆM—Øl¥A—ég¸ù¶ã÷ûÅü²mMâ`'|àn˜vþÐ2Ä•q!"-ÚŽIß‚õ¬ä¸&tæýîPa±ÙÍ€ÓÊËʶ¸Ò#æ;ISšß”/ßTÍ2 £-;¢È-BPkµ±Q=[YËvˆƒœÈXªFtú匡Ž|Qæ�¡³¦v­Ú™yão•µØ�×c~™N½î'Ê–ê~M‹�uû¯ÙcÌ!æÕË>`ª—i5'|þñö€i±ç(»�ºÌ&ØD\k² úoÁaÇémr¿”ï„?ÈÂýbÏTÃüó™¶!u b¼pI¾.}Í–47ŽfÔ.*n(rù%-wÍ�Þ­²a‰¬:ö¯ ’}ÂOBO½—ºØf,Òì` ÝÃçx‚åMé-7¥o~€Ëòè¶£ÔÕHMï×¢½ã$»eôæ‘)rÃÇ:�ˆ¾(¢á«ìi)°«÷Ž ”^e‚Kýù‚¬â5θ•¢DJ3ª�#Œz-ïaK 1™6€¼mpÑ24¬{J<د×Kç šÀûeLv„„/±§½D¿Êç\ ¼¹ u4�=¥Ô½?NYÂmZàß?/öÈ{ûÒ¹àþí72öÊe‰ÊÂffÅYGí^°l_TaÂFyŸfÍ[;à§$ƒÈyß«".6n¿ñª–7™yæ¯�žb¸Ê èë‚MàʶOj ß$iÓ%Ê&,Ï̵Pá²6œ§Ù'òí�à‰Èg¼3J륫jú%|9½]%–’ë ׺æôlé¶7ÀÅÞ0¾'ßehìv±î×èzÙdé À’ꄃ]‡!x©³äæ8ßä/z'h± ¢ Ÿ/NE±'©Óf[‘§âÞóvô¡d„RÞ�{eÅyà¹ù;êœ øAºÛr"Z Õ'žšnša ¼ÂåÊX¹TÍó-õÈ\RÚúÕÖÍ&iæËÙ7Qñ®‹Þop™h0Œôã¢#…%ÑÚ°†|êe‡`s~Bµ‰K-½Fù ÖãQ÷% ØbªÝÙ+dðF_N&à(üÔ�߈øã7w§-[J¾N ƒ�"ño*@£A�&³úYKn´å!JvRNkÀOÒžýë�Î3z´ÔêWœMû´07dš‡$CÙì8]Þ<Žfo«ÄOŽêÌÈøF¿‚Ëû™=6Ö CgÕø%ª1„ßÖº—cþ�£î?ôK�ºÆx‡S˜§ž¯‹ÝáëRýb¤>‚‡™?¥ˆ[ó¸™PsðrIª(èåML¤Ê“˜FŽHlûïÂ=˜«"Òµ$²fñõtÔÇK?Ïùf$>`ŠIClÁŸ‡Õs=Ï£nª᪠AÄUÊÖÐ'ÏS)ðX×Éü-XUðSš/ë í=ê­_o'HF{¥Ø£K±�¬rðUóß?»oÇ�l˜ÝùÑ#i‹sê`CYHÝcž³½KQÓ} Ì\΢ÎÞKÇwzdW�Ñ›d“ë)ý{¸ë’Ò+E.Ò> ð’gHU<¦«žMl؇‰ôiÄDœîƱï-û²@Ïš¹ÒW>6’)<2i;³•ôõy¾Mz™wÞ):®¥}Á�ØL’kêg†^€ÎZÇm‚Õ–zOàb•jÃkXjL'Dpìä+ÒžƒtYùÔgÕGÅ>òl^ÇŒgmñr ¶ÓáåEŒ¦^èÿ•´ïwþ #{ùbOfVõh’ÔA#¡O†‰ z0PÓ¡¬¡ÚN tJæð9fÎÕxS~˜ØÓ®Ì|Ði×\nýc�Væ�ÒÓÓ–½FÌïÊæ,kôÿ¦>nÐAFz_Þ‹È!¦±e�#ZoãlúÇá €_S0yÊ!ÜÛ(ÈUocÜhMi�䦋ՀOдÁ?Åé8 ðÉOÿÝtîôúÇ£—ŽoCvÛS•ãÒ?”ÈŒZ¿Sû寕‰!+;1ý¦ÛQBñèëçL{Ú÷à€Î2Fm5Su×À03®˜[,ik•îUÑŸò€„yÍ*iEïÔÃqi�:ÚØRl˜!ŽõUD¶žº.¬hÄM).¢5ðj¢Nƒ‹`éÛŸ•îç¼!ŒHÍìØxªƒÓž®1w«±|ÁÁÍšª£3í,ÖÇç„WÑqn‚³ìéôbàÜdmï„•MšÊP:ö¶“KïxeÑ�žÎܺù áûÖÏ'ÄnĸœÛ”T^”øC¤ß°ö–ÃÁ- l¬Ê^40«¢x 딀™Æ˜_Ùº;FŸ˜õÉô*GgRöI»h7‚K?ÈtDÙ­§‰·o—ųó¶yÝb&Î*9�Ù-Ùà¨,)Æô¶ògS»õÚÕqŒ�Óã,ÖTc¶Z%8íÀ6F‡L…kÙ«Zº†Qj‰ÔbÔäöë :M‹ç—!D¬§óù¦ÐW>@‚-‹:Ä«Ì_]«gK�͙պ1X£§‚ØÙQ³Øê¤Èé9Oõ /ÔǬd‚ OðÔÓê¾4î;�ó.—›oª¦†?sŽÂDˆ'hEçéYÏ'BÉ̱ÅÖkiu㢫0ªÅm·íÛóÒ¡vïüAœsfø�‚›î>HVb?R ÉNYX£….X:ñäë¥Ã†R ^ ¦êEy#ç©Ò?°Ð@ÓGfÞ›ÖÛ¦ÛUŽ©o²ÌÞ?z#f/«�äÍgäèu³ÎÔ¬½<3õQqïARwZ(øáî=Z@—ÊùU¬R… 2Tºýűf¿µœLÍ�´á¢2ÿõ�³€î Ë7*mK��¹X�¨ÅÉ`•‚®åÍûAv”¥*!ëZŠ–ônä–ží+é__>§¡—º% ¶¹`‡½œœ·ˆ_C.ߣġ]GKŽ"H‚4ôO'˜B ú ä¨Ls%ª™fy 2_ág«6ƒ@ ì7P)k´ Æ4C†VŠ�$ÜÓq˜O–zL ™Äö¨¤¼»Ð†GZòdòwÅϽE“æt8Ñ™iš1‰‡±~§³‹gú¨§‚úýÇ«›Ut.4í2¿?ÿÈ´YÅñm’–1±¹”ÀI[ŽTìÁ |xEYÁÇ"¥Ò‰wGg• ÷>„í#Y*ŤÉ0L…%çW [ V­‡8Á¸ÂzØU¹!Vü¤ï QÃñ�òoîwŠC¢'ê\|Ù ½hf¦ú¾ì ÛÉ­;Uéú'�ñWä ÛBD¯½èȳÓÑ‘¨ö¿!ñË¥<C¥Åä5”îi½Š"C‚Ñç0Zu §Ó@ñnÅ"ÎëûÏŒ0©Ý‹½6 × «Å�‚ú¶;<µ®ä5£K"%rO_�Êc"ÎÔ€kí°ƒHïè¯>vÝÉx:Ä£™‹cª;¹p²›±Y@À¦0d6ˆíLæxŸ%bßut^ EÕZ[;®(S³˜)¡a»Žæãµßœ�4Þfôè6\PÌ ®^KȤ…™àç2 fóÇi£‹.]e’1¿s8×;W0â…õÙXN¡ë½�ïÊô)�આwµ¡Æ+L„Mßg_“¶“ˈè@ìQŠ+áw*Ó:i‹¾‚*…5l6Ó]ÛÀÐH¬A(¥T™î—nž…rO–Ûk*‚1îÝŸ“£c•OÑDδW7©Núëuƒ‰Ò ¯ˆP¹ÿJ¼‘ôß éïX/b戋N'ò,i'Êiø^hY•$�½òÜ–º8�ÍR·Iÿù�µ°$ÁcPœ¨æ±1Ì4ówà‚<ÿ4£̰aí]@ÊN>Tx‘V®DÙiÒFzìÂy¸n2dÝ:òøö -¿>[îŸfoÁÉ©!ú2–ö3Å;Éþ1;¶()²B]1óU6x«�VÜ$ÌËÏ„ù¸ájÞ‹Zmƒ°Ñ<%édå×VÔ:&S¿z`-„…GµÚ0ܾé'Bgõ—Ƕ<ž«1ý7Ø][µ¬ËÌ?/|oÿø#¦’{å«ßýð8–öðN Zl€(|Ȥ”ú¥oµ¥’Ÿßníá>Ç:·÷»íbQ¶I»À(F�V4F.ðšKNmôVj|R9ùÐÞTÎÄd޹EfEBt¯"6ï²¥,šAÚ\Êœ{oƒù{nV"«/¼�‰™æ´Â¼w{�]4êñÒÖ_6t…æûÉG>G¥G�DÒ­‰ä—¼÷κèÃ&µÃ uVõ78òæ(€ª†¦ó½wY\#Y>£{\{|$KO@,F¡l.!mgJ}™Á7 ® �;Yå¾û«4þH2«´ª{u]M´Ù—Eû5'hœ§ˆ©Ä+Ž6<7Å»æõÕ O03×s—™±°c3YQÂ_¥Ì+s?„HîæÎç*b¬›VO¬àã¸7ÖÚî dßf�žÿiEcøÊ,r8ͤ÷y’ų»‘£D °ÿ W%�J`s*ÇRJ¶V \ïîWÓ#ß@ ÍÆ¿!«h™H¿` ;òJ‘ åÕ³} Ÿí2'Ë`pîu2-´x‹ÎVŠìîàµZ wß%ï×½D",`”(Djì_óÖŸSeËB¸@ǰ7qmòn«Qæj½qìq7ËOñ8b�¥œgñ¢Vµ©*ëº.¥»\ßë�oñŠ)ù“ß7/BÍ&ð«vn8—¤3e€E6 ¹¡ÎÓ:|ÐN3vLع|áužÚÔ_ èAÏ8ÅâI`–£Ù×›á�˜6¯#Dp‚HË^;E¸ Bm¯.P9šó€¶Uœ”G“aTVh®Õ[Çàj¶U`ŒK, ÐBM"¿ßBô, ^JÜÍ>­¤Ô¸ØŽ2Ùã͵ÅEª®ú�¡´°ž{Ö��DkâMTnƘ ÆDeýâ®TÎìø¤á­)œqüÕy5ä�žFúâPWœuYYví·µòqFY|“ÊNv¶öûïʸyÔ¸É%uMÆùgøy’™W|†C�ñÑôž¿Ç¶uí:_±�£<—¬•Å�[w¶©.‘¢Wr2´X–µàºÏd‘›äêKºÁÃÚ@hÙ/´Cå»=À 8¿êøJ˜+)çƒ�·Û¾‚Ûýxƒ…$EóÒ.qîªyÖ잸d�^©È;(1:‡l\æÓ²ÐqòDê”d3¦]©3í_�0ÀOoÕür¯Æ(Äã+}Õ±ìÀظÝJ„ÿ¶ÊËpK²•<¥• šÙÝ"³#ßöÃđ[Æ^è·° ¡üWNw(ŠcRxÜÓR>ú`‰º†¶%,§€5øoø‡¤!¼ Ê7¯…-“ Qô­š?XÙ=ËȺìòm ¶ÝÉ& cÁRœ,têƒ;�ø(Í+­BRè®S(bTЈZh¥öº1jkY+¢LâCƒœAôÔCä�l“3Aö2NL& !™ A‘wüÉöAþŽ%ôšÏðnS­šË�ý¿ûjÙ3#s ävýztdÍœ|[ l•X«ÿѵœaBJÏéw8í±ÕÈ¢]xÌ@VîX)EJG׿aU"u=îîÍ.¾Ûþ ôAäN<Êy°ËQü¥F3/á…²ñ ÊNc¾òþ [ÊcÈA4¾”4)ü¨¹?dþR‰9óZªªäI†9‡ ÷�z,œ¤ r3|Øé’_�ɘV03莃£Ú ß,&ôüÞ’òå�4·y ¾˜¿Ë‘ã8�yCBHzË#ÕTi™ÁSV(L;fJsŸüø=ðQª;ä)e0u¨ª¬�ôJ_b�2ž€îÒëÉ¿ÇéÄöoØ›íL§é{(XðDøI>³ý8&êM=ëÑ5$û/þ£ÜOïlÚ÷Û/uE=…l‘àÜüä{öÛ  ý¤Èòè�‹huÉÁéG±!û¢3ŠSúæÁúŠ’ ûŒ‰µdĤ =gó»d;‚xÓõqØ÷gÈ<,Õ„÷}+ïï±XSÀFo´�Bù§¾=Ó‚Á§2€‚V˱à3EþTßj…�ºýûH�±çrz› œKîD�VÆöZI£64–Ú¢š¸&ãTÀc…¾œ®ÄÜnÎdÒ@L•a!i¡äqLÜ{5…Y l (ÒÈûýÚùÀN*óbÓaäæê²sµj1m'g¨0‡b ŒŸÛ€=pûZu5AÚè³È˜9]¾M dÚ—× UFã—`žÇÑܰW3§Kè÷5�l±¥�%%ë:À´>@Ve@3ÕŸíz1ó²>{êïì塬'v¸k¿k æÒ3í¾”hÑ“ûuËøNÈRÀŸæz» ÎÁo–ej’jÊЇ.É'Hßa5`¨YÅmµ€ î�6]}¥3ËÌ »pÈÝãÐh`›ñõÐoSu½Tu>‰Š‡S xÄÅR¥9 üÇ;™ÞÃDzh a»ù¯ri�ßsO9Îߎ=õ}¥À,ÝfvMñ‹Õ—8ô‚È€4“¥OÊ‚�ú+`ILKßÙUÿà•?«ZvƒHÃ\æ©~k2�ÞÛWêe ôˆM�óӧŰ7ÿøWQìð(Ú¹Ìy]Ø&cý|ççA¡¿“Ö‡4�SAF½ºGÖȈ»Åì-—n�Œ81AÐg]7A¢ýà{Yæ$ÊÝÏ(òl2˜¯¦Ùe~ ©´·ÕÛÙfø[0YMŒ”rúÇo¾å#ù•'¢ÂéΛTfØALUÓòþñÅ*nù#sK+ò˜˜}çQ‰°£-¤‡=7¾áã=…­’²ŒáÀ‰2¦^íõ j*;ïù/­hü; 8ç„^-6XiM/875w‰‚¿“0ÔùÖ¥XXûdß.s;fµaÿ Ømº:6Ú·§L* Î’\÷nâv·éçRØÚì ô÷Qã·" äúé}ð õOrHá]€×V¼¢’šÊƺ•Mêñ_ñ6gŹJY&aÓ¢ŠŒyi]õp]_=OÈ<¨Á”ü=¿q~�¶Æ}™™Ý-dvM†>àøûé­µR”°Ð½*ó'`¥l›õ†C°î|'ƒ’q(Sަéò°›GÒV~™uÇtž|ýóäK±.í‰Æjç£Ú¨JŸêõâÛ, ‘üµ÷��eÅ'ƒë¦dsŠà²jÆM˲'ê·4}…43ñc*î{"À>çO:S)âoy$kSLŸƒf+Ÿ$;°·È“ÄÓ*9ÁiØ]aƒVÝnŒÉÔoG_<€�žbÁÃ=ÂX �äÌî[ó‚]¤¿0@Â0‘¿Oó½”òŒ81Ûô§n (üË´¬¦)(7‰ó $ J`܉\ŸfJñÖµuÈÀ ÚÌ­©µÖÏ} Í2�ã�¯ï6 òNÈPÞã÷êØ'ÝòŽŽ6 û<³{Àå•ZÞ§ÌØÊãi_±;4$ÓË…w2s·±†$êçíÒ ñC°¨¢œ¿9Sš#þQ{cçSg‡E¨ˆc¢î}›SR¨Þ^ê±r ¤ßue­f‹ÂÕÝ)œ^Œ(ÚÈO'¤Ìa ÛÖ\¡÷tïìi®%ËMà:³ì˜l»a/mí~p² ‰û¬ˆ-|¼Úá%?pZµ²»‡é£¬Âw•cA‰')ϨˆÖ4kjƒ[ß�¬®FIQô,MAß©çS{QíVSpÀ¡Öã(ÇaZtߪ·ƒ Aü8³A[Ã�Ø Š£ÅÖ.¡÷“>÷û•Ô¿È\V¹ ýkÏYø²Àƒw÷#1êòˆh¨&û 9ÄGméž»šÊ}2*�+i4òsOÀΰ¼ãt,ö¦ßÈ›ö"'Ì„GEæ½¼ª]‘ô ã8pF1‹Ó4= �¥§Gãè“ÄqFûÒó¥–¼’8󷘃ÈPÏ»¼3÷eƒnˆ@>‚ÈžŠ?£Ú¤ @d¸e�–òý5ûo§€È#vߟâÿê$�¾TÒ�”ÓV N Ö™w´¾F".Ë´²…”,ÛÝﷵ˿o;ÏÓ dUà;‡w…Fžä$é$_%…Pº�¿—Uf/@g›î¹#Wôjê ü©Ö²Ïœh �¬ºqH¦+8:Yä]#myŠ#— ® (Od`zÊéûzéýyV¼H³²–KPy$®Xä{ eªr<‘Îuí<_®gÄ8Ú3ú®˜WÉ(»žØÞ4àÚnݵõð[;•Ý!4žŸˆuù@ÜX2-x9eçŸ Ó0†”&/$tßË;wâEÅÛ¸gÙ!iž¼±õ·-.BÚYG-Âv³6éLŒœpëÒï–OãÛz©ZoUC…À¶|·˜yÀõ¬ôâã6¬ •�°¤#é�ÑK®Q”ÆkÒgCŸžB$ÌúrÊ•8b:r³�pŸ´ ÃbŒõJù5´Ý´q[t\œ7üGY`iá«#Ù¶TÚ4 `ã�!'µ7»DÊÍHUãÃÕÀSÊõtò„¿‡"×ã ›IÝ·½¿ 4Ò»½mÖŠ;§ø�<þ/8D¶6‰þê–·þ¸zEóXW¦^Oœ¬¥Ló ‘W,bÚÏ…!:¯¸Àеòñø*¢Qï0C‰p�b¯X`]'T�õ“œÁv¢xûg�²£¯l¼?ÿÀêªK?¾hoÚI>pa QÓ»½ð6TøP;þqŒ_aJd4s^WÐC­E�k}˜åh.W·×RRYè�Yttoš*;¿ôqœgÝ v‡WŒÐ¨Ö’ðS©G"ÇmÊ|{läËÌê©^í¹ý„šÉž [¯}»~iüâ³Ùqù™ñ|ø<.ïz|üÖ@8£¸ ÓIÏx_ôú4™ï‰¸è^1 í;úÊWÊìà>ù8ª%Â3ÝÆíÚ ½ Ì%ïë:b†aêïµázŒözü ÎËG°šZŸäXé¬ÙSÒ¼™ÔöQBâGST\È'C¨¹ÒKGãíü€½8^Ù¸žõH²ÛÄÌ­ç±âk(ÁÜÕÚç»™�d‡>HLL\Du4!­óú^rÙË€‰X}�ä?%âiÆ êÌr8½r˜òÎ(*‰ŽÖ lÝÍåûã£Ù6A�Û_C@ä üHÞËpšÉ1¥Ÿm(Ê)ê�¤ µ9¾é‰ÒjzüޙpŒŠ%ÓK‹¼�ì°s#2½¹M$ˆauèö*Vn^Á¶p¯i«ƒi}áï§™?á©Í’ç/FºøCfÌþä—NØé—°f>b ‘äüsï·Ì=ü121‚-ر³Öf`2È4‰ÅÙœ6u—踔¹èVæó˜éiMÑŸ¶~=Ê›h¯ÙÓ¼ìT—�MCý3…1ÕëÞÓI¶áò©| ô^NÐALÏèl <ÚÎî0�CÒ ‚u7‚…¹:—¹ñøZg òúæÌ$È÷ŽBtUö1½ëAvo-¯òÇ ×Ÿ©à”Z¨L²vX#�BDi-A�@»TÖ ×þt=4ÿU+*´b‚4k7vSݽÈÚOkP|xJ°C}À8üšÈð¦j;ÉA$>Æ�ŠÓÐ"#Yƒi#hº²]–”ªÈ¯ã¼Ënr\4Þ’Ç�‰|¶05]ÓòèÊLj;á¢ÔâîJª[d:§šÈ˜ØÑ&½Qµ³£“X„ÙuøG&DìD©5¬ ’ÅÜ £-„RjH΢?)©ÚvÞ­*þo.²Ž«©aa]i[‰*í}Ÿƒ}ÁÕKÓ3‹J y<[×Ï[œ<� øBõHjrmR¸ÒlZZÛ//3þ�d¿Ô¥±äÛá0ÄO�íèçGT6µc$ã0@™ÉŽÑÍbÊŸ…/ÞOÍd_÷ ‰¨Ë‡®60¹¥SåÂ"œ‡4_?5¸u´ïÛqÃ¥ |x¬ôûlVlqÂn, Æ È+¨&<ú¦ú©`èsD;J®‰Á¶GÁQ§yŸÕšä±Þ“y.$<·~<¶€~-øÜæ¶Îür\ô‘½eý ¿Ii…ÀK÷·|ˆO¶Ã:òé@3f™a£û³¨Z½\™”»ÒNW^>kÌ€ôÁð÷U+ À´a¥®©ŒZ¡sÜ€rBNQÄì|jɵp@!P…ŠQ D¾7|U‘Ñ­^H¯6‰V¦$1‡`L_(´ÖsfÜîR|Á¾$,ôõHÀÍûÖL�^(ž­¥^A;&’¯a†?HA�ç{£ȳ†lüàiª1¤ÚðºŽ_ÉN¿ÝÃb‡¹çÈSUs7�iàÝ,$Czù&sS–Œ÷‘ŸRÙ½Q;e‹j(„XňÜá[äÖ\Ða å'”1§�3Év„Ð1Úg‡Õxi 㔘C;rˆŠðÃÇ×C‘ëå9Kƒ‰2�-wokº˜ùTByµòØyYJ¿–xï[ÖØ`±_>õôÙjž¦ª˜×R?/Œ4"å]º†#Ôži¬}L÷ý4~²„úý&<Q†ÕÉÛâ$üª"èãÒÄà¥"ð5*Nø÷¨{ÎŒ^QxN¢XóÉá ^É x€01 ßì©?t€©�ƒ?0 3)†£Àù2åT1hà$8r€D™†Q1ük1£†ïh¯ÿÅ Ý]ªÍ4ÿ™¥ÜZðˆâu,ž—°„¤Ý¶OÌ䜘Cr 6>79ó�ìn"ÏDÃÊûvtOj�Mù k²B~` ”O­bŠ?j”CÛ�NÈ;§XGuowKÑr‡,HU5ïYëñ·@tþqì<Ùd�öŒS~c²ÏB”ÚÝ0ÃiðmJBçã€}—vIû™<âÍ÷)iy©©€Ëññ7;ŒõC¬Z`v�MûÏS¬,=ô4Ãß«‹p�‹M 0Y~%“kWB#ÙƒªO�¥²‡®0…¡•üƒ…éu‹0‡†©„£æRjÁAÈê.zö1�KwXÌi/¡³˜fçOsŸÐOwbò);ÐçžHž„£%7E÷ðÛ �[è÷¶&|<&´y‘†›yª*ôÒ¶(¯ Ñ[‚”oês¦š©ü?åaÈ<Ãè¸çBæG{c‚)ê«£íüN2þÆïÓ›�²Ktäþ†þ¼øZ@39»7)¤ ½¿à•V ‰d‚hZj�O%+èxÓQ Fáó”xÙ„�KÝ7žRvÿA!PGÆMþÑc {%ÄÉ%Ší5ʨ“¹]¦]’rÚŽZî2"Ü+(j$`#Õµòðn˯¶´ ?þ�‚1ûaøSG‘8Á›Fd9iDëÌ*~^ÞÏÓ(ÀתY§¤Ý²~Á¸3��¼ö�,[ÓøôHéÍYyq�ùbÉÚxе’páAµÝLj�JË{+uí&Ì>á0¤)/ [°£:"ØœÿÂ)wF¿òOÊ„Q!<­ô %áiR[SufbÔ¾ÿÕ w d¤ë¸=ªúº„¬™ë¶áF7œÂR]{Böó4GH¨È™ç]ª¹<ìç{½hv/Ó.ÿ�oÃÀ½›{Y‡¬ýaË·¥î2ÃÞ¯ÏJDú@Dë|Hð÷7éBñ\biòP($}¾yæptd!6ž‚_EQ~üú…fßIDÅ+nVXq€,‹«zTÐäT¤ûi`Xvp‹i„½.Í„á£Á£å䌦®8°†q6¦ý–¢ÉêNÀ“ûÄ`Ânú*Ønѽ›ñcåy/sW'3•�¤é¡`²MÛ±5šüÈúËò”aŽÑ éÛl¼"$¨f�íù3Ø"JšhÔ‰: \fõÆì¶(+Û¬㶪U[âK‹¼þHøG•P±1H‚!s»´ùÜ$’Œqy§³žå¹­NÏl疟ۤ׷¬öeŽæ9t„3–¡§¤Ò®_”µDt-@�o;Û'Çl²â ’3•C8wŽ#?¸ðjvÚÑSŸèóíSj×g$¥÷k¡BMêú+Æ0˜™Eä ÑC¢Ä¾;CÊsk2¸Ø�—ӹи$¡M†� Ü…èÕt�B°"æw£” ۺت®X”<�^Ío<Ý_º‚k¼×«pÁ«–X,=0SÕ–pÒ;>�Ú(±ã—ŸØ-dãÆ>.l+Q¸v>�RßrŽƒ�Ù`´£ü<@.U �šÓ´ƒkãÇl%îUpàþ—LðKÉÆ¬Ù˜ G£J—íÙ“ö³K€3"¬2Ç+�TþdhŽÅ9�ˆÔ’áÈУBÜ\[¦—RÜ1’û°ÞqMì3š ‘}Ęæ&ŸYDƒIíWüá /ÑTb)Àr—+~ÞO¾ÚÚ>õ¸ònAÌ7¤oâ±P®âÒõB(+Ý¡E^âÖb‚E£St×Läç0� â“ÏÀm�üžU©*ê9nøM¨üÔ2ºUXÚÍô¨)ó»ž7<èÔëùý XðY*”Ö©bx¹=©£ÔRÚD-<âýÔCAl›ʇx&*_Ö^¨Óêx[8•eÙ›…6œO<©½lÖ¯Qé†�ª´Qi,ÿ )~Îu9ô­GÞ•­„š^(ÿíDã{}ÄÌ×<=iÙ}Ž^Œ[÷O©mû€pEM7�©N1àßW ›CmQ·£SY9ÉlÁð·ü¸W¯È¨Näv­TáNðòº)¿�•�(?…´’”ž7îgÑ[þ�ƒáR§:œãÐ; H^)Ÿ-y<¹£Úß-! ¹jFt5QdP2ä5Þ[¨'ö±Xzÿ5OÏ„LŸ_‘ÔX+‘a™h*÷ÜŠPdÙ%»Ç%1Y33òWÍÒ„rÃz³I²Ø—HæÓßêt$q&ÚÂT Zê�h8J£@x]ýxbõHÓŽ¡¼÷SM¤X…(Lø»3 wµ2Ÿ‚'›"ÆÙz©p~~†J°‚ýÔë–a¤”ìÝ;p¹wNT˜Cà+¿ÐÄÖ¨ ~Ù.‹�ø è¸6ë}ÇõÜ•§Õ¹”.(7cT¶Ä¿E¤Ô 8ƒÉí1ÁS©®¼Ì&2½ŒãoAadÑ™÷Yq+¸÷^¼Å ¯bv¦É%W[~Œ‰Zu*vJÚ¦ºr�8ß•ÚÓÓ ŒÝú·ònÐ}’þ%ÿ7”/�̰+bt´Ù8ÆÜ¾"�Ü™¦–ŠÙ½FOØ ý!Q ¾ñ®Ü«˪÷ö0á<Í�£œ”W„& š¢vB�l>t·Ý_Å»«°¥™ªá?>ÍP ‹F†ƒ~›fUR’¿5ö„ì’( UÍ¢cwZDL³4A*/?q’=¤ã8 ïŒÈ¤µàù¹%bíkÓôȳۜdÓr颎«®Eõ 'µFaüVêæªIØ,0ä÷‚‘ø•Âìr °—ˆ¦‹ç2èâh%ÝÍDÔ¾@˜æŸ�‰?”ª£”Á1Î9ÝkuÝǘÐ/y"Òp“QÃ2 ¦~[o*‹IÜbƯ¬‹&ð)¦2½cT[5xí ÆU� Ž.}¦ä�£Ö÷¬åÙWËEjFÑA9Û<ÀÄ÷24n“Iz &1¯š VÑ¿ä{jQ©Â´Cæ˜5xÊ´´G·¼=€M®o¨szùºW¬Ç)nZê¼³¦õ|Çe£ ߢâ‡Òͳ[ûò³ü]ï}¸­=ãþìC š1c«›R|‰XSØåÚZ7/Ý.릙`�§R(Ç0Âk˜³`¨_³Ã!VXÝÐQ ðòÂHª_´mãHŒaxƒ¢ xUôÂ(‘ Mõ§gˆv†^% Å” Äž�/ÅÙqÏŒvÐ� Òï2(.{ß>Øs)È´#&ú&—p”õöG“Tøæ¯ð²B( éÂmÄRÄLw~C|,¸vLïÜÌ’~Šà…K;¬‰ç\ Ü� r¥¯ÿëah#ee–[Úrp»prÞ¬÷Qytô| ·é0#·jÊGÁæ‹£ >Ã)nM7:÷ãÏ„â0[EIR\ Ëú¶…zþmd“@“ž®A â¢_Ùª8&|Á`‡’ì¸;éÃ8»÷mM'°Ùm í©~¿\ó�ª&Öe1m³¤6XaU§~h^ÏŒ¯rW“ä(Ff ßäÚ©ÅüúœKq\[2BB˜»4ÂZß«�¨½Ì¨[3ž:JEE´»¾ô~fä$x‰•]Û„¶[+ã )4ø�Ê�dcg²´/Ô.mVLʪwÜâ%ׯ5<¹q‰�aΑfóMzšï %z À- n�ò˜(Ï3;±#�ÏQÍ6O4^ƒ�;¼öƒ½æï !vKI·ÅͦPqÒ)äÎ-[:¼O�¹lFÁ�›cºCÆ09Îß²%<\˜)Ùùƒp%LPÝtØ[ †éy¨µ§ÒŸî7÷Òe­{ƒ‹…^覩C9¤añõ ø‘è�~ÕüãÎðXPÆxuŒáN‹�NzW–b4î’Ö°²g^üÅÏs£ìá“´M™Êì�D±,îc¤m þòp¨ ÀjT?†Ye¨“7.ã+p\Ò�•û¥±à¡pbCSõUƒ�WƲcýÇ©÷Fiÿ¨ÒàÝC£×¹'O \Ø0ž¨È¯sçÞD¥×/ˆð™ì.ó±è‚�û¬äÞÿì­ >œ¼ÅàµSÁT•B�zc—Ã�Œ˜Í,�]?V·x@òu–¨ 8©ÜQ¶žš¦ÒÀpÈÕí7ãêþB÷dî6æ~Ì%4?´¡gÇMb¬ ¤™Rmyó×Þ:ÿ…Žè8¹b/ÄH-ú@ׯ¨÷V”ÁÊ+ðO&¥nZOx§3…~—=.¶§ÐØéDŽË³¦*ÂÃô¶ÈmœÒ:uýV ÒÂ…Çu¨??2³x‚mϳ�÷Å„8ÃIrbn¯·UÄ>uçvvhÙÕC¹3àÛð C]a+˹hNDÊ ¹ób°hÏk¤>@[Ï×üAëÎÕ8Ï|Éfù²2#Á8H†i�M`å²é�ôbÃ¥ãóQ|¦O²&€\Ä¡Ç\‹®ût6&ÀÅ…Ë7<$N‡ÐžfÛ~Ÿþ¬:mo°ÑÅC6 µFèãç‹G‘SoƤ^ð¦¾ôò”k€i úwVÉÈÉÌ`wž¨�Ð.©fÈ#«EƉ]OÒ«ÓEÒ¡8­ø%:î¡|‰ {¶ ¼qô­¼l,ÉÜ1À»É“R 0†Ô´ R,hBMåÈÅ(ÚplÒ“wϲyÀúÖmQ(ÑTaâÍüNÆÝIJòY�—Šz_¾x´Mÿ�e÷`@¬èN[$9�ã ïòÜeá'댯Õ0¡ÃÂ%®…^•8¶vîç§Ymgü‚©¸çX/û®EÕx�—ʺöw�UÞsÖÛxþ˜ŒŽ»•Ð7SºÄ�­x·˜ó�S ìà³{ @åÐß§>¾×úùB)·£J((IT‚ w‡MK½ª*j�F=êå»$7Ÿ±ÏíšHãЌηLü2ñã×RH%ÍdFƒ½‰M›orÛŸóA!»ùôÚ�—ÜÜï k§èßÈ>˪û`q�Ò\JPÔ�œzLÑã'±ÉÒ[uÝÓÿŸâCŸj‰A% {�n…Â’/•P ÁT�{&tO„›Xñ‡¹Ã8:HlêôCü=‹aËÙ¢]od)A]ñ�S6™~ùò;Âzxd[L+íoäiµ‚àŒ±®†.¢YpqýÏZû',›rÕf?&ñd©+:Sæ)p®­!³Ù},êH�t¾™»NMÈkÂì-î�|êYé´!•Ø�¥3³=F¾Â6ì�Z5¯«}çK=äP–j“sŒ�`%äÿÆ$¥â¿îž†‚jCî¼#8å?åkœ§Ê2]¢ä°œ¯àÞ$°_œ¯¯VunÞO4 cW«£þóBp6½"Ê(‹zÐTŒí0Üç§¼øÃ”ÊéRA^k|Û]Ríë¶ §ïB“DÅDZ՛©ÃC÷äd<œæàŒ·=]¯4ià ÁYÓ™ÛîÿqÄ)Z>Sú†õëÒ«:¸ h_判¹5=£T@®ºHS]tòÅ@èÄU/Ò/:ORCÌËÙ@™q°r‹Û«N¤?IìË;9ýâüwõ±÷�)nš�ôƒéK/àµ_ Q/EÅNé•ÿláÓü>ámSCèÀ¤fFÒÀ(¡~øÊdµ“i¦UÏ,D† •±]LìU})X ¥žîÁÒ_ ØòÙŒ4b}¨do˜2FÒ£V¹‚¬ª?llžÙZ«Þß�j”z h(nç§¶/O?²Û²0ËùŠ8Æ‹3»Òõ M2¾šùQÓ#Lm2‹ÅÚ3+ó£s*o¡‹Žò =|Ÿå‘xsàâ²TcU~n(¤ÿ= /c­Œ© Gå€<~¹¹ ZÊ_i"¾†vQ3eåô¯¤ý[éùe¦2ù¦…"P_nK—•·Šº÷WÊë" ³Dby\L76ŽÐ ¥è/ñŸäÜûÈLÉá¶Î[qe;?œÚ@Êå<¢~^É~ÌZêõã ªC¹¯¡q61|¾]��3’‚R}ǨT»Õ�# !ù<ñB\ú„iL~úñ#ÁýŠ Ë+³í¶ÝnJÚYo©s‚ëS�öc~uq7s&Ó'¾4·FCó†Ø�ÄV¿k±7A'+²ŠP\–GÏÓ18ð,ò+U`% úñ‚\´ªÌ }U!ÏÀ  bZ�t)ƒ´œÿ½b­içã†cID‘ÑÒ|ZÞdY\bôí!ò°Í(0ò†ÍI6Qï<ÅÔ�`¡óP ÏÜU ‹Á{|Ç„c‚ê˜è BÌ«ÏÉ<Œ¾BžkPkH¸Æ�$[)VIÉðû)ÓIí&èün¾¨Ué Öø˜DO­ j¢ÛÉAÈ×_q'Ž8< rVdlØ7ë€Ô�ÿ?ÞÐSoX$¿ó·gºYèh&Àzye‘ùž –×\Ȭõï—ù‚�98 ž™ -wj, —ÜêÃwðFnñm<ˆëC•8 yO Kq²ÚÔî>k°Á_ÿÄøÒ:˜Âs(¹ªV7Þ¸•½’šYˆò$®\ÿ°³Óžç&¼¼Sîõ¯�P¢);£4w†?fÊÚ’ÿaÈ…¥è×¾EÏÓc*¬Ñ•*?�X=baú7DÊÐ\t zˆVŒ¾é°æŸ*A¶R4¢D'léY &»6KÑ«÷fhý~äa8ĶÊÏâ.´¼ž—H[Up •ÚÏUê¤t7‘ò7¦À•-Ò±IsÿæH�ƒœÎ 7¾_d™™-6JºñÆO…9›±3y_YÕiß—Eµ0'+Ø^êÒ— Š6êRÚ&ú|T¹d >½Í€vÈÀg¿KpK„iÈë—Òþ#ôs©ïEŸÝêLQ6Â@Ûšúë¢.|±Ô耉‡%7ÑöM%Åd�ŸnšUÿÛX™«ˆ”® &åOa:¼}%S§­ŒNFžodR ³Ë«±³„E´N•´Ðô›«ºocÝŒ‹#úhÎ2ò2>î²èòšsP² kЭ,ÙÊs$Ðñƒç­†m!H{lœ}ÿët‰êœ˜~�¸ é|Ä”e ²ûf•Ä:ç9ÿ'SÓu*1�ä¾Ê6âoe6W’ˆ‹U6¯ê§0lä˸Š}ý}�?Õ£w4xöuò¿{¥¼"c’—Θ`�v>ºšäÏ÷3Tõ`Å‘²þBEn0-²ÄÑ@ÎÁÄ÷áö…*ÝÔw¡ÃÀ“PT˜õlŒ„kw�>¿ÞÜ_ì¹ñ@„¢ü!gY'Ú¶¨÷ñ ­*ZL“‹ßeO ‘¡ §(û“LØÐE¦3n®g€¼ÑPÅ6ÞxAˆ¶/o€3HRÇ:çÝp§Ö>L-™ÁÇëKáeß8Ÿ¼O,ä2¯ækŽ#3ZެÂË E5$¢ÞùÜëwè µ0±Z–l^«eùî º°­Ól”Yš)„E¿î†å`‰öºRÏÉk½§ÐÔaÄ#/ yÓM–=gà iDǬÄc6çµ»‰¦d*e8 g,¢å9¼†»¦•Qž5�ôQǼ1ì] “ßέ8×øh^ùTìö}ÒÔ¥®Þ�Ò&O*æsƒ˜_è·Dˆ¾Ø°ÂYj�͵X#ö¨N7< !æ[¯TC«i©ÎU«UL’d3~§ azêm¿÷¢°,Lä/Tÿ'9æµÍ$¨êø2‡¡™m#á3à)Bá š$:ÒÒ&¬ TNí¡b€EZ¤ï—Î]ù|ÙÔ¶ÑV5ïRóL¸ðõØ{ƒ™[Á6[ bY*Cг·ª‹‚QMWt$þ/» ·:‡•_îØ5R…ÌäbLª2…bDô‹å’W3ÉAsˆã%þ[–·¤û!f^(Ý6åFSºpwo1ù5õx%Q—B_6­ _{Â'þ?ª° ‰¬¾b陨 žôî¸7©C1NVzªj�l.©~_¤|M"e=âö'ÐÕÆŸSIt×\|�‹hö”»¼r �Å0©�’tjñÈ¢íX-¸O@7uðª)ÿ!ˆXtÈ;Íà…9”ÿü¥S@j‹ÜàË´ ímíÜó[Èu Ì8øþ"È�r‰6�sKþQB•Lrÿá£!4nµ�iâSSÎ5^u ë%Q9H ¥œäâ,ÆÌ}ù-�a(� 0Ã#"žWþ^JE›í]½ Èäz#!„ RžF¢ìBv¼<‘�úòèP|®Jòå™Ö£C¸EyYÌÿÃ]­žœª˜Ë¤ƒ‡«Ã¢<Ôp›¯ü/Ó›µ9.û§‘‡s$Æb”½R+÷I,Dþ17@ä ôEjµB⨺´˜~¬xÃV[y¼x3N ocýô°X6(àÏô‰@<=ü!ýÑ^s*c²…˜¾Ø`äÍäc,òž':˘³á¿ŒÑgf–íA½0p¦[Áå3R8wôñB%KßéwíÕwnË…Qâ`ØcTC|^kue-/¿ˆõ*ªé S’°Sw»Áy#Hˆ6 Æ|täu®ÝÊO”'{ #p‡¹Ó¼†[Š¥dH‰)꿼W6X6�é§¶Ÿ¯†l7Ç?/¶*ÍüÏžœk¦pÞÖ‚<®WÓài³-ò›e€ËÁä”Ȥàm¢;ý¹à�»öc‘ûT ©íÇ~4èàîëgŠ»{‹ÉKšµ©â+²K"k=¾ì—®Õã³×iky¼€²Z™4i¶µu5û t�òíÇQÛç'ždwëãòÅÎ6†>¾ØLaEÊhÇ߀S­VùÁ RB‚C{)ć«ñ :í;'Ys 1·Òݱ¹dç/UEcqÔEqëºq2݆Šddàž—åq¥¤µ.ý�hÀ²ˆ|îoY䓞‰’™me¡ÂñÓTý7-îÖ°ãü�D×öãYÝþƒ½õnà À±a.¥:ß�É…DŸÒù¨Ûn¤X¾Š¢ÑþÈ3Ì:=Ýýa*wÆ>Rmþësjî¼^T!/‹t�I�Yb;Ù8�ˆ—Àt”Våú-fa vö“½ó7س�ÎÒðèI g“|ïyy©.\•@Xq=,¹0�ýð†¾ò£�•R¦Ôg£Iý#NüùCÞl0ôW½ú{~ØjqËP7Œ wú±š×œ4Â#ýá¶Ý>ì ·-ÚÚЭ;­ð¨]I"cͳÕn¿d¶çü½ŸçÇžµw‘"GÒf˜ûh1Ü<.ïþâ^” à_êZ|8ïuyÚn–—¯ÀàQÐï�Ck<ï ,‘Â;ÕCãë•yžÆ:“¨`CøN5És]eÎç®çf@·”æ|Ϻ˜o�(p*E¾œäÐð$KÀM-Ævè¢Y¾Í˜p=ÝC2ä=ZÐe�K{¶h_?@ F¦cë�Àªÿ–;XsÏî–=.½®Ì$ΫF“áÿ¢j¹{ÊÑð[§…–õ‰é(mŠ<8åZA1:qÿÐh cVDÆ++î~ß³&Vr§Vø]Î0Y‹/¦Ã*oG~Þ�”’«S-„ÆwÒV©H3É�fI’Ÿ¼æü{"¯mÕÄ Ê´ÜSëÊÊ :-š‘ÆJ a¾Ìd²ûäGòÁ½Rå>EØÑÀÝa®Œ 0ƒÒÞÅIq‚ &ç4H‹•›t“÷ë=œ»t‚aÆ�è"¶Š•Rb‚�ÉáÂh=c ŽDéxç^�üHÁ9 I”>az±MËCgfUÏ�¹³úIØþlU6g�u0 š€<�} ™â\z<õ˜y« Œ÷2e㮡ԟ–ægÈý„·d�àv¡ +O½_µMîóíSÇ Vu»z䎲S8Í+Ê}èEãX+6çÅßfc¶œ ‰.¬É¸ø)Ç[öÙé`PÅ‘:;žqÁ8±ZñbîÊØSS?ºÕ¬Ö�ËóàÄ&W€àÔ0µ"‘K~ËßÇ�•ó¢FçÏ«/e–hŒ¯¿C„Ý¢"{kËë–›¾Ã²JÓ"a6Zmïo˜»C=iÊŒ‹}Àð„¸Åó#‘d੹ñ>â` ùßÄw5îU< Óë-KÝt`ÅQsM#�{"9¼ïGue^D3î&¯@Ã1•C5¿D_IO’Â#„ß�õ':!úø ¡)RzÉ­=YH êŸÙ¤ï›Ç—ø®T¦”ÀêäeÿIJYÅøË‘âfgPi#kÞß´m©e«ôá09õù�.ŽÙ­ýà=% 2PÉÝ(áIý[^þšî¯RQ„60ÔÊê{k¨B)&ÓV[¾,ÂOÈyW—/ñ¯ØÞb¡#F`Ï4Ȳ„ø³8ËïI3r*ž�3øyÆ Þ¾v×—æþqÉ+22qIZO2¤ÅκæššQ0WWå ºy£ÞŦc`Ÿ4d/áî{ߟóä‚*:³'hÁÔd,%‰pM£CXÝS*Þ!¼:î,À“¸˜ò�§�ãüJk>o›&)"O2®Æ¾­åŽ'ý&JäåŸÐ–ï´�Ê|#²™.`,Uw+Ð`-~»ŠËÞ.ve(¦ÿÒ,là’–³‚³ú5#ÜqÌÔ"þ›ü ~—sºùö’˜÷Ž ÌL*ÓÄŒÕ]rFžQG cLñÓê~ëj„ПÎ&æ1„W&Ø#\;<‰H„8z.»®UsrRžEú�Ìëbé9†iŽuÉJÕ¶�yù]]4’ ¥DŽB¡,W@ oËdfp¦¨ìÎm_Ô¯!4ÖÍÊe_ðÔok!9 IwÙhÊÏv"Š3�ÝÏ>e%Ï£|Ý(Ÿ‰N½aØ‚ü«“|úµ#/jüLãr�lí.ãÈEöô;s�¶L{u]¹×¶¦Ûc˜y£WWb³Žâ:1R¦þ9Á€J!T4z¸|ÎW·Ÿâ¤ÏÏ÷ÛÅxYhÜÉZ6Ïšt¹Ò+ú#9½ÖFöI1?0'v«sî»C³j4µà­QJ³‚­f1ówßMPÅžÆÖvg�A¿†�€‹4ÑU5H˜ÍÁ I·­Ím GoÜ-ÔýÆÄÃ7¦þÇ9#]’/ô�‚ãùà-ÐÝUJâ=ÓŽ4*¬¬A �Æè¡Ý•ìi\Ü®ñÄÁ\½àã[e.Hõ-Ä�$ù}YB4Éæù—ÛÐÔ·DŸ»Ì\ÏãcH�‹SÒ²+î=³Òf�JE~D‚[”ª"rœYÓÔ˜ e¯bÕo~ 5‘€ßò‡A^û’Xuúxˆ§Û ¦«v^s\ü–¿¾‡Ýù”j)*¿›ÝÆ\êj[x$W�¨½=Ä¿µ ™Ô[øj)=l6gŸMŒÚ. IÃ7x¨tÓôcp‹ R‰ôÑI¿ Pæßíq\Ð)£°‰=v•2xœù¹&¼K¼ç7æ}]„6A¶ˆebáGóò˜Éþ‰ÍòäTóOCã¿ÎûlA©Ûø¡<Ú¿ÝÜq·¦®}Tí0NÜÑñº¶¢÷žTÖ û¶6X·½ñ8)Wjçí¾ QÓSxk©öjVˆ·Ûí;&Imn{vf¯¹œqn® Hç…0S´ x„›ßºKÑvÍ£KË­Êá�Šl’UË‹#»f²•³Ù>)J2 º’¸‹ÜjŒÑ•¶HU‹9eljƒ†ªh3N¼!u' ³K*¥³ŸBÏ¿û“W1ô‚¿qÕ®ŽÕî ùv0Õ+ˆ�Ga›ñʃbÌÖýÒ™àûÈöÝŠmj3k ÄÑY{ׇD)Ò‚¼CÉwÖáo¬š�Â�´²k˜ËŒ;Hüƒ¿Ö<Ë)*¹üoߪTµøñ_%G×Ì&Ç-ae%cè” ŒP÷@³,Ò,FWˆ«¤îXÔ+ÆÿuÿŸØÝ ½a1¤¶§½öw‚ÄÎàöˆ(g’-:Ï`Ÿ "d¬6ÄSõV¾Þ?Ëé(+m#,6ŠZ‚Ï £�U;cL©�´±©66�˜šS2CSÕ3„…  ÂÁ�½y×B#Ö§7•R¶ˆ�…Zdª`>{ ‰×>óOMc{fqÑ»™F«JÈõ’‘‚¿_DŠäCˤäÏu£3³“Ìõ诂W'˜ðU¥®ñ·eFL.órÇùª �hîSÛR70§nž\¦• ï8˜Oäî€�ŽzA÷AÖù6F¹¸•›@çi’n«Î>“‰T¢¢r%®Ïëݓؕ±âèƒ}Rp9¶ì;_Sp ˜‹¼(‹Ò ú"‹˜dŽý4 ­cÐ)Mo=ý� DaøT7rnÞÞ†ïÇÄmÊ0‡ŽRØü¾u6“¤{ïS�: ôÓÓof†eB…cçðNž¾}þºgÔä›(wªýòiঃ C¹q®9sÿÏ­·¶ùmP”µ£3S`ÎÉ<Ëá8yh6Ms–Е«öyµ`¥îE¿®ºÆÕ†”¾0î)÷jn™Ð´Ò»h†°]8¨ šÅAžÀ nŒ�‰èhy?æÛ¥äL ß¼¿#0m´(—QÀ49²�÷TÓâé Ÿ®kË (͘��@4 w°øwráY­Âô|օȤî:Õïbï8Ø* ý²°i\Pý=útáßö7µÏ:{÷C68‚â Ã$n¨’�æ •^<Ú�þö0alóÞ;°e†iºJÏ~^¼únbß·µÖjâSé�–|I+†¾8ÖWdîd‚LÝ fÜÅǤøÛ›»ÅqK$§¿‹½iP‡w�ÞU¦ÁÃk,—hæ÷� y•J2§~êŽmì\A-^Åu'ÔEô©ì”£i‰‰HüÐ8¶’#¥£n«UøÌv~^<*äªoÙœÿ¶ñ¤�Ù–èrj­=.‹íQ|ÓÄ\EÅÛæÉ¡os¤ááiÆ&Í]Ý“¦òËÙÏ3KÂQþHõ# ¬Rµ!�¸”¡YÝ‘ƒ#ãc™» ”Ô{û¯Cù +£ºÓmß1=L�?˜vë�¹ö9Õž 8K5¬¶Õ‹Âî™ÿ07‘�>’¿ˆ›á;—„q³& ×uLN…†7¼’öšÜæXˆªp.íøîÍ<žŒÉ§N 8-¡QSyý¸¬ƒt¨÷7„ô/ÁP~ÔùÜo"zF‰b�*n$O:UËÆÈã[˜ø+¼8¡|heçåž–Þn·Úÿþ­ Q{õ2P0óÃᶪ]ìz»Jt,[ÍXJgY�‰´SS6 ­ÃåÔjóVO÷ÉµÑ ÝéÇsß]Wë�µ:Έ~û�9QÂ7«J´,Í3ßúµu<»ÎtÓ7!7¾V ?×ÇÏk·¨•k¾te†“Ü‚üÂ$¹Ì—çfEÁ阧50ÊÚõ¿Q œ!©Ñ›ÿˆšK{ùŽI Û–qx,<>“NŒÆ¿g!LîÓ‚dh.¿‹P5‰Ü øRwÇ•|bENð—ÒÕíê1ÎÄðÜÈ$³þÃ{Ø™0F1¢Ñ+&rÁR‚ ÎBн“Y²øÁéýޝz¿ÊB TÙiÕ®ï™HŽÀØÔì¢Àö¢Ñ3nZ,%@‰ƒ¬3â:õä p`‘z@ñ‘«åµ€Y?ŒM‘¶Tô°”ã„O[6¢ÝŸÄ ʆl{³Úá—D_¹Ù…üÈë¼Å¸)¿‚ÄL²“»L~©ß2 ›Îª“\¸uA té6ZçWìNf¤¥‹ÀQ‚‘—“ìˆÅ:O»>­ä@?ü^}¨lЩùäÉà�ôɳ—Ž ²hp�ÓË€Ôv>ÝvP-èI�ù¼EP+–­dt›�s›P“mq ÿ+à¬;H�—…%ÇãM,¨W³LÚMƒ©´¡ñÓ-)­Ë˜ qê`÷K™srØßÓ °”Ž@±�¬k$õ$8[“4ï¼åë˜?f×éz� ¥™ ÎÙpþÎ$ûÞU·åøCb%˜6_²½½šÅ¶rÖ«ùäóT¶~púÇ(©¦¿Ž¼´ ™Ê|˜üÝ8àVf|hëÞ§š+nÑyY*o¤´Qs$çX9½�I(xØE*݉bA”X�¿¥z’û ÏOà£Ï»‡l�ŽfÄrò&õмm@æ ûl÷CîeÄòW{r*&õ‡Çê*ª¨Ð:4Âm´ ,×€$<Ú¸pˆ·lZÐvFñ§h«lÿ<¾a(¨I7|×-ÀЇ{ž„æ�ªb·39‡2ÃÚ1Ì®©ÄhÈ4/émk˜“îŒî³}·ö‡ÚµLpÑvº¼ ¹LÚ¥Òß'u D5Cû N¼3Ï3Y#’ü2p©o£âÍ�·úêîí4/.ŒSBÜ “o’‚Ç×[ª%¨ó6¿.1sª}²»¦/Õß¼ó°ßÛ=‹Ñššl FK1ÎIóÇe)¢ˆ÷Iоë¡4¯u»_j5vo¬‡bW'zA'>;òïÞ*�Ú=Ú¡lafA /«y¾¥q¥�Yžµ³•)wÊM¹‹$Q|Î_Ö­JHËá%Õg&å;a¨ *-y«„š!Ïì7#‰¹ÞBk©À; ¨AÞ×@@6D ª(|t:ûŸà'âð"˜ø]ÿ’ž]»Žñb;#è8zèâ›à >…™ïx ñÇ8hlÑýRé_(?ìÏVvÙ@É�æµä�&–ÂÝ.9køöÃÚÍ¥"‰6ZÕ±¦o=�¼×ÖÊßo¥+”žY�±x«®v”¨-H«^2x_^EÝiÂn«ðW~Ý]òcŸíPW¯yÁbÃgýðqü@MË^o»¡‚\¾ËçKèâr'ͤú‰(D©ˆˆýQäÓÐx��é­ÅîQ½yΗÁ긟c|W.Ëíu¯wif"Æ(>™Y½ò ½#Â]Ñkðf4.Ž>¿ðù…3ŒÜî® ú’ÅùFVâaÔ¢!µ}#�$ðËÉc€Ä?eß<02½w۹ó‹&{^IÓ]ÆÒ~#Á•2šcéÎÉXùÂÛVl`§ZàÿÂ[Hÿhõðp�ì¥ç(q›“{‡3l.v ¸5Òý|ü'~Ñކ۲ȣ -Kë ƒâú–hâ¨xH]�^�ëâö ,hRXÙòÌÑ\ÏNmO4D|Fº¨ˆFÀž¸?i3¡‡±Mùi�˜L'Çñ�Ú¤ÝÓ(,s "·Ú †?NÕ™“z;6%î"è7ÒLL� ¬–ràðA‘¼`ÉTiÇñ˜ÒYв—º÷õÅ]€%QŒõü ;Œ®@˜åœÆ8Tï¨:¨º!iÕ£ˆ2�òx´S¹ NCÜ8ÁÊ7o§®œ÷ ˆÙḴ¢–œî�ýz+oÊ2Å—”/&}sÆÿ¢�Y'�9ë˘³æn¬fšÌD™÷;gšÄ‰ÝŠË0ÊÊñxS†s¤C¿´"‡oÓ?;vj‰L«Ó»ÈÚ›y ;”Âͤ<ð¾[?ƸøŽÃ:5 püŒ„ 2ƒ÷°°Û:{“ò”"ß$U】*ßomìÈ %0x±xè¼�$/m%áÄÛZõÀ, ‹šåhš6R–·Ä™DëÓKŽ Ë²J» ÚÞ§ŽäIËä`çØHE}erÄ:›“} Ðw tÃt�›HÕS·«�µ`%C~ù`Üí�`äRkadÇ\@”pqÄ›5ÿ´O�ClG›FÞú™-ÛÏ:äT.X»n½uè€MBÃyÁ:cú7üsë³—ý:Ýè.ÜJúô"„‘C]–kÞùÁYêvìÀjZC†hëéæÒPüR;[àã;D‚±<ùƒž["œ¬ÙëË¿Ú;6ÚÚ‘úõ·}úýÎÛŽ©yò²hjPD>ɬÔKÄÅœ¾�’|<ÝÁoÔZ*�ÈŸ ¬ð³"ŒPüV iK3ΘÈvÜ6ù]3uôMËÊ[ Ïf¤nCÿWN73¯·ê(4ämÈÇЪBÚÿ?¼EM5‚mí•?D'š�²Õ€®{ö1úK(ð7; Ç9œœ¤q£ÿÓÑ™ÂG!RIä¶È§þS“>0<}=S”$Y®:žf•˜ÖDT‡9PÁdð=Õ$Ñgö 6ëB=®‡_ÑÃã,ÛïÇÜ9©”nÃ"LøN޶1qÕå»™S¢Þ½Ý„î�æ·Ë\ TpDœz¨¡# ·š2j 6ÿkj,~i n.Ϋ¿o{Zñ©•Œ Ùœ3ÍYy¼>7jâ­Õü€\Á'£çÊŽä.å`,õ¬Ð´n5åFVˆò&?µ™´â?´ÜžÀäpǬÂÀ�m.XxB³d^¾Å±¢z„˺ò8f�õ¼m:jqZf¿9þ]\?ʈ±Ó{ΆÎti|¤–)àÆ!,Ð=SÒáŠ24 seÏ2I*©Í¹–ña8³º|™–´¶_ÏÜÓedì…¨Ô�¯Éx%ëºñܸ ±…{UK•cå/Úq¿jŒ€8­µðÆv¯†HÙg2)Ão5¦°ÐýºŒ˜¾éA—nƒœ‡›µ0ù¼“᯦ˆòê#Ð��ù*)Œ½z¡yî>¡‚_³úŽìIÎ>_Ó¤ô<äÍAnÇÜfØ%î7<ÄS³Ž8º@|j Ö“ÓðîOƒb²�“Euˆ>]\]¸D{|vŸ,y²,¿‹Ø�˜>Ód?„÷ó¢WíM¸Ñ=)yÃî¹¾Yærãä´<Îê+m•>%­û“γ~W�aK¸~¹wàdM^G'æ½OÑ|€*‡– .Š.ؼÿ�ÑRÓf.Ìæ¶§š¡ËÞ0c¡iŽVPŸ‰À/ÿFå —b4’iÓÒ˜*˜$A<Üe °¸z ,nÌ�1X…9tqZ½$)íÂ6ŽöÚ¾,÷ÕèË\â@‚k õŠeÞß*“ΈÔý!Àaz‘¿x•F>Ó�Èeøí0W¡wx³@ø°%¸,Vê_‚³^öh?• ~hð7šÒöÚgþÛ¯§. lEø¡�mz—ö4o¨ôèÐ;­d*c'´ÓhSæ»Ò+\Ž„á‚mÖGùæõsP}Ÿ*Eˆ\ûâýÈ$ 6›ð"ÍËÒN.¯˜XkמOÖýz6òhD�~Uú9Óí‚��kA·Þ:^ƒÎÂM,w,uC¾‘’·ót>³«ˆÄô¿ ¾]ÿžè¿áz1Ø[3Z:"SôLQ»€\Ô ïúÇez33{§ñ°ùK¤ÅŸØ�7„Ã�/Ä<õ›5E‚­Óv›Köj¼ø¾¯+^�Ø(™eö%•t¶%ã7+Ï=¾ÿ¼ ±‚If—¾”J‡ÈâÖ@1°úEorø§"î6—Ë&,Ë”y(àˆ˜­És‰‚s÷ïßžµ€ U³b„@øëûj¤xžåãË£Qp¹£½�£éÚÞö ]±:Ö¥mߤì(LcðÏñõ£é!ÞeÁ³ªt<våiëÉ› À}ÚLaÖdP·ymÀΉDò¼±‡ksRKõeOÔ�O-+y­Âm99+ë·ÎÊÜøl·ëœàÔ[׺Å|D‚ê "¯°¬@ÊjÏ*=püù@~‘5/Oöò's«=9qÑó\bJ®PƒªY‡íäÔA÷Z;Q$híPS� †¹¦`²ðFly3¥þhÔ¾¢#º‚3=UÓ5š9vóV�tô5<Ø1‰Ø†ÏLFTÊ"é�TðPŽÛª%Å âàÑC.ubfóôúL{ìj¨h6¯ìbÜø¢ç^*úU‚T¹ ¶Þ^õìDs ‹¶¿m�XÞú›•ÃÙr\0ê ²4 Q€xŽœœ–²âZèo!DbV¶xéS�C)]B爢†2ó©”Ãìï¿•U�©À€þ¹ì÷y¶� [EHmê­98eÃ:ÎáƒÛûö¿†hœS%"ëd&ý\Ý@Ý7˜Øê &oïfnû¹ù #'åØWâÂL¬4a€'ÙÇÝ“S¿R‡&óIÛY^X"û(g¸ÜN=-kKô…ø9lû—_~(õ�¬h�¯…g5;çu\Pè¨ß€è3%e¨tØ×ð…•Ý”¾²TN|fgñ·Z:ûçÜí0‘°Å�”Ì8Il|yyœ/\Úa*T2¸`êæ`½’Ô0ÿ3ËHå>Lÿ_-T(†?O ÊÊu.XoX*1o­Y}g0”*T¡òå�¨7K°Ÿº c,S›|ZUL¸P£\GÆ”eÇ�ŒM�, à»¶¤G#T°Ü‡ý¡~0†3þ®ƒ‹~œÐ­_Ä�ršÔX’´úÊ„Z«ú�µ¿XSÖ1<ûJüþz> #vø6`œÉÔ¤ùS õÚÍ9ÙóVÉ <ç(�uę޶�»‰öa¾¤óÂM·b‡Ê…5Pçç!a¡¾`ô ° ¾8²ˆ¿ð-µ¯HãzRÓ9!�!‰�"™]]Z+ÓãŠLKÖNÞïõ!Œë3ö£3ŒìêFB!‡¥ÉÛ#(Ö•ÎQ«ï˜QùëÎ;K®´Ô\ê3”’^‡;Gj3Þ[°”µÏ‹Á埴üû(^ -þ¥Ê8ƒ³.ÂJ Yl“ ÅæŠnª¤Š—˜›ù²;§¬ɰù%Ç×FÉ̱¹›‚tX,‘IÉQÓ’œA¯¼qè;|ÖÓ¤LùÛn÷ÏgÁ=GÎ!ê¯JyMÖ>ßTzýü€íeÊuŸë°å#�ìœPQ:Àýã§*/é[õ½ÈµÍÃhõÃ0�µVl£wfð�€‚‚¶Ó寧°Ã:º…Cm©­$Î�êt—E±!²ÿ©||lþ´ŸnÆÇ|@EïôÚ7Û ªHþ=«Â¨ªL`5Âci,žT¥õJ³1 T ωm¢ºJ$P&8SiÙü½ößæ�`–*Üù©íêˆÛOH˜õfšG‘âwSxWH¦Í{2”dÐM"ZuõÎô®¸ukƒ@×ÖK¿ò6p¼éA©‡Ý´)v“4 ´jå°†¾áý‹m6ªùß’†„÷Qpþ{»°ü+PÊ€j) À[ÕñQª!¹lû·O±¹5Ø«²YÙ—Ë“A4Å\7ãÑÝí@ÏíÎfQk­Û³\>AU¶3†ÖÔgßà…æÈœÉ zk;´Ž°ò SÛÚ®G hÉQ‹6°÷ײ> F6žÞ¨¬¶€ÄöÍà·Š›Wé¡ $j4`Lñ™Ìž931­Qa¿/·"ç9ìkŠªHÇ‚Ù&=•�}}sŒÃïA–«£1-¢p+lõn‡'K}§�¡g˜ÖÖ*}‘ÐĽXc¨ˆtáÌ^P"Å[ƒK�öï%oï7¶{µ¬%P B¼€ôÄ9È 1ãŽtJyãÃ1.’¾BW¸ÜfŠÇ¢o±ô+øe°ir_4 .î"‘HBÔKF™öÒ15VU¨  ×2ìœßïc(ÉýŠ€—hÍlM€§Y�É)måLJWÐr›’_e�ùáÈÅÉxýý܈Q Þ€—‘9­ ®ª¹º:@Kʰ YÃǬÃáQdø%QÈh•Œ(6¡‚oŸ[FGù¾‰–ð#ÄÖ�‰7¡£ó¶Ù¹š¡E{`€m1vJ“k‡n‹:]ÂbÿÉhß,œÉ2kN¡­È5;߃Kù&eð~šszߦ±ãÊ)«Ë¼¬ÿS,ƒï½ôù³ìNi|RÛîÏZ%,y‰¥Ì|*�‹ÁY=ì˜cØX�¥¢M0îÀ k­8`6~¦;R636$“EvÕž®m½Fù÷þ+©½ˆ5nfûêÝG ¢<”M5ìži‚ Õ�àÜoñÐdI·c�œ®`½Ý�éÝ›²?¦]ÆH*dÿä¼±ˆGfñ 9ðÆêNI»1ùª!8FšTëMëOöHµ ð3B措Ãê0¿QriyQ· ^Ñ´Ô—Ã�¼õ¥ØÚ@ºXöx~Û4ã.×¾ZœÀ¨�:°ºo‹ïíKèäßãpš:@éÝxÚÔk׫UשI³µTs�¬U–8ãf:cššõXVºëÆüaHô½úQ†8ÿê°kjʵö2è+™]±Úÿã� újLÇ|Ä›Ú%ìø”XeA@lk];‹šë�Ú±¨mî‡MTLIsõñ Ò À"ëNŸöx¤l%Úù�� Cl¤�Ò2}ðËð?–±!ä‹‘½—±_³ÜßÍô¹Æ5 †òD?–zŽ, …9ISDHû{'ÕG¶`®÷�:_®¢·F4�Q ŒKh|§›¾5¾à£-zå©Éy,dõ̓ʦ§'Ëþ!¶ÕÏPppÆ_-Ï`j fëéÅ[�Á‰dŒž2kLØ 2¶O“µœ}§|�¦h‹Qæm ÎZ§ HpÒìfÉÐ � ORµ0;XÉXx%‘¨Ò/(–¦¶âá8D1ôñJ„Ï'ÿ/ÊkÇ "(‰ï¥S�qY-üZÞÙ,Œî Žk7×�S–ä7HXhr oŠ€�w¦ÙœùIK8`Hláì[¶ëLƈõYd·°Aŧ‚ÍŽNm„0%SÎËß ›Ts+S Ì�_ù9.2Ìý|E@óªç%Ò Õ$„hh&SÐ+ŽŽ>/B‡ÝÅñ ²�F`ö}%Å>}cJñ§ 92oýk›«&Òl¨æk�‹åk†µÁýØÛΧ¿j¹ÎÃSàeõn¬ˆ¨¢Ça&«ÁŸçm˜®ÃýQÊ.òÀõ¤Ëäø¾w˜ðs ˆM¢Ü•Xí0ZŠ–Åò5m7k2]êx n‹zùYú™&¾�Áåj™ãxì‰Zyï,Û�²ÛciÙè1Θ,’Ð…œÏ˜Cº˜Ã•ÜâÙfF¦á÷‚Ÿûñç4ÄMzŸŒ®üŸ†¼³‡£�ãc°ìJ¬æ›ÝYgp•IM=VÇ×™?pœT³Ò6:Lþ�šsV¸,ÀŒ¼cš§Öçªþ“½Ö†*ì¿@’MV[v‘£&4ïÛSÙ¨ì‹OpÞ“E¯†éŸži)uBYþåŽ2Ñ€ãªÄ¶ä¥b9¨ý©°Ê¾P_C4CM\ÊÓîE�Õ2¬Ž•y˜idˆ�@#æ´nbI·Qh¾Ô,“|ñÅÊób�°8˜„3ß#Wÿä(õ._ˆ\œÿìaz�ë¼rðÚ™!âéÕ.½–çk†¢äðL…Ы¨E�©rú‹w8‹óÃ6.@`¸8CÛ]î�y²�FŒöHÖ°ÏZŽ0¬Ýöv7}êkÛC˜>ŠmóRü¨´gY»ÏÒãðĶ6Òýb´ãßO°A5ÃÛ¾ä¿áO¦åÞ¯,™ŸîéÞPúIØZI5¯°Káq‡�ÄÊå&ð_ØìáóÀ0Ü¥UX¡˜õ�ˆd�Y§sÕ­y›¥œ èQÖ´ °–Uóå˜ uc%>º*A÷DpÁ‡¶“4SÉýá5Šš+ȸž;t€k¹†xô&$)y’Z¾‰éÕ²ë2e>Œ;%ݯ9eÛœÆWn .h‡¬ýOì�8² gÆWnd“+FGáñ‹ûÀs!‡€*ª[ƒ½fˆíbû87åçü‰°Â×­ºÒŽ-�èý\ºÐ¶r†–áOÜÝI^6:ïðŠA @ÚÔIá¹zÊ¡¢¿¢Û#“À ½êúêáñsWX²nX_STG£¨ˆÚ¸'óX¥ÿ@Åß �]l��Ë]â ‘“&&‚Úƒ7íçºl·‹¦`ˆ\îg¬»4¿“?§,·Uü.¶‰Oêé>[eÉ9J­6$Ø‹´hÚ~o­tâµ5 \Ó6¸Ç*ܹáä�ÂÅ ¸Zü€ÁÛÑqyÚAˆ ‹“Æè²¥ytÛ ¼jìuüišù|pj[ý,œ‚„*Ú£Ò%烴WNƒöø3ö~ÚB{Mî©pÑKÉ_\H ÆÖ°x›ÜǸ6÷råµ[#é<™yà²M=SZ¨n(ãxÂZž™ý¹ uîŒ9»Ó'�C%’0"/àˆu§Q1*-Ѭ|ÍÇ‚ÏP´÷U7lKliÿþã(˜êÔCÜ¾å´ ŸõIµƒPüÂ+ÿµ;K–t�§9-¥¸‡> 5û„ÀnÃ&êæùû5¤b7d-*IQ-õqs‹‚Ý—=·2�‰Ñß—ÙîÔ«$ÞNྼÿ! òÒ Ø9ˆ1I0M0a¼¥m닎wÍ ½xãÚ÷±™ƒ"8HY¡º}¶aÕ°Ö½ß ß ËÜdjIÆÏjêLû#uÌñ[ƒ¡ïÏX»–MÅ<ål˜©ð¡;E½1|Y¦€`É –Ò˜Ú!°$ì–‚LF¸£ÌÁÜc<ºx⤠•P5ù‡ IG¼kä]ÒhAŒ š,ÊÝ œÛæ²Ú5õ\®4}YÕžº?·T4ÂÖ>sƒjLBh§‰ËFÓ&`¢ÑÕÓ¡ÝgÏS¶é?Ø>,ÿ•ÖÏ+Ø‹Q?ˆ×¬æ@S"L!Ü·{ºÕP~nD�»ñjç0˜bf†`[À ð*\hVûß� –¸dÎç“鑽WôÚ{»9`%,Íë ÖåøZ»¿gÍs4ü¶3¸ä{§¶f¤;Ê+J]Ô�"…¡pý›×šjÕb8ý%ø¡V€÷_ëµm`uspº6 ²w†Ý�ÖXg ÝYYxçcìK¢–ãô–H …9ò£½»°½­42¼^ýÓ­´N#ÅÎO0s^Rnökïy^|"@éÖÄ®e¬“`;v I—öéõÑI jŽÏQU†¿4ÜNsèrÔm�[ $3Ÿš Ï78„ØÒ,…`_ÃÈÕ•\Bìós†˜Ô‘æY¨“ ±ǶØmW¬ûeÑ óœx*†¿I&Â9) Ë œYV„ØÍXUËO[Ò™ ¿Ö�\X,)Î#lÿÖtôG?[P>XK)5»…ƒ¿Žœ6�?$Äë…ßšµÇ5ÆŠløDx“�Ž'î¬*Ñ��ÒÑ®Â7ÕË5ktÀ0Ð�õƒ‰ÿÂz×/kÒû^‰+@¼³[M´ÇQé-4MéÕâ€`vÅú‰-Fí³DÂqœµì¢Ò Zµc;l«Öq4Ýк‘Xð5=ºAÌþU!ðªmìðÿئn·|$ÀQG?ëÌžxî‘;K…Si6íÓ¾* ­K1C_˜_ŽÑ¿ú$]ÞJiÖ;OèÂI2ˆ¢×ø«ÒÀåÙÓM¼* È \äðÁ$á)YQÌ•x)JÿR>$ ™XMIJº�¿ ÑÞO™pDŽ�™8íéxGwƒ.µ�¥ð�´ žŠJI„ªa7ÃeÅaQÊ…v_T¶Ð÷Zh=®Ì­÷› á~As5HT‡.-ÏZj¬F§ì¦›ç½ýšÑ^ÿtʘAÙÜ; )Å �nîäÔ† ùˆƒéΕÓb§: ùŠ#>¼ÓóÎ|0–RMDBøÐÝMÅjóXÀ¦ùúLÜ#�®ªøMÛWPEAÐé$ãõ( ²HBÖ`rRtÓŠD¦ÃyÒ7¯4Ja|‹O0×&ø¦[G•ç{Õ¢ÉÛ 5:J½ÜPc¦ÏL낉Ëñ„6©’“O• Ú§ÀB[ñ VSI{Žoc½K¸ÍAÄ|Â¥ÖgÑÅfzÙ"±EÛÙßõvƼÕ|ä&è¨$qákâúÝ`à ÜøAcn¯¼dh4•‡ÃþÚŠuOÚ!þÔïJýç ìƒ,~äù„ù=ç»Á�‰ˆ)ô±…O¬ˆ<3Bâ…âSû{óÙTpè¹.߇ÁÁ·[’! �,oo@Æåü z’r}u&ñ÷Òynùº–i9¸Ï) ñ8¹w%®‚ _¤‡çEô)òW§„ʲý,;ÐÚ¯WzV¤â’‚E'\Ÿ!²Œå�1«@iOž]¥ªg ëÚdIàý Sï!‚f ó]<�KOî¥È©ì³ë\s4«±ðî]{üZ>œ¹oµˆ‹�ÖRäâ>øhÞ0?no'KÕÜ c¬Ê°EØ ‹•kso¡‰MæÕñ–Ó'wNQ€Çýª¾fE7Ø®Óäµ¹Õo�8E½tô94ålh Ä)qðyÈéA¤ß2’¨]•ÇÓ0àž_%‚lêQ—™ªˆw”Ë,ô#Aµ‹²f:,™ïï¾¢j÷ZýW=éb8Ïn—ËF„+ *ªüÓÁØE;…àXˆY{ \`N —¨Xß`ö–¾u€òÞrˆ“ pÿ¾I�I*wyé°<ãà„œ\²Ëwç‹@žÍ3Õ’@ì¸e62ð@ø¨?|€÷~ Å?×f›[²s¡X$9—ÍÁb{{`xò-ä£YYÀ ¥Ð�G ¸`"rE5åÈrP\+ÒÉîdÝ �Ûߣô¾Ò½Ï|ÒMSH6ÿ Ü�RøN5Mœ²€_2äÔ>AK\(QG÷$æ¤ü†?Ø}¯îpÐŽ’M>�� lSwg!GÅ!åD2˜žç“Ítü0ˆ© r,1(˜¸.Š�¨ŸGVVŽ¡,¶Çvôõ¶‘o<²’,úíéÞëO¢3ÒL%ÞœTaÍĤ”bü°œW…’ØSÇGzxUGßÿíÙmQûŒ®à„ —ƒt¢.·œª%'ôö”~:)7'©Od¼ž½fÓÓ±:ä¹5ó!º˜asÒœ‰`—ð)l býÁ²S”— ¡Ù~�z^útÈ*Ê„ÃöœÇ¼%™Ÿv)ƒô,¿}<Ë&öÍ·e;FúZÍ8WúÍ­½Éø=•® *$?1ÏT°+°ˆJqò¦e”ñ“ýòÿÖŽmî\fLC \¯DòŒ[ ’w Έ®¤·tŒÇ¥„5«{¶î† €»aÈ*ðü « ÚØÎÍ‘9ùTúZ¤à£Õ±´»ÏHg�ÃGŒÛ–ÇÛÇ~>9Q Ÿ¨$ˆÆu;D‹…åqƒbù¢à¯m€©×Ÿè­¨Q ƒØ³°C qëö’ÃoÄ£›3þôÁö]IfÑ`ÑþAµôE7*§þ½¸xÆÆ[+¡tùÕv;öøÅØ$Æü´Kµ] ËN$ŠOiXéCBä ®*&äQ5ßÉž'l…\}ÃzXš+�ð|ÊÌÞOY#ð~Ì%KHIJcÓÇG®HA^ 2rWÃ�û?re ¦ÖŇ*kjʯ®À™I,Š)Ž÷ ÑDÓGMûh—¾éǤ¾rjæH9Ï?Ø1TLN °²þ ›é6ì¯8?ÚÉ-A+Ô`"/uê™á¾u˜ÏŒ”±8¦úN”† ›õ“ÂuqKªeéäªr¤<éfÛP`讆¯ê~Â÷–×éÑÜån¦;âcY#±Ö ðˆ‹ Ø·màÊŒ% ,<éû[ÝßR†A_‹Èêq?$êöÄ Öp uØþrª=ºýoFy¸Ǜł3Dâ½��¯òjì}7’ïûÂÇnã£èÀLâ;’#Pò�qw¤‚Œ£¡F�‹ýÛ]~Z8uÏ@ÇJö¢±;‹ßîÒ˜¡óOñ÷S*´á-ßyŽ‘-KeªŠÉ‘/ÞÛÓóBóÌ5ÄÛîënL©Ü5•U€ÆßL*¬‘Ñé÷»Î=�öœùgœúÆ÷ðlñ¯QŒìgœÊf&AÌ¢3ÛиŸ_Øèt`¶‡ÁFEä·ýî6ÄX˜N;èUŒ+; ᨬî4Ó N¿(¥Ää°©ôD?�Ý~Ö©rׯ;ÐÞ²‘9±¾ µ’£Ã“C%In2ßn±êsöôÁ� –Žæ:Ob›†*ý§ósH³æY™ ï¡‹ DIx”­}Ÿj§É´[ky¯zã£]+@ÚåVš¿Z¾Å9Zb§ú~®Åà·8UÄw«æZ�D$îw;í¥ �!+HÐyà’æ >àáí׬ZVc’ݹ’¤2CàÉEÊf»L/ýR¾Ÿõ¨€±U3ÁÑ;/æ%K®$¦¥WåÛÆí/ŠÃ@£ 'ƒ€K˜¢ƒ¥*ð×§­Q—Ôq:d¼‡™îY¥ˆßÛâ'£ ¡È~…ìÏì<ßhöc^³Ø2m]©#4çá:ý˜Ú]¹|² š^g¿i·Rr™÷x®+}T£ÿ•1ÿ"„¾²æ+Ó,Ñø9ŠÅ9‰&ÚÑÒ™Og_;yVÃJV)µw¼ZwÜ}�6é¢ÉAÚ‡U½b dR÷ÝŒ{°ž—;$Bñ]¢uYf)!]ˆô·X'd”²ö“øÑÕŽÐZH&l ehL†ò9:¬„�ç5ù õj*˜�¿FºÓQ]bå�[‰ƒ,8ÿ¦+Ú5öO…œQì“ó�ª&Ða{ª?Ä3«Ïzúº­'º�íaË—m«¬V}ï|ùdkKRøå„y#WdlG:ývÞk‡"û•£\Gš*…ç™ædr¡fEm‡‚$€Ó pg5±æ®�WÄl€]ÁèÁÛDø|f#®è(¶T<öÌ‚º oD¡æQhqÏñFlq�k´Ä,ɱ®CÞ4ºëeöi½zü³§&\ãç‘%~FÅÞÏ·]0LŒèwcîÚynãQc¢*/ÜK1Z 3Eà��6R¾–*vìjðl)Ž7³µl¡ÁtÐ-ˆ„zjÈÓ›º ª›™W ’Ž ¦�ÿáÀ±û Ù¹RWg>Ü,{©�Ì:­å^(g¼Ïµt\¥ó�\(êg*ú¼[»Œº]|4ø-Ë:ã m ÙI�ï…xÓ+uåX{]¢"›‰–ÿo_ÊB™»„²r²’Mलü±‡ ]r-'�<¼îˆ^$ -“Á�-õpNBS}¨–eðš=8A�º½^´*¨õÄC½a³þ*üA sâÉ %>G‡� ¥¯H þþâ šG?ë”¨ÊæýMÆ6Ü’m:Éïl»[>eÉô�׌+'1DÈ}ä9Œaø ¹&ïH 0ïŒ(Ž”ÓÔSýÞ|ÑqýØR&»³Õ+œ¦ò8j¤‹X.rT—R¶¿º‡çõoË | ¼#RŽ�' BÍ%*¹e]©®ë¾²hX@ñ·«xôN¤Òì²æö8‡Á3{„"òó¤�Œ*Y‹¼Á‡8„h|µ_À§¶O†×`4 qðNsáÒLÖå™]$Ì4®åãŠÂ²üËêÚ„ªðµêŠ*u´´PÒ²ŠÏŒ¡6ÅãÝS¸Ð˜(qÛhv–pÖ¶° Ñ-é~Ä áØ(ª#3ð¤uèRøÄ<Q~wôý>—˜GŸP;Dµ"@@“¥QX#¿¡™-žqرSm¸oé–®±‰« b›ãeñ<œù‰ƒ^É[ ÝsNù™¸•4@+áÀæ'8ÙbêðAè%õjêñ#|ОßPà§þ ãZÓØ|‚xõដFéß;²àšÝ³1BõÙ=Þ|Cj6ñ%,€ýÀ–†Rt=Õ¤™+dÜ‘_0 «ÜVÈnxàö©!Ô)Ž”íÎLn$FÐý5Ç ßb³¾%(ì8ƒe©tÙ¢‚jwC›†jœE�…yÍ{D‘eÔ¬Ÿ™Ú¼«é‚*]#¾ûî5Ü]ŸÓg§sàœ„Q£vÓuá:Ál¦ïXYr?ÒeÀátÓ®³èZKš�åÕ¼ÆuÕ`鎭=ž¼ZÇçÊ9&0ÁeñuØÀIcd”q­)Ç$ê3)ý‰ ¢=½»¦vÂþ¾ØAúM\ûi—E¢¦hA)Ú_~˜KB!)æ Jn¢zŸú©nÆâ¡¶ ÷a¹È[O‚{*o*ÿ­ÎÄRå5ÒoE³)˜•z{-¿!ŒµîËÄ3ö±>Íé-nmþ6Aö»îd Ý@"bÕA Ä»óí$£.lN˜àêù]2Ù&öÕ¤û¤ö‘èÃØ¦Áo§ÿüe¹Ù}§ý¨æÙ�Ì€}w~/Æ8ðõì%óY‘Ùõ“ó‹· .l†eáø5FÖl•$æ¥�æ!Ó\‹Ë$#ÄE;cº -Àö ht"Z¬]BAI³ ;îã}E,g‚¨”&� Þ½äy$¹ÇæFôÚÄ›.³ü¹û,X˜FŒ µ�;JŽ;ôSL˜uÈ¢ë8Þ¬MöWLªî\]L€¶Ý:2áea;‚oT¤ïÓt´C Ì%^ù—ì»Ý8ýäç–$ðúÃ÷¯6z&Â¥gÍ*µ¬Ù߾7À¹1A 3!ÞØ~ô“Ýo¹n¯çOš æ?¡z߇ÞÅ.¥TRàñÁZNwqzÞ�n£ôèA¸«D½BªP)+Æ–ëc¨®GnLF`zI½µmšS:ªø¢‰ê'e~ðG}%y¯ï#>SrTú/š©É]ÈÕ+cšoÀ~(Èa{’º ±.Ù&iþ³w¤ Ïc4+öBdÑr)ë¸%WÖty#7rÀéþà ðì’=ƒW$|Š’È™Å›óúïèÍwЉqN÷é�² ñò°òçg‡v�}¦™ä}�0cSå1üÅ…)æØ6!iÏæ‡4ÖÓß—¢DtÚ£P�ªf 5)ïDï³74'ö•À±–QaAùHÍ žCä³4Á5E ö^&)a ,ô�dÁ€› ÙƒZ&øøþ#›‡ ƒ9� Y’¼]_—³MQê\½­s•�Š˜Ä©b³Ž$B³çAåì=®ÕëÖt 6}ñ3U]Nò®F¼öi 5ÅNÒ°²p`;Oiœ‡ÝΫ®gõ†üL‰¿x¤á¯Šhß~Â[ „�&s¿‘aP]ÈÔæ° —¢ö‡‰@¥÷m"öœ£6‘ßú·¯5sÇÝö~æ©‚Pò€iÒ)87Ë�€¡ß;·ð¸œpÊG�LáµGi "Ña}�øÇú€Þkv¡‰ƒF!)²šO{6œJ'Ü_½4í!–¹£S› úBCU®*uÄôïM• ‰PKÍ‹Å!‡#|tZãeg—ÕJI’žÎÁJ¤CùFsý͈†}y½÷&Õ!™^¿A´i©"J¾Œ?—Цoæ„âÏonǦ\ ÈáÔ§Y˾ð¹îTpÛrBÓÝÎÆ8&V0ÖzŠÔ{[7 ”ŸRø÷š!ØËuæ0Í{ÃDh—< Ä6ül™3ò^µâ‹„œ¬Æ6œ[ð”6ÎEÛä‡;¿Â±íxß+À|Ûf´2ÝM˜¦\‚Ü£šfƒß婞e Tž;Œ w“ G~@2’T÷…F(["ºƒ•°-–¾X­àn™€Œ>šßx‹z¶êŽÒúJèÒ‚{À­/0¼ÃB«—ç8¡¾âyÈr"Ò§Õ9Ëm컎Ü_p8¬õûkq1�mÖ+K.Ù[òÐ3µ£EïKw,äGµr럢 Åkj5¸¢Vt)ÂÃ?²eTaG}îX-�GÑxš»dЙ�C¯ÉUî,r‹Ú…–c4Ÿø¾Ï>RFÒÄ}„1¡ùø^¤‚÷4†êÔè”ÙmÏòs*�æäW›•ÄÂI‰0ßQhËtœEÙò„ÐE¿·Më}«6Ð)½¿Ö•�å6(ê%ÀîL^bÙ`6È+þ<–ê­ʺƒÕÕpâÜØŽaÒr_6‚å;Z6ÿôÔÏDnRÍ “CŸc\ ³R¢<)³H·Ôå¾[AAƒÎà´ìõ¤Î VÅœ7’R˜éÎåÎg 9FB·È‹fH Ú¼•öï8`¤—ŠÆëÕe´Q,œ%L€'Õ@!y=;[~ìÜ÷�ÎÃÏŽÔ†lbº7©5k$×r â;µ¶Ö+ŽÝ¥#|ÞLþ!½ÆMVw(›½ µ FWF<ªQ•#Ë+ö–XêGC¯ü—/à!!{»›4ܬDþƒ_ˆ>ÞçñK\ ÞvILšÛÎ_‹`Í�AW oäz2Óψ=ÀÅEó8ë¹ü;y“o…ûG©d2 ÌÆYǧê£ì4‘!F:~ƒ< xBTðìj6K¹ŽÌK;äÜy Í›2X#aýlå2 Yǯ`Â…ÈLC žwJ�:"»:E9Yæ2)¬…X³rßJû§Ë|¿ÃcÆÁå$òZö~ �·é¨–ôµô†ÔB%d4]yÑšý,HiDŠ�ouóC“HPý#”&ýp!ûF#Mÿ™^‘àb­8š± è¡»� "©ÑÀm$”,€ M±Ñó<�uú’6QtDÝ,¥x… æÅêý|eQÈ{y4 Æ�'àeó|#ó)¿G1àݤA›498¸r±OcêÈær‰YÁo¬fb=¹³/¡Þš4€Ô±îä’ÅÌælµ×Xy!v„oæ¡­^æÔžÌâªG–»þ'@­GÎâԜلG=ï8·kË«žO@í6íûæéã:/sr@b„~5ÃÿÓ!ÿSj%pTº¤1þÔ35ÞDg!‰Ã^òªÑ›dþ^¹|7å5–MÖp¥JÐa%ɃœáˆØã# †)¹3¢ÃWó[Çx¨�‹€¢‹½ò 2'Ú ”2š’[›D!»<êB·}¡Õҙ òZ¸´þ¯H°× P¤Ãõå=€ã‹ÕhÝÌ;;@fEûþt�ÌI§33ì¡E…‚ÀM^‚—~�ÿFúÈÑ“‘e+5§+AÇy´)§{|âÄä™ùv掞�ßólÖÛv@Ä*påÖ¿ã¯|T�‡¥uYaY‰Â€k`ž^ÔÚì@ÓÂlZè†P…ñt9�øe媜¾¶&¾p<*@¤Jz0aÐ\ÿ%ØŽ¶7:öWu)ƒìÑ3oxXÇó&x;¥ ï“Š®\†�†––¿éÛuiã"E` Ë×VCÄ¡JìÝôwG:7ÀKt`’œžÖé�O¤}ø_òÒ¡Îujƒh„„_l\U;(ñ«à›ã÷îÑþ_qNÁ@Ù7kÞ$Ç4Àâ v�†Ç™8� „/fÁPw‡[Nz(ÚÐSõ ƒØÖüy†¬ËÃã�}¾#$m×ïÉŽƒ�›À_êÎ=FnÚ´çw:qYQÙ_öLô×…I@²—�K‰–2º¯q¡�$ãµCå�.'r~˜PÀ8ú· 9Êâè¨�ñ·1ßܧ‡2EåËÙÜs´ªWÙ¨¼ýø4q*¯x ,aÿÜÌEkÑÙz Á˜m2F"MÉ÷šà2p9em¹žÈ–¦¾¯NŠ@újÞ-�,MÆXPI0@_¼îä z¯¼¤�ͱßäú�¨ú  šy°™ª™~Áõ+(áþºçOZ;®ß1ç—?º0iM%ݙԕñª°íRŠo,x½ãa¸åõjŒ‚yi9§0XË)Uöv¿¦£Æý9á³vÚ&R1åš}�rÎò`ý{¢>Á·£…‰k™Ôa†P�!èø¼4©UÍ»'ŒdÉu3ÛÂZŒµû·&0*¹Ž �Ò -ÂýOÒ©Ãè¢ÌýW„§Ø[½ ç:4Ä#ê–½SÊId€aK‹”WåY�Ô®¸oRÌIš#›5.É<Ü598�¶É%´gÀ‘™^0Ùßk�§wq±dWÖŒDaU&lÄ£Y£&³ƘN¾è¸†�› ¨‡Ï˜.`õÐÜ�(‡EXÞV?$†V\M郂>ÉÖ(Ì"øôòà6 Jœv<תI›+¤‚9Pó`p€s=ò§‡\"xÏô™×Õ„ö,Ú‰çšÜyú‡†ÜÏΉ(lƒ3½×>‰¨¦Ý:/ãÙ£·Y¾VØ�n£2ë^á�s‚‹ôæÚeM ~Þ2òåæÈm_.õ0ï!M‚w7ØJºù–ÞlO‰9äY‘ÁQþt ¥U2dø¿f­)ýå þIŸjs·Wrï16{ýÏÒ$å%ª�¼.ñýö?)²¤nÙÙĸ©ë)޼¹ËÝÕn{Ö]1»æOò‘K0Ú¡‚gJI¥ÀÃå«“�œ>äPvç-Š»ž<‘˜$NuˆôÔß;LkF�ÖÝãþÇâôvœKŠáÜ>ü ŸAeEغÂÇ*õÞùË ö@—^[Td!É4ÝPæè�jEQÀßzÌF�Èý…Ò²/¥2Â7Ù‚1Ñ{°®üÑ&â‹|5öà Æq 5šB(_tF^)çÙƒÖ;ÑfîÔ鯉öG¯íp‰Þ5Hv "§�ã›@xxlÑŸ#ªÜæ©!‰Gîáf¬}m�ƽ¯òkz‚2x8»G”¬—J¬ŽõÀøÅx©[ X9Ë•>ÒêÏFéú ÑÝN Ê¡q§î}†d?ðæ¤§MâïNš›®ñ4ùyjr®Ö=ãß�–<òê]�W°„“Æ7åîið Rz/ÑŒ& ƒ¡ˆ›Å°I›ÏÔ' õçKˆ5S4P¸C«5ÞQ'èž9jb„«S¾OpÙ ì?�Ø'Ú�*×cŦ»ëú#jéQK†çUçê ql¾ÁÁô:\^G[¤œs¼IJ¦%YÀMi‘ßX°\ýjãAÔ©P&2V³%šÈÃãñܰÇîï�Ú1$ä�Å(Hä×ôÁ« �O•›� –ƒ©š½Kx`ÒJL­¥xJ·å–¦Ð Íæ2…±|SЬrBÀ~GlÕ_¥[ëúQ-e¤ ,$åá7yòUyu­hç1>ÚDó~ÿs8ä_ü«¶ÜMy`¿sè†<—½b‡Mh‡åCgþñH)#$· „ÝüüPÑœÒZs%h±ãâü V(܉”ÌYÉf³¾65lc¾­$Èn{'ØX&ÁI#©y°ÕŽ åµ«*|¢ólpò_Rà!@%nBuBä@fp� u¢Dfîv€P©82Çà‚9Ü+N×�³„¢_ÈlY`ŒiÖÕLœ™7þ~’:Ýqp÷O,Ïe”¨Î»äÉæ�tÕnÕV—�» X=_â B—"Ji<žúï÷i–òvZ©-vÔå½äìŽ×Fïµ0†(,zð·$§6žº+—;H÷él¢åR;çɪËYò;Ȱg,œ Ÿ©˜š7”Û°Ï[½:ûØ’™d/äp(YJ,ñÛ eZÇ¥<"Å�믆ü�B¶ÑòL=hf~>Ò_e¿²eë/؉b±ŒR‡ž® ôúS­�©˜h6×m°ß÷=*»Oìc2Kg H!*-Ô/L ÿG Ùâ\ÿ4ÌYÖ8|MÀ³eK™waq]·!ÒrÞµûGhÞŽD6.0Tnú´>}Q<.”¼í9HaÛ‰Á¶¬B>smIOZù6gCO´í5@¢K�Kvê×·=½�Ý[†l˜NÂ;ü;x˜¬VÅ-äÑv絞¦æo¯évâ]®ºLõq…=§-æ1UÍÑ)*jdWMµO “@P´  v¢2Iå6 â+Þ âÐÈø~7Ë8¢> ;ï8‰ÑuÛB÷Ì Œflu ôÃWà|»¯E1 ŠÏø$ôÐ9y|ÄfC¼7 ¥³º?:³N)ç¸Kk0ÁÀ»IÓ…4i³``bå•8»¼†‰Ö�ë é| ×gb5Àš\�ÑG_ÊXRöþPC¯sHîx_Aî :0BV5�¤Iâ& ÓW'}!²Ùg+%3Ë&�šf¼e}omMª© ]Ù>No{qŽÎ0�SJÈtêéÒê‚WüÇ ��s܄ۨèr«7h:­÷zÂYB Ia•¾’¦unÎBH[øCÓ¯QH· ±„ �nÇ1xïí ²*'òO£ž¢mÃÅ86ÀÄ ãZ’Å(q�/ÍUa�EÑz'¡ÐîbOx1�-Ëð™]h¥{¡êðüLø'åM!ñᆵþåc|”1 MÞ½Mª»ì+~|vOŠC�7{9#‘ £ÄñX•Çý‡‰]úhòBÐÒ~goìm¾Ü¿œ’)Og)k¤L ¢<ÒÔDêJß�Ã:¼¹odÔ-Ýcyž‘|ýíBD…b?ãFªà·)ÕÀÃóÐå"ezP”ó`e¿Èú/#¶0À`¼xËp¹­ænÆO¬XÏzÖAù9Sù:ž÷Þð1[*ì ­¸ï'�né*ŠÝï¾¾„({m¨šÓö¯:76ÔÃ�šâ>yȘ—ãÿ9WÉ9 ¼Ä8 §z×l³kç„äðF÷³&Ž{›Pìñ”ð%¤Ä÷Å¡Ëz°Wr\\9G)B]åÚ¿N™ábn ^c§oXªç‡eE˜Ù¸mâíšZ‰ëŸ†ÇÑnÌw—~ÌKÉßogB‚_šŽÛ!“xÒ¿;qBàœî•Ñt£‡s~£2À¿Çby%h+»ÄiS ëQ fª\¦rï§¾Õ(Ò ].J„,0ó%Y÷´fÕ/éØ ËkÔ¨Ë)0ù,¿ÆžÐk? ÁÝmŽuekHç-bŽðNOn\ ¬qo#¶Žò–‹6‰YÆ‹&¡ ×{¤Bšy˜0µ¨v¹‡û<#siÈjª Eü?+øVWdDwŠcMïW|Ä/y/äÆ b5„%¦“¾¡íøþ-GtÖêÍ*ö·Ô8/X$ j–ª¼­&_¿TïÛªÿEàî¬tï�Š˜ãÑ91 _VÔ[ˆàî `sjΔƒÜŽ$߬5#ÏóH{=\¾›fÏ!Æ[ �„÷´ùIñ8¶2‰ä|)S‰ì ÄZQ·  û]™ú–èÉ8v8ÓÃ]Š :ÍFàçσëŸK ü }ܻᴥ._vålÕ(Ý«’ëÞ.M©ØŸRô`ÚdvÛž×'{¢îõBf·@ÇU*`õ —Žmâñ–•òO ƒ&GÅšysÄI‘÷oY{ÙØx/(ËGW Þ«‚Y2Løáʘ% õ'w“s ªAÚy.N[H‘јòžc�Бa1tC âÒÆèµb/r×:ßœ›‰18bØ�p´Ò‹§ÙÓU𭍝cµ“¸‘Ç·÷ŠWÉì§HMqBrm =ÈféXcå”9­á¾–?5ðûQÔ¶\Ûø³¸ÆÄ–üE(ÃErôSc�oá€ÝÍ«ºBeêøP…ïÖq~Nyœ‡™µ÷Z c÷Y^oæà ×úRU‚ zÔH˜¥ Wk.€Œ`fR[c ¹Mnñ×,A©×*þ�ÿ„¨‚œ°i†¬Â7BêäoÖ‰ }ýSÉ ”ˆÑ)p{Òú£m&ª6-ÿ~ùtų­ô0Ø›ß30­PÜ1]X ¤`/¨¦_ßÑëÀy«P%[KJ+/o¸·Mx‚‡ïW¬w›3ûPà×@{m{ù!¾$/§©%¢Òˆ9bw\²º½È}ѩբ¢‚‚—–"8ˆY@ñoz|á Eyùèú¬_àÖ0™�}—†�éö_BùÒ6yTˆù…xFÔ‚ Ê„?™ÙÍ ê¿iõIATÏnÄmÉ|º“:{þÎì�KÛ[ñQ’ãˆ/âÓÅ Öœ—häªÑáÈȰLS±¯_LÎÏD»DµŸšÛ­Ð^›‚#j£‰|± b¸Ý^ †µ«IS¯ügpMgŸ ù[шâ×âîÜðоË�C(VSQßµrÕü&Ðç_Ó˜ÚÔ]°}À îNÙ 6ó%í8u6¼Gô‘ZÇ(É%úN×À¥f   'Xk‘<Þ÷à­p�@‚fÐÒc‹G§(Fôv^ ÜÚ©> ´b¬©·«£Ñ]Ÿ·¤£pÉ¢Z6˜á³jÈ?èuމץPC™îüÛg¡ñ­Í>·ýù¼Ê|xˆjÑ_1†oJ~¾;š�Ä·ÉfçO΃­lžtbËÂ&ôDdy9ü,Ü.Nf‹;+_‘I9*±ŠYQÕ,† M³<©|3brjàÓ³]⬈W�™›œÙð(§8래̔¤ š³ IwC+ÃI¥qr.twO؈æE_6æ@ð6ÒÂJk0v<œ'£+RCà�„6Õdt­úDÀÅ=úŽ^Y·:qeýÏŒ†q>zˆîަÿX*;»} èëÞá×Åñb6FHÊ&Ùä·ðó3Š e®)凬YξI¨²Œ€K³‚ð’jÐ@ü¶y ñ#8^| <ìÊzñhtô�{R«Ÿ›~Úɧ¡rf{tö¨ŒjM¸éÐ;+è/¼*T‹‰­—¨](y;w%.Y‡½øÐa+›¥p‘ª²ŽYµÿU0´da›wúëL$ేLÚzèB«Í|cùçêBÁkûv{}›?¸’¿¿UH)ä^Úéýç ˆ…“ÈÞÅ€‘Ø04öYåq$Tsö:K¶·æŠ£¿G}PuC·TB¸¬¡®Ìÿ$šÐ”s‰¼¦ß»¯‚ï­ž«|¬ï$™G)Åùþ'ÂT³diÉ€…ëÅ…îï¿3„¾`•^¤%¹"Œ³l­ñ‚ëŠx†lÔý oÅL1‘�% £g›Hý3evpäU’!f% ÷d &¼U Œþ°ÅŽÖè­6a• Ý¥M;cSÑȆî4C@8€±°N5Lò—ô>vEf§:®ëNô‡¨Ù”’ŸÅFþ]Ê~¾E�ÚXkö&Ök°€I9ÞãÇ–›äj�DôX‚í�¹ì cFüÌáf “¢—ö:}uª²\ü67EM(/üv±EØ³ß D¡b‘ÂÔØ•‚úÈÑVûÚ=iY¤É¾?E䃉tÏgªxL�¯E¸Õw ƒ©�+hY55/kÊ´/¨OØÕ_‡V'°²Íl‰eRç‰"wH\)‹`¼´¤˜êåÖü hä”2JL3£ªÌ*ó’]¯HÂðc­Qßî <·Z…]ýSjl’ØcÒíûHUð ©C=ÁîVfžÜåî�³´C>ƒ²•’5<„¼­:—ËÐ9vZ¡»ë”v|úTHUP¼¶jX¥ðÌöç>d'¾$'I)ŸÚ� »‰D )‡¾Ûȉâ.™­†e�¡›°Saü$øy=W™ÿöÃJðËp˜¸ÉIL›f­BŰîCë7ðtì0œ ÔÇ Q/R‘ü¤¼7JoóX�û#©Š'ô½ŠˆËc×# O >Á·ãÛ¬ˆ÷�]Ü¢o UÃzyê­‰jÊõ¤N!BhTËõͳÀ¤BL»Ûxmò-›’*~Õ‹æi«Pê‰bÿXJ¿AÐ0LmÆî‹��CTKñÐY¸?HzLµÄMß�!ã*VÒi¦ß)sò6A$zå˜Í>,ÅŽËu NÎÍ—þk%’eIy°úøžå`ŒB.Ò¯ÛPU ÿ4ÖDñË.M¶œnyk ?ߺY‰9¦3%£ÉÞì²¾†Ê¸=ø·I ÈÃ:±F[fßWPå5qùöãÓz²{±¸I½�™?ä}\ )!¹¸•³ô&׆j=uóöàžžÛÕfkìê…Úa�1_7©ýù Ïx4½lA?u�ÑúTËûD¸’([“^d�ToÜN]ýròö5ÍU�dGÒUö¦J–pœ¤*o¬àŸUuÓ¾Ûác#To¨z±t•¿«ËÅAÅP º¦óžâ½ö¶µ®BÎky‡ãÆÿ|œ)Üž|AãÚ¦r ´àý?I–[†­zÁÙw˜1*åRrQ&Ð(tŒ³B­TR›è$Š„E<‚aXè×8ÃR=HV æÝÁžÚfÅ\€@¼$if� }2¿e¦°†²¥¼e´¾è¹@Ë`ú*kH•ÿdÅ›Nì�lo¥3¾´Ì×Öæzs†²ÜÃjÂ,°›»€"n×#¿èÞÇÊÙ‚Y ¿}ƒR,ÆCåÀ!nêÎê=ðÅg˹ ^© WÒüšï5‡ŽÙÍ nv‚ỳª,θ¢ÑÖàGŠÙ@ßü”p€[“�ÌÙ~|Ã#Æìʹ;0KJUÄ0Ô¦vô:™¬‰4ÅAŽ9•éíÔ4&½>Kq½Y-ᣛj£î0ò2ð¥ÛÇ�^‘“‘9€DŒ—d gMü/'‘vä�¸1  l‡ó» —¬àíx%9磗IïÙ´Þº 4¸nFÁ±ÓùKâ‚Í1P£M=ËÐ…`Ò)3&aÌÇ.ñÑ÷h_ °C`&À:3™ Üt’CŒÙÃÃøj®7ïÓÂv¶£_üájØÔTàÿÙʯC+ÖÇ•œýkØ‘@­˜ÑJ£÷¥ot¨Ó©$ãbrAgoV2·QvObS“Úmí6Ô$~¸µqQ}¦è´7z7?pzŸŠr½ (x–ÊŒòs\Oz§ƒÓäåªÏ£�Œ…dÙ=ÑSk ©G2†ýD§¼'�É@àѺtð7òƒS“Ïaßý@#¶ÓRëé(¦§Áæ}ª ¨ö¯É?5söoĆÕ7 o÷ALÓÈbú#»†ÉasãÒÀš…OˆJû¶ðõÞ œ�ä&.©­cB:òG–ÒhÇ¥ÿcÆ|RàŸú ·È°Ö=s"š@/)Ô-ìÄÆ&]g‹¿àùÏÆsƒ¨Bä4`Ë9ߺ…þÛCZ}1 ¸ù$˜€s�M´ÍÓ ‡äZõ/çÆÇ²l”u¸%I¹Xù/ÊE/ŽMÿÀhSVn“¦&h(R—…&aŽŽÙe ­ÍÚ}„à©S,Lµ’? ¾ŠO³ªÊ»ÈÒÑR £ñká§ü2’U ¯gÿ‘àï„Ú€‰áUöæbC[¸ ’ò‰¸©pŽvaS.6 ÙÑsLW¡* ¥XM1ÄÅËõ´¢í$…Nj­×Šê6“ÅÍùþDñƒ vj­æA©i—'Rêkáí‰ÐÍ®µ,Ñ´Ã2½"¬‡s¡ìâ#Þö uz)ªÐd;G\S°Ckªf°ÊÝ©rš#œ¿P—ŒÄÈ H Š�5‰Jt9Æ´‹KŸ/ÊÔ d×]ÔŽ_pˆ€W]ôEï.yØH¶[ûÄŽLQhgÅnt‹Ê/>ÏÔ(!cƒnz»˜ÁÔ_’˜p£×ûë¡Eå.à�§Þ㪟kÓåqt$³I Á §8ô.ZcLÔÉ�$qB•îåy€.þÎÇ^Ž cZÎa›,¹!’gv9½d•cÌ9:¿8¶no·Pc3Þk/ P 'âNîôµ¦ —˜u~$¯Úûß=/Y>ãúÍëœÁhFC4„ºñ,ƒ5Ñ' à–eÑuFDŸÐ€uðp–|Ê o âÖ*†Û[(*tÍÒ¶-Âþ Ã7A V}°"‚“µ˜¤!ƒxd7|¥xܱ˜|ŸcjÒ�#*3 ¥—ÑŠ{ `x–1ü25l š_g wn€¯Z=¸1扼túË37Ÿ/§¡rÕ65R—Êù!Ž¢yÔ»Ž!ñ�÷ÇÀq]ô÷ŠÈ $1BD :.Ÿ†uE)5UÂÝ�UÂAµÛgñ¡û}�7dƒ;÷ 3©¢ŠŠ]ØÅ²dr{Ö–2nÊeUsF+y¿±gŠXI¹¸ƒÈ`ÅŒ‰4ÖÛ±„H>»Na ‰ Ž}Q±¤'ÙOC©ðûA[A3ÆŸìÂvˆØ¡P6¾LŸä# ‚¥žÈÌêý8õÏiZ×þŠÃ:´¸Ì=‘÷_‡ \ŸÁõöx|*û8éŽ==´9ÍpĤ& �”|–ø “) /d%¹5xÃñhàmƒk¾)»á‘…_¼w… Úúf±€  B#Ê_{x?Z6kVñ/¡"AfMžK«8@—AÕvŽË^÷8=–œ�u¬Ç^zU=÷~uðbùY?»)J?>÷ñKôüL1SKïE’î­gì…ŠÚ7~ÝàʳPêÌ×/ßé�î·¸j#¶{0íw¥J¿KC«îŒd‘m:³aŸçh’·¹\üô§ÔdUôÑ?wj»a"â˜CÌš¿)¿ò€[¥üÚ±ñë�/tÎb»DÛ â¨Â+Åýœbš8Q1˜ä^2whˆô�o]¥ÉÉ[iž²§©�Î?J?­o|J�ëÄ/G4ñÝ ³ê„ ½¹§|]kBÞÎómq®7y z2¯Qô%õUÿÂá’æïq ‰²EÁJù¶Ê¦x¨0Zϫ݋ŸFSÙÞý†6%•i¬lEþR죓´ˆ·� 1yÊgw@í“iÑãØH¢&WÀv‚!ŽÎª<±OȲGB%µ°î€Š’”©!�ÌÌ£Ad'ü›³×tV‚.{ö«”îVô7²Š€'n(`"kScÄ x |'­ØR/â‘„A6KPòÀO ®÷¿šû”Ÿ¸�AA±ŸÜôHI Ÿ{ª~ƒ¦=ï�xö¢žD}(9ð‰¤äšŽú7ôR‘lC¿Lp¦ÖÚÀ�{$eÚµª®ªË~ïÏ„¶,ᆽ7!;�ÞdÀ þºÈ ¾#Ü#Ö»iý3®…á>F’‹7±Ÿ§ Ë‘æKÖ<¥ö `ñØ‚øoqH»SâIh¥å�n°c”?f$f2T_FóÄš¶žÐ>?²}}ü“æá8ðÖÚ�"‡›Á1BvXÒÇ+2­Ç€úåË<2HÛsftãô×i-Å¥CEKuéß¿ËþéâRì~ßáÈPÐ�‘{ÄÃóÿ�Lñ»Ú·'Æ4çcÅCúhAà¹W©\ †�HÁ׊ÿÅ|pȸòâ—{óX4D*qrXì&»¹¤´ºô&Á·Ø‰h_Ý Ó…T&*ìåoÍQ¼)äC¸`h]ŒcÇ·uhùïòN�"‘}U¼Á_Ø4Ô 9næÊ·]íò ø×¨ÔØ~úIuå9j¹4aõ¢ÚIxk™Ïdx಼íç«|²ž�Ñ?…š�Ê¦Ô UÉ\Òõ$�‘]yëÑúZz¦|j ý‹·W’OA¬!%îô+)Ï9ÇQh_¶‡æ'\ÌZïÃX±xr\hÕŽn…e*Ì=aö9&‡±³ÂØàñ刭àrÈ–¶G®±žÂ&tœg/x Üçõ\B³=K&ª#],™Ë¤?eÑ´­ì;‚v$ûióÃdþ�âQ :�ƒvÀðF[YÅßyU�‡É\G=tRfž¸Ë8&bM€ÆÃXëÔG[9Ý¥±†K]/š´Mðë–ãm¡`ñ;ÌWÏú8Í\AJŸÑ»ûqÙ†0ã ;ȵ¥èëu.k95ï ]¨$:$va×W‘–&È%Í_€o8¦=�wœÛ$nzÚÄÜj�Á—bñªW¯O…ÎÔa z…5±©—¼ÈÚºz/û’‡™‘ Öšö€%È낟ßý4‡´‡ÉTÀ �HHæ&¹£ÊnªyGP©Ê­†¾êʹ %ûFþŸÁ™–Ú�6Qú>* ™ÀÿåÛ>ñ^ZeÈ‚áýÙa¸…i`Q� ó³¾Qå™Þ}¯–?ÚKÌÏI“Ç—ú ¦â²þÈÉ}¢‘[njef˜aJ¨€—÷¡)ìSC©ø‡Õ ýi>ó½:ïgåP™àÞ¯¬…ŦQé)Ì…õ³õÎÅØ›ã)ÃðcÝ¿­.)Òw]ìSòâ€è3 o¶Àí ¾ÆfÈ¡Ÿ‚™³wÈÖàÒÏÅeç0#ƒKY±ùoq8zpÿ.€ÝÝ’c¶H¬‹y°ã¾eqÒê¹wžxÙd~÷RhÑJk›©�:x„ RäR\4´:”ê7œf9f;~¡XàÞ§(m© ÑNúŽÕÀö-ÉßoLÆ©ì·PêjGÞ�ušyD¥1LÛ�Su�:¢ØuÅ‚LÇÁÐ�ßþ½�•cžÞ8p÷© âû¯ñÖz”IP!;{Áhß×dÅ%†LàyÊ{РܸŸ¿O.¿œ¯÷ôË ¸Uÿ8^éÔ—‘Ó+§¬ —µ†OÆ#û^#¦�Fv7¼l’,¤.B"Á`,8g®£(ͱ@>N“NqdÑ­¬fÀmŽVÕ4šë*ü¼x ¿‰[ŒXÂlnS–Ê>9c@%Sfý–;„Ï{Érb`ÝT» ò�­væÖOÎÅÊÊë˜?$�µý¾´ä;é_ØY‹xj! ú€·Ëb«»q T?ÿ°ÞþÐu5«à~ïR&¿ÕÊ¿ýÐ Œó¨öœèpÐt‹O$´äe а ìxjeJGº;87û�R£»rneIŽi I\á»Ë³Au�eF°pƒè(qC+4£nЦÍì7ô<³šÀ꬜ýæ„1ýѶ¬´ÿ°^žƒ>cæÏ/ÝÖrjɹ{ÝØl6$ ÛØÎ ]ç¶;üMZ î¦|,„:¾ª1)‘îîX± Á*Hm0>”œN0æM2(± µ^_“òé¶³iÉ ¼hÌØy”bÚ�-™�šÊOÔŠËå²NÑŽxé¿Ãep÷2»&RiŽÇ¨¾%‚–øzR©Ñü!¯^Ù¹8àQõ+T¢g¿Ý½ÄŽñ­O*˾£ÝÑS!÷>¡# ´±Ë¥%ã?˜Yáý÷ò™�oS¸¸ü>fÁi‚cºî¬¯—P ¿ l3™îéh±á|BcXSrI5þ™H»q^ñ:iwß9ÊŒ'×úAýËX=¯“çÛ5£)IsòòPëG.x ç¾Èß\�¥…—hÓ×yv‘§7 P½ÔÜ´Veém˜pêúp�AÍŽO)Ð>-^jÎAĽñÌ’�ä«2gðŠf –‹]ÓƒÁÔ´Æ‘àý‚è(9ÖÌs6‘ÞV s‚[î)WbX³B¿é÷Aœp,*zé ;ßEA¦‚rŒ¦µäª0•Ê«dq¡4¨r œÉ€y•+Å ¿¡ŒW&µ(P—iDnÙ!0mÃà˜™À(K1!¶y¦4 “î4I·˜$~+˜ðaUò.âT 7¿<¸.mYk v2r gQ V*¹ÂÌÑ—›—’x4¢Õ5g�­×CÊ„•¨Ò¯`[ì’wF gIÍ3m�Ý 6IHºóq·õØ�.H>fc+}¬€q?5ïÉ2Õ‹µ"òú½?ÙUËÌ Aû¡þƒŠwUh™¸hŃ1°(Cξ'ôöÒ3H¿¾¿²FBŸ.x'.› Óþ–E¬7É¥N ™‰fƒ®oØ. ¹ 3D¸\óÿc£È4þW¥KÜœ¿ÚjYDä­ÁÐÄqÉ)è¼Cî"¼wbË®©n™I’Òs,ö¤´„ndGÇ DÄ£)²'¼_~)ž³ Of€÷½ìª<Œ›ÓrðiK$Å]©Q܆ëóÝ£ôk…ËfC¾À5‘µXÈhäO–?( ÞŠÊ}õ’�*2xióýç£6èÑ�'$†§T]½*mÆû¦IL:·ÿÏÁ·]�‚TýÚÿ¼i®'ê˜`ž#]÷íƒÊ^nê-°¯¶ Ýž°à�ù o*6ÔáM“ü××@’ ¦6BÅj®ê”3�ÑÜìc\ít?ØÒ ìvO†Êq=CA—¦ !!œœi hF„ƒŒ¬žpy’v\𥷻$4ÑìO7¯ÂÅ\ég¿½ØTMš¸@]s [’´¾Þ2”³è/�ÅÇ��l|ê¾ü×!°oÕAûC]±ÄÔ`Ì^J™¥ã»‹#öü×ü÷ÕËñ2ÀÖ°i‘u÷¶üp“êF¸œH,æ7ñ�ä¯ÉhbçâË‘•ÉKêªøãQïݨ—bP°ã•É6¡Í¶15@b´+ØzúGð9“|O•Ÿ>=òñ»¼g'ä5ÙüâË.>©u×H4ðAÿ éÝÖ‰jÈrEš"`'žá/y�X‘Ó{>„CÉ~q¨$v]_ç§/p‹k;Bn“!stΉÉ_MÖ ŸÍºÅ¼†Óf ”<�¸="}Ág äñÛ"ø‘Áy¹/x¼ÌÞvM'Ë¡ÜÞ!Ö­`Žæ[±båK#º‰ImV8M³š)K#ý�½“µäá<~¦Añ ¿~_Ohk§üãåDáÎIÓešÝ‰ñ\c»êPP]¤l!çám-/‡Pùª2«{$™ü¼ë§ ‹NßÁä§s\ H�ûCÔJÚƒ5”€žàÀ±-ì…çˆÐ€ó!=9Êg§ðò†ŽUó�p¿y^…P¦ù”{Öž} $­º�(¥F°  ' )¥NzÆ={Øpr!¬0ÀáϽrz,ûQE+¬zkÓP{&C˜ÓQ„¼Ÿ_¦O¬ÀN8¬,ÚÄãÈK•…~/hqßOùàáâÜ¿âä^"Â\•U°Ò#9‘l®;͵˜ù;�µRO–ÆÅùÒB×Öä¡¿öêAq Å åNVœWPLäç.»6yHi]g¤¶šDñÍn§©o(¿(\ª�òttÀØYÖª’áç­Sdž‘_‰ÔåÛÁ•Êø ´Ë�PàÔ>my:ì….\/M$rAí29Zrwº«Œj­V�æoì&û™„»=:€ Ô)ÜkLÜu<ð™^Óéù8&ä_qþš>¾Ÿ'vAÂMÔ&[x{œÀòår-ÒŸ*άŚp¢6Z£PÍ|NÏ æí¿Å^ê#÷§Û²D�袅aZ-F—Ý\ æòJo¨²™@F­ `kò鯳¨ô1”4ñBNá$†{WMØç²³“]1 l«À\Ž=Ž��›ªƒ‰™„£TLöàU§¿ËFïƒñÕÚĽämŠé›ÐÔ�¯I>ûáòû3¶ÿÍ•ˆÇuƒãvîç¾Ç’4ð¿àT� Ã’Oº¶vníWðûPÁD üŠ~Ðan>t3—ôäwÆC"a�ÛEÜŽëÙ5Þ ý÷ ’ZÅþÕ—Ú®ÛTàždàã)ZI2³Â´E\ð²-Õ�s)*?Í2å¶fÒµJÁæpzÅÙeN(Üa<ޝuFav ÿ�eQ¡k_ òµï.öÒ�ºN´ïˆiyWðpkÈ')DêM�WwhïvÅbwÆ×ÚÜ Ñ¬ˆyzˆ€ X(z.µLt)ºÃ×(qêõk œ’”yâW!ŽÌ?D‹ð#Ÿƒ<ã{Ò�¬å•Æ@>ÍgÚ©¼Z¹åó úX �Ô/'­uúƒ½GBæævl¹¥£?|å—‡£ƒwžI’Î8�ûÁ–+p÷ ëÔ]_ˆ üèpXè¬)J ³a²~($E{bULZß)-¿h¬-éîO0ж…ªµA!Aâ…ʈÈʺÐsüæ›ÉnI†žýkG)«†ª½}"`nö‹ù7x –Ô–'–ý$!AÆÙt‡ûu hYŸñ£RÅošk4Ž]NGK]¸¿�IMÎAxfÝó.Á„÷íùix.§ã˜KýdR¡Žõã*� Æ=*q00»ÖH;ÿ=‹jÎSÛß6)ÚÀÏi«2Œ½²wª.WÉ~p†-`q[ùQ û(÷ºû^¬ùFk/Á öÒéÖ"ùN5¨^$¾�l½%1ú~}Å Pu0Ç4úx‰�ZÛÛp$¥þ2mí_?äĈçÄ «:s…ë\î²åØýàÚ®“ÇžÈd]ly;¶Æ%†ê—ÍiákK®«)éªåÂ)”žY¬qàph£Aêší[O¦:„Ž[$n·)ë �ÔÞO î{Ý4žV}NòÌ„Ñ_I©ëj**ù‘fü¬b/áÆ8 aÖÜÑ>!Wìù,ôìI<¿èyªqnÊ3žuvÖ2»n¯«€zV}O³÷où”¬Ð\Væ+½gÒ]¡Q7¹!hªÚ¶g°_´g©X=¼=,ŠÒU{�PÏÒ JŸ=)é¤\¸9õfèËMïx§1Aǘ; AC9šw#Ó.Óšßø,!¯ÿ[sû›&MYüTØê1¡T gçP1I˜6†Šú’¼>ôêŠ8¦é¼V‘¼k;‚±& v)“×±å±÷Ë—Ä\¹~U¼oãXõû�²=h�}ñ”’lØœS`lŽï3hÞthîkÊšf�üWêF{@ƒqßXwF®©[[>»à¯LK5.-HXW#ÿhg¼’ ‹•,n() fAÄYS^Ú �Ik[÷I‰1¼ñOòSþ3}k·cà‡oµ›üòá»'u8‰€úsxì³@DxOþοf¯�¨G�ñ…óÛ~Ò¦øãÛ¤Z½ðxjW^9g¥j\á~f8¶âÊsàÎ@E¨ßSÈÌ”¥ C �•‰Ð áHˆTþRçc®C>ÔD¥…´zG�©/žÂïJ«Î+r)‘§‚JÀ¡ðµº6Ó'Óî‡$[íÁÙ‹é¬/=õË%š:åñM�l¬+ï=Î ÖyåŸëÉgÇEY½3½+32v?%"d�äÛ°;$D£[`áH‰½‹ÀÏ=%ÿv¯­D­û‡+´æ»x.zÐ8ZÛž+&{_˜i“~¶Å'®hòFHŠ‚– òõ"`²n;ùÜ2˜)W Ç›œ±Ù�~WR#TdÑÿÐè*›umÀ@3®OD³Yz„rQVQ³éðö=¨+¥  Î o‚ÚQcú z´`êR×èªy¸n¦Öqß!‘^é©°_Z­’L¦Ó©vŸYd§º7ˆPMMf^vœb´�‰ƒ»µ nvòu�åh/H´A Ã7öEkY°"—oPy\÷~'3î%Ô·š•²,/1s>o¯ 1_1ëÂ=ø&Lªê$À#ë³xç¬JÏé<<àïCß«!x‹$gç¥@ï`<È/=-ç@Ès$¤ÓMFÇIH¨œ³io ;Ó¼,ê8eÿK`5x†>¤ÞŠ€M.דӶE©GáÉ3ËÓ9VÚzIÝï«Zyq„â gOĈÇJJgº€~ ÄE!’“C7RmÐà¼w@Ëß#y!»SÉÞa’`;Úµ;¦èWùËÄÃm�;‰Ööh…!6~ùpÒŸQ®-¾ŸÝ}¬UŸÖÎM^A�ªäµ5oÖ–ÄœŠ[×€bâá*bq�Oo°^…c8R-@‹B!-´ßµ¯ŠG¹: ùöo[Åñ¿ š9~h]î)nÙNs Ò8›B8i:§ˆ7ø´ã.šÉ÷qD†n;JáΔ¤˜ä@Dê6¨Þ³AsS¥R×…¢_¬y¼z^†ïF.1 ‡÷�ÌoƒÔ)Õñ€»‚gqŽa òZY™ú}#å_X¨Aq­ç ºTU„Àý|ðà i¯ïˆ¸z¤qƲÛÙ£¤‹'%Ó�½faÄ€E,E;v,¼iaáå,JÍV–X_ü»ûà«Íï÷2•˜ºÁCƒbÒ÷ìz7g e 7ÑëL?Ä&‡µ¯¦¢!yÖÒà¹Ü!€qÜ£¢õ\4×*켘§iÊÞ'ñ5œIð€¤Û×ÍíNZÃyònU31'çJô2–õM_÷×åÅ‹¨WKʹ6x4=R +$Êt,µêâòAÍXj¥ºl “"#šÅÒ[ ‘*ê2îŽÓ�ñ� 7ïÒr�bÙ<°š8Ø‹Á³ã¢_Aµé—JKÉŒ¿Î¬Æ¶Þ8ö8Κ-ýa¬œó…KèÓ-9é›f^š<‹ÊÚù-,}qd¹m2žI´€Ø[R_,Wו9Ãb‚ S�ÞbTÌ6^ ¦öHo.³}ï]uh&Ï6`ñâôûÉš1„Ôµ¥ŒÒÕJÙd¶›HXhµ»NÉ“€K¹¢ Æ4Æù”Ïó3ÎÞˆôæmãÎz¾Ô±±çøf×yé–Úü†ü¿�±�΢ïÜ;i¢Äj;µÏ a2УUrðßè%?JÙ+ÇæuLéP�«Aô“,õ'àŽ×Òšà�ääéø¡¨N�悃�þ³ÖSpá–û œï- ‰‹vŽc_Ý÷‰’f}*S«N£Ê\x£Âë4ÌÅ–˜¸EZN°;ª»¬}QN*‰J»æ8YŠøxº6ǼðW¾yLiD>ÂéµlÎä‘Vg°€g,U^ó-Zw1{ç¯R¡qÂS}%JJpd/PëAIU‚”çìüi\Ì{ƒ)Ø^i§qªsùÿXÀ¤w”Y­ ¨µÈN¹ÅÀ.œ­c¬Äõ£÷+Ú€_À5FFtýª—Û¥¦?ÿæ îÔÇP QKQÿjç þ<>º™iÑ“ZM«pP>饭ßòU;N¦ñߺ>%Š©oáY#.KÄZ¹»Èë�;ƒý0¾‡¬y@™ÁS³°{¼7°xÄd½J”1�õÉ<×¢‘1~TÞ`'ò'M"Š€ K¸œ¯ž\w w+¢4)(¨ 6ˆ_¼š;V0è\*×î7øº�1 Ûîüvu`ú)�òé õ”ÛF‡–gn^“!¿Y``#ôÐÿÌW®üö²k~£Ÿ›MWÛÿ\¦_.~fóÉìØÈ2$ƒB$†ÃcO·ÂG.eq¤XÿÙ«„f761CòM>d¸ÜÊ-¯f²ØÄ½“ÁcÀ½¾pj†:8-ùîeP£{àå<@ÿ8€yñ¢Úİpc‘�¬¢Á¢‹(=_ÁãÂéæ¼Çí§|»ÅE“"GÖwR§dz­zé5Z‘d"/ápøJ½í²ÕŠo1zãô ß›VŸ¦´.NL½k‡þì^Ýæ¶p³ºØ”¤Q6šp޽,$�í9�Œ`áßVA°áEò6NÇ ™,Èø�ÎvŽ×æ[6`, ‘¯µþÝ §Í~6{zËý9C7.¥=ØÖžv¢HAKƒb ‡·¡­ÍU4ÓoAUÌ3f6Ž…Ë1(åœ ^¨ñ7Dµ;„ôðî|† ­˜ÉLT§åí�ZµI¢ Úc—yC΋é.'?þ3<áÞ\0¸{0J³¸cï-ñUí¢KÃöV¨sÂö$áE/‰M¢ï‹…î•ULÇè Ä)_2 ǹ„׉ÝÛ7;•gЩVtœmbÓŠdaÀâÛjVh’½ÉHò5íE�Þë+ÌOmSI޳%&ÏF 9Ïak‰–%U�?ÿÓ . ×q�HW!híû#¨’Y�™ú/…fƒÎÍN")HY# *š3 ‚‹ÎÕ |4¾Ï/›Õ)õ �¯t]‚�/%ÛĔ郾n^‰t2ze5”˜v±àôå�QWw¬2\N¦Šî<Ý!îNVb¦µŒ-•�c¸)xmŠÉ� wU¥†g€„ZQQßç^JóbCæbMúKØ×8ò‘/æ\r$¶^যвç½%’Lj}ôbNHv g wÕ®ñé~f�:˜‡½ðÓIÐ}¸ÏÖðÙsî]AyRan˜bø‚€‘3¨á³$·Ÿêê¾…´Æà6—¼R$È„ö‡4žÈwÍ*ÊžÎw‡ þSj…ñ"Ì´˜éŠ4mü’è¾Ã± )ü!€{¥C³"(uB_³`C,FïæÈíª1ÔkÚ«4­=9%™µN#/Ü\ðzø/L�¦Bï�bZïNgnÈxÀ}þw>˜œ�WRÛô¿üPƇŽo÷^-æ7Í“´ Ay,�>¯c泟ײŒÑRékïb\U­IäÔ¾.tFîø—À{Ê@Ûi4bÞ«Š;»ƒP� mW—«ò¡ÁL$"{U“GGäÙA$üS^�¶¥ýJ™ÎS1T¾ÂnóðúúãI¤MÇWÞá0Išê®u´ ¦-tGÎ/Ð åIÇHð0ªr?»ÐÐ4 ³Òèuí~³Xq¤ÛïmÎ`ûX2Ü87Ù‰´}zOf‰I7`ÏY MåˆhÛW_{£¤Û•‹wPmÑøHâôæ³{~Uj–—k%ùa>î]w™Úr*ež_ïáí»“Úúû”ɘÊñ(uY4NM›J£å=¥÷•v4 ⡬µÏ¥#¨ÆÚ ¿gj�&Ç+iÞqDRêy­qä.D TϦ­w±F™=Å/ë7µÑ­a•lÁ…Ï,†öCô·;µ{@n‹Œ«²eý*¡£¯&�œÅ”c[¡¤íõ¬õÐ)ˆôZñüŠ @Úv£™Ö¸ó?>82àq„Ì"7“+\Œ ®eu 9‚˜;â¨Eèjb»gÞ¾ †»L´êŒjÇ.¯-=Æ–Zå1[›Íò&¾3›5¬Ðò˜¥ªØèfÄ΀¿‚_œ~J¨ •nX†|&ÅþØòj«¹ÉÉÞ@p–•Á¨o1@‡¤å.æÞscíˆÇTƒg�ï (•©î&RZ>®ñìM•s™ä·ÈsöU¥X•ªÈ§w¯¢´16¶ý3зet6åéœH'%¿Ú=ÞŽ3QS¯�á²¾oõ›ö©™Zë‚ÄŸåF:ïg˜�‘ðNAöÑúOP…Í)ýà y#);¥ sÓjp­Ç“³SK¹‰xgc&rzþÀžªÉ‹Q$ âµ¶‹ü_Æœ‡úe…=®×Q+…FÛí $~ÞÂ÷¨ë!â8ÿQµÒÀô§hßF“ °Rßµ|éä#1ÜÜŠÆ<Ú¤ÖƒQyHÏù|‹J°¯÷CCê¿ÿg‹‡Q|Ó­3-uT fzo¦|j>b ÉDáX~2ú¶º|´ŠfÀwÎ!+”tP�¥Ø–Âõ2Ê´àß^?ÚŒ‰sjà8ŸƒšM†h"is7'v�‚}¨ú ›¶_/ºˆÛÑ">$¡š˜=�ĺ+a]ç>8Š»9Eë¨4�à+OÆ@”ø%ÜBeš%,Ç«1VÊ& „öì(R´öðgæPG<¡¨â»—~¡ü7}ÿ¹lG’‘w³rþn ­„ŒÓB;y�¥Õ„Òk(0ÿ¤™–g ³,,(½ÄèKÃÓ±�©Ÿ„ 2ô§�‹ê$°óˆßÅD¯Ü«�´æ­•nØz¸»±Õ£Uиf—kÚÇ�ډέ÷_å CÆð�ÖcyëÄÔgÚŸjÆœãZ¯“ëã.ÁŠW„’‹ëîEX ó—w&4Äΰ½XÄ™Ü OC“ó]y_u­ö×U_ À¦G¥?¸y1¥PoJÐ×I$Y�~É5¼¼� Ë~ÌùtógtDi$ÕýŒ‚ÏæBO%'Ħµ§�Mì€Ôóv�hÊ$‘Î%PÑöu‘FpšSÁ¹ŸG|­X+>®}ÛIߣ)ï“­8?zf†/úš¼xš‚L qš3^à0{OÓüš'¶8çàZqh”sŸ¨¬|ý…êÿYsY"ÜÏeò)�Nd `æ¦Gìå,^^o'4DÒŽË%÷ÜZ(Ý`þÏ·†�“ÃTémüΰx]ᥒî©Èë£ëÇ®¾/×34¯A´8JŸO‹3öO`ÅG FãÔŸ‹zrÞ¬vÁ\÷AéЩQ_+]¦ç…�jÚŽŸ ðØáE–“Ô“w'£Û¨Ûâ µeµM‚UµZù´.¡S`™²…[îÇÄõôêéÚèÇ›š÷‡JB—G �¦ü~9>nl²ù•È�wz²­�ëÈ€®äêñTkH¬ z¤³ëÛN¿…¦k»ëÎd.÷`µPém*¬y»ßk1o— úSDŸ£Á¨céI‹Ìeò{.#”ΈҳÞ:×B3Ј†1'ùkž¥þ•¶Šrlür›VÎÁýûÑP’M¹–¤q¬”Ðÿ"z Kî¨%ã'zPœ‡\Ï_¥¢5Ô’ ÓövÞu!øá÷Œmýkçɘ7˜1êÖ†ð:?¯�ç7ø“Xv×Õs0�-”îp´â­[-—æsk¿ÍÄqÙôvY~—œéyø�Ï­öγB;¯“ÞEE�Èe+Ì¥œ¾Ñ:t<Uc Y¬àª*YªD4J‹+ÔbhׯÌf%¡¶Ñê}Fõââ¿�ŒùKQ½¬ Ý;"0 i»$ß¼�Ï ã0e’ˆ¹²ßU¢wc¼“ŠºI÷Ô8Ê,â=,è�H.´ i,>{%ü¶ö‚™ït6”©Uè÷ñؤðÞÐrk(_íIÓ|‘*~è’¸Òäî*ã/ñÍz"rÇ©ܺB3ôˆEǶ†r¹Ä–�ïûn›2Àù»Hß0› 70áŠüñÆFÙU-HÙBË€»¦'ˆ´�xÛÑ-ƒ†0$iinÜàØ¬ë1Ì–O_Þ㊋@àhñ¢ÛŠ]•¹÷æêÐþHzvž‰òÁX¿ˆdlQ}·JE³'éxž‹N>·ç?ÏVÊË,;Ì£ÜÔû.VIÊ1¾áðÀl•0}Ò²'³[eL:*U¹´0gnŒÅ‘äø2}Êì®ntU¥‰eÀo¶&4SèS�~³HÖ] ˜+¼šQaÚXIa%ë£Ìç ôý‰»‹ËXÖý´‘ s•1¸A+Ll‚?h}„‡÷èÜ)g0G|M|Dctî{i5o<Èî¢ò—O�¡§{¼ÔJ­u\ÕíA ( j~KαQVå»h&úAc°’ÕÍ$o.,k¦aä(KI“ßWå£=ø Aiqt!^÷?¶ �" (&ðºÓ“÷ÙΑ§¾‰%¶`@]µ•]Mø¦eôGUmøX¾døiy<„§¬@‚óGüð×YÏåuhS]‰_–žH ‘N�« * ÁvÜNb³jƒ»6äÖ³8± õ4åPô>9i:g† ÂzÝ�íéÛ¹V]ðò œ/¡ÿÁìùe•2^:-­ð;”rÁ©Ø:F‚b®<]$D { ³`ºˆe­ƒ©f`¯Fk|$ m‰Œ7¢ˆ›,¹Û—Ȳl>÷´<4ž<>ހɫL�là>Y!‡f¼ ÔθGGiüýÜÉãÍ5�ðCàˆÄÅBæÈ#ÊÚr=òØËŒ�™Èœ··j+�\v¡þÞÌýì'OSà^|¨ÂfÃ&ÿíW¹¥:Á>±­(=™>ýÏò|ºÑ×/B1€gsã§/!~@Íä3שIä$<•h?£_OÆìÎU¥�ô�ÑŽy2­†P…ýxŒXŠE8@Œ|-:Íœ’‡ÙÂÍqRއyÒÌs½¼›­Óã"hZ‰ðŽuëÅyûƒŸUtð´ö‰íô ¾˜±"ˆ|þ¯a7ù¼FA¨=t¢éœ¦ÛÞëšm-Æõ«·á4+­±RºsM^ÇÖZ @�ÚW¢š~~½o%:£5‹�:-#q4Ük6;¤#6qKuOdE 2âÙœ±„Å §•®u0ë"„cL Í\œe¦ž§Ê˜ŽP±%åÍŸOûœ±#µ ÜM�¹"\µ‡êQc¹|Ñ`Ξє‘.é_¿¸gJ¶ÿNE¡ÑÇ„0üs*CË^áÔÁœˆ¦(sì¿6P0Œ]4=àÎ;¤ûØ%\‘/%Lü+ÓDó+}«FE—%ñRï¹’òFŒ«AæÒ{”;9©N¯w”kéûÖ*;<² |ÙʱýXÖÖßœBio1 nCNžIŸ~Ô`¢d¨¤};ŠÃØ“àŬcZåGлYâ.œm7dû%À%2ŠÏ“ÒHs×õÞÝÔ]ˆù{ì¾ Ò1¯ÍåÝÑÒãÝÌ£~ô#Wcf¦Œ©»j[ 7vƒ¹Kµçã°u‹e:s›-­Óˆ'!õóVOAH%Pó®Œíõ_áñ!m®‚Aß”zåÄi­E|(!aXŽ/H$=ºGlñ±|«Ó„k¯\"$U®jÀ©EyÔX÷=‰ØÐ¶*Æ0á-;¦5 M¨)Š1°ñ‘mÅ݇åú}žU*.?¿¨Û„oŸ}©P�:®Í ê“ÈA¡…O›8È_»*Ùd˜ë=~’¹dkÒ§¤á·x9=^±¤TÄ:›×Ñ•52%=v©èßÈ»LÖÌKô$ÀrN_ì7ûmåNC$޶Õ26A²ÍTkþÞ`Õ‰ $ù•¼1ñ2(ÞÚ}ß6@�í”̹ÊwV„�΄¨LëÞ:ÁÞ w  9V V¥¾îˆ�!¾q�Úµ}‰÷˱�.%QÜ»ïø0.E1Cs±Ÿäê,ãêöoD�º=rÔ’”/ ¤†þƒøÀ‘'NýlAu±¢nYgõ jo�×/zqZ›Š‡v40˜ÿDº´�HE¨Ã]ÕG$6�¦ôÕFÂ5‰ÇÈÿ‘‹"Gh“Iø]ÚVÔÉ–5¹ 1aÏÒ3A•A§�Kjê•vy!=؃Åwø´^49Dæ85ÿÚlXíÒfs�í�VÕ¬³LÊ« \ñ1yGÿR“[Ÿ�Ói‹[fŠ„þ÷ÝÊ„j™ÙZèýÈÚ$¸¹ý»$RÁ�{%“ï®o‚~©¹—Úv´X”¤ÉJ ³r|+a;ÚL ìˆc0ÅødÞ"jÙoÎÖ/!Àõ¥rÇþ°–#y˜÷ß"ÇÕ|;r vô’*ª«C&f Dl$ùõt¶ö’ìEÉFe´ÊYÌá?¾xkÀSFókr ˵®M©Œ%°éÕƒ.÷´-b“w8fo6]AÏûy&rh¾3u»S�²ò,WxhvBË5†„‡%X‡Å kjÊÕÅüd¶Æpxo!üEñwÀ¥«m¶> –'Çö8ŽõèÀ³(0ü-w'Ú¸uÿf¥‚ˆônÿd{7¢ºåòù‰/‚d-†�›`ÐD–i€¼Ck€òŒ¯R�?ÊpÁµQŸ3¤F3c4æœYÿ­v}lšP1AP‰ðäUÿ �ñfKØÂüweäÔeÐü?pº „ßú7¯”=�&Ä*Û‡ƒ Ž|ÍVóGÀÓÊ”Šq­‘ÄKçº[l%À -àPI[æ ©°¯óñêfrwË ³ ¨†lÛ¦8OÃH‡2›ˆìt–m�ZCûjT Á¹ÚBÀvF]*_œªit. E®i­åÆwÞ@ÆÖhBpÓˆÚ˜Z@€³·Ð^¿Iªì˜8†7ÈcÞòb©ŸÍh®…«D%É…ø�µo\h'Æ¢†ó~©NTˆRM'™Œ18ëðw‰ûãór±g³þ‡×µúþ'©AEO�gÞÝ-jÊÁŽž ^¬~¸^­Yh¡`׬ۇˆAeèÀAO·…FàŠ;¦èԋοÂ"‹ãš<ݵ´õûÈóæ)± ˜ÚÉRTœýåZ[;«¹Í0@‘óÇ ¿FºxÉahŸ†Ë;…Œþz¹©‘SŠt,,�¦…€â˜M*hí´öô —‚è¶]Gw$^ýÞ'aÿ}uA>)°ÅÜÍ—ê9àšÅj’§æóÓ@DK€ýwˆ[çnC¯ZT ŘbÏjM¡,½õ÷ X¤2F6Æq�¦¿Êú¸+ìW¹IÅ®�쾎÷hQº°ã\½¼`cöKÀÞ–G‘ßi†‘ ž:vîØ Ù7Ì &¦j޾æeŽ«­¤ƒOüI×äD\÷õí¤9O±Ä«´!XïŒØ*^a;À§âüu¼ëÊäzl(¹jD�ÞO’Ÿ¶Œj3ªl䥽47íIƒÞT#–•jZ~Ô$z ãÄ~F+‡9×G�8´|—S©�ÍEœSωÿ|  ã‹9d­&äjÉ�æ£f…yᛣäƒ-ú|_6 šj–*�×im}yUX WÎ eÒ÷ÄéÐè}zC˜ø%8Q¤ˆD�לà`5M@+ï]±t2‘<¡mœ¦¢wöºYg䥫ZŠ p·:yp÷Ù˜,[Ãõž&R-ÚÙ/ÃàpÚ¨)˜ zˆþ@Ñi{+ûõ!*es:�ð^�°f$–¡›¼sÛ�(»Òƒ$7)ðÚAˆ ûH3h=/»Ö‚¢è€+ÈŒöf“œ{qç車ï˜pV$–SPIe~ùU&Dø° ÷8»ü|dôXW_”Y £»ñßèÝ…�ò×Dp—ë ヒ€ „>ÙÕèžÛfÐ�éδŽ8¼˜|Ój'ANyë︋¥W0 È·lÿb½ÊÞ8>׃\aÀì>ö<âã1ö€ë2€æ÷ášJš}ZÍJJ ÄõH¡í%U5vtþ=Ê‹9sÚ(ö�Äõ¿š%80²áÌ�×V´ù!ÁîÂ÷ÊëV½ïÿòOaà„̺V×å.´€@sTfªÍtíÁ�ä°xÐ$XĆ®—Wä¤,=2uCÉìg9ÉÂIÁâ•áÈïù 9lPˆbE?ðÝ:ëö+ÇAßÅLq5bC(þõÀøûµÛB ³P¼ÊAÎ.âš»_A¹�˜É°y„µtØM\¾ÿ…ìG¡ 9@‘ªBk…ƒâjŒöp$ò�u†fÏŸR^ÌZaÜ�åŒtfusBñÒºÖbë}“üoã€÷+µÇ<|?ÈÉ£rà, ÛÖ[:¯ëª»öŒ‰Û �_Ÿuícˆ †jçH>ù­¿¤þS;%h”�1OB:GÔiÛÁQIÀ«sÙÄI¯éÒú ½…£UiÛ D¸Ò]¬Èk9œì�`­ÛÔiAkš@C™"® è�8�Šç44‘Ü}ÿ²Rl§ÔwnÈK»Èˆ~–)m„6±J3õóŠÕWlÍW1@¯gÇíš7)1ç±’‚ÌW �BŸíÑɽFðÓ�á/_�î^Ëó–úæy…Š^¶ÊU|A8¿JÔÉC÷}�Ù·C�u®…£'”Hó¡3_÷Î4Ы�¦EŸÊ£qNg".ôøúЀj‘mðõ]çx!0ÛsQ‡­;Yz.¾}4šÎ„ÑÁ,òñä£/‚ÆÂÏx:~ý�,:áÄRÒ1æ§ú’ŽÓå%ÔùZ9ýÀ·=¤ÎD)¯ DŒ A®ŒkhŸõûnÓ°«—;gŠjÓ»PÐ'íã,Ì…»íjÇ:´šÜßmDwÂþˆ3² Ù§v"-|'H'|£ô‰¼|Г2]mÛ7àæMç�—»&SŒ– L)öG,W* ¦œ‡H´iG ¯/©ÇøX{°Ub÷•º‡qª©ô š·ç^Ž ‡ _�Ø1äŸgbŽæ{²w›9œEéãFï–‡� ~²®²%ÐÒµ²ëùzúèN}+YÆw!Tò>â)“ÚžwL{²o’½K<áM!ú�QÐ"e³ï¦”ìóq F"&‰ N±[ñK“§»h—ßËAk�6ŽB|Ü‹;[-ñÒ7z)Ó÷?p 8]5 „õéFŽœ¡f`ê0«ÐW)B°B�¡œÒˆ-b®�ò£=¡Í>Ô2º ŸîH æänÝêéßÙå–˜85`O„„vÞcy´ê…ײ’CR“XŒO“¢{¶g©khW*î–´ª\V•$€¬8 ì#³¯�¬w:ïJïo9ëÕYDœš Sæ BÌ V±ˆk7tiW{Ðõz܇ö±#éæß{ë )í±¥#êOPXé#¥p‹s˜Û]�5\åm%?o�8[1÷;&Ùü–Rÿ–ùQaɱýW£àø¿˜ñ!.8ÈŽ,7°ú Ê ³Þ Ùzk׺‘TòN$ Oç<·…ï—þãI®¾yšRø–ùŒ´‘áÑ‘s›é…>ʇ˜Î9�1<컲ò�\Óy›hû·M>•"ZøçcsªÇìõµÐ‰¾YÈì¼,×”¨fa��šs¢´Ëª$øìÿe«žµãò‚oñ.­?O!(Nv¦t½„zPj%éK‹×Âêý·šyä7�‹µÖÀ84gÝíúq)øõ�õ¨þeBoÒ÷ûôg5ˆ³Ð›«Y*‰†®fü™Ò–+kaeó§–o³ÓgW‰é²nyB/akÍc“gÒ;UHQ—]NØHêÝ–)Ýþl"ÜëøªMñn´5•‚)Lÿ„—ðTvá9±Œ8Ù„Z‰¿âôa?”ÛìÓR¶Ÿ€lÆ�iÁ[2‚Mÿì2Ô¼Ä!¶U%WÐðÊDµµò‰,J3Z?E2–~E=À Ü/xÙ½hóËr%ÇRBûçŒò—„tš’â9MÝ@ÐLD☀qÖ�ã0ß ÓÝæŒ¼Oܵ ‘d)b‡ÂmÞ ¤ö�œñ˜¿þ |?’âË"Ç.€×§]¿à€}‡¤Lâ[×LIÒ8ÿ»þÓUPÜK5«k<-……uîÇ:Ú Ð[]Iè"£ñ²…'�å{¸ÒEd¹7G1tÝ´?ÝÂ×>_ì«Zg£Y"Ö§®±]þ~õTòŸÂcwå¬J£¬C`—Üx;°eçbÚúõñ3;>\ç­bÍ� þ‚�¢t.¿ ©5Y2?â4o—Í 6Ôwè(¿U’óeH%3C?;JßÃàŠ(Ǿ%mÌš¬l÷êY›÷ðª±mÑjé�w£"É~qÈo˜¥„9w#×va’î!°Í‚È|£‚’8`ÎrÍý ‹_Ýucp3×mL%!h” žuU©ú§QäÞ¬¡Ü)`'Äb '£TJ÷Ãþ™ÊxÏç«QZÕ"ðöÚ¾¿Ëy¢VHò§,7V�<Û�·ž%Ó†¯ ÔÇiÿG{@Ïæä¬î¶k�ßœàÛD!þ£ïvãjÒ~´S+›Lƒ)�™63첪 :ßÐ)Ï8ï0êÚÅ»XGÕ¢\e©Jeä�¶÷C3ëÑ‘8ÏÜýs2@†2iH ´…;Ù9HÙA¾Pç¨m¬ND"ôÊpÉ3’¡ü“4(⩚E8¡ }m2i.b¡Ÿujõ­âR{Ì”ãð’¾cwziŠ›“_x±“Z�„_,@ j]s¤hDº^¤`½ùb܃µ“e ÆŠ²Ñ4 ~à[Ž+›¹s ç8 Dp«Bzñ€–«—NêàÂèù€™3ý$RxO�Ô‹ÇónßèÇ{±¥¨Ëý§|Ï·Íz횆sF@ª§næFõ&}¿Ý!: ¥‹Hu߀ˆg/„× °…wòið§ÂÏÓÀIñ–ôƒ¶«{—¥÷s"i›IÚ)ùc믛lÌzqÑ0£—S°`nÒžç |a�…±“áxhZ{Sɇ/µqö% aß’/y½Æ¦3©Ý¼ý¼¦þV)ñÚ§Šæb?”)Æä¦rOD¿í].UP[R‡4mù¯Rôì3ÇZ°Tyý±A �ä:ðÊ^czg=HHV…¶Lt%ñæ�º tÿ¿»˜¿¿ÜY.Þ§�ó±œë¡\m¾Âyxvƒ¸ÏÜ$Õ[2(÷eÜ‚W7<ÏÙÈ1ïîÇÌ+ÑSÛ7{ÐêñíÒf ‹'¨Ø9nï*E]¤ÂsIC¦8t¦Ý)Ÿ[®Òƒ4Ê÷LŠÓðÌ9¹‰¦¦Œv'#çâ‹Bqæ€ÐF cœš[„4Væ@‘U½ª,&‡²펙;«¥Èv`.I˘V•V$D¯„‘ë°Ëc·Ç~Ü1‰]‚Ý£r�šõˆ÷#ç,·ðº÷FS´² �ÀŸRl~…  ¤ñ¸¾ƒDV ïxcùÉ¢0>�X´úËÓžW¨´ÏOCØ¢–¤‡¶mǪd÷«L¯X~Yæ'å�±òÀî›à) r•AžüŸ–Uþ•#Ýb÷&ƒiô.¸ #ôs«Cb»|Å: wËé$¦y/6E¸é¯§¥ƒ#"%Ž'äºUÓôa™mïcDu(ž|¶Ï¶œ™c�–ÿ¶_í…ç¦iÌ�+½*ñ­Å–R`“Ôê˜å‘ …ÔîŠèOëžnކÎ&ñör¿è?P¹a½«çÓ~W´å}ý9¢Ÿ.È6Ú'§Ã÷HxÁ {ƒEH0NzÅZ® ‚}¹+ë©E¤9“Jÿ¤"& 9V`6¾ ¡/ÜCj•£ˆ9Gb|($`˜Ô¢©–¶>ûj2j#Â]£Å„Êâ�—�гý±âYuIPÿ”›êÔ·°.gñÖ4;ÈAR=Ü[‘ OñµÂ‰X„)© ~˜E˱7’ µ°”ƒâ‰Ðö} 4®­¯šÕ5‹^�tÌ2Wp1ÞþŽ.C¯I{€Y¹òzÙ�®‘8ÜUTð?Ÿ²õžcú­qì68œõ%߇ÛA•²1ܺ¿¾ÛV?•Й„Ú2[o ÀQn4è–— ½t$laŸl’¶øŒ6Là9Ÿ°!âfçý ´Ïk.;Ü‹s(+@±}¾«Å~øÕ¢aÌ ¸Í©¹ÈòôEgnÔLìd"QÒÓè£Ð@JsÚ­×tgƒ8¼êY’%[FÃ$µ«Û`Ÿ­"ØçÑçBÃ�Xc¡OCaÒd¬1�]»rú-‘ÒÛÔ*=Åÿ„~b£^@69`ø‡€™xªß�™0!°|@˺¹šIýJ»·yó=�ðs0¸Fº—\ÊØqø,Чö4ñLö�6Õ¿c�—Ù}õxµ•ãc]RB‰é>¬þ~gŸäxÍ"»Rœ^¿õÕ?$Ä>Îiúüî¤ÛÝ�î€kÙ5Y(ì†ýÏ:WV­âyfÇ/èòl¤Ë€kN“ç º²?ÌVýóÌ+ªöìÚÿæÀ=ÃÈ�„Ýdé´Y^=`+XݘĽ`†˜ÈÒhÀ�¢aaÎØf"¿+@ "è0€–8ˆ�Šxü¥Û£q9Ä~¢—.�n�eäÄŸ­å‹¨è˜VÖÞs�­FµöÆŸjÜ;<ô•‰˜3<¡L]>ä\ýSkÌl$§Ÿ²¹$a^É"k·sxt7å~©Å¸:ûÀè\NƺîZ§x#að 46T*À¨d°L"e%úD5J)”㛿Î�ô7XlÅ[µú‚¨AŒ D9ªæ�^³g"l÷wlÀ¬Ÿú±U»ñ^æ4b½ÜƒE#{B¬å&zYù2ú9KE2döëV�RTw…jÅc>(G~STµ†Š…o‘tˆ€D¨Ëúå5ðîP†yï‘IE œœ¿Ê+�ÃX•.¨�Ú´~Né‰; »…Éô ¥óø2©öÐð0‹îÌ[ba"ÑU·ŽÓ™Úêð1 ?Yë³|òìðƒñç¢ù ÈÉ8&Ekì3uMCLt.·�šÃº8ÕÓ`ßW¥­Ù†ÔW§o™á¢Ž*sð°f&Òýkp)�ÅHšü¬aÓ8¸¡¤šy)Lórx— <%ÿ‰“»>ÃÄz­ž×ck9 K¾M_yvy/~ÌAý™§){OiæYÚÓûWê=õˆBlõŠ—{NÎÑ øºcÛÍRÖÃÅ&Êrü–NÁDãø�Œ½›÷)’mÊ$\FKÍŒ¡ú�3«±ˆ0¦\vkŸ¨GGª€’·Lg¯ÝÿÓÙd]³— Ž[»'öIméuÃ{:„kÿ¯6ÄÔšO²J†]sbcâaÙºHë&*ŸßÞ„½¶ÿýðnµ›ìNX‚ f³øˆ¯:Ôÿa²?±:Q>¿ðt›Lv¬pÊUªÎ­Vz¡ç3¡“諪ƒa\;s–9¿‰Ödä¡¡Ö×àáÆÖ7`‰] J˜vkl#á]4dlæŽåìÍ=òµj§?´‹¸´¶½^;^Fh)ý3Øøå|ø¯ªñi—ª.)1FªÜz‚-ƒæGf1(fÆ- ‡,H†œ¤TÔÇ´$Õa–Ç=Ò;AÅ¡uÄépWz±+žõ"åJ1AMª¥s«b;ñi4ýê=¶q‡:íx5Ûš•¬Ä-^á ÙL¼(¥–_·„�äÚ¥ÔO›0�«àsò䯤eÌüS¤â¡áÅèxvÌ�ÚyÈ…4yJaPùÄØbÆ»+ƒÏĹM'©CÏarÏ‚©Ói«%¡fråYe‘ ,ç« À§©h:Çï yë;Ë‹(Ð6ÌÈ…\6ã—€Õ™V³”�[hëpûÌß$Ö'¨þ¡Ý�bäè0±¦HŒ÷Ë_OÎ]šGîEe¹�e—ÛÎýþQÔ Ò¸ò„½Mr Y ÚÄû�-©¬N C #'’ðàæüÒWÚÑ1Í×O±û‡¨¹¾,RKV\Ï HjâKuÕF*Ž0¾ñm¤d­ŠÜèÀŽÑü±/ÀûŠ�É|.\ýJX¬ÓÉÊÅ¥dÔÙ!s¹ÌÉ�X%‰–>€{Æ}~œ÷p�ž%‹ ¦�#ÆÅNTKn4ºš³ïÅÛvfÿáÚ-¯tÝw„甌×xŠW–ï~ÚJÀ'…{Çvìz:”âÄòX£�~‘ûD»y-õ£EFät+5’1y·ï~Îò&D¨–Þëd”A¢dƒmÞÿàJÏÄÁ®úª�gãÊÜ“ #LŽç¿ô¹ƒ'á�¡´€”Q*ZÒ¢U6S¶þÛÿÑä,È]rºÝqm­`±Pµº¹;e ³Bׄ;¥ÿì”ïñfè [y±ÚéjÚbÁ$¸xß]òe§æ`‚Ïü™¤»XQAfõ ¸{òšíZ)\>æÝ¶‡klš´fz˜]‘¢ ©|‰B½ô#ôj§à7o³�V‡“×Ë)Š×Fb`ðé?eµ€°Þ�„ü“T®ДÇ2k!ºŽö×½'б>»È\Î1oó߬íå,­í:û:yréµ}e‘VáÏÑà‹–ë÷Êw“xHv>ù¬~];¹Á‰k�2{M7¼MÆX·*éÁ‡áÓ#I«§E íØ«U©t©i^Bˆü�ãnzš×m¥7$ø¥Ú]¯³' Þúœaki*²ˆFö”uÕ�®m<ºŸØœ»-=¨“0í׊�)@ap­¶¾ û©ü4ÛO\ð!ƪ,��iXLOÖ}šlØš!ßßóçú<™Â«á ØUëSZñf�M c›‘“è=J."¾€“°.úë*H|–-IE¥®#¥Í/åÝ 8E˜BfL¦�ׯ·íîéA/>k¬·³:¿�‚ë©yR=zÕŸZ íž�¬.Œ“VP&óMs#Î…†£Ђ'Ž›‘ªê˪ë²V:÷·5ÈÎ`À}@¹ª lP*Ä+KÖ“ÈI}¿ä�ëXyOÑDN»·ˆÝQ{ƽO± ÿÕ¬=a`ZÊáôeá êK�Ã$$¾2è­™êZ 8û éÃè”àÿjû‘n™Ž…Ê5÷�°O!-ÁšÛŠx4ƒ²*ÿ3”ó±^Hûz*Õ³w}ÇÛÛF•«ØGªd:ω¨2©{:Ë;0F‡KµG~?#/QfH&�­mÉWeDòîÕ¶î˜ÿ\H¸�¯Å5_T›)5'o\ö¼=ÏŽOC;iÖRLñ`/•û‹0eVcNÿìÿpþ]·õå!Ó3Z»Šx9»5‡ãŠÖiª® à,v.Ì©OìÞS½Ø+U©öJhÍÇCÕU{&÷fDµÀªÙ¥ØÛÒ2Íü—WÐF¡›Œæd@( F£Á ò &ó êc\1w�hD"Âj‡ÿZë»Wá-Šò™ÍmæNäE„J‘1'¨œñ� …!rJBçêDe@ƒ)ÕqsÌÿ˜JvÍÚ­£…} 3Á“ÿ¯«�vÇ—§�ª/í»RwÃËHzÀ£¯œ,(maž&ò½ëež[GØýewêšœâÇJ ‰"A¸ ÛŸÁþöBÃwa .è¨W±gRÞœ˜îà÷¦$4tÞÞ/»õm?õç‘, ÀõÔ÷E]ÊUõÚKŠŠ·p|žÔë¦e#šÞYD‘À-2'GÍQMR¤¿ fPƒa+ ôLÛå(!ô>ÖP‰•þ?U‰Ð! úâÂÊP½&Ú}!­”XñWع[µãûÄ„p‘–¥ÏiÓ+×îP’Ø2º]øÅ]g&„rOÿ }� ßRÇV\uœñÉ\"¼Ùò �+j¦[NÚ9f©Ä] ÉÃËñ¬ùz6@õJŸÀBz?”¨H!åt׎&–2/vÿŠÚìúÂýÝ|5kiAê†p˜y­Œ<mbM‰¬%Iý×`3Ï"y‡Ê¿¹.3lôJt›ïKø@çÚUùýA:½bÂðùmC`O›ÇÄ«Ƭ—~Íæ»ã<¸‹S ®b•Qq—tË;k¯×v" ˜*/ŸžÒá¨4»ÌúЈÅ%̺ã½n@b`_üÙý:F:ß²Ç;¤A ”¹ýˆ,þýr"]µûIV¾ïÐ�ðèH5¥¬üz[¸Qr¿3!c†Ju@h¸yI (Ó‡Në˜X$#\_Åú�óÑÙQ\ °  þZï˜ 9% ×ÛbÚ´ÒäÚ–©Á õ§–¬K@>»‚Í[ 63…j®·Ç�†õ˜ŠŽÊþ…qÖÇnOXA•…;íï”\Õ¹Ôj€Ô¡ó��åÇ MwûÞŠ’a®˜D) ‚t}65S×�b�Kå\Ø"ÞoÔHO * î¸v¯m6áÜž`©�Z8…Ö1ò;é°PM*ñT\½P€·Ñ‡ß‹sç”Jâ®Ù¯"̳�}w'ª-¼3~£…ŽXN³zç3—Óø¼NQWhaOÀ–Ø”sžµLQs| *�¸‘�ÐM!ëE\ƒQ;Æ­6Ü}™ ê;Üø¤ßã™0éâ’+�œÛØ'A;ªÞQU»’d—ƒô!H§ˆ~¢m>Ô4¤ä9¥ži˜Ó®Øú�!›ë'8& &š³?µÉ"ýÎCOé¿c²•#h¹Ò~)2±?7=¡$�.‡ë+Z0Š7ve¨Ë\ «ŠmÂgQÃZ¯e��‰;ýÔ²žrpƒ…=-Uô *\¨ÏFÿK�âJÆù¢m¥�WeR’�ô0«ÖÕ :zâàê.7@édª{‚‡º¥—ås,+à)­ gòª‰8$Qü¨¾; ;ip.³dÚ—ð»¶y\è¹Ï«ÁŠêN‘µ2£äåwñ›3¢‹­¡ÕPÕ‹-m_;JÛ<¶–âäçüšª ëôÃNú¤ hË£*zå.S»:È8q wk¼ÚKÅ+–hÝ«õ‚×Jñ°3Pñ« �7QIv}¬­Q¿ƒp£J„‘�ØÂµZÙ‘önVFã'©Te„€ e}Ns r òãwÑÿúŽß^.³Á~kø‘¹Ó'_à—Í­;-÷3³kÅ£ßË~§ FYÜTÛ.ry°¥åç‡VŒÔ�6 9”±-&šÅQ=é°�ƒUÚªßS™0Ë7•�}\u†v¹:~Ù‘¨‚½G¯tÍý»P>ˆNøMyë΂`“Ú¯ÁŒ¯ì?ÏþaÃ?Øò骛bpm´Øj=fä9kõì×v4s4‚Yï¤ÐóÇbÅÇéwPe¨‹“z+÷Ç"Åй‚”Rl:âöžoå|�,C%òœêÂJM wnG>ñœ”Aº etß+ϼÐ1>»RšÐ£S¿ŒpXgD*GŸ€ÞÙ÷!z¡ÅÊõ…V#‚ S\|;{Ó�üVÚÃÇ×¾®_c6iöåÿJ» öUk¨¼/hÙŽËÿÃÆ3©crŽÂ1fݾd’¶¹±Ë fòÚ59›v”¼êèG‡Ûí+3£Ç×+´”Gy]@ö´¸ºxìü³: ì!~Tøª¨˜‰ gz›#û“)—÷:RªÍ`®dÝx]¶lï’Ïÿ[·ßU-¼€ë á·Bê3?”Å2ê¢â¢^ï?ž›[Xs3¬©ö~Y¤1T3ßñZ8vXU‰Ò‹Þ9÷Žàï“Ñ£ÏÂ^³rG·�/ÐGý¸G 8¾dñ�Ó‘f¸_ƒb¬GH&+n­†뇉jiòFÚáø‡—´wZ}€9yQ™É†¿­Œ0üÑÜ©%êôÚ*WÙT„ë9Ø7cÂú,ž8 <Ša@£¸4½)—CŽÖe“5èózŸÜEË?u�‰¡1Ž·rþs%NÚúÁ,¼Ã£ü¼Ž7Üfd—င*,S5ô¹(ÇŸ†z˜1ÀROùœ´ý¡‹ %Ó-&J%cñ΋"=áTV¹¦kåfBÃh�óŸ:SÅ ˆy‘³Jjåƒ'e#_b$¥_c‰ŠhÞ&îmL|Fæ½8;³ÿŸùëJªûZÌ%ýpBi!ïqí1e O±ÃšáOoŠ - Ðß]23“Äx?¤u]ï ¿(» º‰Nv»# ï�Á]0çÄ¡0’lç Ȳœ½ ¯‘5³Òì×¹©Îñ�§£Žö‚wd:+Jø=J€ ¾Ãéàä&õ`h;Ö �r =ÜJ9¤²âW²T±beçdàÉ_fŸˆ"xª*;Xè§µƒ�àtwÅN ³;P;¶ª$c~^iø†’�BŠð\êÚž­°–˜Òà0…¥ ó˜97wÇ2ßê÷¬�Ç�ak’¿¡b= áÊ©$¨E/‰žì¾±¹˜qÂIs�r6kÔÇ“¥¦½” ãW=¼4‰ç� ¹}dç%©<]“Ýî Vp:É{'ëÊÖç¯Ï{}N`Âô€{�™w/²!KnÃ0£(¾àoR�i– �L�N�™¾æ}þº–§²@�;fL:ѽÜÀŽ›¾•®T…Ë ‰NrÐËÉjQUUÚcêNrÑ‚––Ž©ÑDÉ=fl%ÔÂ5Ó– ¡øs¬×V›Ä’jdÐF„ÉÆœUôk®þŸþ¢1Œ¤”«o–2Cï`dŃي;+�ų­zìzù—¦·-ûüB¿—ƒ>Ϊí7ð‚kÍv^ Áÿ÷‰Ÿ¹<Éæwþcgø0†w°žŽ>à×FQÓéa^>Wrï{²M™I¹(Š”Ê+»µ7f÷>P®Æ^ê?â³Ë~–‚>˜öbdÑ ýŸ²r1�¿í±AÇò*În”اšêÜ"|·Òœ9s ëáÕ™ÈlP¢+ž?Ã?2?šR7(?­ð�«T1ô–54~bú‰hèšÀÞD¤spÁFèxEM¤ëåÓv‰X�(5ú‘YlžÎŠZ�cÚhâãú|®3"K· Dµ¼ÑVÙoµJî~²,RFvù²7âÏë^aì°0%¢}Ö)Ð.�Œ,¨;^ù©]8ü5$Â4 Ûwéj½ÓkF‰ÇñœÄd–%ä*©¸»–óúVèÚ%¿i9WpŸ}-©”|‚8‚ ‹g±c!ž¸!A˜íƹ|R)N¦lJåÌ~8çÿ:³w¼êDC"�Å­×õZ×Ç®Œü?¾»±¿oVyÂV5"~eBkÎbÕÇqé¬{!Ÿu^$þ›Ö¦ÇñèˆbeðÁ«;qy€ÜÖøÆ¹8ûН?öV®·ò ý)l(#*ãVÓ+–UÊv¹™ëœÔEM9;ªìlGŸÖc½šæÊHsiï…m¤°ªÀòÄaÏè©B·fªq§Ô ñü„÷ *8'"Û–Êàn„Øt·e˵í(|“›*G¢{1[ÊÇn%ŸSâëšB° ûV-T™-¸³Š¿^ÛÜàhÅ5•ã岤jB lí%zþûû°B{ß<ÙäÊÅ IáŸãR·mæŽ"ø¡p|‘Mx�èÍr˜ )¿½ŒÔ¥…Šb6'¼\1ŒÞ¾ûm/ºÖ º›%i=Øé 'ÎÎ Œ«ŽÔ5§ËáàÄ>ÁàtËY‰àS2êC�ÆÝ‘Ѐé»1�²ìŸöñÖnô3Á¿ÆVÐÞû(¶†up»;µ>è…·¶ÉN]ÛF2zÙ®úÔgO 0­…;ˆZøÝ�ÞÏF¯5œ0 Gù•-ls/TÇY% tŸBßI,dz�· /ð¿‰í–k›>õ¬Þéb%ˆ¤9ÑSÕ®ƒ#ô¥ÁR©…�~c÷IN:+ÃZä%]Íð†™ÐI¿ª�¥ÝSš3/Ä-^(sÙ¯/+ÙòéßZU¿�Wk·j9ãŠðŠÕxšñiV(ŒøL†tÃa| ½\MàlØ¡L·ï5N‘ënm ¥ƒâ%e˜õÆùxJ‰ùFUú–˜R:gmIM |Žâñ`Š¢ož÷cXUÖC¸ïß®ákÏ"…5÷;`)‡A}ÃÓ´Gà^>hÙ’þ§×€§Ãÿ]à˜4®ÙUåœË©vã‹îˆþf« ßy«—êUÙqaw…§!xs¨íÃyÆŠE½X_óFÄû�wk£ ~v(ønÖS¤VÓÒðí±%•º'çï·Ï·¹·qŸ�%Oǘ�v‹¹_M ÎŽ\a›kã*ÉŒIèËÁ•‡éû¨�Á¢3ì´iB\‰)Cܶ¢êÐäý(ÄÙuWÓ/‹v §ßáp|ñ%ž÷+ ÊŠdºæ}ã`oXσdÊÚ)X/ Î ëzô:õ‡T#9ÅT hÐháL1rQe» f/ü~ªúiÄ dl PnE¹zY­éêP®ˆŠ%š´ðÆ„±ê~#�Ñ }–v?ÿÕ Ù ]¿[ân÷ÿ¯2d¡|Šßm¯‰„w(•ñóŽãÎèDà•/í£ ëÒZxNÝ'c‘BÒl4´Ó2zR×é(?¦¬±+`,lÉŽĆý�v§†õôœ(jwõÄO@Û Ìçõc+<èm Â]Ào1‚ÊPù«³¶“6øúY»7ªöió¯êðs\H(±Ý1@o-S. aóQX¦Þæ#6]&uÿ†þâ#øiRSHuJ+­ÿ+¹Ý­í*„Õc³èÌ8EP¼ê˜a"hÉP¥î�Ü[õdBfCƒôiXöˆŠ‰ í|sÜÄ[Û¤ÕT4€Ùô%ºFÝÑ}‘ýhlúµôR3õ4'žb^´júÿ�Geù”) ø0À� §žpbY»9�tÑFrvù$/p ǹŒ“ p-'¢ƒ`þ�I­ùé €Œ1½„w‰ø2 K3}2ZJ¸EÊ*SlÅ#é;c›™.ð;fXQ06òXY—¨Dý~·á{ÿœ8à°¨Œš�×uœ‘Ó‡ÂvwÑ˃¡„T«ƒxŠÀ�¬1KŒü6< Ù)0ü²¯—13ŸŽÛîÿa‘ âþ"4­š, œP„«4'®ÉÕ+¼2¶‹Öpqw‰ŒÃÑùø<ThÔ8ï®Ç Â{b”x8”Ʀ-ò†Ôt|ÅccHú(R®„'¾‰ð†# “ÔTˆ†U‹HBÉ7¯Š’C¡é™}Û‡f‹Š¥+rÈë‡öŒä‹â2ÂèâGª*‚Ѿðº9=jóeåã?߃i߯?„(¯”›¡Þ’ }‹³Û»]ÐÖúÀ©×ÙÏŒÌSVF­|Yõ<^¯Ñ…|ÅvÙZ–¾¶JÓuG­ ­ë‹fæ;£Û¾g)ŸÙüÓ­ XЛÄL�8SÇ„lÀ̓·Û¢m±ƒ0¬-$Å^KbˆGö»ÈNӰч¬|8R·$”�¹&Bºôèâ‰ÏH ¡CÕøK* Úgr/äê<ô•¡3.,/ žeª…Î÷ Ù$–”²îê¥CâÑJ¯jšµŒÍÅÈ…Ú ŸJ�a<¬{“©*ZØpü•Î&(äÄ�V…tT€ðT�ÑTœ#.Qhƒ™Á¿Ùsú�ÿK¹¢ ­âÔæd�ªq³|± fÞÙ¸þ3Ây[r�ê y„�[ÿø]‡¼…$Äg|eK­8„®’÷e„X/ú =NÚz³Åþ®bÛ©²½¿»%„Ö¹J‡nÌŽ�© õÝüÖà ßaŽâŸ1ÅH‰‹79sÄèvàس‡þ±!ÒÊ”·Za„‰¡(NÚEGHɱ ±"|�i¹ïdóÀÌÏײ�¸=Ì �Ýðü®Üî-'íZíè�ÿ”kLºù|SÚ‰n¡±ó�3$Äñ7$ëÉgë´k& A�‡ÒHI#G¹9PE %¾F‰ŽS<~Ê×�ÍÖ½g,¶Á ‡&÷3µžûv‚,Ýﲋê]ɘ"bÄY 7u.½C×±/x=Fh‹;Pž•\#ß°H§2Ô3ÄÑOëâÿ€û]íÞô¶¬o ydKƒ{·À¸ÃDe0äÅ«G¤ãÔ"V·"sà,«TÄ™™¸°&Q1@ø4Òž  „]"îM%‘+Çõ­†ôKf¾:rÁ#©«ì 9âÉVQ ù¶¤ÆÉ§q àÙ¿ÃS”5É�7¶M�Gó •ᘩ‡yB¨¨ag—I�*ȵëðU“ÕZÕI3˜kÕYÙL@)¤4~‘è•b;ªøcY�ã1á7r8 G¬�YÔz)ñ è€'²¯¦Ï<ííGL–6ß"Çu¯�¡a›V–xÜQ©%‰hëEX�Š%æì]ïÀ³‹oš0 ?¤ŸãF‚§vT%ËÒYJa'Å;_èNH7üÜB_Œæ~OFi%*È5�åwåujíÄfÂ]ÖföpÍ¡Ž>e’�ʰj<Á£šÞWÄÛ!vx°9„*áø¥µ‡W±ÛϼõºxÑ´§–£¶´eò f,y‚‡¡ CÜ>[«_”½)[µU[Óßpîò�ÀTdÍ[üÍuQ×£‡L(Ugu¥Ù•Ž]ï?ÂE’[jк1¶£VÂt·^D ñÊr*kú_(؉ŠÜ;3‹dx7Œ—O¥=¥™öõ°Ú6ð·ál溯½ÓÖ£ DÇü9£Š/“ ÿjËÒ%8”‘ �¨\ìžN#{ª�EÿÕ…]öÊò+üb�w ­Ì{µT[ªzaÞ™g;`üîÆ«#LÁ×y_Êzæê'ÜÝl‹eëÊ–Î!ul­Z`tn‡Ù•“¦,k*NÛ€­²6̼Ýå² 0DPÛÚÿw´ÒË5ÕˆSs�§m,à©|ÑïZD‹üW™ueY¥¾«þ¬¥G92‰^’jP{«h�LuÐü¡†ôE¨óY[Û\0£§&÷-ïEÄÊ* TÒÍtm3Kä¥ý$c�V¢ø±è,Ê#ïˆ~ýüC«; nÐQ¢A) ꘤¥,øâŒ‚`™¨³8eÕPš&÷l®¡©J¨lA&Z­ÏõIMnâ x:F “´ˆNÏñ{¸‰ýg3!QL©QvþâtÂ| kqý¸–‚.`*ùJìš8â÷·LVlò^†£¹ãÔ¸]`ûu`6FÒ7aÿøÕàŽ²¨Bã]!oÏ`y„ 8Ù£3sÜH­¯bcåˆ�ú?`üûb‹¸Ú�ÓŸØÃÜCêÝ™)ÊÉC¥•L,ø¥OÂÆöĽV/a›<•œz ãf/ò‰NküJûÈÚÍM R4°±Îh!»D¾�v¸´& Éeû;u`VzV®ç­ºÎ³×��h$¹m#e-„´9rú9¼ó“Rä¤}cWf¯Â*ÿ“È ã‹X•r”9_‹æ ÞæDéa×»µñw×ËÆ¡ÚXlöUh¡D°_©pPþÇÐ&Šÿ×íºê}/y“f°:ŸX¬ ¼`„A,5,À2yžn—²™›1AàJìQ]ýá`»1°®·ÊMŠL;¿)[ˆë:@ϯ p®“EBCÕD{´E° ÐÇéoBÙs(!�©5†]›zî=Q•�imï-èJ«?¸K0#ɺw€$WiàƒØðyÒtQ뮵È.Úá–ˆý×ÊߢÜnÄ«&Ôn®Þ›Ù´÷žÞ#En‹Ë1 ‘‡t•Ë�‘,û°&Ÿø®b>fçÞ¤7*|F-ÂJ›f‚Åh*‰üç#…WÎ䨄8ViU‡‚ýÊÓ–žJ<.ߟW˜ÛämטËílªÉW£6›Ó„Dº¢oàë:W3™îêï)J ì”2¿^ _Õ&üû e>Ãw,0B-¾©5IÆD£är{æŒ`3×�Cwï'Ê-öyäü½çsà!·Ñ-õ.,øÅÝ3!Úi‹ójhk�wˆòê×CwÚ qâÕpËœüm׿ ø$´y8Nj©+6îÔ‡(ç3ûßÝMVÏÏÿ´‰ÿ;¹4“)Tt«èª ÙÍ àÌš˜ÑòGJÝË�%qð}´N¾ã£Øj>Mî9!(£‹ð9Óµëxæþà¯!Ô¨o´ëÅXjqÎ5GÒ)Ý�%T—ÓGf~ç‘„Y-Ó‹ÔRÀôéÄÁà’ð<Šl^¦jn·ÈÏ¿A�¯¹Ǫޣó tù÷Lü–bļíV�ñ4þ–¤£©\(û‡Ãúó GÐéÈ1»ã]زµ5=Ê‚³÷‹áo|·…€´Î`�½M@RáD5A'Í3+Ãâ•21 ��N.P‘ƒL�ƒ †j€"Ýʦ?¼ï> &׈—²:ºlááyMœîyͶ¦´ˆQÇéî÷¼.Έ÷•üÏ„Æ/V[îé§@à£Ü)”©¦�3S—½á×¾$ùeXdwÈ'à]:šÑZ˜ž(µµÊˆö¥ãíñߥ;¸Úy|=§œî² x¨³Tòe08z�÷�%|hŒU㟮ß-½ah£|UçA�`Õ£-Ri@„ü!8Äð=ßn³&?#AtPt ûŸí.Å=¹vÂf@`•ËK£÷.Ÿ2a°¾#ÈYnXYã ZynŒW-Lém•;äL ¨À£ˆÃ¼°ûHÞ/ºÖþr[öºÙ =†qWfüNĹ¹+þ€|š?Ç>!öa2L¾+~È¥üx\à_âÔƒ™PÒ£/ƒ±™¥u¹æOg‚Ü5¨VÈsƒW9-)jZ…©dw±M™¨ Ù�ŠQ[7rm÷މ‡×°Œ!ÎJ£8D�XArb… F·Å®}aq¢õ‡ZªSmªékñh­ó�91tôÂí˱µWîJº¡ñ�EEœ0ÚèîdÂë©A9ÚÌ2àuðmèÎæ%ý�Þã¸Æ¶RºÛë¬�–TˆWwvÑ2þ{½³ˬƓFD®»wd.5í)^‹%SkàR-Ǿ¢òÄl¯¨"èhC)«æ&|ÿ´5!u�(KôP«�#¶-š‹\Sð‘ö^´Fíô |@œ§%Ms_úíë(â[ÏÑ^€µjë¾®¤¬±Àf^Æì— 2óZiùºÛYµ{Z-ižæðëqb/§Üë¬ÜØìÍ™¼÷R»&êåñ ~âïÀJ˜.À똟8¼ ÑĪ1 H»J€Ô#L ;À�TtcysTã‚ëøÈÔ¡ò·�HsHHøÎ¬%8)½K Ì”YeÆÎ"e¯Ã®ù¶gÊŒ¯éì£éÁeÝÒžCÐÕѤ1Š=ÒàÃbÍ UþÙŠ¼²®�pÜ’|Î"¥ddŠ ±5bGÖU†ò×Ü83ä=LðϹËeÅeTŠÍpe^<îŠ8¨Ú±ªëvŽâ4�܆àM°‹íNŽXfݪ–�e²¢+"˜¬ÃÔjtž22ÙŸ~…Jâ´ã@‚QwÎîÜNPƒ¹ÆUú±4ˆ-òŸ”<ËaI˜¶'xè1—Z§‹úyï<�@<¯ö3bOÒÛo,˰øøï[RúÂAŸö¡�6íÆ0›®:ÑB”9!€Æ‹Bh]u•;Y:U­T×\ðíZ\ävã#àΞ“€ :×Ѹ£k%ìòcm\/çÏŸ…�êÛº­ÊM»:™ÇHŒ¡ß UÔvá³Ø#m°íÑx˜_Œýï²Q\…A@¬:~†‹²ei_ØÁ‚çþÛt¬žû¨þYwówÁ-`-;’ª%i+Ë$æmÏ�Å žLçi�Ýv5· y O}q½ajqªê\e»Ñׂ\ö%ý¿²­þœ4›¿èÉÄ’˜ô­`„0ùW¹Eß{ã‘y)ß6cÕbÅ^Àµ£Œ%QÐs?SRg§ÍÝFmcb W�¿N»Ù ìŽbG\«y2ú´*À·sýY7N„aØMp6D檟괡e¿Œš�5÷î§:pƒ8js„‚Í•K.äÜÚ—<ÎyíA‚6Ú+hU¬ U½Î› ]HU!1X;ÉÉêÛT- L½¨U“/eFru§¶°�V­l¿ØÇËñ¡rĬr=ä£-e“0Ÿ¦êÖ Á¿>¢”1ªàûäÇÚ¯ç¡b�åö|Ô²´o>ŽâeI'4p,œ•$%'EÌÀ.8buêÖÌöÍàìäïpï`«ìa¨$"Ž vàÃ|A43ôólðFÇ7!I¿òДÔmüÈ&–h²!³6¢çä\„{Ó#ƒÀζÉ<áF6��ËÉŽ8º|‘¨Öû D^‘€Õ$R4‡W&GH1ù§L·'CÖë*J "qÌ�ÏÃÞ~C•¹â"ª×;“ ¡P &TAb »†• Ú»Ýv»“B¥k¾÷[í„—5ëÚjÅBG�13}´ÛbgxÙ5�¼? Ú¢‡b‚2Šnúè÷ì‡zuú6:«¡”ök¹8AC'ü¹îy |1.Êj¡ñgaŒò^ϸ‚ÁI‹ºÐ‚_1à±ËÛž01Ÿæ¨ë³tźúV…¿ÿˆ`œžuþ"rÁÆÐE'g{v5/ý/¡¯Mï>†«6m3�.6•)/e³Ÿßù$V%_˜Á—r3[M{�zåêð*“ÝLq5Ïœ-�œQËߺ¢ÎV ÛHC²Ò(½Øl‚Y7¿é¾ùÐÑ/¸�[úúÅ�»þV×¾J‡› �“Óy5ÝR âÁy%M¿�ÍCQ‚î,<{£$dÖ.1¹;„]æÂÍäuººS÷•>Pvï°RF1–´9£~iÁ�?pÇÆÙ-û IR;<öóÉõh¤£¡ìBŠÃuß;¨>Á·»˜¤6ØiÑÿbg¢ïžý]Â?˜Wib£Œö"6Yi�™õËdÄíŽëÑ?õ•SºPÿ+p;[äàž=@– SQœ±Ó´Å”x$QrÛ}ŸTzÿmØA*6©ñL¸PBàÕ~Â16ÜÁ¼ì@#Á¯~[ÅBÄ #ÚŠc¸Õ«Òsý~!~×¹t ‰!}î™>ÐRw\Ûròzcs& ‘5ÖÀ Ã\¾Á#‰ð_{‘ÍŸÁ`ô“ÒF�b¿lV†f”©štÞ�øaR‘F}�² ó�à8ÑAO±c“­¶Zó˜ „÷v()Ò±Ì=&æ_+éË.tþA¸6ø×¬1Ô#5Û–vv‡ÇB/Õ§ÔTPº)èW·<#†•�%«î´f ¼ßà Ï;~Wz¼Ï�C÷J,nÅARm‰ÍáþrdÕ¹ÿÎyhfì)†šäº™‡*ÿ¬ÁŒâ©p~›WBÊT¿ÍQ>¥ÎO‡NùÝ]oŽu8°žž`ë*P�¯X/MÈPEǧúxÚ'SBé ²k†þHÚ ~UäÜžæ_Í‘ÚÉLtZ, å #T¯ŸpäÈMrL×%µ6#‹¢EEE¾3S:•×F {3h² Â{¢}cÕNûÀ:ž¢¦ÁÝÑé­·}z„›JP4$}ÛQ!÷À EfÁï DÌÓ¢4gÈÜÇvÁúÓãðâXÎñ‡—àßn:# _†ÁBïým§’„jY\Q¸•1´Ê4¦HNÞMæ˜à�‘s€>¹9 êÝfº©€‹N—²÷DÖ Âðp('愽G-0¬PÈšoÃÚ£�ÛòŸ§ €½…àëeԷɇâ¨ðâá'8‘ö¦m5��Ï ^‚¿2ˆj¤ã§Urž<(e`PÍ Àa Ñ:a˜YBlp×ï0²é7Âþt¬�ˆaÛ¬l7–8뀡{]h?˜—x’ .ØãÉ:kÔÃ!ŸôzÃ��o¢gqýøF»U Ó™ô®Šfƽùü‹TÈg°ÒþÄÑž6µøGÂhrTOçž–û¢Ðâýd:6íÂ×–t ¡ÒîÕˆà?„ËðPš¯ A˜A!ö£ØN‘.:5WE([x óùÂÎÕÆ�–Ü=R§] ›NÆç^ë. i>‘Wªc£×’¡�©óईžã=k|sóÁÍ/9]&Ь×pÝïH ¸Á�Exì(o2Y_Ìþµ¥žtÆSÄÇyY,xìöÓ†ïQ…# Rëqtñ2@Ãðsè°ÁW aÚ|þ•>ÇÊSÈzHxé·b^%}ç¿vÏn³Ñ,À+#‹V þtÎnØ6Hz1B‚OËÈÊä¡ÁáöG?X:µ¸@ÁìOA.ÇÅC4søÒ�nŠ.,(º½¼ÄWŸÂZÉuîPº.&¼àT#Ø…ÄžïôzpW1*G•LÏñEqÿƒ@üñur3±™ XrMà4Ã5W}¥ÅƽŒ§RRk Þìcb1âéÔ/ùŠF=X5�¬¼*ÖÜ�Ü ­òÁ�Òyg}+¹ð†$™’öXé”Þ�ömñï,%Úyç¡âÇ=å\Ïz®ÝØ;z¤Ù9¡9ÕŽ†$:~+›L Nóùrè­¿/¹ÿжå»oô�hBíKýÉët\ï*l[Kó³•ia¥©0 ïN­Ì6Áu¢×ãKºÖã; áã|×sônˆQáÄJ‡†�${…�ÌYÊ–;ÊÍ‹…@»N W� `\©vÃ…æû|S’ìݨPÒÁ-5ÐäZâµÜ|ˆÃ�¨á(f_ò0€‡3yR+Ù.쓞”{¤')�\¡I¸QO)?ø€·%Aæ8ÂYt „Û×^ääs/™OŸ—YËF¶lãy5á,üHµ+ƒž‡– êh'%$]CåE‘�êNÁéÔ­%>ԩѤ%›\�£®Ó¢À þr—ÿÔ/·Yš‹QÁÊïÆh 1æõçx_ÙÁ¦F´oFf¬*ñ·&qÞ½°>QŒQD_}¢I^6ÉYù1¹GDhÙm‚ø%–œj—}žz8ú`tsFÙQ•OBTÏ~Ñtá¬CÙ£Ü\§¤-û×¹™DîYÒ¸Ûa2± _Þ?üž#2h«`éIšxûb!Ö œÞi]p—�lx *LßUgo›,[&¾ÜÎû>­¨'8ZQjK'zH¥ºœy¦•i„þô¡3lTœl@ÕÜ{“>yÓÝ­xtM�’•vkv‰褲ðzH~’Ð…£xÕGŠ_T�kÛf™WÛ.TCú{)t Â1Í&(õ¤ß¿j6.ëT)‘¢xLæÏñð4¦tÄì#;±ú˜ ?è�P…´ì0•Ë¦ÕæÙ7ÆÆÝô™6jü óò$”¥í” ¿�waæÔ7#€o±m;¿{—kY�öؾÜý�³MÐé¼�¬¥RÅ¡zïDÚ£ÔËÉVí•IrEßÉVƒÚŸ@¬ ô òg<ü4Xä§Ý‰VÀB½›:zXYÿVú}�Š‘]Ñ:0ÚæG¤„]D0N¶39!]ÜÆÐœ­7aÍ\%ÍÞƒÜ)·õ%ôQø¾7±C"N#1¹*ÔÁ›šÂ»}ý‚i(¿ŸU9U�¯·ºº€+÷¬°>Žì5َПï©�EëðÝÓéð«uª*º¯î¡žßJ”¯HtR”½ng"îš*>�nJZ‹¥6²/}I¥” ÒÅôéP3·fò9y䟞™.�Aï%wµX ë<Œ+…‘Á †ôάÔ'RoDnÀ)˜) £œ¹[móµV¼¸y”s<Ú-¬ä”g‰+W,ÿpñû›ÆÂø&ìÕöx/à™‡ª1°J¼ü+ë‹·|gµÖ!Š¿ê…›æªa¦ß¡LdÍ™ÈYÌ\Å×n”þEȧOYšE±Óñ�]I_¸o­5ÞO[!'‡™É-êKš’Ô3Õìuh±°d"�ôæ@FX™†[ÛÄ€ŸUÝÁV"‹( K¤Á“ß�Çü™êî2öß$õ„ùŠ+’Œ4�ROp«ûÅÕ p‰KZîvNò ì7po>ÝG·AúZûÍ©yñs 1ã¹±Ù_K¨48kòáÛO³»4~àÐ�=9Ž8AåN’Ë-«ñgf�{øxd&x‰��²Bs¸NÞÖ®dæƒP˜êÝçÍÿÁEùÅVrˆp‹ùwà1¹eï7ËlìÖ��$ÉÀ´pB[qµ5súǨfº*Òjàå)²èûZŠüw£Âƒî'<$w¹Sb!h ”L¡[.vö§™¸fD¡i�õ‰ûmÏ�^ÏëÅ+Óó>1«ï'#�f$ýñ{ žc ÌxÌ>:ý–“£áï÷$œÍâÌHóö†[ ÜŠ°lЄóôÜé1^W‚ Z¾ŽYjõÆà¨¾g)V‚GF£ÐîkÅ¿› ‘l G=‰ó ncœÊõç´dy0¿6ºÎ\ÜÕÝ™E�-)¢BcÐòôÕTÖGÌÀ5±æýÝÑæߟ^qZ!ueV‡£’�¸³Š.úÛíttnßñÈdÌjE(ROw>h ‡³×yšp%ÐH†¨ÌO ÖrN ZLfàVsÞ„c.\ùŠòÆù‹¬�vJnÈ•vñýM p¸PÜýj…˨`=N¢Ž× ! »|Ãßev03ÕqUO EZâ$Cï–óðaŠ PJô®¬þ¾ä­–d-}|´€Óö&––ä HŒcÀÂV¤éþáÁÎI¨—Kˆº]Xà™WUÁºé<{< yÀa}Dºø­,vÚ<¨W}t’ÛaÐ`¹°ö24�”$C1™Qw~T•NV.À~hÊéå"¸êÎcŸAG´(­dª×ª�œÃ#,5.ÿ‹ Í-8±>ç×�•ofИ ])9¶_I¬ˆ�Wƒ`È¿5.Oœ*Õ˜ý‚P)Ý�Ÿä«oXHËk“1Úo‘økÛ–“öÔÑ’,“!87whÃ$;>¢Ò˜ñ›žÈƒïíS©,¯�y»Ù?ó[Ù³—åhc… ªr>»Ý¼¦%€å¬8€LU‘g‘‘H@ÁWN|öeHMi~û0>A°Æ:qçGav_VVγ¶O½ùýsø1o’õ@ó%…#äÒtÚo…Å 0ƒ‚%w±ù 1†YØ÷x&³ÍRžÉûØï¦v&…â<ª¹l‚:8z%+kó bôÆæÈ Ö qUì¨ùD Â;�‰?2ó_Tv†Æ¶¦ŸtâG}ù¿aÍâ%޹>ùÐó#Qr½Ÿ¯Š ªVk†Øßúí¬ŽW%C–wvXýó7_�µôÎåmV6Âüp• �N­†¸8}gm]Y—9Ýϰ3a¯^‰ÜZ1ËØ.tó¢Û›ž�òµšV)¢àa^ðd©ÖúF•)ÙÓUkcVI#A� i)¯ì$ÞžYÂßIýl%³…hޱ�`­‚§úǹ]Üz5kÛ„¿·IŒ|ô@K¤: ´¾±v¤ÝxÙ´\3§M‰4 °ô‹´IcO…yÏämð}¦î¶ Z€´$Oôe d{·g€eŸQ-¶DÒ«…=Ö1²œÖ`æ’♂»ó>ŸDÔt;¬¾yÊFßöÎgÕ¸&¶¾¬™§o¿l¯c”“´Yöcæ‹‘QZ>9뾤ów¤Î,š�õ>H”ë¼.…þ-JCMAÓ9¶!OjÀ(ß°KäðpŠÕ¯|ÅÛ&ù4ÒÍý£à,M|î>Ù�«F..ŠäW^8I)¼§Öl[AÁÇíP¼‘œ×ZÓšyZë>r�Uºk8=s­– µ˜´”YüðÂÕ‚3ë#›Î7°fêN‘'Ôå%�r�]hoC…?8•j�c®ÀR¦]u3<œWˆÚ:ä^]å�¶l{æý2òŠ‘.V‚Y%ƒz(ÀH-"˜¹ z˜Ùnu ê�[ò틼TÝ•óTiúk½®‹Íã=¶b†Ûˆš±°L6Ãð s;qŸn@¦TˆvÞ²û °ûýç@¿»‹1± :2»Œ‹ÆÓb�˜³Shµ�‰QƒB.�k8„… ü"DÅ>{óñ0à'(êDÈI;¤PBY®²­#ÂÌÏž�þ©Xvbc˜3ÒÍi°GmðeBÊ•h·0‹�wG:¦‰…””C<«0÷icxª|7¬Šƒw1yUóZ¹{‚zKAq»�Ó/#™šPwé�$ü‡^ÛPñoOû¸_s…9eôDÐ |p ðT™óµNàlL’Qù/ÓR¥³UÄ'{œT�håÉSh:d%‘"š; ¯¨¤žÌÛ€FC»Þvqç€÷Š�Å4…ÿá¢=¯ÓP1îÆÒÐÁy$šê¥‡df¸©‚“ 5:³)ƶ*OŠã~ƒ}�£¶ÉoÀS­G³§ÿÛy!¯¿O~ ÒÜt—ë³Ç^ŒçÍOÒ„G*r¯´zÄÝ]éU/«®—Èœm/ÚÜ™k06Ê\ »-«h¦°bͦi–C‘‘säaÑ7gÎÄÓ‹®—ƒ¦¤:ª8Ü&›$@_¾ƒµì¯aÅX ˆÞƒYJtn“5¿ s°XÁïžàà2©ûû¬¶Í]âg*ßÖŒÖLÐÆÃoF¼›´¿O8¹E¼Ñ8¼.–b'ÿŸF;£xœ÷�kj^*M�T°lRtß=úd‰äz·i…P<-”"Z– 3åå°‘tHì}šÇî©Å”‰ªZÏŠÖ>!-«u:Ô“åN"¹¿8ÂÜ`\U#è‡P_6Ž8c6lý'45+j¦6Û�®fÏ*¬Ñª›�¯Ù¸LUÑ’§•õ÷{Gàa¨»ðºå+Á @öRÕ]v‹ ùGŽº§Öp½8ò{¢"~T¬)΄¢býC¡of6#áè TÖŠõµ»n3v·×þ`vA)­ðý‹fàNÃjÖÕ{óÆ'Y„ÿt3÷Âûõ„²fBÈi $ܯ~uÇ[%ûp:n" •$Tß-Ò¢7î�‰“+z‹³²„ò›°Aг?5üý,&&@¬ó›ùwÃ’$1ætŠE«ÝÀlU7ßl—¢ÜÃam“¦@⻆ƒAg½o•HjŽ'£ð–§p‘„2ˆ÷k>häÑBâxqÀ‰žþ ͧÐfÈž7f;ÞNáØå ®M ð0bJ¸îÏ ÚÙ 0Ëi“”5†MK~‡­; HµSÜXéš Üð$V�ñ?ÆÃb¦Kpq,;ÊÓX yÞ˜„Õ1UVøµ"¨õ±A»°Î ßêXG·íf¤9¹®´ýÎq¥'– `Œu½‰¦ÙÈ›èzçì0C“ab2ŽÞèðoqù¹Oì \òÕÆËªàηNX¦’x_NÂù ²S¨¶ài-Ä86ˆú\&7•²qí£+„°²q3pCÙáÀ9ŸaÃTšï4€+T8t)Kë9 8@-½cQ€í¥9üõÙLnãî³ 8Ü&jk¼‡žf‚ºÊMÊNÖñÃe¬uùß“_dM™nAˆÅ)öÁqp»d¢™^Å1M¯àÓ6h~¶8•ì¥ö‘íÇ{éÙà¯CVJòapSjê^tÛÓ)]ßÏ¢1¦ ph¯VC*/žmáOiT¦õÉÄ®ˆÎùª0jaÛW¶7¢…Î{têंG¼Xq¹Aa•X暈XÔ×.ÑLæ�3«g“fûÛ-bgGT¶‹ƒ‰/£Y:u¿Ú—u‘Î|Œ�³$ÑË'iyÒ¾ÒŽkeÆ;8HjÇ…)BÓ|ZKÄÇ%zÏs¼kN:s¾>h¶.Û.p2°ÛŽ*-(ÁrP‰A½Ã·¦[ÔŒ„`…Nkús"÷2Ë-Hö±>óÌ'í¦1=‚q逑ƒ8hrN®ù†ëµä­¦…æAÂ%w³‹üó@}(Qxl$ÑB°Å!ÇÂ8’øãùÈâC·]^ón)œµÈPÉLöû*Xsô>4Z5Þð}ŸØŸlX¼ÆaýíÊàÌ_&÷u(„ºÜÙelÚE=:ðºr¶"аÜ-1ÏýQÈ6ù1ݽ ÆºìÛ3·Óðï‚è鵊·_­Åã{I/§<+1UÍ(úp€/ƒŠžƒºï•woÿ"z‘¼¤´ì†Ü™mLo×|ÐjÃái²EÅ @¹®§ŒÆ{bÜIü%f !~U¨N#!$HMz?#ecû5¬Þ”QäPçŽÄ£êƒTÐÝg£c¿±AÙ¬ð÷y@Ì!#°t ^ºäýLÚMjÆa‘ØC<«ø¾û©Ç™9çx@SŽ ûÒÙ•ÜÛ Üâ².Rçe•õÇÓ¥bõ¥(˜ Š�îšÏ!K”yâ"Ð�pGªŠ¸X¢ 7ÂôY¢ù‘bTT…�ÛSÞa˜$:Ò [kᮆ�½pkÿ‘fÂt3Ñt‚‹'ÓßÐ&ñê�bG Eø®��Ljuæú²¯�ÂÕûÝø7†ŦqqñAœfevHgÖéàxîJ¬ß|I Y0¾ÙŸæBs?�pXÍ·xªÃ}˜ÖØÉH¬55ž/a*ÅŸ:#üÆîMçÝ¢ªa›ù9æœ|Vs‘Q.BŒÂÒn¬€ŒhpK¤.…7a*ZÆt¬”;+e5‹$‡Rch3!S7,DázÞ˜ŸªsÇ Ói:èæÙ‰YI!óæK’h!㇟ïwz»F RBéÒ¤òÔ®0„¼Ä¡‰y½½°¥ún1f{¾Õ`æhÊi<«Æ%‹å"�ìÀ¾šëºúƒÄ¡Àh¾Vçõ®�:캠@á•ëÐ�¹‹oŠÂ±‰–EG5U¦y{»çlÇœB@Ùuбªñ %ÆCÑëx£f4ÎYgñ“‰®f ®)¦�?kL íÄÛÕ;ª`ÝãVå);e°Bï”ð-\ŒµÐ^ùA £=jãF×\ÕÝ\©Ù!ü<á1"‰ç»ÿHw—íFBÄø‚˹§ðàË€ùTcE´c9WL-ÉWlµpKÐg]î7Û¾þ}wT‚î¯/rj£1©9wa#"ÍXxt õYÓ¢ç|1ÑìW½uÂvs<^×jO1¸?Þ³°NÖ±�íæuŽymÿc˜]#‹c7,~ÔÐW$¾R |ZI›’KÓë³Àªˆ'õƒgZJV ,á¸é´öVs …5®CÅÙ”g•™Ç3×% ›y\’qLÇ2R*€ôŽçõZLó`1bҵƊFÀÂ'æj`îÖ4ºrmbõ™: œ>7{U\ß ìžÜç&@UÏmDäOÂ3óHí¨E®<$H†]íñWÑÿÜ}®ÏBU_r³1¤�(‡-©U© ª‚ŠHÿ¬¤û x´ÛIk9¬ü :ž{xâC×ùLšÔ¹Í#ЧrV\I&VõŽáA´é2Yvk‡‰™'Ìg7Ç.õÂe‚ßo´ò�BäÒ}/}Gö–;åwâø¥ž+×f;é̡թC“£JÊç{y�…+J"3;oÍl+)”rÕN »…ƒ´t]‹o¢ð!¾Js.¡—¸­|ëbOgiîØ¥ùÍóõ–5�zDîro Ûp5Ÿ£õ“oȉ¨{œ¤e‘'à:%ZÕL©ÅÛûáO¯�f0Ú.r²sœ¢Ä²–Ÿ9£Å¡ÄI�Ä=5ëd­ÎP h› ¤4ôRÉ 3„x£Î@qÆS�”e?rL~ùŸ¼ Äð{¡æ`¤«ª9ªšràXHŸ{paþBâ®çÖ}Ã7XÒ5oY�LÈ#´åëÒ&ÊÁÓÊž'8ø3Ë­•{)/ ±kŸöI¶4Ïà”#C}D<ßÃÆzE“±¾$ÕîšÈ¹&vœ4kI(ÿúÙSó½ü1ºÖ9TÄ{ת¿‹¹Ç;Þ†#õŠŹ# ö× �·QBØ�€+Š7D8’ûøÿHÀ™2s‰¾«/±*§Ë»¯¹Êz¿„¾)¨Å–…a§:Á-)¦†ÏRãw ’yš;ð#Ñ¥qö˜+x"+=àç…áR“ÕýR—zýhß—1ZÝ «Ü€|]£]–nÍbÚ‡5A7JOÔ+.võ×.u†ÝSLªd>²šnJŒÇ}˜„Iƒ¢9Âøë ý�f™¢Û’‡-µŠk}‡¦;\Ù%šMü9Ÿq? (éøÄrˆÀ3ôв/Þ_[BÄãƒå(¸†¹l×Ògg5à……x&Ã^Ùs@f‚ÂÃj·k潄z"ß—‘R¦ÊèTÛ´9Þ†'N¯˜µN}G[>Y¾XL¬S€?>‚°Ž÷y)éñ’/øwi‚ °´Îô¾EœeŽ(ØNå°(Á�o™ËÏkBâ8þ{2žZfo= �°'á‹‘ÐDáQÝ…ÒŽ~íXìæ„x>¤´Ûç| — D9,vNž‚j) õÑ9QÁÛtœ5°ËÏŒtQ`ì=%ØÐ§ƒr%fÓñ)kYä¾ Q9È®ôb|'?œ!Ùêì|—¢Y¸M„³ ÊZ¨#ßÝojïñ¢á)5‡R=Œ°J…[ªýÞH«´?S,ÃÀô¡7�´û%]Œô¼�k€ŒUê÷›]ßYb©Ðáaðñß7üÉ?ã�5.ÀlK¬eãud¢âž¦!¶ ™œV†"p"XÂúÌ� Ö€élj¼Óe:ÂB·Y ÏÒB�ÌÉé”jÒ( N4ùæqÒE1´­ëP]–ÏÂ:}î?êË/ nWîîì›>kÎt·}aÚ XÐ"Ôå'—mAüζ{;qÅÆ8û�þæ Q¥=6` æ\ÉŸÀNå\”ƒ=†S Œ�_ ¶í8(e‘r¦^Î�rÉ{<þû2; `~¦W*'‡@K%è¥N„%·:Gë}Ü/ŠõYïµ! v1§œ%„Ñ~SZIøàC†¹…Þ§t¼9Äè#�su‡�8\ ß1î¿/øÎÐP¿|.qŽŒ]ñŸŽÃ¸ÆžFÖþâ~Î$ +c�€¢å4œ`þ¤ºçSà»ÚÒ¿¶’ç½�¸®x�¢UV×ì.Ï›�Ÿ¬-µ:hüŒW Æ…VÝöȾÊzȬ´Y<È£Ù»ev‹ 0=„csÏ‹ê'tÔÏ…÷4þ½ëÒð@—±û{üº|™DÃv-ÎjÝ‡Ö b_]§…É|hÓ–í6Pí1¯¢Þ-’h<2{Rì€Ñ¸³ �ä´‚~‚ {fk–øÖµ†~ˆò:¶ùU5v7Ö ±ÉÀÓœà¨gv3ׯMDì�« Ã@Ôòûlr®t�”?Ó]-ÆðÄj›˜¶}Fo^šóó–׎÷ˆpàØ„–Ý©$Ö7 Bó³Yû>ß!ERbíõà”Ÿcê˜Aß÷v—ÜÿÌ5-c\¨ iQç�k¬,³¼+ÌcC›×Vx¬nçh€¨Z�T=¨ÝryðŠ—Ì'—‹™×¹/Û˜–—�Ÿ+æ'�V…dö$õ#mNý,/yaÊB@³†Ùèyœ#<Æ Är{TÀèw _˜Óár_"®>ÿ±ñ%*Zà‡§(ʹº� J„תŸ1`ºìrœkDÍ%ïõdté “b9€KzÞ”öwä¤ákäd•i¸bÀ „àkÚ;±"ÇÂá%hÝ)èmžAmY‡©F¾²ýñƃ7¾ÓVe%yO3ùi›nì-“‘©…-ü<âDâ¦gæ@£" PkZAæp€£þYx«S�œ²1ˆZRjË+®»—ë,N;{ˆ<¸à§'§Ms Õ’&=!C¢#õZ[;GÑu]ìk±ÿéï ¨ˆ �§Tî„?yÚ=EVágÅZjÖT‘¨e� f¶ß¥ýÕ ë±><Ò’cOÜ{ŠÇ8q§…Aµ }u;áUï›ÿ[Ÿßª‰¶¾¬e¢à‰sÚR™_ääåÿ�¦QÑ^Þ4¦'!­+ƒj®0E_£‘„}"ŸÈu £ÈK­T\O–a¼‰ùÙ9 tQ¼NIº:Ñ #Þ–U&H& æ”Í L�RdÃ;ô‚8ÚpÑæ™d÷W9ÜRf´ÊÑ»¢YDSÆ©®l€ÞŽfæ÷áôÝê† ´¦Tõ±G±¸’Òâ"” &IýõŒm¦À쀃AÌælCà©}‡¥?¾…/ç•;Ëùœk {:çOÙGÝ=œQ‡“;βq¿â”ŸÇ _ϼö³ò†¼ßRÕ¯�eÉ‘r*kíÜîÍ2™��¶ËDXÇ«;”¨ˆÒqmÓ¥SH( CùhË^TzÙ¹(èà´D¥¯�Ë£YhÁC÷)&¦ï�4Ç*. �#†š+pÛ3_ìéœW¤!&žÛuýßWч½*–C„ÉáÆ©[2DæêºÜ1¬I(,ÖnAdž= ÌU÷õÕñë¡­†êŸýz3¢ÒŠõzk‡eVGE�£5¥›^hqB%;üÚT3ü“Œæ¹‘)2 7±8M,�0å÷kUzÒë¾V»ÏS×ç_�—'â•J›è÷ˆ9SeLvlë™wrÈG[º@GEþs%ß–w�;MXÞ0gOà„4¼I�f·+:ê$8[“º”ž%!0dß>Uq ÉË\dbø 4ŸíÿþÑ9…°áEíì²8V¡yô³Ö9×…ú‰ÄÑö YL.ž"–K5¾¹êEyÂéIµ Í'+¥‹X<0€‘!éëë±�ÇkzÖ�Nß…L ¶öFô…Z e7ƒ³a’ʤ¡y~ˆ'èÀÚF.âÄË?O3ÒÈôi±À’ŠD¬´Y«¦°,ç0wáÓCÌÁ‹Ãh©‰:ÝÙÎ:Ù“gãëžÕæüŸîBsj£Â²r¦=ÂJûm¡%OXÒ;wÀÒßÜ""+ïqÍ¥ÝÆ�éò¼qL"±ß”[sKœ45tË8®A ìãdà&NLb\¡®Í�"@'šoÔ—ÚÔ¾Àdu8ìañýq¦þ½+ùQ-_¼ìäI§ZR2Z˜ï1XKgÖØœ™€~-ðì)ÑœÑ�‚S¬Ô×áÉ®ä k‰¤Fà©õÂy¯<&�éUYu•øêËÝÀ#ŠwÆ}I¤(œ½öèºØxÚQÒs¼Vâè7ãè�CvëaÃÁ›Á“9‰îRLÎCyÂ%õô«Å°]õŒæ? •ÆóþøÎi™ºB%°½=�ýdµ2WÛu%Sè"T¤øVìYØæÍß×ò�äJ¥J`*±ÿG2Àúèt¡{õWAââ©ñyµÇ÷YûŸ£møGì•ãÌÁð`Sä´ÙÚÔuÙÉ"ê—BjC™e„þ ú”öÊpX(V•B¬Kšƒ¶å¼+ÅÊ@:”ç„Î<]€Ó¥ûSQÇÞ¢™ÉÀH"cŒ_ම,C �(ŒÇú€ò·úVGQ.øÓ ¨ð6æ©Ò°ßsÂ) ¥ÈÆ åã#¸Å' IZSDè¤ÆÎK^ðbV‚/ù޳©%�¥›ÉøÃ˘¢×¦uì.¿UJC34u¶pH„‹Öå=pdoûguœ‚¿�êøÎnwªÉcz'ãFR4Ýr"í¸(†eøþ°û ôæ "Þ³­Gç Îeç7æp±=õAÉž_Yަѯ^ô&À<,.hT¡C‰vвM'ÿÕ7©Ø€�ê­Ȇ{«$"5óu¸”Há^~»MØq�áld‘’ãÀä¶r ›¾²›tø¨.�kÛ¹,~Vy–[¼hvGý!ò�Ë…~kÜi¶YLG6­“ÃVÍËKí«lX;f)4uAÝd‰P\=)9yq´s#Þ‰xýù´ÃE¿<~”¶cÇ2•f^;i�îi¶ŽÏhæäùpWg}>H¤¿QÕz{ž8UVLž¬vªê5#Y†½ŒGe -ÚîÈQ£¦IÑ‘¾Å�4#X›Æ&•ýXi�P EIVÁÚ󞌚x¡.,F#¾Þèáj¼}c7?-–8ªEɯ½žì^h/a}øSÛ·9ÇÁ'ö�æ`® 7µîo½!B3,–µ»µ3ùT4¢ÍST"V¤KºXáií¤ñNbU/‰ v÷›7Úƒƒ¹¤Œ”'~‰^JrÇ¡XÖ2:õmê×îZ“³Ýø?7q™¦øJŠŒ3ç.t ôÿ ¸‰Z“ñ &‹+�b‰½¥S•N‘cž’ý™¨•œç¸_ëAcA'×8Êm,ç¹�µ9K áPùòœBƒ¢ |©5IÃ24¹6bvT£^œ·–qZjÚòñºKvN·6U¸³Z4ÖOºCéM6ÿöËsVͽ6ç¡ü©´qNWyÂu¥é^3•õ è4µ¤ÝjùQnw2€6Ö-9ð68={¦âx·W·P _|oÂÂs�gÜùªíûímA@ŽC6_k·,e[\/Mˆ2Ô(OØ,sЬé@ðýrE )Ïøx=éÇ6{¡L…,³É>ïŠ|Ýùˆ¯Ö LVz`íCœIJá¸ê�f½ƒr—Ð’Ej`jaˆƒ9cç ô·îpÃó¢š>WTRÔºÄE´t¸jü—Ó\Ï¥­õ³˜@a4‡1£ÈÏ71€·Œ9=£ÖÀzùEäžFÒ�$þ�\™¬æ*“û×g¿ñìàý¤!§ÑÛ–ä@É÷¹jíðÆÜoqÉ‚9“>OÝ®Rs?YÔ=ÝfÁBÍNæ%}¸4<”,8Ù.Ñ�x »ü4¨u9:® d“Z׋ˆßÔ§Îïðáæ|4ê‘i�·Ö'çLyÀ\ ‚VrÃÌã§ûãc¯�ç¿þå³ã[‡;cµ¾iÞ]QÑ’‹ÎBUV4n‹#cñ|ss�3�±ÄO†œªYÞ+»`M}3�¼å’‘Ëa„�âek[¥j¶±+¹†«³£˜ù¡œJì=¦‡˜ »Ç|ùóo#ѽ['¸„دMûÔ|6ÄÊâ\ ˜îWxdÏ)Â…8dcjž€6¦š¯þñ(Qú+À è¾²c7Q{WÇnü=wÞ%ê™®:Çt~}^l!?0 +—¶ÐÏ“Ž1»ìêÝâ$=h­ÈH^ð�þ]IeN%$l:k?~ƒü2¡ ŠDp˜�:iÊZ‹ß[Èœ¶‚LhÜáÇ…ë‰CÔ笰«a eù`y«¸=äJ3[_NO~�:òÊÊÛ¤Ïy’Éû`ÞœõŸçëÞ4°&^&k«Éy:"#6[è(³ÀRYQ]åaãÈbpC÷¢ê +#Ó±k05oeöŒüÞùì¯QÉ É±¼+(îDÜ¢þúÌø �®¾sqÌõOÐï¨lO,P|á:þCŽz+\ãˆywéÞŸî7jæ ´·Ä}-7r:Ó†(6eÂ3K²†z/´›ÊQŸˆŸV¯vš±JµÇœ X$鳇c±Ùn×§XCFÜì@r ›Øbq¢¶ãRõsûeÊwèÁÁEPšw6q ¹^åw£qQ‚úîe);2ÍOQþˆ|»²ÑñÙ¦[ªë¾˜•ñKº”ÝõÞ#&]8SJ%÷À3ýJÇhñ–@¦îb†•ä]w�k´!�›óƒ¶UBôÜACã8zæ‰þÅ—–â÷ƒÎH•¥#- ͼ#lµ'É”�(&y’g'øzIž§Ú¢¡·+°/ôŽIq`˰q|¤7]ú5ag Ì3,· ¿©‡WØÝÙòBR�hC÷¥_'[Ä’>>•,…«A'ê�PÔ5Ô-BFQ5þH& 5÷ËœýëUÉWÛ\'ßCõôüJÌñO Äí 1r2«N§ò"$ªRlî©võºàpy"¦hÜuTS‚íQ÷#êtD5$cÅÙ¥j?ø p¬•Clt£Ujìõ\ZKZ:-1âXr'2ýÐÙ¯u ].§”VЩײ£IÁè>@§¿ó‰4;º6¸Ù�&ÎõÒH²Ë¸Œ3I2P31~'ZÁÂÂÄôšÑ*£É†MÍé=ÿ][*Æ?¡EŠå*�¾ G•yòõ~°Ž©±xó,v͹ Hi3Pb,¾id” :£]lnMr>O²5¤"k2ßÉiçAMV²2ÆaTj­­»P’$Õ‰ý)Í,ÿh,!…<øœhóº” 8dK`L¯¨™%dQnûÝ�/ÆtuEÉž¢2Çä™6Þl{k?H"ô-ˆöâiø.Ó)ÖÅR,èÛÍI×€=ÕúKǤv=½:_P+ f’V‰�캦›•Gðª§8ñ†ûj-ÂCû4oÐ4-£¯q]ï Ãíe¸]Ê”UÌX¥èèÔhãHÆüçås%\K¾âðX<ÜS… §EÆö$ >]:V˜+C‡¶ÌŽ6zOé¾¾�D ~ã‡4’ðµ�É#`ŽÔ�úÈtŸç¦Ý²ÿ Ž^Q\MË`Ð"§öÌ\¶�d ±&ì­s_þ¤èð=cá÷3¢ÑíuH·´Á�¨çcð4ÇŽ‡U�„¬�ĈÀuûn×l�¡/}¢õއ³ÙxýS5F½1mÕêNÚ¤9ï‰ÔŒÀ:^mQ ÿéîÇä�²�²Þ9ß¶P.ÞE´{Ë™š }ŸË‹ó«1GÞ_ùl` *bð å6øö\*kïßç– »Âp&ÊÓ-í¦Ÿ“-ì�a·'×ÿŠ>CC戔¥¥€?ë¡L¸‘ANKXÃÖ"ç|ô Œ�fŒÆ%sº©}�0gªP,¹…”n¬tŒÇc¹"åB8ì¡'¬ÈB€a¥Æ™ `¦¬fÑ œ*ú«…8ÙW[®íJí5îob”SÌ„µ¥+å Þu{··’2’Ö8f=C]*òŠZ>O²‹òg îÛÀyÁ ˆ€¨Üáv =+´¤W¸Éq�ÙyÎù…sGÞ#FEaàÒ}Ãë�o›€àK\äZ)2<‰�p»xc¯qý ¿É.~M–š÷h^1|ûO«w‘dÎy¦°‘‰ˆôUÕŒ�óI“ÒÌ)Ì&n 0jd˜îÉ”eù9Þìk¯ÚDdRJÓ×&�€*ÓkïÐÔ�:›õ‹Ò‚ ëñÐñ)›–øPÔ›9%´ÃLâlµ³_}üÊÄ m‘�ÙñpG¬°\SÞˆª¶¬ÞB®Ðb5Ȥ9ô,02[%&¡p¬™�tÍ×Ь — âõ|Š<š!F£[³Â^mg�Ê®®pû=¦¥û²�úPµwt-îQå¿ =(ÜM‚Þ4X{œPŠêʰ¸† _PŸÄœ.îN¤(}¦„œ‡ƒ uÈ™MÔ*g£Yí�x²êÙÖªŽæ�]<7~>>g'L:0ÜE/íQí"Ž^7íbóÀyvÉÎU žcTOS=lñÏFâBíï««ÍÏîÅó³ VÚçzqø¦e¹bP|©�w´6©o#«öóü?ºâÇK�º|Ίõõ¤£2wç˜Owl÷¯Õpo¤!iß]µÊm÷² zu“¢fbEúnXŽáuêa Ýd¹c—t¡0ʃŽ <¯©Åp,—%C„�‡C5 "޲·YWžòÞîÛCÌÇóªj>»‹)¥d.�&ÞÈœÍnâo¥Š€�T’yÇò` ã6õû|,§é_�cRâ#ïâ3„Âõ¾³«©®q‘‚3ŽsGÃ$Κ½q×u¬ÉŽ„“uÅ{Ô¡¹uFÙöÄØ»¿™¢Ü^â—ð+ õ+;¯Åñ&òNë‚´yv˜ÇÊî¬×Âñ#Ùú3rqUÓµið:è¶-‡ž =KYƒ0|éªÙNkî±mU9 ¨—ÿ{À¬¾+‹h ®Ö|{"éä;àK“!ü‹žQÇ g?r’ì7´-�åÈ;è‘ã™�½*»ßLŒ–ÕÎàF¶i°à&‘)¨9Æ ·+@ŒÜNè4i4å·jWy™Ÿ>_y»bTÆà$Û¯ÎJÚØ.ž M²@4ÓÀFå´öí=t6>y~§§1”yûÝmà ÔÀQ(A e�;žÅ#¶4yi�f nÍÞ({·|ìÛ&†u–ëV0ó‹A�A¸’!íèHõCûŠÿ±r� m]î˜ïVsÇW™�¢»ƒpç#†j]ÉsØVŠ‘bÎ4…],Kÿåë"Ù]‰­¦EÂtü\M— Y§[@ËÀ:œê²[×»,­ïZ¼Û¹Å'§;âõWL— ‹ Ý½f9¤�ë­oBÝX>×™‹4EoØ2»H°L©˜'nÑyùøƒYt4nt ð”>†e¾B/$p {7>œ…'“>îï¹ÒÒ|Md²û žÞzÙó}8ŠöÒÁµ£5YÙ6¶Á!óm‡ Ê[ª{%xKáVæ­Ånq¶ †ÐwTI½Û+ºÁ|佉s«þÈ“ìv·~Ñ´W/W˜&"Üù(¢A€IŽ–O%[·�ÄúG«$Ô-ñØüa ã\%ÑjÉ·M €ÄÞ8˜·ÓN­ÓŽœÊôE‚£ÖdŸÉoÎÀ¨dóè넃Bº�¨�€KÇ¢%ø¤~…j¼Ïë!&CÜÙ›?]„ � µ:Y–2ƒqÉQ�K†„¾N²3ŠšÐ[ nœáX€û8y€Å�Wœ1¦i–¤ŒÐ1ÜÙ­‰efÉ¡«O³qâúÉŸ%‰Òú#�Å*.íéö�qsßô¼œ©Ãq�“p,fK·i$è5McPmXOn¥íVšÚƒ¨Úîrd59h!èéî€9š‡ÀMï6ÒÆ�wÚ­SÁó¥b%f�CÕs/‰¬â”„fp"È�?àQþ²v=$Œùá‹x›K>ù×ÜIÀ$E�©{üQ†³;òuú²‚Ñù3â+ˆÀ{ÐVMž^X‰6vÛÙç=•–ÀÅ QͧB¤¹€í·�cgnô+(¦®?1¡:g‹0® Ã…‘pK›˜+qL$`Bï$JE®ºÓž¼öß/ðqßu—g=«VO|Œ¨|� tÑZìÄŽ/3]E4ÎŽÓóΗ¥�UYÊéyÜ"‹˜îÌñÀJ4§7»œs9´gqúC.§•óöòâ˜"A®»Å$Ìaú¾do$â£/>íðÞð¤¶ròdJz§l)…SÒãi =»žßµôËð߈ K6¤Lia¼�´ªJÛ­/íŒ6¢cס؞X< ª·ã’g#ÑD÷ücC �{ £…W'�-ivóO»?ÄdǦg QÌ7Á%ÕÓ~Á©¾6Wƒµ‰ÏÍ9ÝÝæï§£&R{³BÎYiÀjµá×e‡ë´.á¯Û½*Xð!gXOSVÇ•’[ðØÑùI;_¯*3\vq1>Ûa}ç)UÁÈš!ý㵬Cnaî4Ò2uT;¶J ¦z-©Ýöá̺Úá§g‹H ©BØÜ€›.œMÇÕ—}K wH�©© cц;°Ú—¬¢Õù�º”8äDóÉ�‹:¾¥ô:ÙÑ‹‹RüU=Õ"M–©­‰tÎG“|;‡›h¼ "UeþR$íF´o+.JéµjŽeû¡y$ù(À"…¾W凜P…Õâ"lhç¼íÝýÒ~Ø„m¥uGì—Z÷èÈLS•ÒÂòHø·�³u[ÅøÖÒÏžT¨è éõÁiwß)�áž×PôŽÏÈ�J |¥{Þ”é ¼�—¾˜öSÊ…E'BãõrÞ *³”nõdÉ?B‡:WÇÔâ«’wüLNU‚�w?æËÍëé@–JØ7ª�ÒÑû©gÞNfûõ~fÿz¿·µCG·Tã«<º¹0¦Çx«{Ëœ<°›¯É|è4­ààrt FkX{u�[WÛN=Æ6|�¹PÍÐÙr7›ñ;bŠ —ÌÑÉdÆy#Cé2LtB�÷©°�˜Ã†%÷Zõè-·WX2x„JrÒ Ã¿æ˜Ô›ägÁhû ²S´øGÞÈ~Çh—üù-L²¯‰ëç;‹¸þåz²0º¬6ý´îûÚÛÉeÒ2³¹¹gŽ* ÑŒÿ—„½Ðâ¡•7• °0Ð?�e ›ðG7Q¤´F]ä ÐŽ„#¡güe?±FªGÐYœòØ™2µN<=—ç/BXMo ¾½PçÚîÈ[«¥šíƒÑïoè®92mmôp0£n¹Xo@ßлxh{޲åÓá§Ú}9W t¯éx‘á½/ÕÕ z2טìÁ]�MtVµï�Œz¤¢ˆ¼_þ™9ÁTÿ¤ •Æ™ÎÁAp’“ŸçoFÖÚ§` '“á Q�Ü5¯'ièÔ4Š‚Ò¾KØ®ÿo&Œ®2 (Žh¤ çÙ} 'Kjý‚÷RÝÞ#\&ºÒ•jª‰“IÎÞSÑÎÏ'üp•ñ8ˆ÷Ió{²¡åºXÒ '.¸íÿè¸Þ%¥²LuiøTkçŸrš‘Þçœÿ‘šÅÃ8[·z�ñÃÈëmNOÔ5˵Mwb=ÇÖ5XAºZÉÖ€ÖôgšôÀ\J§çMš>Dö½�ú’a 'âbÍW´à\ …WÔ7Íf;–ªšíˆ{³ ¦b¾\®#.…! ½zʳ2§“!õyy�ž­6CX�»Dtºþt÷$ˆ£ø)Tq”»R˜ÂgEÉcåä mewá;�êѺëà™ù .ÜÛTs°ŒuÀ½#%³öU�·¨�Y�+±×Œ%è±ç1]wï凰¹ ��·&sjÃójXOä‘yЈ]….-—¾7õoïx“í•QáE@¬X‹®•1€+|íâÄÔ}¶Õ*2í¨U‘SŠûh¨hœûÓ-!�=ŠÂékpéò¸ž6"é%ºý'›ÀšgÀÜ’ÿñÄ“ÆÞ¾MŒ–v/2¡E²ZC1‡h]ÀPÙ 4“|éûRüÛú¦vn.�¢æ=,¢Í7o•#dSÀŸÆ›îÿÏßöÄ6Û,Ô•%U½Y¡r ÕqX5&2V»$�ãÉ´+¹!BíÉw\º\!žHzþ”©Î ^>¡@íC! ö"Ó(ýÑæª­T�ÃÓ#ùÁm|6ˆKX¢›Újç 9š½&¦ê”Ö¤­ j�\£c;<‚‘�D•Òíì©P†ÖÔ$ø…Îìw"ŒÏ‹ü‘…Ý\2»¸DûÖXDÿÚÂ{™T ß‹[5ÞïÜÛ‹ó|£O´×„‡@�Ü&Ý}zØp*Oê¼ØAó¦.¬É¼c¨M’&]•�…H/ÒÈ較_ÇÆ©¾eV¨fx6MÖÀdž�LSär׉\ü$”ä°ƒ™o¡ÚÍ”ŸŸú$4U1t‚ø®IZÉòí�úÁ)Ú Ýû?�€„‘çãðL†™ŒÁžTw§zÙpgYªzkQKvJÒ­¾€ ‰ªO1¤*iiøüôæ§3U‘ ŸD¢U!jJýÀVò�a͹4cöøõÖz�O]”µš{‚©¡w§�&¬Á³d@SM˜‚w<Çž’A”m0Í)ösÖÕ©Y17_‘®½|Ý¥'ëÒLC™½×ɧ+Gaãá >Z/âñèwSõŠ‘­´GCë»íëyÿÙ¡/YÛy†e|ĸãÖ¸‰·‰]¼œ5êÝS€5!¨q>©I èW0 ÂÙ©ä¸S]µ$ñÆò»Ûc�Pæù3ŽÑVR~¡°¦<™ó*þ)¹>z‹ê ÎDâk‘(fî/�x·äç©ÖöI.mùô$äÙw!cÀ�ht{Múm´l?Ásïu(OÞ9ßi[’·C}ÿs.«×ü§Wbôg6ÉTæ”ss�>:g#2ôÁL¦ºÔKÙ`³#�E¡r®ééO茈KÂ/V2^b¯{ÁؾÓiÍÖšcLÈLrÎäñøËZÞ¾æV—Ý|u;ÝÏâÜñÀ~Â!êBçmIÚ.ÝPQx”3öÎEG¤"dFž|u*þn]¡hí=ï³AÎJ"14ãÿ³à ]µgšq ÙÑcÄhF®Ï¨Ü¬ aUÑ|ñÁº�Ø®„ ¹&ò_^ƒ}m®Ò<¦Oy �£´AàLx±XN ?R»> *Ü´ò,nŸ…V£`Ù|èAÁvOb ‡±®¹Nª ú"‹Ec§P¼ušŠodÛÓ1å·†b"mòÁõ5àQ.¸ƒäîË'{r +qñF·IC«¹Î+B}W'׻믖Q‰³W¶ƒŠQFº Àê™"T¤ãŸ†Ö¦Ší« Ê¿S¹8¥ôl¯õøQÀ {]öo#¹µi6å&À€œ<ë;oOym€ƒÜñ6N.àÜtŠds1TÏUöTtù«žl·Åû›Ò…Ѷ£Vkþâž)!&ÍßA�^÷Ž^+N“‰-'ÕÐ0¨!XÕ§a|xn·¾ú°>Í<õ0c“4xÚ ŸZå³pAxA\¨å/(Ò�§,»ÃŸDÖ][Ñø»ß°ˆu ‚_k)M9(.°µÉߟ>6×g•¤€8>ãÌ’Ë Í@¦|t"‹Ÿ°[û�ÿù'ºõÀÂg¶5ؿ׹i÷#ϼÑ>â}‹.EeµhE¶‹Ÿ[TqSgyJûúÛBýl¥qêl“ËYµ‰˜~}cÔÕÛ”±%{ÉË‘÷ÈQD« ݆Ű'n×%\|7NRツyÕÕ AJç¨Ç¬[™O€Wý€‘”b:©uŠ7bðé?.Bj�’è�cNåç>jqJmpU‚á:údq¿2Þ;¸²Ò-fS­�ËÜÙÔFY8/wc°Ì~o{áWŸbæŸ#ܶ7›¥˜"ôò‘¬ðQz}$"! /ŒçŸ1H1¦ ðÁ•àœöEE� «n^מÑ*Z`“/ô¼þõhÔ-3Ê +º„òBñy\Æs‰Ë…á€r�4£ô] �›M#B2Ø��ÄI¹ÛÐZ+v¢ü“:KÆé%×ZPEì}ÀÃ0 ¿Î%º¸¼ß ¦bŠnœ5ßð�²BM]ð©íãöeš�ùM{�'ZsW»ühìí[# ¦X�¶ôü¥‘prþÓßÎèù )æ"=€ƒËâ�}%+tÜi£B¿3·ÑÖbFJBv™ÑÛ5w"mÓ¶}ø·… Ü~‚cîC̫ݾ¾#“)íŠ'EꃴÍ�âËⶦ·H�#»öÊ|´®i¤¦HbíBBçxÞ˜�Á)CæÛ2_桞_w¡ÀQ“v„Ï5ʺ�Gm}lkCÁKH žiÎx핈Âýʪוý`W'Çkg›zhVüA¦I<ÈÑêálö¹—îK¨<§¾‰‹:›WÓÖ&ñjwÇ;NoÞEqqìÊ,rÿ¦5+—³ª;]:œRÛ“ˆ|Rù^ƒKp¥Í<…w×1;Ó/-gî¾µ”ÞHZÍz/Ty½ �Õ­tš(‚8'(éÐÛ´OJؽ€  4I=ç; +±(~a�IŠ+ß8—Ç“‚©a(hã„5/ër6¾ªÕ³¬q·x2 J"ßJ–E^Í `³§³\—+Nrh˜Þé _�ò ÿ·í:!rØ$RûxÆÜÙ÷ïj¨Ñ®Òoè!ãê!õ^�"yR–d’ŠqûuY¼ÅÚƒÛ>˜4|„“Ö—ívxç³Ö‰ÿÉÌ ž%TZÆžÂð¨G¡…¥'­{ ïëZ#1!äzTÏ;,=!Rü9‹PiWΟîAnß»'0ê€è«ð $’,%«±w/ á9OÒ½¶‹RÚhçТÝÒè�tÌt!»î wÚaÞÙa0 Ì®x†@ª~(;;ýÚQ¹ñ}ŽŠRÊ:ó©©5ºµè#ÐÀ ˆŽ‹¶ÍžÍ%{CáÎ0½’Â;bk6Y¢4Å@±n{IÆÀIÎkøÜë mÅif¿tv®ó1ü!mBqGše/!w•<¼�(?¨Ôœ¾QvàäÀ…ãï+&½‡Žq²ÞöME¹# ®ÜYRhÇ�ï«Ã–d?O:ÃÚ&JÉ ¦Ç3%ëì'3jnW =0vå9u7· 2M3Í›d�qÚäÐѦ͟ѠйVÚ`¹$Æ�þ»eÍàEà Â%«¯PØO¬6P‹¤šÛ4mÉs?“6�±´`Ør“ÚÉ€È)ïèÜ¿Poù´…kq9ÃO�Ih(RŸN:Ï¢šxoº¤Ü0Wu~O¡®uvRŒHn@±ÓD�âú Ë7NŒü{Ò��³çÌzqîÊ&Ú»¢v=ûšC›·¸Ãô„nêµÿ q%@‚v!�Þæ=ç³-'0YòÐvV^÷£·éÇŒgå6üðÃÔ�Ø5�x >N½©ýGþ-!|�ä+T«UºuHüÅV‘× /pó‘wŒ.®D‡ Búrb)/ q™WUÉ£ÿ00ï\X�ÜôäËȵLSÙ”Ya˜ð>Ö—ðà3b’%/ˆqÔ@?à¥ak!îàWkÅsk6¡`!Ë#‘ì„8õ3þ‰oxj¼Xñvt\¹*„P¯šwÁ=5ù™ô$Ûó�Ù- ç¯7WÚݶöq&™{¡‰©2c_þÃQ¾$f*# �/�‰³yBÞ�Ìq!¨á€ìa@N8©·¾V‚Qc�vX¡åÜòlÌ@¡: û_.6päã·­zÍL±ãç‰ÎZÖºÃz[L«œŸÓ—ú¥ab­êï."Px\W‘+ü ç+nùf’ëÓI�§MŸ%pQ�’~·ÕΤ·ªu‚Ù¡Üåì!‰žæ4¢räg¯p]Ü<ýå½òxÄ€ Oz›ÊÌ ¡£Y/œ5 ö°9Õ­C8÷,;`Δö9­gü!,J÷¨µSõårfò þ¾_çå²øuwQ•—4ô�øMõ•ÄÊ_…¹Êß›¾¸�ž±»1»‰ç×¾÷ÃðÍ ‘ä8 QÂÐup0Å"Êù2~ˆÍ‹-‰´šÕj'Ù¯ÛòÒk±«faýÂç{‹fYèÖ%¿eÞõò§N)0H‘Ôgq·jÕþLSnÑÂÀ7'aпp2£3ó!ÐÍð‰fö Ä$@9uKU¢�îóžàÌÈZ3ëóƒþëKoö›‚‰“dý/²½Ùu±à5‡ýï£X[¼‰ë7È3žC ÕÀÍã&Ò=Jb¯Çà;'òd[eê.Iš­½Ð¬�§046ï~äÄ|SåG]k<|B¸ŒžHºÑè¢=¿>Y N¹%iodØz}mDÑ ‡ðdظà‘0ì áM^�S[¥~&¯¹�Î}˜(šà„›¹ãÞS�pBÌãÁn,€ü|õƒàk;„¤�¸¼ô[’ÍÇ2Fªé/¼^A@~¨Â\Mœ<ÂÀ—,6î{¢Á‡y¤¸›«`—&ʾàcÔ¸J)镯…ù–l¥§oÐZÆB±ˤX<ê•ݰä~Î"Å’PúŸ¹lF0óZÁ½! u_y!™}íˆ&b‘É]6lÕj<Ú‡dqB6¯ô‹qÚìýíAƒiÏ"Kl§½^·’2© ©”ä/ç ø#°}ÁA´�ø @Ìí¨Vdù[vê©=¨2ª)æ¦�^äa y—5ÿ,ÙÏìl¶ðcít/{[³GÌóH62ùÚ}€@¼ÿß]Öׄ99Ø\�Ð|Çnˆ.+t^_ÖLÒÄD²ö_²kt Ky4YÛ%^aEâ¯àÔz#ó»ÏeÚã�0aµ§(‹_CZ`Ì7c†#pÛ޽í[¸i½_ Ê´¾«_p¥(§˜½¨ŠZ ÷þ5Ž Þ÷t£8·æ3�ÿL � GQG—¶–ðˆú+�+Þ2× W&o ¨Î;hì÷~ó¯è;¯ Wå?lC¨¦”ù"yk¾–w¸<† å`B œÒ§öBñÃþM+ ª.lÀJ9ÍK¼ñ<Ö$EÔ� œm�pynF,ˆæ}™ {ŽÐ摤qüþVú¾´oÍuåë }I›VÄÁLÈc6¢xr¼:‹ªäg.9m°Ç"ž¶Ã²TëZ˜ùoj [æº /Íf2 ý”Xù….Ý\ôØ]®Ü5Ær�Sˆ~”ûÒ��eí…ÄBx3øÆ…$ ‰Ö½ÐÏ(M À¢ ,Ýûà5ºTM/•öø‹Êô R¿Sƒ½}¬>aébTܨמmñ™/îå+ýr©Üñ]‰OqOˆ3L9n ®UÓ%#J UÒ5àFiQ‚üVF®Ý×1Ϭò‹ ’žg–åÐ<¿‚'qX�ÿ«¹L U�ý¥QŒ mŽÎÈ!`(ÎX¶úºü‘œ[£¬E鿾bYà~¹°h= ë§(L&m€2Yœ�ï�î-Bpå>¼\‘ð5áUH>)ÇDÌ�?ºðÀå¤Z‘gÓMM_SôI4Ø Å�W£—<ÊkÐÆ9õ‰Ž^Uúâ‰`MÉïÖTòÇ®fÂì¶ë·ÙL%·$w!A»/cm[¹J¬º\ÀÖƒÈ)}o{ÔP‹3<À+mªÂ‚M.Ôö¢Å.¾ìèPÆ÷•Ý­:î?矟Q"s'NËgÖ°i?È—®ò ;”$Öã„ùò¼õßûýÚ©óY/\¬žãBzÏ“"orFð  è¥xñoÔh¬†`ÜZ'Ön¿JBheÛ#sgqÔ‹‰é�Ð=�M¥È¼«Å{éßo…`@»„{^÷,©ŒE¯’»˜—_øB?‰Ód4Œ—4�'Ý-U�z~Êjy¶ …ÑdãåÎ1Ž€ ?Ÿ¹ë¦ö”AWo] ÓPàþ` ¸ëúý§³Zθïÿõ#0\ƒjkôG¸+}#sWµ‰éfÑÁ@:WpoݧvdU5�“ŠÂ¦yÆ•…D€øì�0**£”•4H�ŧ¤Óùú×>iúž!PP�6z¢�ã !ºLþÀ™t"…/g¶ãn8” Afà§ŸðËÏU+R,ÁG±Þý,ÓEy×zJãÏ[\N¼áVXt\V~�² ßv͈Wæ~tÎ~TçZBƒèômÀëhëÈ\a˪׷Ȣ ¸nï„Ï/³«+@ï£Ë;�.�ØÙ‹�-�å郜äÿ0ú†.[¯CD«Ã~vºt!¨Ež ¦÷Tñ¤ç½P¼C,¹rññ=Ÿ y&Oê^žl¥“P˜‚H¥àà �N ¬ñY¼I D¬*›†ôÃ’e�†.rà\á,¿?ž¥mzõ…2ڠij©c NFÖP¨¸…H˜XPÙÃï¨;Bx�}J‚7\{Úœw¯_o¸²˜ ¨´­öüdü)9Ð-«{y"ºT$_¸¼îhðœý¾Ï!´’Ž÷!ÄíÎ^dz6áÝ�véˆø¤~ájžØí`.. ãŸ>¿×ö•Ewl§ô²\› <ßõ3USE⺒-6¹àæF©vÝ‹õõC1H(… …êÿ+ 4¦‹‡-ª¬Æ‰ð\j‹)oë|wKÌØ‚—cZÐI p×Ð(g¯@Ã,5÷Åw _j¢ÅYƒ)"´y½½(?‚¶ø%MoMˆs­¸©˜L³0Ýå�­cK�Pú'¹Ál‡¯…qòÃVa£�úý•0-¸;¾´÷�ñ+2øÆWMTLBëFqƒJP‚šÖ§ðÊ-aq8¦n‰`1~÷Ún‡¨Žþ–%G¦ÒØžÜJ„¿>ÙQ„ý¦{÷ç×9D=Œv÷‡‡Œ�µ#nP‘ÛáνkÛ+¢Ç*ró8ð¥”ô±Ÿµý1\=ç\�L¾‹•SÎ5 õ7h3Eëx~ŠÂª^°¸ˆ5‹ZðŸ…Áo�1ÄÙÕ…œìÖ“]ÓÚžŸÿ~v™[„“m;ºŸi”žC]¯kõB‚tÔä)à Žï�œ*«¡`¾æhr =ã7Žž€+cÓª¨†ÀT?{ä¯_ñ[ID>nÐÇJ·(ØÿB::ˆµk?ÁVk46Ðru"žµDH3Ñ E¹ÉqÈBúÍ—#B¡Γ{f­¾ƒüMÞ Y½·œA‘u¡É—›„Ù^C¹d2Rk=mÕçü³—·ËÉYÂ2 5CàSîq‘55*å½ Ya±g L‘ÛÈÌÌ(,áûÜX6�ñ`*ªi¬€*úÌi�KŸƒ�ˆë5÷ûz=êq2<«Ÿ¬¥ÅÜp²f4öÂé�ct:Mœ6Ë#ˆ½Ì¸€LèþÓVfþÄùœ/Oz‹lª®ø“•ù:^y‚EÞÚ€A ‘ªΡb7V&0«àÐòg�wÑ£õ#\m+JÃ<øa,R# e}N¸� × ¹Ów ÓO[[ƒ ¦$ýü h8‘Áae§•÷{û­* 㻟NE.ÃòYw('Éߎ «®Vr¥êÄ¡êÚ Ñó� ÉÚz0\×Ö(NÖ¸ ×'Ë<øh{…î« ˆÐ&+ŸhAöȉ_ý/ys¾ çŸAî­ØOH“2|§¸zK5®ªà�3y• °­±FŽÍfôÉxÀ(¢d¿ß}n„²)þoü¯#õ®<�å5!�ÚŠH mS(‹áµd⤿õ~;a'ÖÂHŒlü¬Ü·T土#»¤Êï•öííͤòˆÞ£”’ƒt (Üsåߘ� ``øcšá?´ÐÏIì^±à¨ÉŽ,Yk,Òý–$&ÍÔQz *ò,Æ^í|­¯vÔË&Öš‰¬uv ™ _HòÏ_e#H%a[ŒØ¾qÖ$§Ç�¶£/õ¿¤bÙ³ˆ<8J¹ô5}šøöNõ'ÍBXET6~¼ò›ÿ‰©B®¥B¸¯@4ó6º`gÅ æž'Õ$•tQH“BT2—:qˆQ½¨ºÝþ-ï]vã»B“EÛ›~4É€›LdÇ^îÛ{ô\NÐõšm�_Øñ¤¡|š)ßí× ~þ–òk°à†9¿šã"«Š$Yæ“S›M³LÕM•�iÈîKÕr_s xhÒÝÑ™m�¿üöš2Ê!ã‡2oïM�Èß}Ô½�ÿÄÅiO–OW+8ÁFžDTº¸æþ¢ë±Ä¸Êó3Ôt#±ÊãÚÁLÄ•²¨>…ƒ„’5¹áÜX¬©‚fxØú�K?õøì>Õ]c–`ygWhaðI ´�ˆ!ÓõiFU\Ñž–`ÙŸSyöN9šX:Ÿ°àê×Là¤jÑuFus‹?8 n0Í}qƶ]Ñ\šê ïå‡pv›wâ·^¿•>ßÙ {¾`ßÞu3˜pÆ`€*§Ê@€€DÖ–žqG¹™t´áœ¦laó©)²'ÞÛ¦Åký3ô Єí&íßj±®�²å§íÑÞPr»d?»68K]Qó?ðªú­Å«¯ùmW¥‹^®+« ÙK Å¿—<\ã6s@*‹Ž<×cþáA|âd-Ôã_[>€8ðʧ‹æ23–±xò_“Aá¶ c]IVÃSÏx°}2û‘øµ¨&�»,"Š 53-Ƈ°¸‡t'Ð0­Úý»“8RÀ1ÀeÊz~‹ã“™£¾Ê†·4b?JàÛØ¢¥ciE]…ñ…wíŠQÔ+ ¶é¬/G’öÑaBíùŸô‰´šÂŽnLzÙ9A¯7þ«™ªo 6Mùè �»ç@ÍÓ…ÊZÆ�¤Â–IFã ’ªxúZC€€ï·Ü¢¼pÚ„YæbzÞ$³¼u¦‘XCî×gH_g=A�S ¤'=ªJá ¯±Ù{Õ¥’47½ÂÔ‚Äiu̓/'ç­? u44Ì‚ªÆ"óìÿ÷iª¦…ìX¡1ûég€Ë_‰–xòH§ã÷ "ÓNüx”à¼p4³õ1Y!C‡Üº2£;É”íi²ˆš¢xÃù�ëÂÓÞ\[¿²ñ6ÀðÔv×çf2¨ÛÅ~É&Û–P²X´tø† jsÚ„ W“¤‡©øtÊŽUµ(ª±T‰IÅ: [, [X d‚f„º×ÁDÆŠ÷ àÆèÓnô¨}Ú¬Ç}†âÚ‘¿Œø]'Ðû"¨rCiðŽaiM~F ‡ˆ¤/u�Ï¯ÕØBåVÁ×U†•‚=ɪ¢/Zu7E8î[¥öÒGmeœyBÆPTjar’ݬ&ù‘ãÃnÖÿŠÆ¼H€=9•{8„Yë}èb±cÍ`KÝ%˜!@¨Iá%³õD/í[ é(;𞌖ŸyÁ$Yh@{_BÐÉÊ’‚ë—vçÓ�žo¡_k*°dgËp³@XL%¦ð?¸FtÌg$×—G°=�?Wœj]ûõüð„|¥£(µVé~MëdCp¶ˆ˜fHÀØÍ•[á6ï S$ÿHjDÚZ…÷Øà±ãyá¾nu´€wý«\ÁöÛ–�l¢}0«›ÆçÞû„P¯ÈTja¼ƒH�ÝDH_ç&ŒC%ìVøvż›R±!Rœ¥G]Ƕ …çXÜkS{‹ŒEû (éºyçRô_бd±¦§äƒ Õƒüüˆ�j’V×�ú~ø6 ÙÏáÎ!f5lÚÎɾB@'Ö˜çÎð¾UjE.@ùZK8¡í¡ èV¸{É’€ ·Âu{­'£¼[O‰�öÜŸL¼�Š×…a¼|S|éÿƬö—×åuB¥¦¤&ó¯§Õ¹Œë+w¼gZœ×yˆ£*§,¹þÉç2kSº¡ò¬%¸PÁŒ¢OðBóŠüxñµÄ‚àµíŠhÑOlAg3Ì™¿j2-Ù°=¿6ŒÏ&bfì5àJ¾eUYÕÀ±¶ T’YlåÉBw2ªA^öÉÈ TÖ u�jØ¥ÈE�bëögöÑeE2¢—Õ†—a—m‚“‰ISù&6¾2çgk‡›,ï%p4Ëî"ŸÊÀóíE}lÐnJwŸÂ—·`õ:/6¨¦ls ƒÖÆü˜«•^·¸?®¨€Ob L7JßÞ¢Ã×ÂÉa”{"Ê…ƒ-؉Ñ#éË\oKçÜ*óÉÏ€×Ùv?Á¹Më6Ÿ‹?²,�ˆNC½v°2?8ã‘úÚã(-¡œâÊ­ðÛ«är2ÁnёįQ«ÿöPôZ”ð´¡ !––œJ82w!´�%)t5'è7“rTqOš¯õxœµD#Ù ´É?óaÝÞ9f¸á¸"·?ÄÛk£(«ó(ÔIë[ßáiã18ÙäúѳS«‚HíXÀ«øüc»ÂSgØ2hÖ¯ CN†>‰�/0~Ke¡fQ‡1‘q¸úCSñ*úGâ³*,¶Ï¤!Î\b¿Ik7ŒÓÕ¾#Eß;ÕÏß;3áHÒkŽ¢\¬‰:/©F÷ð[aX9Éè1üŠR‚¢/Ç :r�Þ/ËûLÈ#lÚ…ÓÄä;÷¤  W^Ô*�´ƒ îgéæD¦îE+(ä¿EÂ!åC+ÒÝ9mÅì˜sfXŒ¨Ûí˜ ÿ‰u�›}m„zŠîzujŸ!VߎH¢¼ì-ŽQÁíõ°~ª7ÿ§™' þ�î¥I?_ç `G²Æ‘¼²R"ÝI,«V ù*o$"6iÿüÃrNéù‚�¨ ÂêRÎ埥x™Žb[˜+ „‰\Ño%œv8D]T³í.'°èr&âÇÊ=ŒN’¼$LBs)g ÒST±ICwƒdºn<ÿáÊÆ,R¨-ó=�½Ž 0ù1üoùþSK7m ™&F8„šd ;±‘ã©34xNŽŒÈôE{þ†8[´®µÍ»0èÀFU§yˆ\7]bİdêO\0¢³)ɺ�ªcfÓó–ÄãËŠgg ðÛ0CÅØ¶Y¼SÂ+Æ0L)¥Sªp ïø/"…+a,}éS%IlIpƇÞéãY)3…_°ùÞ!JûÒë�CqñMGD:ªS©Á]³j^8&©”þúÇàó‹Ôhíàc›À'®�5ì+ /Se)Áò¦3g˜äèP«Á=û:)dSΘ?¸TM;+J@sý?Hj©�¬ж~%ü£M·“P5« q nncè“bRzk™ I«8ëÄ»üà&(Ty‹ç(‰½K0ÒóÌË�²˜yñRß-õèû¥Áv� ÁìZxÞXŠ…ÑkíÚÒ�ádâ~íò× 3Õጳ˜^‘?|Ÿ Ep§"��Ãöάó§Þê[Ëh@ËAZØdQ’×DGnµ­'ÁóýÁ v{óÓbïvw×Q�ŽQaTFË>á²kåwñCü¢VN#8•V´°½ÿî¶n-�§ÅFƒtêÁI«L™§ø ²Å,³xÚom¯¤¾- ~ð—«œµ’Xnžmþ8Á÷eKw™…ΕÚÈ$¼û×L~®$ÿj|.¥çƒ vK»I-î�Dp>*¤V–zj�…ð !bQâù·‘îR …¨ŽêÓl8¹h CuSI¶wñTc=u•ð}Ó¯6Òx²v´û})c(Z}1‡¿b¿3‰ÃŸŽkF�é¤ «óÒOˆ¾æÇºNš�¯]�2ºœÐ¤œóÈMØÐ/ìIÞZÕ¸†C G@ ïÎ=©e8œ¢¼ã¤ýÇ�¦�‡ïÙè™Ì­vÎùùUµ2è)ËJ+Ͳ¾÷þ†]”Cúâè^± d]Þìî(ˆG¥~ÿBo‰>ßû­¥·šC%½å†Ÿ�ŠD¶C×EÍúðƒýkÉD«_NÐ^êð„Í-RîÞÇïó�ãow#;«ß’×[�>0šßÔ¥žÔæJЧRZ§Ô§øXT2Sc‘ðí¶5˜™æ"Ðã²(à$£÷]»H*[œ\q·ðÀ¼$›¸ç;„}Ýir…|x 0ŸõF¡Ë@Ë‚[M±ÝÅHy)êf‘ÜŠ¥{ aòâf„«ò·"bBê”[ë ÙjF,ƒí¢TíWe`» à½ãÛëÌ<\Ü—Ü£ ˜¦À(%‡Gó´8¯1V8ÓWa@±L‚ÂÚö¸I¢d€Fªd^H·Ó°ñ‰°\ðšJölïpÇâý=¢'Ì9±ä–£pNÚvþòµRô<àR£vÈvÊ Ó†¥-°Q"Эÿ•ÆÚ£[’ú†æÚO� Fæä–,°°’B“˜–Œl­\[‰#�ËÑdôQÁañä¬2,ŒÊ/A@þqTYe—H*;3 ´”6̘lòQÅa{þN!Ÿ¶¢Œó:በ�{tà*wuwh¨è{ÝsÔ®$LtN>´Ð*£žA¿J¿§8xI¤y-a‰å³â�ö§§O–ñ *ªãÔ§ËätqÌåd!îñšnŸ�cãüÀ\ø¡U²›ÛlçúDI£±Ä ÕŠq� CšxLç«M&‘8?Mí\3í “ÇyлðÿŽLè4Ƽ+2æ�9Ó1¶�ðFÊB^E‹q;Ñò]!o*4n¥ jW-³�$ý,|¹"IªhÎe<›C«ëÆá LAZ²�´ñ×µ–Ï8�ú?CÒvÓ8n}Þ÷^µnTÏŽÒiª€`yá£ìÀ‡mŸuE{ ú©SÖØ·ò.,ðL’®Èæ>w'=J�Q„ˆÎj EW+ÊÜ1{C *x,¼¬ÒŠ*h(]L™çø�j¬|½†ó/¼èLÐ7´;"ãžýç)‡$V(NmŒ[¡sM Q�khÎÿ‘ýFq´Ñ›WûN'#!ƒ±ÃSVëšÊB=7î›sd S,4P&Ýðræ`êõ�‹#¦_hÆÈÞݦÜ\dCt:4ÍéðЏB ºdÞR|´Ná>ÇãÁÎT‰ûR„Åʰ£&ˈoÞôÐVì¥ðu’C˲›SÝTãùwC&Ì;  ì-õ6³¶ƒªƒ"á�­LSÒþ7+ Õ+þf2òŸž0&ú˜…6/»·æõ£:×$Å—dà ¶¡ëçè¹AHWbð¦?…IÅ,÷Žajlq«¾ã2f¬'RêS %¸A”úò%/Š4v„BT^›9}Jj¶‚kYà0™Cà"ªô¿ÁÞmßšX~d÷v°Uëöõ åÜpþQQ$-.°Ãú¶€ìܼb[âÔNMQäg]år÷¿*¡v'Ëòü$·Žg¾ÚÉP�ýuk%ÕÈ«Œè‘ôúä³)©þ¦�6UÌa«Ò|3(x�Aãô<¨!sl”±Øi›Ú?ó*›2ÿ;]̲ :Fd“‰(𼳑Ý}ÏÖl‰¢{ö3KeòEš ÛªÕ¡+ =ÿ°hJX2W…Ê벨“ôc¿ØTg¦Õ ¬ä…Ö1 óK´(R${üùàz¢Eä¡iú©ÚØ*’OTæ2hÁ¹lõòA¿ü|[K™9V×Oç̦eØAý�ÐÌ0æî£ �E�‘á‡pŠ#°Wø½ï´·yœo¶(B˜ƒšüãé£Ô€:ðR}%zÆçž‚Ä‚,ÀcöÅÃÍVÎC-ˆ>c0í\ÜS¬ODKŒ=[d±¡„zJûšmˆ­…wí(“–zÍÑy7į€e1á 4D½ëfiyαA;çׯ�â³,l a‹T×hÁñ²;[ BÜÐE:LÕß-$OwfŠxË俑֦—ª±…r)?'ç�£ß­‘î"†êÑüçÈ*žÐ? ‘c¢®Þ±[W4êo�&”OCÕξœ­(³A—úà¶vn»% x÷Q$¥$ˆü%¨jsl¿qÐÃg'<Æ�”Ïkî¹ 'À¤M£3ŸøôÈÐ<øÊ“Ù늠o¹€• ¸C„ç9ÏÐ ´ŸE$?i†¡>Õã\I# X÷šr¢ #•Þûõ}|²{tUO@�³³ó"õ‹6„â >ì”ëÄHf£ 5M��‰�JpÁãÂÕÛÁ#d‹ó-r'¶¨v© 5ŒiEàÊD=B×{ñoäÄpÇŠâòŠÛ»àl4·R'c8½»f�—+Øy–1üµ†º=T8:wlïX6R¾î—ª�êHPþ ˆ‘)'KmX´.Lî®Xà.“ÌM™‚*‡SFí΂<6^ áN=«ÙÄ2ónTy^f¥YÎ6F|Ìõ¸Ó2¸&+ØKj|¿ãO53òw Ž�m˜T0üµ"Wºß©E¨ª^°”}ÃtÞð½Ðas9ÿdüp諆iÚ NkWà¸a^µòLÂ2‘àðË$A�Cº°»àºÓ«Ï¢$[‰¶º¡Süb®ínTÃrDr› ¬bfçÿ—›œé¢ñ…Æó%߆mMRw@óºÙ•õyìd°8XˆºÑÍË2Öû1PöïÜ(' +ôZ¤©�RjÊ¥O…LÓÎHdÙ±_,衜M×:ãe÷–&Õ–pNöi7“DÝ[Prôâ°^¶-­ý¬„xŪ‚ÕÇÅúwÁ¯p¦4ùã«£¿¥›B€SD¹"’”˜P]Š) cÜΤ�刎¼I¸†äaX'ïj€]JíÖ{xƒøÁ$ÿÃkÖÂý šÖ’µ,ÆĬK0�¶^ ;œ²SŸpXI�ËÜ#œk Òº=ÚJ·}5�x¢«pÌë_G6•M*—âR£ñqÐåb„Iðq|¼°ë’çà e)‹I"º5D0¦Gý¡ø8Æ7lÕiJ¦§жŸÈòO ~L ¥Øb± YfнÒâ•ÖšPÉ$ÂFÌÐ*ãíH_·cÿx ‡Ç·7 ÷#ÅJRƒbž’!�O>&�9]­ÜK!º%,¨_pßð†ðd$ohýAÊFAÊü²úÈ\=…®š }ƒ;-Õ^“þO�Ïß ú5µÄ Çæõ»°ÚqÜ£(h�2¦aÆ„™ëM¯K’2 ]+ÕоÛd8.£ß½Ó;:~(ˆIú”¯¢|Ĭ붢–”ˆêlW¦/éuH@¼”PxòyQNÚÄÖ%ù8›9Òù{Á!xý-Õ¦‚Ó’væ4Ë šÖ¼^?Cä±ç@t„’hÚ7FžTä,= »Ú†|ÍÑÏi£ƒþõ—8îµË’¡ØÛH‰v@n)гÃ)ßGùÓIŒ°ò“߆ÿT`xHJO™6j V¢º!O¸Ý �DÓ1‹@ žsy~.ÁpÞ‚Ÿcx¸–VÑ2è7ƒ`1cZÞê(ÞRqgD›WÀnIË‚}ý1jQj7çÞoóîI‹§È#Jm#Дçç \�ä�jêì´Í\KpWµíÔÑ óT¶lÁôÿ(dŸwš~XwbQ¥vür:%‘«ËßåÑ6s-_`&qæyÙDì4 »ÜƒµëF,Þ–åy…k$vKp_¥¿ü—ŠÅ¼VÆáÜbü÷þn‰ÕøÿKµ Ê»—Ô”K›99á+R.ÈB<,ˆ@â}<‰¸¥— }s f‰z©Ø�‡„Ô¼@ù‹¥®ˆY,â#G¢=¿ñ̾½¶²N‚ÇÐ0iŸj´…P¼4è†H‡•«Ã)¿äòÜÃj)om¼Rè›p«ûc¡]»U”�êûÖ¯oôí.ͦXÔ%�Ú-lQmˆ%$12ôüÐç¶¿.tææ.Ñ_ð™üM=þ{ˆÑ�óžp«ó�µo êƒWޱ|ü]†W�ø‘BPSQöOÇ™*i„‹&íè`~Pµ«×ëóv,”-{u–éX1´æ·êòÏ�jÿLi1ÒÀåü@+C“5;m0,šn$­îDçí‚Z ©¹¶®â} ¶y¨g„õ1Ï5“¿ØmýÅ}°vyÎן°�4oÛ¯ŽŽRüöެ´DɽîèÜSÀ*JÈŽ`Ên-Û'ß<›ŽP^ôÔ,ÿ ÃÑçÔ äµQðr&Êm0 ße½"bù™vÉC#Q@/7£$#�]5vÏ?Ütý/8x¨ú+|%B>&ü�@àÕÜw²‡]±¨|s‘‡Ü�8�ll¶g†'¨§ô[Àa)¡%Ãv.ùвtl’F|Žóž�ê4xG¸óÍ£"„Íßw7#àT.™2Ú }µÚOÃÿô�NîS†ÇNH„Ž?,ÙXy–,FYp)­£hm‹N‡ÐBfrcá}+ÊãC +øf¹ßÞ3Z±}xÓ0óN“b‘$"åûñ•àA@)ª°Šû$^3ÜtuG8ìhÉÒÑå¡QsÄ(à .é¨�nZà’¡ ÆgYÀ—ºÛHC“ƒè‘õÓêlUÔSPëÁÁ^Ü­L®½?\*‘�TíRYJÛ´mJͽ¨HxV·<’Ù%Š)ÑÅ/阼#ß&ÿ­öL;c—ýy¶ ×ÙKGò €˜Ü¢ØÚ±óNyv† �õ@ÏÓ‚¼¦Ÿba±ŽaÜ §’ו1®û½^?`k�‹*�Ãèä&®»Ô´¶ÕªNƒ¦íj&äÒS8ãd ×Ìx*ø[ Œ“ÜÅ‘†)•u`óaþÏ‚OMÙ4¬üc£F�|¦%ï ‘m4¹ì!G™èT…à =J‰%*Žú÷æ�Ìt'ÒÅéÌ<‹¡$�¢Žg;öhÏHÇIzK³?i4µ è”1ÉÍ3!³OØl¸N÷î„Ö´mÑÑ>(‚sz <ó/›…0°œ?Wa‘þ›±ˆÚ¦ë²1olAu<¶�´i.X©¬KõÓðø6 T™~z#ƒkêxa2g°@×ÝÄ‹$'ÔiHÉ¿þX;0ŽuuÒ‚qô¸?‰ø'òø�Ø/[VûTüˆÌÔ1�~Ð�c-8Ê!Ö?EƒZå:øÑ­�<q“G܈oMÔVêÌ—¤`}piZ.@`Ú¥Ã6-E½ômb²¡�I¼·ÚŒïQö¨7\_ÝKXUÑa1@ «©ó2·Ú[=+Sý¬˜!}p6#ù¢nÕÌ’Ý�ì´×Â3ª<ÇÚ¨a×jþo=ÿ¦*éäuرNpN³`É_ ÎÏÝL I¤,�G°5cÞryœœ¶W'Wøú†à^d—º.pYŠy{½qa¢Ø‘¸¼î޶&¿ZD‰\_p˜2YÔ¡ÚzÌž#( ùÇö!¢ÿð) ˆ¡|Â�þZDÖäQçÿ§äž^ß§ÿöyY�éù.\GD‹ó9dÿ‹»Á|:xEÜÙ)CýÐ<¶ÿGœuù§|}jÍÝ¡°t±¿�Uy`†9¨W·ÿ5H·4Ìr‹ßºÔ„4˜�J€A¼þQ)Â<ÎlþþíƒYM&(ÿ®`ÿÀyöòøFòKÀפëÜ‘ùG§pŒXÏTý±‘Gôwˆ…Ï$kS$ Þ¿‹' +’ÑYî¸õÉëaŠÝ˦ØáWƒk¾›‹eG�-|MFÏ_A.i¡š­®Ï >M˜âEϽÜú8b;8õ$Eïl£õô,nǘěª»£ÓxÄuÑÛiì¬P¬7çy9Å©NÙ&GʆÀêuª©7¹ –ÑýÂÏa^FÑ ] ©â meMq<Â4íaûPð8É æ&â^ˆÀ±Wæºp�Hmt.‹â4׊ÿôYê–.£>'œëy¿’yÈT%ï_‘‰2‚#áå  ò ± ý¹*ó RjŒ®}§t½*oã ·5zÙÆè?œeVyÉ–Þ犎©Wã(ö½:ñR&b44mt—Äè–pƒn”*é Çøi™öç~H%F‰ýuuí¨ˆ¢ƒ£Þ¡]ÉOYq–äà¹ñ÷ÓAG¿E;cˆ+!W£Ø bhPkŒ ª t�S dg¢85ß°¨»€ùœ= .'€y[g!añ³öÆ+ð0.gu ª'^̹ ¡ÈYÐØÞ‹´;í&C�i¥S±ì¹ÒØ'/…„EM[‹9L]lìÚ]Ùy$‡mਿùƤB2ï3á_ ^¤bJèr¿�ôȨÍVXaË=\ßñÿbÝG“J;NÑÈ)O˜í¡u¾P,í&yW’Säâ€èt¥Ô• t^£”ai�ÃÖ£°MÝ\UËrbœ˜\v)r“­ $Ù/Ý^#}ß— Sè 2ݦ'B «Ò|mŒ°LØdŠªÿÒf̲}tp WgÇ¡†Áû®êÏr1‰üweOQÒĶx–ëêbÙ—NÐë"Û‰û?´ 4+2Ú-˾$¦EqVM£¹�|+Ã-TŸÌEÞpí„A:×ÜàÔ¨S(§l嬒‡SªU+:„â#¥ªÖ˜S+/A ü÷`,¹ú~Înx[f¡íõ8„~ª8–Øâ>…,RyÁ;À®ÁÃ|Òoð©bEkñÀü Ü`îs±Ø�—…T³÷D†î¡�Ü�f‘0çUlø^·nX$Ó![OP#¹‹û#«ÒñÌŒ“]z LEU]J´Ò/ÊG–5›Ò°8} êd¾UÛ�Ï<ÅäWÄmqT•±Yáfz„‰dÅ8Œv‘ìÒdí¸˜ô¬IeäwH~þsÉ�[”Ó°Yì7rÒ©°Ž¢G!m�LÍ1 ‘vV^f‚úS`wêìÿ„]½dœŸ;q©'Cñ V4»–´Ÿ ±‚>å¦&ÝÊ‹o„ÆÒ&®‡Šîw[œ¨CYãYZÏ'i7Æó ›>Æø¥>>žSœìîUƒ)‡ç!ܦzB|[Õ¹¶¤&ñ¨zž‰Þ7fm;ª ÕW=)UÀDǧC[éÿ¨jmµÌCm×Ú¾�(“E²jU²`r™ïÐ?À "sö‘oÎ%‡ÇqÛÐíóF»h¹èsÞ ò$ÒÍþ#~q5÷Ô§Ê x¬H«7paÒ¾sKÑXÞðÚ{Ü|G€¼:"e®>Àql\q×’ž˜Ãi8±ìR„�ÕLàjkÐú­± Å�¬ö«&µ|b_X”~ä‚;3BÍ_s|CW˜daš5vŒ Ö+‹?DZòpPɼhþ·;LÚáñ•zÄ_Iã?lÛ0’²çmâhjØo.O»Ï`Zv+?uÁUt’�U<7™®ð8ûl<Ú…µS†Öø'À[Í}D<ö±ÁÁí‹`¢ÕÙ§«Æ\•ìY£x#±†ù°¢L¿mŸ«FÎ6N 9ÖTŽš Ð™ãêÂÝ‘ ¥¡_ØO÷6…ñ¥ëûYjà\`/ÃÓwe"6+ÿj¯©´E—�n&8É U—°ÀÑ8 žæ<Óe³�"àvdPÂÆIeìÔŒ¡Ù…Ÿ¬ñJrãT4¾ÒœÄÉ�´È‹ndÁ’ÊU‡ŠJõxôÃI¸ªùÏ�,&"á8-¢ô!l…³S§+îÿ¶Šgýœ‘Ô1þ†%àlÅ€P¾¤—_ ã¬ýâv§ºI‡2›îjû¶ç#¸xôòwN‚_¯ÏˆÖ )0áÝGž°L:NÚÛ’»ÜAsj§µö”<—Z¬ÿ<€o’ g�ñ7+U'KÙé×ÿù’æIãe¤  IíÞÏm//ã]ºeÙƒFi†›Ö 2ö ¬ÐP+ƒ²_f}†QaæÑÙ“›´ÄÅ�IØb8ø!^ã ®‡n8%nPÂqÚ”mC¯ ‰¶³2uŒÝZVU¿,Zqo‚Ž‘£à!2S»&¤bK±BàÅd#´wÏ#ð#qæ7(å÷¥ÙN? ÄZ?—çòÇ(,}{‘Ù7¡¶ºk\Ñé×È4¤F, ªŸaÊ/}óõÙƒ]@æU?ü?Í�ÄäÚ[™0œ¨„5m[§Á¾ùœ½Ý¾.]—Á)½;jûƉ015¶ÈÃÚ6bbcqím·­`¼N° D©‚Äw`—ì&òëYnêú¸ÉÇ�\Žª ’w­ªÚ ™Ho[rÚåß™àˆÂ¤ô8¸[DzóΦ=ÖŒår"?p¢ìEjæüU]1¨î6aúhr‘>#tÁ îNˆ,gJˆ˜ƒ®Ì)IüƇ �ÜÊ9BZ‘jñéP6®ŽÒÿB�«u*�PæFïÊç|ÿ£™)PÂÊc”%-JòJàÜA\´oéÓ·aX­ˆ�©hpÃÂQµðËNгˆÿ�-•³çýn;™ÉÜ�@nD ‘XÿÖy/‘ž|´Îq„x¤ÏjÏyˆ43 8�ïX¥ ú;Bþe�ÿ‰—n ÓÚ>Òãeà_ª¬˜û+ÉÌûrúPF=†¢F—AmŒÒì^1¶Í4sl­]Ô˜b×›œ£dóÊñó|ÀU6,×0ò¸D:óîPtõL™†uœ}÷†É}SžÆ €Jå-!õNÏ_êa¼MZöLd´õ[èâÑ‘¸lQ) ²¨¤È®Å¨4-l*ëÕ"=‡>™¡$*À©.±¾`«7 íþ§ÊôÅ!Vc‡§>ä]¢ÃV�ë �.·t—6Ð.„‘zyð§ »UAmb^XeXz�êZÃÐ…˜5d „ P0sm”µ)zÆ3‡�C€�zÓ �å´6Pô;­daÁWÉLìwdº_ÏÒ_­v ÑkA PFþQy�v÷׫•’Ü(!µ¸eÄmª§u˜�™e\PÈN-x \çûÜà=N@Ž® 8�Ÿ5 5Ð …Nfœòß%4.ÿ– ¦¬ÈÿÌþÓƒ)·Ÿà8W…qàõ^<‡ÙGy3à™å:z ½€0³iBiÃ|êˆÏ'�ùá±¢1Ùc„ZEl:7ûêÖ|aE>àËð.ÖsdÔÞ�¦ew¸2Þ‚ ø¥²`9à¬Å>¸oØ]CŽKtMíДÁ'o¬Èkô �õ:ªX²•ØsbFÔ©ÜEƒ¥gv…R7 /–­, JÙ‰h"¯ 8ó`7ŽíHϬœ‡5§VÔ›2$sè�ºï_°nólÊ´£f“��kQD)…N1 ]ƒ3¤·süü (¬>%‰¿]to÷0ªˆ²–È|4LÝéá´!TX‡;ÓÜ:aŽÏB…ŹYXY¼Ë¬¶0²Û:—‘ÒÖ–(šŠì ÓÜÜ�ëêÚïAÔâÕo<^š9lY r-‡d§£˜jžÕ§±ÊÓ«Ú€~²e\3>ð‰™¯ô¹õ9�C¦™šD[féÍéS�À´íÓ—œ`®wÁV†5°õ±vsPÉdºËvvVùìè©Ò¦„Vîõ¬Z—lg|…–T;ÑC1¥�T†ñد¿€ÁªšË匠“lŒÚ{Ñȼµàêv¡Êº‡x|D1û•XÞÕj$÷EÞ?¹b–P‰·$”kkÖ7ÙœI¶:ð»Q-×%�R}p…šþÈ'½\Ç6*qZ>S<@¼,¾¤ß6n¿eŒ¯`´l·©,JÏ@�ÌNž‹+ lÕm­·ü—e`ìNYè*Ô+ì¡ï…míÔί£r»<7îG¥¾.-FÔZŸ¥Ä®ª·E¬Ýç ª<‹â\~fôü<…�­ã»¿~ ý»Dè›^‡j'j«žÉíSòœc¶×:4öy7„ߪƟ’v€‚ÿU�„…eÔà›=·×£X`! å&:–OÝ ^œÅ*«wþL.1¨d ì|YD!hï>?| º`E“<Î#¯e¬«½è ¾Å¢Bf.k§Yž•ðߺÀhZ•„³-·\PË0Åv[R_)Wrcq­½8@…kˆÒ[e­÷zyK\17U!ÌÀÃÅfy^;?Ïo�/ýÚ7°´ަ ¯Ã¾7}˜9ÅÎfV/ïITÙÂøªï>Õ:–åªú„ gþÌÌmT+~ôåØ}?ÀÅ/6Cy bAµËâ¼œŠ¨³)¨2)‰uLÇI±ÿþ¥)=VO¡ô|€Ó±Db-¢ÅåCYã]÷Ä#˜àà?dôç�óM‡%Î@¶ß¹—W\öDNJâ²FÏJ)Bæ�Û¨Ð3¯qßÅ×$h¹S�lÞ-ÐL)JY19ùgaºe†?cX•¶/ 9RíSÀTãL»|4öõ�™kñ‹;–Òñ>\-#$ÚZ„È9ìzI&´Øl.ã¤ûÜsl-“ÕM.�Ø–"‡|>úz8Ó….]@ŽÑš ¶±íZ˜©j’Óù�Š¡Í2½ôÒ£%áã#âz×€¹�]g™ëWgW¯'+²òþš[Jãdöo8H¨\º’ϦÝý®t¾ ÕR“a¯ŸY ÀìÏœ©Û2ßóÎH�œl' xÔHF&¿?¦Îã17§kË53þU¥-.y¸ö•)Gßý“àð•AHÔ/,�cALJ,bbøe{¨y|tÌ�þQú ŽOn<âºPfiFÚ‡ME‘Ø7•ŠTúkœbWx 3í¡�FZoNVÜEbñy¼©œ¾%h`)�ÂÓúwø"ILÕ ­ðÓ,wJ%ëR=U\W~"BÑôvY ÕÍebgº¾ qR\á_œù>úWE¡G‰’€ •ßÛ±qtﮚ`¡Ðä!:=ãùK‘­‹åuÄëEô‚ºdÛW²yS…v¤ˆ¾¦Že†¦°¸@•Á.˜~Ñý€'�Ñ­Ya³]�SöŽnîï“wÕuOóU)’Ûi- nqà™,äµR„oП&f‘”¿o彞à^gb¥™–Spw †Š¾c[#Õ#ÐÑž·öX¸Ç¸Ï½sElmÅóCf€E-{qÒ�'šhrü‡FgÝK­³¥?¸“d²¿zS€‰îGÜ€ºRi“ë4p¸³°MËc*Î>xAÁßtLý4¿ÖÝró¸Tìp,(ióBMøïØèFňüÕ®8Uút蜙 £ïÂŒDÉO|£«(¡Èk\ãæ go‹1 61 ·þ»¼7f«\p¨—(­œt‚ÒÕ~!uö€˜¿¤Êv¡!ÉФ{_„·l\äk˜¬—ù^£û®Ž<0S_Çu^$Ó'ËEN±8-Œƒ~ Jëè? ÎÅLš{¸ˆÝdBë„k¿ëÄúþ',T|¡åÀžÜ&j T©ÓU-GîÀÐíÚcÀMv‹ZpÁ¦ë0t HŽ–Ã~€ï=èEYô2X 3yêÉQܽ^Yd¢Ä‘DG*d®¦lŸð58‘h‘.ý÷Ü�ù¾ÌO ¾ç–Ü\.’÷Çî]mñºYà�8õãßÉ]£F�¬‚�é¤%¿»â‚×P �S•¯QcÇYO;<r³è*Ì5¤ÛñuG^²…ã¡SÕÏ;×Ä…_ÑϘËuÂüDK©e‰wÄRïBP“5°L"‹xËñ=P©8¤ŠÅ¯MlmsîYSõ,¦ÄÝô%õRÉûBbt0e," Œ.Ú�`k+¥ï+®º _ò–¶[:N/3R­T#*â´7Jö´JAy FdˆdÒкõha�ø•èÃ̯å6رõ¤“r¾uÇB(>õ‹Iж½tj‘ Fã2?µ¤´b°b}’ç’ gT™áXmMá7h7gÝ,ªÊ¶a…L‘7Ñ ‹] f ¤2Ñ"Ñ%W—áWo§žÈý|§Þ¯9¾G„–±eb‚þÕ�úÿ† –î’𬟚Ú%éÀ/²Ü&´º[W˜rÞ4l8‘ë�ùw2ç"³fæÇÕòqâk‚ù x9¶ÃÃËtàˆ3δ‡¡Çô g»Yvkè4ÕÂXšüuØfºê„Óì=â1>µaÆE{;F¸úÍl{¹1Þù£1E[tQÒcªdÐÕhÅ£°‰º9Sü„¤wߨöÛàZéc°EµW÷ŒåÊU¾Ì§¸gßU¹]�Œ;<¬híç)]|öò�÷�*|ü+±xlø¨ÜBgiáâDä!?”cf—ö®Ã�©�·¸ìó_Äw;Š)r@FRsGV"JÉHÄ‚ Œš@v­ÞϦå}}!‰¬ep•½ÅJVüd)@K@MïQè1Ù†y¼ãcDÑ,/|ß±äǯW Ñõsùcäç΢ÄAƒ�7óî Ððé+æNÜÒ`]Á¿¿ÿ8­KFˆ)þû>®WVtF�ꯆËã˜^—'ëÁƒÌÿÅ‹*2¬¼3˜Ö·,[˜ôöÑö©clÎ\ûþè�î4Tf–&öK+‚Ñ? :a¢ú]·gÇ ââØÆ�¦±’rûLTn£|yg°�³Uø`©Š'ÌywÐXØ Ô{šã+à%­}^Ã!2+˜ �&Kô&[Wˆã ³�ïý,kgµ´=kk‘NKê¬_*ÛáXɼíf³Àל­joy›¨éâÌÃ@íàp€×ŠÉ‡Ž×%®õÜ»3c,QÍÓ»ñI|‰“%Iò’tµNùYƤBôhðˆŽb«Ô ~&U? ±¹Þ&©l‡iÕ±»-»¬¼»èÃYÁõT3ÑJ™7þFé¤ð†I�É&ˆüqÝ\£Š~˜Î¸IÓBy š¼yã(Ñ®&k>3ÑS¾‹SÄ„òaÿð¤6T`¬^”ŸÜ| ·ºb 3À[Iö}´O±³5yL™(ªDâÓh_²ø¾ %<1/=ÎÈŸ°Ä‘¡B)„h’Ѐ«y*9Ùˆ(wþ²>ɉ~çê’[cm&œÜýÚ’íÌ'ðË/RCžË•Úçã¬4�ê„4�˜§8eR ÅýˆêrÓTbs›ÒÉÓ…|Ï–.Õhï!˜È´‡Ng0‰`Õ^¾•u48é|ÚÒ3lÓ¦¹Ädv¼>þ;°ô8`[÷KÞãŽSÞ;.‰€1ŽÊ~T³FlšížèJ‡¨Sç� r«L±¾gº¥ô'^8‡"Ðѹ2¢®)»q’í…ä²1¾ææ§š_e3”¥»ñÞFš¯TNô̶P§•¶‚é뎳}6Óí'Ãz�ë�§Xö¤"–ƒý¹Hj™»ol{ Õ´¿‡éÃÈhöo²>Ôï6˜ÎÏ@A‰§XÌÊÂµŠ´FJ¤¡ùóí)Fd*†Ú\h|ÜéVÖˆŒa+ÖG�â爦ÃéÃ’[_¶»Ã-öQç»èÌ}Ç”ÜÙæ{|t”�›8ÁYáÁ7nG­v»MòŠå|v¤@ÆÜìzBƒr7CO¼ÃÔ“{, Êej “f²o+ž7»Â{Ìd޹©x1‚à¬�aP5í“íãaÏ2TGÙì¾IƒîÝû¸÷I7|vúnØÖÝpŽæji£cQ@a-±z-Çê ù~¯Óf!Ô‚n††ô»pçÓ{8�‚e`…6±?)¬åL5�½ä hRYgÄÔz¥Ý‹Z›D‹·ÖzËÛ™F{6¤¾¨û~£Â¦œ�e,"×1ÉØH Ýe ÅW¤T‚·¡‡�=Õ­hÓMÐþÌëØ‚°MŽŸNbûbàS«¸“a¤ù¡xêøÉ¶ÎÏôífи�Rôo!²�ô¤¨Y@î#òc\Å�Ê„‡‰ç27‹G´?ö„‰…ŸùDª¥÷»ç"ñE¦`5Å1ë�¯f%jY„k-5¼Ÿ­V�¦Ì2»žÏÊQ.¦ê• fˆúFYô}:Ž~>Ô+�“ª‹Ò}þ¾F Ôz�hE¯Ã?ýúrúY 0(y(Ðe7m`—/&Aû¼l¢`àÃ8þøÕÄq†|yô&Bu©ß˜&ïáÜ#¸Áð—ÃF ±µ‘[Œ‚vd„w€}äÂÀà“ûdëÎÎêB½½’2nŒFÒcB–‰œž!˜0ˆŒ#sft'¸·WBÍ�Û¥ }¢´620ÑÞ™/¯Í=ͳ&Ÿ~_é}_ÄáÂg¥r?ûAº¦n7gzY˜TÂ{-‹X#6˜�ͯ¾Øéò)¾€ðÔPÈÓ£óãÂuÈGfœ‘鸶ѷüyšŒhòåˆïk{î]n~Ò:fZü°èeÏ¡b7ˆ#TR~ÙI^×xó–·· ÇÓ�äÚÕ÷¼WPŠÍCï½| àÝ|6…Ȓ˙¥‡âáz)^«!×8Џ“d¾×2ˆ|wÞ€ý~ökYBi’_¤î­‰TO(€º|ª¹ú/C¢Í>Õö7Un‹(Ð%räU½¥ b4¤RŠwï¸ÅóêRHl;Ñþ›è›MãÔñq…Ø}ñR|jsæG¸�éÌ.�O«å&‹ßYîu¡ÂÛx�;„^M�“‘›‡BŒæÐKX(r »›…FLv° >ßíÎY‚-öCý‘ð +ÞñŒæ8,ÊÀ_µüsvZIJªE¸Ôhv÷ížiöúö( ¦º÷Ú"_�Ϙ «èŸ‰w‘Œð4wƒè7dMp�̧×N³—{i=:OË�âÃ�O­ d-üÝMø¢7"eÀ~ž!æ©ò’qŽÑŒ74»p3ÐyB©¬T¸4«¿ %s˜ì‰Tä´Àñ�¤.å¶a«eG¤¾ð)”QÆÉÿR�zð&ôts·ÑxL”ìJ}gôTáŒcÉêO$œgU¦Ñ,Q(�¦*€1.OŒ¤ª®Î&®ìD¹ý"ð,€éŠ™€ºkÁýºªãÊŠr¸^,}ï¶wIÜCÜŒ!ªšUèÍT®úÂÚ²þý52Q´ÎÞSv/ìÇ×4,5Ë#â9^úç¹á˜l•É—;ÜB×Jº ؇ïI{£¾™îÈ1Ê”�/GM‰¨ þŸº€GÜ)Ðôk,ähZ4Égçój¾Yµ’ZÐÍý‚ªææ‚¼ßvjgë>Ä—À.¶‘³7“ì†ÈÞ–œÒ£96"‹ópÄõ�$Œ¢Ù‡ðUÇd· \$Ú÷÷ƒ†† ¬½Á•¡e²,[ÔØ*M•CD›ÝÂèùUxüÆ&öD=ø‡ÂŠö¥¿ƒ^-õ}d+¤Ð>�)y±‡øÍ¿÷Á¢3âÁ O?Bgzç |ô1Þ&1\ЀՓâïz ö$Ë~Úwp˜ÚÝÜ=ŽB×nåØ�ª\}¹²SNví´ä° 䃀« 0óüEû-˜z_b®QOÇ1c:w™P³ÞΙÊ8ÜWt5ÚDdßWK“[Ä¢è2ÞkaÑ6Ê&{302CóÀúa€íbßk‘ßd‹ós‹®Éh>`8*ºöx$ò¿‡]o�û8³%*¬ä…"=ý‚S_›©³ÂL#:$ïÞº¨ #(îü>g Qôi¶EAŽ>M{Ø~ζɿ%¿'`k¸…('Gvô?ão†¥'ÑúνÒn™5 `8ãÑÒõx…�–~Tó²Æà¹�à?²Â¦ÿ†áõ;rƹû N �Šܹ&ñhÇÌí»tÃf�ƒ¹=Æ!©µI¡§5FømuÝ:–/Ke âÁ~Íø›y…mBfmòŒÕMøl€ûäÜÝC-Š­ !Ô%Zc¼�Øo¿AV§߀I2Ê|OEcM½qª¿U£þLz9Ó™,€¾ »9?é­‰7ªêåïd&«‘DÓr“@�Õæ¾jÇA ØKœgÇŠæD�÷W+"A+yEÑîk±Ådg$b[ŒKìd‘¢®6ûzh@ÀÄâËÆÍ§]™TÓÆÚõÔfWŸÁÚš@ïz”¯<±UäñÉÜÑU §éBϯ�ñw Žº‡ð¦I/Ù<ååp·¾\ cÊÖ…±ó߬–¼Ù¯¼¸5üû½¿Àc¸i)Œ†?Ë9Ðnn0'4ê§JnìÝçɆªsl¿œ^á:;í¢ÆíÀ;7“¼¡%gåJ%+¬ŒYÒEÄú_3¸�üvJfMê‡á¢ü<°?C% ¦Y݈¤×ãôNŒ™TPÎ\Úfî‘™bÌîÁÒiNX¶€§eEî¢p¯dÅ›÷åú�O${Àx¶ÝXÌ\•õ…ôùFÅ0ÿPÞsEU:‡© `¦ŸDÇ&Ûòù´þˆ¦=-ˆAÀ1‰¿Û·M=ÇóÑ1ò¬BA¤/úº>!„Ó~áâ1â_Z¾Ë2ôj¤¡ŠQŒ$Ë:DY‚!ÊýFyÝñûsÂdÜ@ÝñKñ:J&M©¥kç0‡ú+H€ŒãmãðÚñ�¦zq¥ðCc•¼Â¬¡}QwÉG&HçœÄÛ«=ƽÖÇ`2#%Û…BáÜ>‘ãtÒfÊ/ˆÿevïø^J€’TDqȇÔ’iï°IŒ)Éé‘�g6}І¢ JÐÝÚ: üeÓÌ-W±P\µ®^;I´88Œ úĵØÊ ÖDø’­Ë)Œ¦©ŽR�îq±q©š{87.?kí1òR:תm2?§Üî3Cÿåì M Òy¡°|ð×,¢Õ€&þm~¥€µo3 +}jx¶ßTbÖl�ôbsŒ-GWjªÀøçÔþóáòsáŒ<–’Ÿç”?äYz0â ÆÅ+i*Á®°\`;A\<*†¢óV�j›X¨Å_Öéôå5€isáÁÑî¥g(~òùëà‹ë=rÆurÓ‡ åMXgßBg·™ ±2ü&®ÐTFùÉĆqgud¡dLJʱ|ÙÕñÌlUFc£/e±ù>AWðÏw7ØöÙ ºó)¡KÚ%ê‡Í²ßó\+|n•Ѽ³Ë|S勬£‹xs¢°ö‚]ÇÀÑê×;†‹®Ï‰õ¹VÁ­| sQ� (É6ïsÜ5)È}â:ø10` �ZV/™‘¢Ì£¿ÆGëYÚ¾1‰EˆÈ Å@ýíº¶¦½Õž›Øთ}/ ,^ÀNL«c¾xuÁ³ÂU–»:ŽÜ‚: òÊvÅ…ä0ðQð@ �Ò x”o¼[AÖÕXvÖXìU6ޱÉ#.ÙR§zp}7c×Ä*Í‘Úc-äì‚SuÎð@û@††A¿céYôŠÛ<ïè7À‰ùó[½·ÕVx�KüÙ…ÒÐ’o¦¡Ï%G„&]¸à\Ù° _#'ˆ½ìîôàFæ.¸þ9£= =Z[�­gÚ[ýèØ1Ì —±+íŒÐ�2V[·PD±ý=_45ÿ9ìCéH‹í¥”¦(½ûþv‚Éì¬ó-CöçW�åR_rdÏ0°[]€ûN�Lžî1e—ïj–/”ð•ôÉZ{\Th3<]Å jãumz®�½lúRmŒ%ÈèŠÒÇ l®?þ òS…¶œ(ñðAFãã7½"›lA?î�ˆÙÐîâ„U­Ób$"5<[‹Õ�áGITÒ·Sºd^w=6$Rÿ¤� ãÖKý^'ómØ: ÿK“â¹, [Ñ÷XþùŽ|‚Ω4*+*9÷ìµ™”ݸµ~ÀŒà:¥µ – âêæPkÝDqä‹•hœáeöב dZž„†âÙõ\LóBE–W„–25vø«+BëîæP�ýWÙ�Ú]Šk$§œO_¿�/dÿ\±ÈÄæ7�¹Ó% öð…L0Ps¤æÎŒT¯Rîî¤}š ;NÙÛ¬W¶âÄlB‡ÙH“%k¿5�4â¡60iXNsdL#ä¥ì#¿·ÌܺJée ¯»E}ñx gä–úTe#e6•’àá}0ùûÁÏW—>”}9âßÿx$”´Cr6�fy¸×ÓæÇ¢EPžgç;‡8Ö²€ô„ßKͪùöZ!Gî�NîYô�¿úšåçï¸l/Óq!iw´ “:cÓÉ-ØF޵â‘þ‹jk@ÜÍÑ–®Û¤RîUeÃôMü¸”\&vGÞzÒΊ»Iø,›B’C}‚Ÿ&Ê6ÖÍ+ ‡˜ÜfH÷åÖërÓÃVL±$™’Æ$­ÏH[?¤*/‚¢S»aÿob Êò>…³߇u­� ^Sîn¿P#~ƒG¾¾ÖNHÖ:‹"�öÀjÒἆ‹‡–z<¸R#?BµMž›í—2õ'¼%"·ì%E1öÍ}×Ò)©Ö Ž'‚ýKù«®°âí (1t÷ ß80–cY c"ßéiNÞøþåL •Yq;áþ>.¡÷†! ñÒoªØÌ�@G ßx3Fȃpï2žêtIê6÷ù’»cÕ°x:p… ¤¨»µý“ôrõ§[./Öºí¤(˜Ó Z‹‘óÃ0a}aâs¼àsAv±~g­jg†$–ã­�•è†ö·2~PC“Y£g9Ç)i®Øp6H³^Æfl~“²¼¸jXámpX¶Dø³ÆYæä†ñ¢¹ÿkÞ³äãó¸Ë]�R2ߥe,õ; ¦÷jøî祫}úƒPÍš9Ãs⃰2xâí Œý äzÕkŠ×›‚„ä[L*f‹u¯WD¦­À¢×Í̲UúÊ©ëdó¯ë‘1*c­Eâ”U. nW)V"Õ„ÄSk/g‘24ý_o+¾Öt‘Óú#ɺ`¤MXñéN:ÝPÛ ªCW!LH%%6…n�ÎB�ÿU´ �+Z‚L|5XW(�>ëED5shÔ|€IÍ–€žZIöŽ’@Žÿ&jý$'€ÅYh´fèÿÖÔÐHb¹¥ø¥­è(>:W\l´÷jíÞ<îÖqŃ¥ÄM" ±EÓ,·îW•Õgçª.»«Ñ,µw`“H§³¿¯ã_lCNÿP¡†�_�aHÖ0§‘˜”›†®Oˆ@iâ(•9H� $ÐlÜîžpÞð YŠÌ 2¹$w6Ëô£â¸¼£\»  �Ê•3g?¾l�÷0ˆ�ÈÒ ™5`ÓÆÈίû3}Qv¯:Lóát0™ÑGœOQ`¶Æ&¯o–žœ�¯¿Fú…ܱ/ưd–rµGVsÝâÓšw˜J݉1(ÈÄœ}ß(Ê„§F.«{ß-¶ì*�=¼îâ­ò„(ßÁxÅCÚ‡÷1­F”CÎ'³GHÒTüq^u.Sé†Ï|Î’D’wöH™ðz¸nQNú,Öôh(Mqï"­­ñD g1\kÓ$’‰Óߌe u,U)jŸ`‰²Çp&LÇQ€Å‡ò»�ïÍù¶S°x$¯G9]¡]duëòîÅ?(þ¶Þw·a>}ü7Ü”´¤@;€½&¨â…ønVfRž¤›9.æÕ4wK@O$“ú‹ÚúX5 ¼Âƒ^4ìõ®½œ?j pTÞvÎI´ø¾Yq«§ªÁg5%ZS ×kŸÝ®œŽt¾S éjæD©x÷ÌÁ‡>ëÒ, Îè)bˆµÔ¼ ^¯ü°q̦݇χ�7ÛP•I™L´â¥Ø ›9±ÓßæÄÔ¨ý !íh–‹,>¦.aÝj¸<Š2x½”ãÓ» Nƒ¥qA5z#çú‹ÙÔ n.Qgò×R?3«¸C%Ð!Çûî}}}Ù@Ô8²#äHüí/� ‡ÕFöÌkKÀDÕð¡Ãõ„]݈Á‚ñ!É1cC~Ó¤J7âÔi¸Û¬«nêÒt�>ÜJH~Î]tuÁ£Z·Oÿ!ÖàébÛží 0³‚—Rr hÑÅ-!€¬t×,ò$ßý¶÷xúÂ�!aë_íÞ�'€ŠÏ’„®6!£ÐMŸF‚ )Ü2ܬ”J•±¡;:ïäªÀèYf­‡Ž·x–׺ºó�ªø£Ô‹AHìR7JU^Ëi]2Hc—²Ïj�ÊË»!V¸©ô®×óau4 ä~�ÞýÂrÜÞ•ö}µ2™æ�ÅÉš·Ég6¾ªÖ.éÇý\‘C ¢Ûß dà‰J"¶îªS�t Eõ�59ˆX@Ëp±W¾§Þ„‰·Ú!±É=É]ƒ›Í�N(ƒ)1 -ewŽG{VÈ�µ˜.àïã@<ø—',Žùr&´µ‹t´ ¾rûoʱ£?†Ç¹Äw ×V¶O 7dk P±RþÊ�Ò"]~S扵‰ÿü÷Áš "ô{”a]îŽ×áÇÙô­¸I4]�ŒäŽØÖˆ{‹-öÍ÷‡4Ë]À,(Þ_˜À/Å-¼9¾XƒSBFÀq÷Ïp&½|í8ñPžm«/¸ý–µ¢ÐîP× 8úþl�ÜØ…ŸÇX«QÆC‹Zdz¢Ž>•NQf)“}0,xwéB‹è­�˜%K,ð(ÆÎ†9À2á WÓâQ¯&#¡ú®÷¸ U¬ëéO9+7ËìðòF¬½Üìæÿàïò ¹æªÂ~|S秈�Á¤„ü}sjËmH€ ä×aªåaç}éEÔÓ\o⦊«08O+X€sÙ‡h-Υߜ`|Oݾ6€î®èœîav_Ñ :JÎ2{i¥ Ù¼ñgµ¬JìÒíPȄĒÂìC}žf=i�ýÑò¤‡˜4§zÓß@pì+Y’°ƒ%žÇ3ò­MŽÂB´@`™^7¶ó£ŸÐ!#„l�¹à‚¸d{æGEë«„ö ôƒƒ±,–Ê(ïËïq[]4­NíŒß*¦Ô°9sù}‚JgJÒã Xf'(©;yˆÿ¬ j‹¾8\f•ÞŽ;“‰Ÿ¢Z"¨ïá–áàÏ÷4Æ„ñžÇ›#+43W ‚“Ür3ݾwGmêxqͯEèë¯õ›Òè&ÍF¸-ÆÒB»4Ì5Ñæz:›.$xë/öÝ€æ³7¥…äðƒ: Á1“³U}¤¬ÇŽPµN½öÔ—H5êCÃ2=¦l…,<;1� ¨Ð 9¢b˜> ÈnE«3ööš¥xÃLì'õþõU¬˜ã4•�ÕæätY¤’Ï«¾ÌÄñ“ ÝÆz²äã¢\ó°SÃ@?0\©Vl‹AP^œaœ>0{Y·P­®(¶�þÎX ó¬ÁtA`¬*d´ä«ÉÆ\½¢‚c¹Ã¸z�ñ áüÛkmË0Yi|Y̼wPLÿ&V(]Lõ£ˆÞ¦��|ܸb 8NÓgË´ü3÷a¨Jì)÷&ìy?Ï?Šèj7ÃnÇÔ°ˆ�šÃ ä´êçœÒ�<û`¾H‘5�­ë“ ¹ÐÀãÞtÝê¤ÌðcÿçgXA ¼à ¤XÁI'ØàÐkaý�[¢ƒhÙŸºwýÉLA­¾cQÁ6!mOVc3ðþœBËô¦Êsx§s¿,K^ÖGŒ&”~UäxÑX,]˯÷âgBFØ|Žñ*Ö}È<“¢AßÂשÁX¹À!ßÈ ¯}�ù;¯®¤'±©ã'—]öÉ`ÐÓ�;úÞj¸Á&…}…ýµÕº³J ô¶`ëeSÑ��c©ÐwËgýÇ`ܘÏB·@OâÁ¸éÿ÷«¹ BØUà\Ñ1?°pŒ†ìÓøYîSÛšm-ælЇ‡?³¦À‰G}çÝËp|$•ÐÀ‹n*Ô‘Fá:4òB¡O*1QwÙy6¼)¸÷³²YI”)nñÍ3N“´òõ'N6¨2noxU¥:éB‡ž¯kï³ ²d2Y#´™Â8 ^p Ë‹ ¦'O�‚ù–Ï£–2˜¢ÐÝ Ê*žb:w“)QBÓà0,Z÷Ñt éÎc3K‰�Êõbë›þ;:S wptÆ13€Ý…ë~ûµáÄ›]f}ú1PŸhKÁ ­Î®ë¨ÇO=Uî¢âª±T 7«à=·0¿΢ÉÝ>.|û#Ý“ÞE¹PA X´wŸüGxw} åÿù×_}ý9»£»v™ŸÑ¤:u€1JÊ®›ï¯U;ƒçmæÍt'´d/nï�ïôË¥ˆ¤#p%jó| =ÏCuC•“„wØ|¿(z� ªÛ×Ì�#t—×,ôp%˜¡ ´¯Ñ�ûçÙô¨˜HÀ¥…9–¢Ö;õ½ä(´§Æ„R÷{žîaõùênÿÁðݰå×ß©ßgø=÷ÔÈÖ‘3´ð�ÑfqtdÀx½¸åt©ÁFgL‘ÿ¢ PäŒÍOÊÖƒ­¿SpÇ,äS…ë¡÷¢˜>¢ž‘iìdT@w}yÇæz)póÏ3Á�¸¨ Âþì»Êq;¬×âkŒ“ nÁC�©£yù–G'õ`ûŽ�ªÒniAB§²w:è˜ ›�dþu•s>ÍZ;Ý­—  Â+œ^ª6› �ï¸g‡Ë”.«úýqù±ºôºu‚” Ïnôk# SÉN¯d¼ÀGß]˜´eVï ôæIJdVœä%q=ZÀˆ“dDäe¬Ör %)M¾ÐQTŒGü̵uíü¿:\ãqL9 •ãÀ(}^˜)½èè¾^¸ŒÌ8Y~ØHYŸm¸ÃtÙ¿ØR+áTy  ¢0\ƈ^K¸Á$4¬jE0iF÷±vÒÑߑٰ{ê<2y Fu:ÊBõeL¾®ôdÙç< ”o:ÙìQJ†PÒU4PóŠêe¿`°@ á(Èšų̂ġÄÒÚM¢�µb‡ÖÑT³º™}nÙöÏ%K¨xk÷ÍBÔ碲{P衘µ¹‘ÛˆZ±v ~0¶åy+t á¿¶¤8FÚÉtÄíÜ®Û|*“¦+ÌlUá6Ý×Y£.»ä×co­i—q©oð Y„jÂrJíÐ}Ý3Ž ±Ÿ¤Ú2ÁžáAùÓ²k®=œ)_ÍÑüETËhXŽG¼#CÛmé ?qÚÂàɆ¸Úe[+äÚ�ö�Þïf²ýŠ Þ¡@ f­ë9È|›r±X J =A™²DNTµdÑ!b%ìë©{ËøüС–«D†%¤4ç6NbìE VK²SaAÒžÃ6/;¡ÍD´·B©à^QX•lËÔsAeU|¢ø·Ç‡³ƒÇJñÓO ¶dZj;vö#dFû JÓshÄ�˜û̹®4ŠDØÿe;ÔC·ðë‹iQ³Z¾#-€£�ƒMår™)]Ÿ¸�M.¤™¢D§eCü—}q÷%Ì@‰•Í×GÛG¹›x@üŠÜB5ý•³^ˆ#Z‚M<·ÊÍ6IÓ‘‰’ðĺ(©x¡#t¶w¬ æQ'ù@&øHKa63†ÙBJaêPãê®ÇŠ.ÕÎÚX 0<¦×9�ýŽÐ[Ï�Ÿ|I7-�¸Y5€žtÃ#ðò6~6ÝÅëcõ‰YAšt(�"7‘µ{ ºÒà¿H[LB«J/Iª•¯>À_Z~_6So¤®­8séS$$¬)R×)#áä˜üZl„�•MJaàÈß¿§ó3vM,ÑE$r)“Ç€´>ðòŽ-ˆ°Ö¥)8M×£l[ðØþÔâf®ðéÓ+T«­¬ÏÀf@¿ ë+ý[@o/¯òt Èõg·ºÝÐ_¸ã¨¤³Dð£oüÇ9+Ê)–ëÊ8Ivð=ƒL¿JZ‡•â—úsôVÌÁQÆ\ÆŸ¡Û§^÷h‰7ä¿xXÇC`!2Ýýû+ì|(¡Üæ.zCeÿ-7ŒHxZ*‘i:ïÕñßRzå�¯\žÖ.ÖÞûË‘ñvMì­⟵�z&·”¦G/-Mc¼Õú®2®¨»ˆŽDоžÏSWëÐat³Ž8 Ù¶LÑåç™\)HUËì—h‡õ|+á%ÒôÕu@)”Ì"?)Á{y=ÃîINYZ:‚ÊÐí²1Ê)n4b&͈w¢tˆA…EÄÞûö­ +POR¨»Ñj€oÛ䲉?‘k¡›ÏK‡ìTš»=ÙEðÞìÂi(¥a/xQÞ`€¯¦¹èaó6ÿh ðu¤šFrH>R¯;R¨ˆÃž›(¥ÆF™þÛÙ[¨å\ÐÓl¾c“'À†?¯J62†mãOêÿ´yÑ«^ïP‘}jFX$†Ë7}‡%ÊÂ[&’‚ûE'‚i{í}­„¿�éù%sòï3gÇÉJó¼œo“œb¬"Mòktû-÷ø.Ö…½3p†Êù EEJ€)7#X=)K¾AÚûâhTxaØ.*“þîXóŠ–§±„6¡sº63Š‚µÞ›¹  ‚—ç×À³œÄ¼ßWÁ Ÿ–d{@¾Ï�ÞÂì{×gÁ¬Ý¾mY+¾òü"þÊ ïU�Æ™'¶0]UÊå¶Ï; ž“¤"È?DwŒÃæ¹yÂØ,î˜�2¿K—L‡‡ÍxÁZø=VJ%ÊHlfþ¡•RÝ}<ŠÇ( <’Š“b31:C¤µ#ú_ÑÛ õ­#. ) Âà"åîX�B73š¸1�þ r QÈê7"}(ᤨØ+eéUS†ÿØÛë&{üy´]…™|¨k¨s©ž–=Ø-PK¾5ú½t ¾í"$Ž$Ìí%8 æ½Úa+óĬ$Q9D™¶nNhHb°PB6´ÇZw#FåOQ•ýâwr·Þ[Ÿ˜û¹çÔÚIQu4†ÂN9é+jÛ½q„§²•$W­˜:SŠÑ/ük" Sa-Oa›¹®èÒ“øˆè£Í¬Ó;~~߯#UU9‡Üþ‹5Si†ÐX”Ð\7P�ÂåÝü‡�Ÿ"*£z—¯Ù±ønigÈLÒš›}1©–Yþk�×&ÅÂFƒ‡DßœÖ/ÅÞÚ¨��è:�Êgx¨J!¨ïAâ0 ©2”1n%Í"c"Vв×$æÒ|§Q©Ÿ ²r‘*ö´Ïæ/m¹L˜Úi?è"”�=9’[xã!àwþËpÛ€‘/ýKtJÔðÇjKªÊ/f„AÆ·«ÀÒÿ§10[ƒ±½K0ô±æ æÑagö°8WiRXë�Ô³u‚±ž51ClœqyNc¥?¡ªC{3ù:ˆM´`HæëS¸6Üï’+ÊLY’z `+�ÉeÊkC…Yœ³T Ýu==æ?› Ç�q=͉ƒÀru(HªCmuÌÄ ÈVÎ�ÂüÊ‘ I0‹”óÑb!ZÐc5ï+Ÿ9áï�âõ¨ÊVesN©Wø^2©»Y2N™Ïˆ (ßÝZ4Oô”9ËØ¢Éûë;½oTÉzho*{+Am葪Q"áUÄŠ#õ}´²\m‹K@sQñ i,ÂpÄǪPâ�RM“œvyr2 órw[¿b÷bÄàO Ê´E‹ü=Ÿ‚»ãCKîÔ¿ c–cÙ’Ý-§o/—ycÒ¹‹ ¢ qvÑšaN©õÒ½XN6©ôïݨ?í\šŠ·¨~.ÓGç|sª�€[ËÓµÙŸÌ@P Ç.m0#Ç£5ÐÀ,»O>Š¢}R ¼ªVæw�š©÷6ù\w8 ðiä9”• j´ÝÂyŽH¨/Ëm}™ô#;Ř-Š4É ³}\�ѽ±_Y„ÖÆN-€­Û‰ò}mÆ`…ŠéX0¿fwÍ9þÞãê\lH ûuSÖT‡I9Uׯ"b™…!Gåª&0 ƒäOŸ`;¬Jüšüg†¾:Zz¢!?*Ì xMÿÝPĬ¾Vsp/üŽ"Îvh Ž/À~Äൟ±[єڟ@…ŒhÚß"¿ÆæÆW7Ï®oPî\7ø�ÅÛ(aK‡¢'Að‰�Ÿ®·WoéúÖ�ý[VçÜØ4àlDã´$†²qÏtÀ Ñ7¸N¡Ñãà\®ôݸ¨H´Œû†È]$r¸ªÒòP¹1~°…´Ìýc:8°ø Þ„ÆØÂÄ‘©`; ŸšöºPœ2¦‹P¯U.#Ž1Ï®EÆVð½—×h tO©QÊàãðUY³Ì¾›0à¦Ñ4vàÿé'Ì=á‰<�:~¶%@·È´}=: ÷r¦dÝs-Ð`bø*÷�ó.?¥Ñs°Îó©�l;üÝ”‹QdWlôÜV@Ò‹TÍË� Á¶àÌðG®6Ò¦‘°2�×µ/³Ï>dfM aß.Y8”ìõ=ú°íT]ì–_L èYû:öI$Ÿ7bî_òMÒîÖy“˜Hš`iC ªæóM¢È ³ßF“@D ¶Ïçm$zmã=÷ŽïƾØÂ‰U�ÿXµ!{] Z‹Ý¿s8ªe¢µò5‰ ¯0µQ$ãUƒqG‡¦éVܾ:2�2’n�l¯ x÷‰6È…vTRð#‚Í/hRœØpÆ�ŸN¬µ_ìÔZ 1å$û68à÷}¬÷O¶Vñ'1†RÊSæ›8EîB)X¶ }zŠ =ðšÙøˆG ƒ{¡ôÎ_m¿’È/Ž8 Aã%Ó?lnj@ó¯5ñn�2¾©Ø è/±U¬f:)wõ*F„÷ûÅÈÊÜ:#þ2šæþ9nRuú쪩r}éúiÊó¥¤#=YŸú‘íÐßhK:‡ú½­ëÑ9YòcvÔ3ï‘×÷ †{ðú/R† ”QA^e! öC"˜ Êj96tÓ2K �;«Ê걉ñ3aÃÍaW‡†ßl%‹ï~tÖWHul%¹2Pц!�ºhYWUbüÊh¥dcC`³q]àg˜Ì îF®‡kªŒSà°Âå÷äÍ@dy¡{mC²[±Ò}ëŸÀXÞ$q9ùî‘9e¨§RŒPCgÎöj‰MxVYÌ,dæ?2g1IÍý²cZz”¤-<#¯§ŽVä€Wcd7ð­uí@Ca2ÎB KÑjñ7¥=¹uΤ ²2²þSÉ�¦€â·Rþ‡W'JíÝîÅ¥aè/ˉ:”}wû˜R©jÒº Ò¯kڞݾgKþ×e� #ie%ïe kÝäŠÔF»²ñºîIA<ŒÆU£F™$ù2²v óFÆB3_AuB0Á¾ž¿"ézš‘Y{+@eœM©ýiŸ­–2œ�˜õÑ+ä â€îÉŸâ•à ”ÞzÅØ8 Ù…,‹z¥%~KZh’ežaÓ»xWql‰DÂx£PØjÉZxõÀ}±‹=ûŸúQw¢èDÌ£nU0¶ÿLNI/4W=AR}{iäìÊ­R�› ˆ×CÜ„-¥†|1€µlƒj»‰l�¿ÃsG¢{qIp…"qwƒ¹é²eíctÑVet Ïü—oŒé ‹·‘qëƒ8"²_cÌP:f�V1[ìš*§Ø· eD¼aŠÐž¬�’:šž�àJQΞ/tEY>üà ÍóvUP~—âæ…æ×¡×—;æ‰î¼2\�¶é—§C¬'{äFtòEŽñý‘I·½@@˜B{ûÖos`ÉQ_2pÊ*8:ͯ÷v‘(áŽ)) f?¥dºrˆ,Ë“¨n={ÒîY�k�ß!:=TóÑô«x³—¹¿´Êåg}*U“¡ûóâk²0Õ´šfÕš�4g%¡ÁuR2à(Ékö4tÒ‡iåÛ|Àl„Æ#<Í?,V“[Ä`;ßÃO›à“cz—ÿE˜™]!Ñkèò¿œ�us1žÜ’�a3Ì.�Õ©'š¾°t´‰Ê,‚ø/2¦î¨6éap'wš9A™c|•»ÇU¿0´Ýwnù­Ä…j \ååo»=/m݆ß�Ü~h?Wë“.{Tø‹G@mMD™ã˜—óO}êxFJ›»Å|Áv´Pµâ4 Óô�Ôv2{x“ö~�‰é‘`Ÿ3¬[Ô$&»^'Í¥–ç‰ò¢yßAk¹þ†e�:˜¿ÌJ@ÝP~™fáxõß9›�SwÝ?3~|Ze Ç75ù�Òì´Xll®§pód7–C3«CåÚŽPÁG¤u»A�Üÿ”ÜM¡ o­WìàëwK¼…�T¦í©á �uÞQ†fg¼Ž×ò-–°«éó­ÿé/ðÐ¥0´.&dôÇ Ÿž¹cKÚ(½óŽ;X÷Ð Ûl#Ý 1 G‡hJ¾ ŸÁ”§•!B/O¢ýx„\ÁþbïOÅ2¬FZ~RL”í‡Yï yõrÉ÷u ÏêÜñ°ý#-h¦ü{„¤ `Áy÷§(yh[œ)�ÜÈ/]ÜçÎEBÓÒÊ4ük%…$t'Ô˜|h3;)òI0¬®>älÓK¡`âËBTl>Ê—þD'Ó¯iD²¥Úñé$Ī…©Â�(�™�\e0ú‘ÒÿÆ„ÿ G(2»IÄûÝéË–y8›\°öMWäÎ'É7ôNǸ| ð´ãï2[«lÎ@™®bJæõ²ùz/Â��ãûF—ˆ85Ù´<›&-3¶¤Ÿv¨¶Dç^ÙY¸F¯“#bㄜ'ÀF¼;±a û¢| „~h�HáÚÈRâ6nç aã„ν–MY^gÝt%O�axX.“‘ªáNS+ŒŒú|Æ™šˆÛOÙ½ ´7}ålÑ`0zûŠ—ºá ˜ÊÖ^ª¯ÁÕWŸ¨¬Þ4ˆÌ¿L¼…ƒ©”ŸÈ6ÍÀÂz0+HèaË”·ÎÖ‘îÅw‘<4œ2¿P]:.ûÒ×À^ó‚t2»ûíKVü&=ìf¨{AkÌÂ+#Åß’³¹}Óð®>gíðÉ…è验ü¶­¶’kJ ÂÎi¼AÝ/„&z”CdVºµ*¦»:®AEmÚŸ:õ^Ü¢—Iȹ=OPJÔÞ`¦Ú×qûíæ¢WñëGÅ2ÇŸoµõS‰VÞû™XdK¸K­S†6‘K5U9&‘é ¡­HÃOí—ãrno4gMmÞת?¼KCâN×øól̰C÷‰9†ƒfªÀšÃ(8ö$•Ç© ÜXb—à¨EAVü#ó*ð ½Y!˜”ÂÂR†v&+êŠ/}Ї§B|bñÙ¶Ä3A홳!((Â_m²^Ú)qnF„oÊ™‰0hi-=' $�U¬ÔÑÄŠÒÑŸ’{g^¬y�C„�…¨�’pãt,²ë ¾ÚþÒ]Á*´žbvHÐŽÙ7ÕÁüÞʵ‡è5§14?Øê‹Cš`w ¥�Ñn‘¤¢+|Rm «÷œ¡Îâ£ìzkE.ª×wÌå·*c ä 8K¾þ­Göš{”ƒèñ°UÍe“»œs¡çÜ_Ë3ƒõ$cätœ“/Ñè…•ÞúÜu¬9qY„PÆÃŠ$™¦ùÝ(}ÚIQ�@g‹)‘�_ù¥Ÿˆ®.àÈø³ C½¯œÓ!ÖµU±&Ð þ¾1ðÖ…Œ¬h_¶|kƒÊÜ»wˆ”Olî�íðPôµÄÃòÕÁ/=!ÏA­�üˆy2ª5ëÿ�>N€á«Ë¼p¨¹�W×/G•ÇÍ4N¢ßT{ÔúK’ž-�:f»ÆFù´ÁÕZ˜W÷ÝcÊ&" £ÂQ_(5zþ/ô¤ÖveXÍ+ÎcOˆ‹¦ÿÿ=¯õ{P{ü4/ê˜4·œ¶4u§ ùÀ øqSÄDÕ*ùRæÝwz GÏÒ• "Äc�a"®¼þŒ»æþãë’mJ–—]¼~^Y”ÚæÓU@M�Cè"t6ž‡sðûpãAG;nûjɱÔd¯#Ün BPєق³­`u|õnÏF=�³Š/#Ù¤š‰yf.�$½+ ëŠ.uˆ][ë T 8ÞŠí®ÝPB]þb1zÅÜnH‚Ù‹�:™°4¡ShéÏ;% Ë ï’“¹ô±c÷fBp³o? 1¡�?Q­Ü×㉗ �‚를¢2œqaôTiƫߋL"¾ñG r\^3 "È¡7 bý�¹»uþs:Ä â<ä¯LL3ú2ÒUtò‘Š`Kû°QWÁ<r×ßÕx©Ÿªiâî¦xÎÓúÛÿŒØBr:mw3(Áî ¨s:4³x‡ÎÙ×=‰_ÁÐð½8òî«1¯Oøtm½K·½ŸÚAÄ(ò¨º;Ы¹¼™ÓrNjÆ |àÔœ/F‰õéÝ«)Ïa nç#ã+õþ�÷¿íïTÌv?ÕÞ¾ú¦ïŽ4 ðÿÚõÜš°0š¨¼2jVj¥Zâï<=ZœåHJOÔ¤¢"=6ïy �ˆþÅo5ªFÇÕd´ôaÎtÆdëZ�/©Ëä¢9ÃBŸ¶lU¼bñ·d-?™ÂÉp-'ÿR¾ö´'ÛɯåE”FojGME¿¢ œ^� 4ÜKÿòt˜%:Z4d×åD¿[v‰'20äõÁæ)Þ!¸ïÉJ®¼!R»¼“@gCƒ(+ýU�›-ÒÛÇJ'A•¯¼>�¿¥ à9]e;V)W't]³¾”÷¿óì{Ö×ðžA"^D›©º°Ô_Òß2Y9ÈžìàmŸìÚèÖ”PÖÖ4ëÊ#$ä˜5,y >y|z­kÏ ÍŠ·ÞŠ0Dªü½.áA!¹Ôå²Â2­®<ùôbE¹ýëð³bÁÀš†2çÒ§>Ò5µ"—9—ëgbBšÝ˜1ðE5d)ý.ÕÊ?…ÝøkC…ökøîµgƒÁ4‚ÓÅÌp6[RZ;%l±QÞƒ†µé»e:¿YÓtpËÂÅfÚZÛä_¡týnBH` %RÉR Ž(Öé'CœÒÛîwÃ…˜P³]‚õPÂÏ•@Ïì.Ë€ùkf¾°À >b#UìüPpGÖ,wPðú`‡SF¯÷ñ%i¿[B <1J�àÔŠ@SÙx sVzÁõBOÉ©ö5fãˆÈeZdV]�®sË<˜§ÐŧÙ÷¯:x¡w‚r¶˜aÇ•¨}µFéS Ìo)Rl7@šoi°Ad!0³lVÎN5n‚í»;+ÉÕqãQu çSUYAµ9\wáx¡‘ Œ‚’_ÁdD4"%ž’é�ÌÇ â!_¾Nzê�F ÓO¿õwYqï®7U;£ë´Æí­¼%+G,ÇŸ»…×UHŠ;trÀØa ÀoZÑpN†â†ižŒ©Ç“ E s€ÞˆsMÆU�é�ÄCÓ$…Éw[å€dæsA¦–Ä.n48¸yA0í>x¬³t:aŸœˆÇ}Aât%�ÙrÄ!)ë¢Ã.è Ø¿i_º>¬‹ck;¢³fwøÈÏNê¸ß7³J +j�Øýb˜_ŒÛ ®©åªÂíoËb ¨ÓXq*ÏU)¸øS!AÊ¡º;8à£þF9¸j{ ƒäßv4Èû¿3NbÚìßRFOzŒ±š‡ È-¶'æü±m©HO Ë1Ù4éØ�%Û²|TW| 7@®¼;ÍNr]BAz\52þ:¹`×zË!xs­? º™`ïÉYs­Òê�Lš]G÷Cs9HR+¾˜¯àHüì„|…Ÿ�|T M‘Æïº|`�‰¯ž¡æN‰g~�šêˬ¢üX³÷ÿÝ Õ*5ó¥Š,à)k_€=¢1„Ã$ÈøŽñv'&™™o¹3LfxuÌ›ñY½CFömÛ¶lŒ×Ä—Çë˜QÖÿDBm-´f•§ÙŠ~@0Q[;Þê±zœ¹²=Õ°œ�˜ü¾_‡V-²fe_µÿ ¥|“‡|@â]é±tÇÄ4Á„1reá\ÁWƒIy´¢3@Ð)â(Žõ•z9)Sä*…/ŠÑÊ�¶%¿C-z$ù"PbHM[v%l–÷ï=ÄW“w AI�µo×û­Þ©®3&ÆÎ£¤WÖòXšóìÇ£”ÒÙì*6pHH)&ôf�2þ…oêÚÛäùì…ÝÒ}k`;þÀ�Ysu“êîrÁ#VP¶eVwA[`)Òófw4ÎW„&¡V8žšf›¿ôSH:Ì]ùï¶bo3Ü‚ʨ(ÂqØ6�¥™TÒ CRÈÖû<¨®Ê2 L…FRfîøiD.´mÞ�÷ÒXQ—ÓdñuðÔHj¿` JAFqÑÞ¦ÿ;�¿3²¬åßgó˶¨K\xóàV¡šÁPoÞlr¤ÄÀ)Â�Õºù–=z„nâ»´$v.íó"†yªÍ¾þÒÂã¨"êСTÍY—iS_øWYٚ¤l²n&îWÚ¤x�m5U�[ÐkL×ïùÈšä¾nÂCƒsþ 2ê“i!ç(Íó@ž´ÙˆŸHûJ�8RüÆœÞ-}'°Wmãó¬ Ív¶ Ê�¼%Ž˜¸ÉHÅ~ÂáÃ4?Ð[è«xSkÜugÃ]úJ1†¨eÖ¿�fÀQFÊ?D<*!õ#«ðj 7’ïËÑÙ©�’SÇjìmANL3ùXš^ËàM€P@�šò&4Ôó`)¿yLÌeg�ð:á’Tk�HZ¯F�+4�è�–� 6À2G‰‰› �T�—[ pæžåhT–Á>W°¶ð þñÿ`?$È~Bcƒô„fHÈ®aÏ{ÃÄšÿUEª›¾£Âö¾i¿ À�¹”Ê^¿˜y‡>”ñ/»¹^‰åOýä|óò·ÜHA*Ç´ôª £,G™%º”iN[·ˆÿkÜà39ÿÖzQçQì8ÒÂg³!ùÎI/ ˆ¼òhQÄ_É΋GºäÒߌŠWzÓcš“ÁùH€n!”JÈ|l튜ßùÅ>ò@_h[´xGE¿Qå_ë-Ù³^™55|¿ !!bÖO—à£îçð è5Poå\ÄHâÛPƒ¨!�ì}š'¸nt.Ï}ão©7.R¼­1Íq zVf�HO=°&_Ô ­l‘‡ÕJ•š-Êäq†~]‚¾N÷ðê=rÆ·2^m±ûÏðw³9ü¹¬TÜ{qœžmÞ*êrøì‹S·„œǺ=hŽ’*BÓ”|ù®L!•YæV“LGE\OH¸PZô°„¬”Ƙ‚kª…ù*£f ÛGï­ß¼^vìµ$ù=>€6þ× aïï0º‰†½Ãõ‡õ´àÔJ}AÉëgˆg+‘:_¡,¶4dûÍ/kðè÷²x@lH¢î—Ò•`ŠÈ;ÔÆÁ4=Ìæj¿w±7ø4L4à%Íq5(¹^ü’Î`RªŸÇÐúP€oŽ•ã¿™8$ö¤ˆü_¶«¨¶Úi<«™À˜G<ìŒtkf9¬â1<»®ÐaŸµ¥f'~‡Óu ©{Bþ(ø3(èÕ�“Õ)5z×]}Žª—¢ª®cÌühüºº€oc`¤f2’yÝ¿m¯ùàsnFs¶>`,õ¹�±Õ…œt°l:»;G O¨>�¸<öìh`(Qj´d™”¼ewrßU…¡Š×[ùCPJÄ;TÚ¿ËpcfÉ�w(ÊÚüœÒ=Å�¡ö ´:þË•Ð4*V•¿„yøÅZZ"ÓÅçÞ�PZ…JrYPE¾+Ÿ�rÛ8­I{¹¤¶‘Î)`0:‰ö Š4ÍÇHî ’…-•sOV<¥8Ibš·.[%*’R)ðÉ §¯½ªë‹Ð|¦Ý¹fªïþÛA¨ñëó¼`2¾#›e/–†oP­´l(Ø`ùÛcÜ€1 �|壴ٞ@@•†õÅ'À�©$ö‘þÇÒ¸ï'Ãtñ 0³OÉQ¾ž—Wâ‹0‘~S�?ÉßA*'ˆ=*ö9’`{i‚Éšûâñ¯ÙnäâùØ\º4Ÿ,‚|°ß°5]c…Ö•Heëã†ÕÖ…·‹úÙ°cÌñ�^>ˆ$ÅŒ!elwZá6< a‰—%Z±u¨q˜m�:Äž`|qj{]5ƒ¾)ÂÔqîUÉ}wÔÁßܽX¸SãÐZºâ{y–'NTø?¦ _Î)°tì™¶Š¯ZDZI£’µdv&wœ©Ô:ä¡YÀ%Û)]äññ¤£LÒÆP5}]¯ìñQOlcØTÄ1w·]´5ëKo�Ü{úF¡Ä€5”·Ü5 Y¥¸S�"U)ÓŽzÒñ!s´ÈB8B¤H=p"†Å³èRŸOÝÛíËFSv[»Eºr€W3ú~Wr"ÏVÈ�Hÿ#†�ô'�÷ÊêŶ(¢- ~9Dý±0E‡Øˆ…‡ :ea¡›3£µ;r#`uÈË=|dmî\oï§´‚w”2-¥r!M¡8®j‹éU%*Ì)Å×g©ì¯LÿÈÅQÈW-úgøAÍ(.wØ4þ‡É ðˆøK†.H Ë-2»@Ђ"lû)p²ƒºVW~ô ¡eTHr¼n“˜€RGH´¿R ʳQž·ñÜÅI5òè 0>x!îlÖ(Ù7x OÅDó£“ ¥2«Å Bß…Æl„¡¡éá¦ÀþGú} Äýú·Iƒ¹H/iñÕqòh­¿ |K"ÉéR¬%ólÅ�–jj«xŸ÷F—kä¹ãrxßJ²&€©‘n ÚsÀÊÝ6�Ѭ>’ÏÒ‡âñ"ÊÛàlÂ0e?>H˜až•'©mѪŠÛl—cÙ¢X&Ú0¶ ó³bVðeá“À:&xó*Ý +D«Û˜Âfl+GZO 9•Deb»€ËsIï2È}ó“›e°°* b=3°Å†äq5[¡×b~d”5m üVb�+±[ÓÊ�[æA 9c¬ 3\Ò\ßß×[Kè1œÒ›dªœo�Ûõðž ‚A]©.['3³CnÑ{ñ£ ß®4º!èŒhûõ [õ(³1"Èn*?ºï z<íª¹~^}ÿ²n®¿0çÉØŽbž³›Å–…§‡¼}qJÍ]¼óâ.oÐ:â¦râŠ{¿XGWÅ’UJj …»Yr65*¾õã6X .vîíù†°\‹ë Ž m™ç¼l²Òm[ð"fhíê.ÓØSþæÎ¾@<âV9•ng³êRAîb_R ÀBäwÓ$íMk­PóS ˜öñ–Öà7PÑš�AWÊ‹KÖþõŽ×¥é»æÝ*‚ã÷�ÃO�Âß‚íj�p�õQÑØ$^0ÿªóAf~FÕô9 ^hÝå¤iŸ&x�ùÇBn¯÷.kEØáË·Ÿ|šÄ‹§t†­²_Ö'öô~ lß&€KJ_ Ì�ãD‚ç$ïhJ°RØÐ@ÆZ{e³z'Ù5ôNdr0¶÷3É3yþí=ÕÏ ,lª %¥3“—ù³8vÉÃWæç*ñöÑc£$˜á ¾7n¤˜Õ™g� 40•…ý%jÝ”…QÉÛ‰±>½@ŸPô ÀÔ�‡ó°&ÒÖ¬Bõ4ð'�‡æœ°rüE:D Æq $¾:Üî^š¢ó§6;®'˜V/–šãš:F¨l$í&•7�N–NAÀž¶…Q <¹B1?×�ƒëÅÍf·‘ ý3D¤ô0uðÛ)Ð÷¨,xŠÕR—é6ó=DÌ<©æ5…Qœô£+d’“ÂU^²jëúõn %´%U�öz¤ ì¬*•šÕ\Žƒªp�¬áOAé é Ý 9/Xµòà1� Ð@±Šâ-ŠƒˆB)>046qÞûý t>îD^ë-¢.BW‹’ËZ‡‚s¸6ÆÀƒ­…šÇñe´FúC™ÖUgЖdž›Ð’•5#†jbøPø²£öŸÉýøMºkylqDÀŽÏlR<tÏ"flÕ¸~Ýœ¼½be=>·Ó°ª1Ó(¦‹’ÉBí»êp“ ²Km¦FEÁ®˜ƒš_d”-/áäPÚB-~Ùu³T…58ÚIÃWËŒZ¹ºt¡#â«m PÎbìÔ ¸©æ·"»å}+ƒÆDFBÙPù³‘sÚÅ»Lƒ€u¥Âç\èeX!.eáð¯KÄ$|[›ËE£d=½@Ö=,iuÏbK��(ߘh¥v;•KyRN.õ§yKrFÒ•�Ùv¾ˆÿ‡GèËòt Ô�ªª¿:??z=þWQÂpº­~–<`zº¶èôá¤Bü=z %HdÒ»žrÉ�ùºC$zšÏÖbb·š<>‡M/Šâ k<�¤øõOò #¡G;.à7Hj¿RÏØEö0îmL§yoáù«îk’È–Ç®åâ¢è7£pžûãôˆÑ¦ÚžVùœ¦kü8xÿ-ĸ2A…î…øž¨QÒ‡GÖíÚ•,;à·£êÝÙ||ñ¢ì-H�¡›peè¦;Ðã‚ØJnº-QŸo¹8�OÙãæ?;  y™¼ú[x.& z�FâfHM‘øÚ½W(Ò;ÃØßô,ûX²(q¢;¬K;È*5‘)I$ Ó=ù«{̲ïÆb·w)_v°L0Ĭ ±tûõ—v9,=øŒ(k+¦@tøŸgèáÙ{½›†Ôw˜[`BøXë�Cvss ¹k«lp«L¿q—ö4�PÇ?¥ÂQ§Ì™k³(¥ÁZè5ÏÃ…»‚áæ°£©c¸ûlj~¥›B=–ôîÒóÙ,ô}±’GZ÷9p Ìœ¸5º¥Îez•:ô»?¿úÚ `R‹}l y>ÁeQŠÈyX]B#ñV¡²ý6ÒLö«Ú"¿½�ZfÈ·åm5à@Î$IuDqUHæï"®£§T£F|·\زà}äo¼ð}}¡mŠ=[ZJ’br¶4]± "“r€�*c·U¿‡ÂI¢� h9O€ë Uw ‚FÙÖà ð¶àÅ¡À,�c‚ÎÆ‡Ì^Ð}ñœºY`Žø*ä¨PÅÕÆŠÇ =Eé–ÿÌØ<˜ŽÑ<Ö áøÛÜ�LÄ‘¦äô¥ˆN»n4ù«Â‚¤°Ðf‡7h¤+ânó2؆‰¬NÛ^øªSÖL «º+^Ô$Àt°R‚”^CwmÞ4?.t‚Eëþ=ÐT žþoOë¾A~HYînvã*h! Ý�¿D‚¨œ¾ªD¿Æ¸[KYCϽÌm¿âAuÆ¥=°5øCø ¡ÄͽKd'®Ô‰¶PtΆ›þ‡÷!Ý,,Ú0y>VX[eå#˜ÎJfcú·l••ÂÏiY+µY"%Nµ²·®D¸ÞÕÔj—Û“41�s3>*ÞGÜcÎäÌrc'!npƒ‹­ŒN³z']c²­`pÇ3!¶•h6¯èCѬÉwöÁ™&’ Ay8]gæú�õoEËùtp‘dXqÍKS– –£‘ãƒv^3haj>×ÓSµŠ‹tí¾ãô?éÐn&¬I±¥¸3,­ísH:ïÝ™8V>d¦Oæ�ü•Ê¢ãDKÙ0 C5Qï BÀ÷?x—j\J=×XûÏ€Þe"qjðk¡hÆ&È­\:úcºÔmâ>-—¥¤¤ðOáØ7så¡%”(¡¦(Iné5ô‘À>ëÈ ÕP™Ñ¯qO)óè“0.ñc}NÚS˜ BP�¸¬l1¼OñŽú˜ý–,œŸžßI „O`…f «§�hˆM¿‰ª®áÿ‰)[�^„ÅD`Z39—4pûfé·¸¯‚æêxÔ"ê@b/0x_b® L‡Á�¦�P=§íáFjˆ�),ÄZ�Bò®¶…@Œ ü×jÿà\á#Öþ{É– ÍÙŸòfpcÈTòº5­Uâùgð/.,f°›¤Áž¯Ç¬³÷dÀ¾®ÿ¦Ǹ0ž¿bC×îz¤ìgIªs 3è’d"œöÄRT¡‰8×R½ŽOAseªoé )¹‹Ad;èö�IÍ­‡*ß å£ ô22µN»þ˜-•Ùª„…Â@¼Ì'Ú�v­¸LŽq©�R59.Â;Áÿ•PŒû‹]æäXFϧ§yöB›à"�5Â]pÝÀ Ò�¶Ú=:Ðv|bÐD�¿…X '÷îìÏ ¹nǤ_¶;šÐØÚ�ó¥ÕŽ>ëÉ8¸dbùÂÂ)KôöMøÿи±Ñ`ñ‡¨#,ùïWqê�¡Ü‰Û°ž{v?ÎÄMÝ›s“­ĨY­/ô þL|H¶wŸh’ …*Ÿh¥Ñ'ðê.ß1ÁN{ çdB0ùGi¾!KY1æ,xÙ .{T+Šƒøã¡ Ð檃Œýt›®†½ÓoöÕ¾13›IŸ€«v@�Xþ¾!ÇZå"™™¹qHô�AâNYøQz6Ÿ•|IΆ „®ºyI»Š.vÜV6a3<¶* õ‘°�M¹¿åÞQLhmÆVÃ…‘ÌÇv7co^6š;Á:ÒxÇ/ˆÀ�R6Úà7&"^TÕÚ3ÛÔ�I`)Pܵ#�´BÍ‚î_}�Pæ¨ÈŽ’ò6>ÖJâ—nð%ÏàÄçÊðG-t’3õœUŸî8&ç’ªMnÂâK—ÆS{’bLY*MÊ–j/öå¶,câ°óýoWžºÑB]¨P®GΨ(˜å€b´“üÔw’8’ÁYg²öLÖhE,H"oBçÏm 4Þ…éß}.¦€�`žQj(гC˜Sc®¨�Ë6¼’z»#Ïoèèኋ…yc½»Vk¾üV6xØ<Íe’pkx‹Dm‚ÀL}…ø¤ÆtðŸ|¿¥èÀÉDùo0­¹L£®Å*»6KÖeÙ 5 èÍ2b£W}Á9�!ú4›ë?Ä·œf4Á?X(¬Bÿ-Bb¶‡‹u° $û2c’gÁJ�#pK_W’yíPÎ4œ�6±Ë½žE}¢¾è_}t$Ä·}èùK%U��+"ÊhB,} ø­® {  dìð¿ÈØ¥Ëï ¼kƒ�t ¥g÷]—AÈ4¹•ß¿X£`ãNùöíc­ÆûÕ‘eWA²ÔÔm‹I‚�ˆeu€µÊ ºí'D¿¶wâFfcŽûŠuƒ‹ÑR±hNx»#išvzÿ«’a{%%à„ÔøTýêÆN–9NîÈ»PØA·Í{Riw™&O^i Càzþ÷ƒåѬý59 ©S¬G¥S€¯°×O¥í;UÕ²}³Þ?�‚²&EéØìJ‡Q«’ê¿N¥ÏF餋¡»|ÿ³WÍŒ€ UYÑI8 +�biéã)„4°òÈO¤~rpóü�¨oã´£ciøTµH0WVkYht~±½Ôç®™{ÍÛT�ÏѳL»ý>ÝÔÜbx‚qH|0É>Ý@Gc X|Iç ¢B.W7ëeÎÃ……“¡ì�ÂïŠÞ±E.RÂfLgR.9Ÿä@&&©«Ëd¨Q:¬Ú˜û8ñÕ˜~KfaKÞ}ììõ”Ä­šf£Æðv,±p)àµhÒâüB�á?ZA¥²<ä¿tsûò äxY¦n÷IÄïj“ƒ˜2°ß˜ÕdëËøj«x#E“G//Y!² &Év¨Ã='õßäã(¾g5`®‘5äÌ uí�–Fôà‚}î™þ±*‰°?üeqÈÅŽMB ލz}#1Wñ}"ƆN5¯5h.#ï~Eò$îm>U¸�Ñë7ÝêPTB!Ê4*ƒ �.Á¬®…º.Gñ,£ŒL(y²�”Öã�¹³²µ,«¾;iêæ¬¿ÁÔ}ñUõ¢Î –á1HAÂ?÷HJ+ÿW­SWn�G,F³ "©–ý@ñè!q+=gû·­ÉhÖ‘Ì[Õû¡u�…÷·ì7*Û5#3ßÝ̨�’ž,Ö}�\êÉ]!d_¡ºŸ‡õØgJ�]ˆ5 ЦØh\íÁ©¤ }qãÍn¬¡Š Ô@cèšl-„o.c‘#1åÏ�ý«£¿ÑX?ƒ9r›«‰D»Ø!;ZoWpnaï­ûÊ>&f‰^¥òŒu�§4­áðÝ»0þà’�aA©—ÜØO²QAWÒÒëØÉx÷@†4UŸ ˜Är—âN$2Jž) }JÏN&+»Ù!—R=ü¬¶‚hHÔ)#? K&�b‘A¦Bî�—÷P¹ûÇœ±CÜÏoîÜqK&ìi}Š!)½1¿ý͉ £&Øðò&tá†3z¨SìUУâŸX¿ß’[E�€Æš08‰ �ˆ>\蕯ªÔ-X–|!æ&±ºXf~pð¿{Já]�4 ÔÈw­‰K½f;B+Ìd£Ç,Ä¢%!øÐ}Åtmì<¦†×‚ûŠ;U.¼aºµ>¦½ñ’/À ¶×éDz•<òöI:Y,"|» å?£¾» *FññÓ°2õ™æEhä½åÇâ¹<Ü­Ó3k–>~&5ÙÚêó¥pìõ‹ÇUƒÆ3±5j¶KHÙÖ3úƲ¡Ét0‡�‡>_�alÓ}æÍKj¯¿8Èò»¨Žö7ë²O¤+LÉ£Ô˜^&–4þõ!6B‚¯8[[XQàÒŽC•áÍ!©©yªÇÕšÜUwÿÌdµ~£GVÑÈ<³irƒ5±‘}�L¦�}ಯÏr£7iA¾½ò<©&®…Îñæà�·KÒ3ÿˆ=\ÇÌ(µ/Üt»Iþˆ?÷ŸÃâÞF³k¨ÒQD£Ã2l®u½Þ�Ð@i”™¾«ˆàjÈEá hMYÍ'´€Vò8ai«î¦ú½¿~²¶©© ¥ÔwÓ! ³´TTBÖ÷´¥e¼ÚZzÿv¨¦,¸òmX¿Õb"\IðSÖ˜¾åh¯·ZåJmˆæm_R‰Åò—NÏT*p Z·qx(Y�PPÍÇÕ“$ƒÌíˆyh|%�-½O±TÔ“f×ÍÁ¡ž]â‡L ƒ9“ûúQ¼¥ ÎÌs†G) ½U®Êr”pL¥ÂªeøÉ22Ï»ï„Èâ{E{&¿Š Ë]Â6ZÃM5¨i̦3é†,í³à«ì¸äÂQº!Äí²§?§÷�¶%­ô$ Ý“kFO¨Ž—¹"Œ V»Ã@/ Xk½‰ÍÞÈØÉ f3ÈoÅ2Ù’ 2qëÝ–KËÓ§½VwÕáCË«Äd&^Ú·£fKþù#|Œ¥°(ãWœèGø§ÔA3—úbcX+í_ˆ/;t©±áU $ïê¯L‡·un´ÞmÑÜB‚J³@°óäÿí>&ÌJ9{ƒÛÛo„q™­©Á +#z}õõ¹‹ÐVèõâ+)øô‹Ç̹–©o�–š1ßqúåaÚè —ýúÛ3¿iýÑÙ{&ÑÓü›·#UF<éÔâ¯Ä,¹;þCë“Ù4÷U®ò3ï«-^¡óõ¦ÿl•¶½¶c,u¯Ø…eÄú¿ßz,nr䑯@&Ú¤3¦ücb¦¦ Ð:[º‹e:2âÿM§)³¬ÆûÌ©±¤�D��‘xÎ`"Œá°»Ã’դȎ>P°ƒI¦tOŠâ{GmØÃ›XÞä°Lˆ�S�µ7QSˆÙùqbV¹h­Ûqº|s…ÄfJþÅ9ݬ›1WÈÑÜV‡ïôŸ|©Í~Þa¶4|’½ú�:e³µ²TŠéMÇÊŸÁoµâ`†8«ûõÙQo��®ƒ}²¸ö�£ ™¤"n ³r¯�¼ÓÏߦ½ÄËä °†H¼‹(&°–Jq˜…-áեލY˜Zcß';—Íá„…èQ·ü6P…ôÚÞ’ŒZÅ_M£`eF­ žØê§�ö�PFp’OmÊxñb€ ¼v¶i]f‡"nuFŒdÌØæ×ÒåÏ¥°cÌ-Ϋéñ›p¹+X*Eµ:Øå4ÂT: cé衼€÷AÃ:}ò4�&YÍjÙ"Ž8„sêúf•Jé_ áî±8‹…žA ]‰Æ‹I8Ö‚$™»—Œ3�ròCf«Kh¿�ú±µ!ký©,� …ß,°±Á\ç;{[ó Þü»€éXZiǽ@ÄÖ%ŠòY1:?Þ J™V”³ƒhÂü~Šù� s«Ë—šaø?`U ÖMö­ˆLfw7†û-È]¬žcT²×Ï£ s�2|¨Q�UÄö¸PÄs½œ•4¦¥Aˆéígvú¤p¤áŠøU£¾$íÄ�ËoêÏÈìJr¿êo‚ÖÎìL"œâ ª:m HÆ NªýU`1FüÚ~­êiÓZR6´¨»‹ÒÔ™Ê&Å5| tµÒ ÎËéIƒfaà+�Ct(�{´Q%˜+õCŠù\ìñþy²ãð3CQ eýx•>Çξ�€ {ª¶•±�ºõêt‰–¿\m Ô•*‘r©¥W„ w�O&Ïênš!l£3`tPâ\^e¦D=ðö®Ý_kWí8î¼W¶&Æ]òªbp ’&’êF®ç?² lˆ !0ÌžÑïy®"^�“|�&ÓvõãÒà€�ÓåÒ¡>S·Å1²à™y ôçÉ×_¥_+ø{]ü—j [câU«ì\©ªàAJlïUÌõǨ€çòq�?pÈ$Í?ÈŽíW)´�P Ù|ÃÉÒ�ä”óóéú ÅœŽaÑÅÁ4G?ÏyaééœÆsDs«ƒÑbÀqíj>ªšp-Aô½ÜƒŠŒ&Á »a-iAža+¾à ñÜ„ßãR"R`&™#&ËÀjÃïù�§°ÍÒ£¼ÎÜîMÑA±dGìÝ ýE¯¥iE#y‡ˆ<òÂÉÀ´^¼Àì~ÅØ:�ßw¿ÛE69ÞÌ©KÞ Õƒ=FrËe¯÷g’\©ËʼnEˆY¾£»× <ïB±.¬’™È!ÜÐ@pp§“Ip¿R=äasyB�”{2æt°Øáz’�¢¬KïŸñõ­x¹‹~†¬Þ‚0ò|…X^õì`$7‹Ä*êKœœS™È³FâIÑÅæ5=æ´Jì£ÐÆHÓf‡(O{U!&aöô¦),éMÂöõƒTpûLÊ+»:€{e›“ÉCU } Ò��Hùd)xÑG—œP»}ªå”ãÿÀÄ è•Íä\áÒõM’þ’ ¢~x{9Y�¦«æ+c­Këz?N»ŠŒì¤GU»�c Gä²ö¿+%qä8ðVv ¹#q‰Pcƒ#­t2ãˆIIÜa>Úûù› w—th$.�b©udøÆtâû„Ïþ^Ö”ö›½¸õI”ì[î<§©` ’�XWB\x'átá¹Í/_þ´%Šs–ݽÅÛŽ7¬uÇ—ã’”u å$Ú™×ôlõ×ÙœöË«w?OyÝ8tƒ­¶ð]’v¤#f :�JZÅÓ…çM/I,¶Áa>-c‚¾wÆPvƒ¼Jœµ³rîïO¼Ùõ SȹçrÖW�Ú¶Ìd¼Vfxâ¬,/ܨhOZÖé<†ŽÔÕ&Â2¿Cò'½ ±ÛìÐw6e+ FVlŽD¿Š™Ñ≊ÜÇ©,Û�¯¸ÓÞ¨ñ8q#¨Ç:¼Ç ½�(¢8ýtƒhQ4Ç-–)N>ŒÃ¢5Ô|;ÝÓÞ4´ ú’J…´dP(`>8®–T®vfÑ%.Fã_ü‚b7¤bGº±=#ò—¢Íö ; úŠ÷ÈN�Ü8iàMTNˆî•å:7x!Äi›æ¹C1s�únn¯åBúf¤�uî/Rè$õ·ÔuLw%|´ÜGÙ�Û‚tä4_íãgî¼ñjÑØÈM ­Nÿ«²pUE¢Y]�Xˆ¿uü‘Ñ4èä·¾å¥�­Ö—‹ “×äÍ”.üÙ™½�¤ 1¡Ó|QÐXeåp2mþŽÁw<対 yv]-“šªT‘¶™ÎÒ@_•îßCK§Ý#G~Š€ÔùbƒÇ´Tn³°Ãà ü2e֝й¸Õ9þ^î=otÍ )CÁ6o¿�÷!Æ>µ XÖI p1Å9•0Y K¾‘Ä!†ÑÎHïXc«Á¯#¯—¤ˆ†€(ܾ¸×k¥ÿ_üåyn™øfÃÍH¯EWžuÔ±Â`k�œµÙÛ›ÅǤ7ålª6?h;Ðe(]YÅ`Zó�ß ×AEÒyFúÿ§n:ž¯m…¿=9ly�Ê0*B9óÇWéÙb€³Õô¡Ÿ{•e´q 8ˆÙå¸$¯î|š"æeqë…í?¤åÜJ=7×pªaP§+—BówOO–ñk~'%ç{ !u59Ú”EöfÀË8œm­Y*Ws qç`¬o¶•{ùîÎaZâ@¥+Ó®K^Ã8”A%9Î@òOo|ãV!Güi„±ÒÓL?ØÌ³Ù£¼Nªlõ»0“”Ì‹“Ñ) šfÃ/:k½Ä8•Ì ~÷¦æ©Sö걟p —z÷ØõnJ lÈ6¯vÖ"/?|5þz´”ëŸøuua3t!Œ¼ÂEþÈhzH?ו ÕœÔ Q6µÌ˜›¼IáÊÄ[ΙtXâ)Î"Ä�&CÒÄÖäEÖÿÒ°ÞrFY¾!Áù¡à‰wTég).£�—¯e:F�ÔÛ¶¦VºSjœ q5Ø:4ÎU÷Vô®ÍðMq-Ö|±yp3nÄ'¥1»¡e<»EŒ¸…‰ú]�*HÚHH ÞÛÙ:‰lªÉçMÚÍcS÷½œ†ƒÑˆ‚~âfÃrA§ŽsÇMž3‹ÊZ+Åcïtˆd^VÈUUþš4Þ½ !3J’öä‹ß]5¬§õ'ÂøÎŠŠ¬ËÂŲMƒN� ÒÜpD|. 6vaý†cÊÜ;þ^ª™ÖÚ-‚æ“�eZ³&ØÄú ]ç1}¶Ô”Uê00RÍXÈ¡yæPysP¹àß�@�xIÔè á�îRœ�àËÍUàüfj›YŠ˜ÜQò°@‰¿‰ªƒ]\˜ñ¾‚¯Qk�¼£ZGÄÄC3súRè˜FÆ2ý„ Ñö*ݪ­>ðýžÇÛͯiàhs¬¶ýL/‘„Oìã‡õ²3Ø·@uð&À3E¬<–üÄ®èb1ÊÛ#AÐA[‰É0¼Ç2%Ï^_” ÆDœî5O�Ö”’jmx¤É娴jQM¼ûužÞ†ÄÚò7ù÷ݓԟÑÏŠˆ‚á¦übÜL^Ìje ²?u ’ævßTï€ã*ö``Û¢ …l�4mGá²)ܪ»ÿͼ^ÙåЖü>7d_ã™Dâûцv°¯„­MK·zÀÙT¢1ÏÞƒ�¾¼…`<ï›j(ŸÆÂ¸j@¾næå±5I´³úHiIɦŃâ5މ¿aÀ´A¶Ï õmïÕž/Ð|(Œi–¿ÒXg+£ øâ�”ÏösϲQw€>|H‚whä¶²ZQî÷�¯oª^�W4y ½ Å.ÚŽƒ³chjáÎ@&Ô¡Õi×\+‰ï5ЬG®ÑlçB0ê]žÛ²Ës†ÄîîYîÜKì%¨m®‡QhàŒU ( —á{­€ÜϧwäʨwÈÅŽÛ=¶³ah%Bÿ3óÖ6÷ÚN„ÔÙEè£'Ü ¨�ÅÊ޳ʿ´×¾EÄH9�L4Ö{Zer Cb÷€³ �¯üQ5¼'‚¬nKÏ+Cêmö°R¼qÚ#š–Y]ôo©ìÝS]$ Ím¹S5"…Ny¬°À‰B¾bÙ 1—˜µ0¤©Üµ dÀ:­ašœvÒ%ÒŠLÎê©û×#ìw§$g¨‚I t"$²&4æ«wÓ‚gn{¸ˆH|³wŽø/NH�þ…ŒF잃›Ê|Mbâq†Òj®ü­YâÚÍv#‹;ˆO°1€ÁÇû«¿L‘ŠÁ�Ö¬ü%7yñŠ£r'O„ 3†l5­|M“M¼gÃæW_˜ºmú vÆ´[GºôË•Êèß¾×bgùç�Oþ ¸“ö©*ﲓ|Ýõ¥e�Õ ù…$$jõ޹öNÛè0I{”i]E7¥–ªuè–ž„e0æf ¼v‡§5nnÁÜÉ]7ó¼2¶eUûàœ¢»òœºUc~ÄÖÒ|:è¯h[jÐt�Œ%ý¤~ʪŽ0«âÿôÒÒ¯V¯‰Çé –ØEø ;D K¬B‚tXQ(§U2¼„b^4B]Ž4a÷xíc‚Ü MrÞÓë,ÂjÆYtâÝŒ�=_g.õìúùP?m·²CO$Xá 1ŒÛ”MÍÄNø€‹¦¡T­™ Ö?·½¼Ü4w³áŽ{„ô$™…™ ÿ6ÿHd˜.;­•<‡ª<�Êì$˜v§çδIÓ=Zù’u2–7 @£•ë\ŠN‘ؘ7v •êè篠 Ô¼ÿâ¾ÆZ@ÇòÓÑIA(™b?à)�V`3èoâØé‰ž/ZGĺ>—ãWý¾ëD.Sút#°™Œ×ïÎàõ-OTx9˜,ÿŽÏlrÔß/ª‰µ™ 9E^,¼½ÅTã�»©@:ì>éö•·Q.¿hBz¤�ŒkO…zŠ‚ý%F3ÏxdÒŪ¥ä÷ÛÜÁ/=˳hÜ69N,{ݼC5,H¡ÝDBaó4¼�’ºCÏ×­ãu³Ê�ž¤€ŠX|öªY«Å%I+Cœ¤²;”Žo£3Þh K-;³—Yé£àÉÉžámÚOžFœÆ³4>©ÙòCµ¦$-×µ}c+ÿäbë?ý0/œ»çm'Hå7øÛ÷Ö{çƒ~ß´rÝxŠzØÉªåîù—.szÒØ‹Áqz æúû‘á*ú®Ø«Þч…¶lH£½ ‘æõ˜—ù1BP™ ¹:8Œ¹±)]"þä¦R’8cÓ/@‡.ÿØÇ%°µ‘RîÛÄ.d ƒf–Ô.Ûþ*ãm¿ñ@ O®Cz³Ú)ñdF�ÂÞמÈCŠJÈ´Ù©™qˆäÁGìA±X¥6pNˆÎ€Æªu¨þ=U€YŽ_ Õ ó¥b­5"¾“c‘Úpž×ɼ ˜— ³Šiz¬[yãoVF]ë�øÁ†,P7]¬¯oÏ5í¦Î¨˜›&9hé•Þ`Xñ½;çs›€çÊ–ÏuN Ê‘á,—¢Q¸H -”á²ç3—˜ŠÝ;ÉíP–¼OÍOž0à>™4`š0½�¸Â��à7›ƒÊ <âVár5{%KÌ ñnN"0‰P´Þ·i'�)þ)r¼!¸ë Ì8è]€õk·&�Êd² °‰ïðub'7ÔaPKë`�UË®1œÆ¡ƒ¯ø5EáÒ�•ãÒrfî½}-ðO�_Yó-úô;”‚ ½õQ–ýUi:^ªgÀ«3l7åå7rØb›È•Tá=í¾iæ!\;Á¬ß°C©ÁE§k]æ£÷�ƨ.�n`ºÐ�2-‚6Yù¥v·“¸—9ã”oF™Y6þÿ¯sQ,ÐÄýb^Ñ2´[)â¿Ht†Øò‡´�²IäJ!½q9�Ìe°µùàÃÉ0M”Õ`îGÑ®U¶òç&_·|â²t½„� ðéC<ð/|òP|”Ið_å Åš„!ó©Ððœ*´(7ýHà§›ÚÁ¥6„½”¸¬ ¼ ’†zNªIíŒØ\åŒMl $töÁ‚[¸!œÓ“ç€2…çŒßØ&äe í7òÕæí³¦Ÿå�¦� ì%Ü\7òî‘V‰†7b¤¯<çÛ†ÌÈ‘¡鋼ý=VašòE�¥ÃŽD8•‘—¦#²æÝ ëí*Ö9¿‚ÉQ0” ¹l4Þñ­®‡ü´0Åp‰0üÀã³®)D.ãöK+§È´Aò\>¾ŸB,‘¢‚`i®ýFC7�ûéOÅ#ÇqW_àÇ�:´œzÖÝTŸ½AXëê ]ŠŒš¯õØ@¡#2±öÉ®\�µ•¬êÛ{мÌrjÉÁòè�¦Ä¢U1òž®™PD©As.�iƒœ^R”!žz„Ö¬räÀÍ!²,ÌÎ-dQ«GO=Ñì;wˆ åˆîË�YNž kN V>Þ'OD¯#ª.$ÁŒ«&—ÊGj á·h=pLzäHËìOAÿ(z? ÌÓ0.Üâ�U(‘;Í�úorZ»‰u…*›Ï a:»W)  Õ1@9‘æ¤�ç�|Ä�]¶a²Ön÷\%`ÍÐÎ=%þ'±«Ï­ëú€2Ûÿ�ÊØuà\N¦pDɰü�ë_åÜ7æýË9Ž9Ûy¶ä™ÈÈåtóéWïáñºú±¬ƒ©û?€ôìÔÒj ­àœk4·/éó)À‰Cê:…c‘ÂI3è±ñÅì@×Kz>X¤ ÏÍ};_ft#AÆƒÖ ƒ- 'î–ž246«»¡‹ „Ü×-”§½‰Üû*ÙFú|Ô±«°¨A$Ê8cñg'`ñ�Êf+–Rèšð EU÷…qMVÖµï. æ/:‡7ÑÙàE» åB»P¿¾o޲7ÂDW#B¦¤ÿxû½€^G>ÅÙÐÕC—©ÜZ1�³‘çég|z‰^C¹l;Žèdç‚W¸?ð–bv‡Ü°†è† ·ÖÁ¯%ùSR-ÝÐÍ©dÌy»¢l@è�;ò8>Dqw¬3X@O0+?ÏHflÆê¥Ì�.,’8X´ üá/dž&éLåü›>Ú—ˆÛËÆ§»jh ÝÂ:*ê¾ýÎOÑ´% ÷Oú÷G6‘ž€vù³ø³–-h&ߤq�ËûØ©&ôÆqõ#Ž�˜xåÔgP›EżÈÿE˜KAŒÕLï²&ÔÃ#{ükv[QÜ-4Cfi§¦!¦3¾ë?Mü1 ›’7a÷΋†°yàEÀ¡«’�Œ #A§+´ê€°�ATfî�ân1“M0ÿêû)ûVp,c›¡?Ó¬Ë D]uMk•Ô¤LÑe¸›ç¡ÜÀÕ[=æé¡„g;Ä‚ÖÌÉaOH˜v"�›™ú Y,F&„rÅÒK.‚2hgS;Sàˆãÿ]×3Ö5MJâð’ùtó8o·…Ñ„lÂ�Á~û`s L:*�˜MÌ€ãll2Ú=ÓÆ>Õ§:Â-O¬c„FgRÓ¹¢öœÎSs™ ¯þ›‰�{ÍñR“ÎgžraèmÞŒ¼‡²Ñ«ÙÇÎ)g`0L.üí ¤;ôÄŒ"ý†ºVr¬“§›ê-°¾Ÿ|­7å àœdG¿ÔÊùªP’ç$¸›Ù<�i×Ö/,2š×ßIÆ»vÂÜÄ:ÌHUn�P‡•¼‡PHøMŠåøt5‘¾·È”­šr…ÆÛËÂÚÌpz¢Ï‡Ñ}¯ŒgS?:P)/hó gÚ"×#¸³€už n¢ÆŽ­a]@Ï—JÔF€ðw²1#‘/l©Ktü§ ^÷Sû„R~†Ó�ËûCgv *ËV3šPogމm3XÅcÖÅ»™2` ‡ý…ÑÚ�HãÐ8#¡³AZ*ΰE§=AÓ¶�'»ÏËúÈ�l+Ì�ûò<™CU�[dn›»xž'«ñiºáf*ÜSŽdÝ9Ñ“%½Æ,žJJ” é/6V§‹¬11ÔS.…ìHúûË›PwÚ9ÑùSÚòìëéº�F›èÊè­Z<ØÖº>J“ÿ@±ŸP% "-¤¡Yy>&ñã<È×�ªôZ@@oK~>(;Ÿ§¹ÔepyÂ�ÇK ˜ÔxÀ1ˆÑ¨[.-[`ôHühZVNVÍÊÖ(ôÁÔÀîâöaU¾{R’]F!a�–¡®< ®{;„M‘Y÷½eÔ.A2Úò=… ÅÆ�šÐ�QB§Õ)uÒá.Œç9I�z+´¹e†Ü|æü†ÝHŸôa=uoß´y8pó€ÂñSÜaw§5ßp«^T3ÉUÃMÊæì¿©ˆðj^öì°OB‡àïW?—9�Æç•Ûx½Ë2òŠÄ1C §ïs©àœØÍ ¦áv BäG¢ËÔ¯bCOCª„Ja\¨íØ-G‰ñQ}îðùøkî`e4íO‘Q÷^—§âeº ˜¯nK™¼1ýpBŽéö Æ/.kg�ÀO(‡-H¶vúcP1úu?½ °4½ºï·4YÃn²rg¡™X—FÎN6_Xœo@ƒ7Ò,Ûˆ®Øšì�a”»ÕÉ ¡‹[ 77àwi1$dýäŠ-Ëx…iI—ættþ#úO¨IçgKf3êxÄ<ÜAûÎK46ÙÂ@09í˜Ë¢ÕR=µ!�ÉX4’ûŒlJÓ˜ê2ê«ÐÍÀ:]fó7¥¤iÛÄ*5Õ{T¤R¬E7‹9õNåòÿM²l4™¹“¯×±8ÊæªVü‡˜NŠF9 >Iàî V—°ÝÏ;{5…–`¾pÍ'šURŸèÊsx±UkfOq`“ÙŒ É}á÷Q¿‰½Ùök'ÿzƵ-İÿø@”‡Í­}‹g¿uªD§"c-R!oߨýIÌ [£– (.sJ¦©ýýŽýùýß % ¡OLD`S®ïç0m‹»( TAE%†mN­Zå™…cà×c‘%/€ƒƒ®+ª æp°]/„%ö‡‰s• ª†bØN0ýh7þÕˆó@8}ÆKCÇç8úƲõˆ©ÐXkn×H=’R8_+½uäÌ&εߔ�áVv½Õr¦½Ép¿‚jå±µTQ¦˜õd鉌G]AÓk1ìÑÜ ŒÄ<�ÚjÔñÁgÚº«,g¤QÄa\î}ó-&+(c Ë’+–Ý´JÝÓq€tZˆfð{ëã¬Tt$ü‰C¶ ©ŽÙÞÞ»,U3ŽÅ ‘sªËÙÝzšG©PA9ûbx+‹’ÖQ:‚o¼Ò n’¯SlÂ@‡0·€ ƒötn’Ã6¿§Ø ÏŒR;å¤ÕŽÂÅ©”£ŸºßjrÐ�Q °Q X{“2Úü¤±`U«q�1�ŒÊ›œ•…|C2¹1ˆX8'¾,Ud ïf÷¤–}©BÁ{Þ HÎæ¨§1Ûµ#õq÷yfËÊTÏQV>.l„(ßpÕ³?ÞpSŒíÜ¡,À’‘ǵ進ØSy>tü÷ê¾0Q¼èÊd�Ù¶È\#ë22ô§˜N[lzÊööû5óÆ?õ-€uE1ô"˜ß²_–c«–×Åí¿À«=š37�y|[�¼šßJ`¾�þ‡¥7~ŸùaR ƒ¿²¾Ëìu,ï̲ 2�2Ö?< ~‡Wœ°K=¶Sàì×½=–Ûøüå_l-–QlÛ¼VÌvoºÎY÷È™Vs˜":Iì¿ ›óºÕ Ãâ�úŸ»…_\¬ö‘j\œãÈ‚¬‹OšŽ)´PI.³¨aÐæ¹2fçžÓ;~ûTµ¶u.étÈ8ŸÃ<ÿ>ÈÞgáåG[Îæñá:¦ ÁN¶Ú|*ëÖË9M ÃEÄŸP@Ô|ð}®¼YòA´ï–½ŸÝ½3 Á´Ô~¢£ª§¯§”UåõtdÐ:O¨»Þ=÷+"ìà‘FÔ^UžrÎõ§‰©$ø/•„Õ쇨\üç�òW÷[¶®nvŠZk´”Ê—¬+ÜFí3Õ ÉBïÉ#¹?¬uÔo•¨ÿƒÜtéB`tµäpÞ ù:–˜B&zÍèýʰӕÉÙ$„}%³IØ‹î|ÑýÞ™Ôue÷#^Xm¿-«“üà8@Ž®+°[“0æu{­o2Ûa›dÕªªmöeat9sŒ¾½‰É{Lïàb³Ž:‡–¹TíTÕ""úkŸ«ÞŽƒŸM ć§GóNî¸ù ÿ°#ȱ»c.ue¶¯ Áá‹H{a¡i‘~²ï¿+ Z—QÇ�ðzwï¥m6™7Œk߸Çóè¹I½?kWíú§bÝY´9ôWL€ƒ|ýûõÍõÇo»5@bðÔ…É?`5“Eÿ-ÐOÎ<´qÎ ÊÝË®‘ºá—½íÚ7$Q éDª W-Òkß=vìï3Ývj Y/ÞaË%V1™üŠlŠñ±dc£�ø$l8d&Ý0>û}F��Ø%f ÅOá*ååvë‡ßç¿°`R£6’˜/üÒzè)ÍÓ rÛ§³’|Qj¸:Û°$ÒM3ƒgîýg­vg»± _ sHx;Æ’>Ÿ$¥è¶ÔÅ^Mxm,²5ìæÝ;ȱçÒ¼Ø�VátYóØ-ZLé`ó9|ò½9¸äxñºHT’JͨŠS‡›æÝêö¥Áé:Ï_'�”…}>¤X2³:×£Öðœ˜C¿ ÌŸoDcUà"cE箜|fš¹DÎ� é"~¡9(Vµpg­Î};Lè|÷Ç¿‘|‚´ 9¹Š=HU �u¤E¼å&B¿a¥g2«i7l,¥_7&<[]d6•Íû#ª*ÿC":èz ú�Bµ¥l¸·Lõh“‹ ^£b´œGJ/\gyÑ­—œ½4v ¥”Õ¦˜—Å(L*^­ù•Äêì„/W7щ%f$Ä´„®±9„I}}ªÒ@ Å´lì;c\ð B”–ç^AG3\Êl‘y„t€±n2œsÆjS÷FKºî°8)›Ñ«W‰ó?¯_J�”ßðìîˆ`»‹ W¸SÎ$ªA½j�-<­©°­‰Ž5�}:h[+ˆ%½+¸ÄK¤8ßb:h¹R�ÏôÄ ¦ÑزUfí=Ãô®Ô. Ù¼Ä�Çk-¼h®Ã�dºWªˆEQAs¯qT}×h+›äКH™�»%Æ)Wz-ƒ\¿1Š25kw®Ú\Ý’ÁAuaó—1ÈÇ=}ˬ½,Þ>^~K] À<é0qð|“1·ie]ª¼™˜ß[%g4ÏœÿÆsAËl0ÇåízºÄˆÞ‰í¸ÙÛå‡õÔ¢ ±¹NÙ°FÇp–p*ö3z`5-@jea݉fà<èè;›ÞkÛÀ3jæ‰áõg{ëf]¬ñ’]¾_‹Ó�¹æ¼¨.4ÕìT†¯ íÆSc™»¦go^öM>ªm¿þô˜L‰ÜÑ—.>UÑ5wSéyh4d#³ 6ˆÓè\ùÒ ¢õúšìç-§ �¾Ÿ°Œ('=àÙt¿�ÀÉ߃ ú¢fÛôŒÅ½8|ù��®D­´ÿ¾¬;�­ç4o ¡ú~ }ÙÆ[PÏ|ïó•³>b>Ë%"%¦gÒ(Üµì ƒg�æ7 —/hw?�ºo8XãÌ9z-yMª>6yÖÖpÍ�•§%(¥z ”‰xÀ…5S ‰‰‹¼xX�-ÿ>™&üßÏrb‹¸W$édüi=á÷tëÙ¬K¯›"s>ž%*�ë x�X¾>´¾³@lÂðJ¶§Ì%ÁFR±_>1—鈴’¶¬ÜšS„»a!ŠÞ%w¦ Ü嶹ϯLÑ;ØŒÞf$ó×;`éYŸäú-ÿÁŸùOF†¢æ'qÔ„*R ck—¡uÿ†ö¸ìØ®™W6X‰—¤co~–xG™j®õ ‘ËŠ~§j€cl4ú¦��üÒS6÷ñy�—0:vTNüBþÉͺ;^ê-Œ*t^S'ô ,9”‹þm…NIZCÛc�bÑ.ì¶XC(žd(ÒPÑÆ�iâÂ�þÊ�w¡t¾×_n¡þN,']£0:<|�, ­f, í¢†Ë‹ž…¦È #nL¸Šü<’�µ²0è1ÓòaÍí*û®DœÎUˆ + [t2‚¡KK%€•h'¿ç “ .šÈ{Ň|õƒò«ÏìØl}{ÖUŽI^}Hˆ‹åÚYŸ!’÷E9î\îTßÊ›�Õ—­¼Ïô»[ó"_i¢‚bÕ*è±x_rþ ¸ÓÞ¹Ú6óFÖ†?±euJ`ÙM°ÜN�ŠO+ ƒ!Ýkã{ÒxœHŸGmåÛ°XÁn ¼Æ¾yW$ɶôSçæc=K ÔªjX�øžŽÑ;X2á¦Fw�ž†ëßÃDÛ4ê™i�9𻩕Sk®JE:J”‰Q†¥p šÉ¶Òôúnðò¿w\}×Ýd‘Ë6‰43?…Þ.5̹ï$Þ½gGËýgZºÄÎRVÐcÁCq| ˜‚±BkcVè7hŒF—þ ”§¹I…̧٪û/)ãßÕúuQ�´nî”;ë±ýn2�3¦£¼ÎŸò_ñÿmÎR£W·3|«F3GReõ®_|+3]¹¿˜ ƒ*i·ø§Ð-º6qpXoÏAݳ^t˜�W’¹�ŬV4Š+KТ©lbˆºNhaMæ6 \ƒ¤@Hÿòƒ•Þµ*ƒŸ“Ÿ/ ÿ�¹-¸ÍDn²Ý�gÊ{¦£zšùªT.cè荒¢´4±È+!(œt¶@­±¸ƒ’¾Á ³µâiÂJô^�çóíeüèð>ZQ‰@¢8»wèd3]·g\j—QoISŸ¶·Ò„ÉÞ¡‹!«‚an$fÓpf ‡Ih*¨´è)Ê´F�ÛÛB!_®l½ñsK`ê�+ÕË�jY–Ú Šþ6¦QÒÒzƒõCëÁ"ÐwnÞ÷ï%[GK¿hWÌz˜r‘0…¢øg¿{"¹Ý•lJÊXeZá�“Ô•X;IlHŽ6^®Îkž Œ½uçÑÕÃÐ"‚£'ª*K$dg@Âíö>ÌíÕ�r¼™ÞƒºåmG}ZülÜ«!†N»Ú ¦ßŽ7ö-hº–Mi²q€i½L„¦<`uajÌaO™òBn‘«dûÛš !°è‹åÈq Ò«ÆÌtPᢡ‡ôÙ,ócÛ‘ú@•FP�Ô´� < É<&bМaª‰å¯Òý©¥ØXi’Kà$ |×òÚq$š÷Gpf¹CÓã›Â…ÐËEÂÈŒH“=%{‚¨¥cn{üF)ÒLåç+‹ki{ÌÈ,2b}(è´t�fÂpUuF¤�ÑÉ #WrX"^øÇa”;·Ó?CÈŽ�7ªÏMK»Cå‘ NÀË &È�#îàHCer ö‚ú 4šг¸—ùwLÛ¸Í�Œç˜M <ºÔÝV襮ˆ¼}È7M º Ø h¡ùi·¶à{÷w|�W¬˜<œã î)€«kÄ«o!Ú<O€e{��É8µ©pl*Í ü™â9ùhμùòÆáôv¤Ð[0'2-‰PÔV!ÇÐ2ØËò›•r²ÿä.V¤”×H$@öâ˧J!`$Ïy‘%�«[ �jxŒ%ïk]5(ð7wºE&JD˜œ©Š&ƒ‰ÃÛ¼ÖRñZ[¶Uî5¿ùxo/¤�4B4²”†+ߢjç²ÄÕÊ0Þ(TB°BØ{X‰cê·Ubkp¿Ø�|¹~[3‡'S›ÕÄs>ß!Aé°麅fü¡5á ¿R¹¸1º¼Ôˆ�‰ŠofUI:Š� ¦A[Þí› €Ø%ÏüUnÊGeºmšK6e+KÅÔ„õ„¡Õ#vÑ{Q÷~„R믗bõxº8ëR]„ðw€$[b+“4 €˜’>y+±zÔ•Ù‡úb}d“SB¾†¦ŠD>QBà ‚¸ùéœ;(y¨²ƒàÍB2ppo£_ÅjL ,uhnÎ�áe=A~1L‘µM‰Š€Yµˆ%0ZšÍ‰8I“Æf¨\šbü¦†/ù¯ΠI�ÚàU#î6´O~ÌÄÏò}mìãÛ²¡D­�ùIã±?'V!¤Ö4,¹³§† fi©'‚`ìjâÈণùöCïwÄ·.%bDžå~=ü�ZžWmF±»•95¿HÓ7òËíi“9è‹ø/,Üçr¸(P¯x¯¤{á‘-ÿÙ;%ÆYæJ÷Ü©�²G�¸�ÒꛑEþc0žÖD(øúæÚãæxl$Š‘“rÙï)–ÈÊÉ Ö¤™¹âÑú�$\»Æâ:*~{GóÁýÃ01± c™¼zK§ö«4 ²s|î�sŸv—}u»êm  Æ(K3y9¢Z”)A²-Wÿ˜ð85œîÈ &~v¼/´ÁF•aœ¿!;>v2vNîÕÁëÙLpŠ5¨²©À8×(©/CµQC¸‘Î:W}8¬ÐRIc�Œ{Ûñì¸Ü÷Â5G§—yœ¹©òdöÛ¯;ZñOusÿ¸g3½ü,ÑÒñ‚Øw¼sòªU=¢Ã«¯iõ±—�uðÆ ûàX^[ˆgŽ‘ŽûPlöB6¨±g¤Å>“LØç„äó4¶¸¢äç8±Ä¿šÆ}œªæªže G¡‹Õð^=„†‰;ûAèUä�2Ýe(áuT'Äžl Á÷®ä ]¤˜-ƃ}gÌuº(¸åêÈüÁK!cDPmšTýhºŸ*�' PI„Iác#$yTPpª"Ùž€u7F›L Ü-BÕ”c4=uHc5ðî6œ îö†‘ÚMÇ÷I}’Ïbδ Ñø…‚m<Ø‚ã‘Y«˜¾÷”ZÕ2°1QÔ•òj%'öŒ‚Ý—Yð°t×1"ÝEEe‘Ä.`ebˆXjOÅÞy+´¯öI >Þ4ã”î3ƒºQ¡T_bg–I�•Ãýyga<Æ…¥“®ðX’”c­M†­D. ±¨\ø°JÁ®rJ¶¤€]åÀ…c²ß «­bOèÓ7æö<­ù&Ìsq¶ÊçM�úãM×!X 6ÉõiÂôo\.výZ)ž?µX¼©rôCöÆ–Æ‚X-(R.ìÂïº_2Lñâ+˜¢Pž…Ò@`ðÞKQ{à© -òVR§ÑSs÷©E0™1Ôê;Râj±Kôl®Éá€ÏJm“v >½Öç‘^�G¤¹<�WLm©®Þ^ D‚±£²'ø†7!a,ñâʯý­3~Ëi �êù‹´ûP¢˜#’{.Ѹ¤ì0ýCªˆÎ˜&H~‰*Ü +ž[Œ ¨„X¢@;ö,›ŸZ«lT–M@©*ÍÒ€+©z2šQ5Uˆ¥K·9§ ÊÿUo±¶&ífå÷¼íìÃúÄe2%¸ …بIŒÂÕãÆàß—äE_Œ 9ÏÓᶦö£Ü¶-/“À–¸7>ÊN �ªé}2ÍËÜÔs¸§ÁðؾÒÞCÉgOûßÎî"³âB»2&¸ Ô´ª¥\:‚.yëCÇŒ2IG¤  hʦÞz�‘7 ûÃåp£±ºî}80†é€‡ÙÛÜØà¬’v4º Ådú­¦>FæÐ;�qó^{ƒœµÏ–Ö¨ã}Ù3&UÄì¶j¬/g¾á¬ÏÅÇ‹ù² е©Ó"ß=rvN™… ,ÍW!{êÏñ-âgôÈ»áÀ´`ÜÊFÝYr°\û`ÿ¨ÛØSRM1ÜØÕ;uö1¡Æè)P6wQï`¶¡:r¿+R'¡G D›°t˜ßÿ»Ù°Ö[c=Ê6R<14ÙÒÏA«@3‹€Ÿ•Ûã8Ðw‹ê\Q7àŽŽ�[UÁ ašw…ò±vŠÙ:‹á·m÷-9úLŒêu[ùXL¤Åñ¥‰ä&ñ{%— 퀟=Í;½=žmÄêïÉßkv. 8œnÎgä’ÀGWÈcvøDì `¾ñ½Z4ºc½ÉoæqÛªN&áXÿiáË ‰"c´v5Ò3LW1x:b.܈C9*|LüÜO�·Œ58/œ°Oˆ�B"ó7^ rܲuÅ00®�ÅJø.B ÞØT àwxû`SÄ$Jÿ_›XöJ¹é¢%_£N9þkC6F ´`ü·¹GÝà«u~/H©ŽèÝ6Ž^�bKµ•JcFÂçOU0³º¥1¥7ÿxNezOó½X¾bnXbÔ�Ô)*~aƒèŒ‡ÿlL,D «ýÕ–_°@Û†ZÞIH�T�{G÷¡©ûºFšY@xÏ<Ô× ´¶€²|M�Ç™Ò=¯Ù’G,)¾îém}ý² îEå|e*FQµõ,GãØ�'äjgLÛ'1VÁú˦e†6Y³V±.í:iè¶8Ý’w(pïñ¥lL’�aäÐê’þ7œk¯*q�ý\[8 xºáâe�þ¸òì;üΑ‚ÿË$KsðAr^xÕêöP·¦}hÀ/×Ff† ñÀ,æâ”íëCïÛýóFæü|šæŸ8ô'€s¢cÓÍ({d[Nó L\«åüèùW.òháK,`¥ÒTYú×F*¥œÝW›Y?MophöÙ·îkÒ1g`ÑJ÷á~©c¦ôÒ¦æ†É:oÃO½FÝÄ·Iì§ÖñO´/DŒ²ñßú.´Ÿ<‘‡Z§MYÂ÷GPÜ�¡nyîçµÉ§Ë¥ùìÓó™Êo™Ò#i\\Ä&:� ÃÖoãwUɼµÕüWîø/¾xhG>bo–I²S¿7²áÚúK÷MÂÊâ²ZAÍj•0lþ C–Ljw7ÊSX–8pA»O„fô&hn\ù�´,rl0x¹j¢®žwiŽàêR³ü°ˆxÄ}BÖ%I4H¸È#2còM/r?ðj(ÑR¹Gå­(ÑÓìl® ŒÈ¬ñþêUoBßÂ|Œ˜¿¯©!$“¾ó›ÇP'„Wb&8vRýÚz„, ÷¼PSÊ7y“Š`ÏàIÍ1ç×ü7j‹…(X©Gþ¶!A%ò�{Õ&º¸dØ&†‚®qÆgef̽{dx 1P ®YN)þXñQôx÷ÞÁ'©â3ÒJRf*À’èüÇín]Â#Øõ,23àûÄSz=‡m[÷³õ¸Ù¢á6|³ UÖ·KˆÔ2§ò¤‰þ^Ð¥ ˜Šíç p>{Ü_×5É#I¨È”ÊÿV†5 )Øš¦C@øHq™É2ZÓ $&·À�O€/fErÎÑÚuØ Ó„ ê-0—VÒÕ`æ 6¬[Xµëîx%—EÜB«Ž¶1¤þËýØ’+DÌì=®¯ÿ…“êõ&�šìmŠ)A# SLÄìøBR•vCűZÌ'É·Le4�[Å�ñ;ûIžŸÔOAmµ†i+tÉçßfo^®ng«^PÕ%­'FDÞ+:\ߟŸ�.»J°¾P'k_â‚:n�å®Á(ÜàÕ=<ѱ÷ñJ? OëñÕᜠôÇûðw†º�¬'ææê—ÓQ[Ñ>ŒQÌtÎR.¨û’l±/—[̨ÆBØ@8耻ͬ÷XÃ*ðñ­§Œì& =]Р͉� …Ðå›` ˆïgsxª=�Ñà0¬×ûwb¯¬7ORò߆_�ùêÛÎ쿼k/ujÚ�^”oø60­w²™‡’S»ùü¸¿H·ô¦Li; �`xhgãžæë‡F¤“h^_9� "îL̳>8ï+•\HWDYÖ˜—á1Á_ÊYò•Àwê¡Þ‡d¯ž]|÷Ì>o—ïí:KçRF¯ó.ƒb3e¦­ƒßlÑ÷V‡ÏôÇ.Ôæ› @¼qôà¦pVçZs‹¶#€Ê›ˆu¹7pήëÀSJXÂX¶èKí�ɺˋ;’ ‘Úò˜›–Èm4Š¿ÐÏ!FavC ÖÕô€Ý·­ê sýzÑk‡qüJuƒÉ3ÄÙz÷rÌèrý—Ç^æ^«k„ÁwûòJ<'<)˜_Å…)›dž§¶c õ ªŠïÀ;kȺØ-‡rCm[èO˜yyY¤éD÷"îo‘¨ÜËZúÅŸƒËü!Ü(K½}- s¿j_Äzºãc3ƒ¬Yi5ƒüÌò4pë‹ãjÈ}чyÖÛ\ÿ¼ÙzNŒ‘°ÛŽîU‘ϧh¾j‰×œØG�¡®äû­ó•¶”B$9æot¥tåkÈç\ÎXÂÙ7‚w¾ûÛ ˜úèì]×"+Øzj¶=iƒ3[¹oMÀªþ9hï�¦v`—D¾ÂÂE8è‡xÞ…°[¶‡l„P.²8ä07MÔ ­8•zÇ“®UÂ$)™ƒm\s�©\©H¡�k¢¬yDöWm³:§a�2�Ë²Ô ·Ú%÷.Ù§©  Çòz9]”¤,=¼t-ûš(®7�AÍ&ú"‹;òD& Òëd—Ís[ ùI׮˛4òì^tó‘gò²P¼$qeþ©ŠmFÑýcÐö¾È�ט^éiÈß'ȶÛ?®Ì ¾=51wT7Oâ!E¶åß ‚�Ú BAÛã›üÞq†˜C)õì¯À®.¡uÂk'TzæXChŽªµ-Ç^Þ)ù|/GíßÈ>m0D»)ÚcŽ¢9!ÐÞ{uV�é,¾ù3]ò:%M-b”\U[—†ìñÖQšÑ«N«ü¹¡Rò �åæ„U˜æo{rDvÂŠŠ¬Ü(!è11&¿šzyÀeëÝ´¦�Wüè_)Œ­ab¤¯Ò«_vž£ûþRtä~ÅÛÕà…Ï2Úú$SB—{·Ãõä){û+Œq¨A gÕzÜ^9/{9`Z«™¨„{mkz‘§gù§,Ûô•˜ÍFa"Ù^.Ù4 Ç`§ðÇcϰöˆsëŠJæ½ub÷Ò}H_R†/Ke´`©ìkBP¼~à·qùGWp¢ŒÞœö»Ù­ë{*°�–¬7ÇwóeÉ~É9ïhçmúfS «ÿââÏOÚ¥øØÀzÀÃUò³1ËK,µcz(“­¶ÎïKºMz;§ûw]ø Y¶U¯b¬01€$ ô>ðkM×AìH>oÞ˜ 6Äô ¸¥€žØÖ1äI\Øøª€¼F³œ ”Å5M.�h� EÀµ‰A�1®Æÿa¼“´$4 ÍXõ¶"Z�#¥�L ±iú\-�B²eúBEÍB™n܇pŽˆL›l¦ÃµíòÊ¡» ãÔÛàõ÷¥{êån2–3OÛöð[gëÁhñ<£u Šo»Í2Ÿ™zóll¶ÚñPú÷L¨˜TÖ"Û #§a‚SS))%b�R|ýçõ–w^ï"ÛcY8Ó’ùé²ÈNhóÞ“@‡VHÑ+ºþX·Á'€h&Ç‹æH£Ÿ÷ðÎý­Äá¥Ñ SùòVùÁSý¨F2 vÂÒy!ØèRƒ'$ôàúÁ h5ca¯i½­ÁÈ£È%sGg�=¤ñh\5€Rk°A³ò[ˆ]o�Ï#ÐAÓyôvªÚÐ7¯ O³O±ºÂ)÷àN_2X²Q 6ð ¥Íúýá0{ûÐÂÑÚxo¹ ži›Q»Ðù¯ÒÞ½/�ž/à­ÂŸ,ÈÅ# Šªêí5{½�òÆ ?­ÏZ5.\¯pÅŸ£ûÏèÓ® 2Á§Än´[ÿ²ö�páœi«T;gð3ô+"Þ—:1J«„=çâÜ0^öU°¸––Š�vabæã„Âú° rn6Ê»;p|c¿ºÅf*Ç‘Øø£ˆ•­‡Ÿ]rÚw!ÿ$ýʬj>Eí}[ud|]½NÆïg¬*mÁW'Í2VMÓ“}]p´b¶‰¹÷TP™†Fž¸¹Õ)¨u²+Ê—Ù-F ÊH1ÂÙÃň-�»îšëîBNsDú¸¡Ÿc¼œ×>G³¶!l1×Ò+õÀ=îkn”v;æŽAw�»Θ߃±£¡ËîéwH ?ÛÂÇrj°\OäNOh:Ôjù¹þb²„JÛ8!jL&DS~“qB¿Á´¬‹˜]ç-ZʉWâiUj6hõ0TnÌ7¡µ e¨»ÆíˆÁ^B¸^_ì9NüÚèX½}žæP²ùÒš#þBãb‚JÛ39æÆøÎè8¼Å–¸ Çš*?séä]¤Òc<±|áèšiÛõjÄŸ€ _ÌøæÔ'ï°§ÒGëÓª5¥éþþì'¯d¾Æ{EAÕ‹þf®3�l7"§ÒPn<¾æØô?`öuz¨l�ªíÛÃÞŠ±ØV5Œc 1³ Ë¿ (FƒáÌY—ÝÓ�UÃe /õ\¾ ÕÿÁµô�½–ØO:3õ[­ú µó�Ši{­eйœv><ò×5jþÎh\!æ¸{IT¸ ýpY«L ï·ü­5CPr†"ÕsífÇç¼¢T(†2Uæ,ö¶*Hï !a抓ËNN] âœ*9 bÄKÑqæ61g©jä¥ HSò|@°þ5O]kÄÛÒöŒ‹„ô.4³%âãeá ¡Üò-4qkt¤YÔŠÂÇ%<¦Åç‚ÿ_DÁrJ©ö°�Ëë’¬YËämoíåÈAÌ¥ÿïΩ÷¥¿ö÷Á˜m¦â­ŽD%1Òñ¯òÝ9Ñÿ,ºâñßbŸøŒ ®æôñªeׄ¬ÅŒXCt‹Ü1cÖŸ†6W@})òx”¡€>‰áýQíú¯¯ªp¡’7ÒÌ„Ï.<åÑ­º�bõAVqu.Èx·å;uïû¸W&ÉÝ1pDœ3“}’bq¡“óµ%|/ª žÊÜ4œ¨&'ii¡/ 7B¶®úѧ\KÍ8l—Ÿ"ol\D¡ç鲿µ“Lpè½~cÆ2KJD ^PÃͯs âqÓ�l“³j2ˆËÖÖÏîÚñ>"ágÙá2îaÀ@Ï(1î”·:Ôß9êì†J)> ܱIŒÖY‘®[°D 3ÏÕ¬éæ{»˜âö’ }'†²À�Çwbð�rŠÇ}„*z�$2’³ï¿æl½^OŠô X“•yíåîM¾¸`vf“™ 5Ò6ïºTÂËÕNûÝìá ›Û9´â5I%á°©ã¡9%œ<.Ù'¯MÑ2$ eŒH�}¸¬¦ýBøæß�âA¬äiÆ ‡w~EUæ=vºPd]u ?+kZÜ}?’ƒ¼V{hUdN6!Xø=¼S÷ÞŒ8Ôï È«°½®"³ksç¬m£óìäZ7'BüÑQX‹%”“ÒùÞ0¾Š±ãŠë‡o‘$þS®jëWßÎlô™â ™¨˜ü§54ØùAÅ3�‹åáƒq[”f†ü=×42ãrÆw—{³7õrr°3«wûE•7 óJ#òÐÔo^‹KA­Þë™tÁ‹q#ëŸ)Ö�Eï.p°4D‰äôÑØiM<ÙqZ TiÃ||€�ü zc%†�W[£VJº¸P9ÐØs;eæ0J•´îó̇P‡¾Ô=âW/rStpí“·u¢¡Íh-…-€|rÝ5ƒ=¼¢Íæz9éåï$}L—8ù§œèU>ü×ð6ª91î¡Û³!E4[¦¨è·þ݈õ§Ø7_׿œ3Ú—¥ÇÓ÷@­•¿J=ì=?zJžg¹¯@é»ÃÙZ] —³q¾äLŽ�´åŽ®áJ–oŽÿ{RØy×*³høx�çFóå&>³4 A7%Ë*Lƒ Uñm޵&Jî©ø@¼ mÆ61ÙþY’ÙS4ï&�~ýš£ˆ7Y¤¿ÿ+3ϽçXÃZУ�yÕç¥Zzñx}¶Q›‚�9ãúïPdY‹(÷`5H¾òYp†�½´oݲì왨¶Sþ7V„]·Þ�%U}‰¢ÿOËñø}u…{.1£‡Ç²·œ“«ØKa±qž‚*›[@ÁX9óÝ'hçlïýލYúèDÚ+[…mê&<äKy×=cSßæ«!š‹5þ÷!¦,°â»º½‚M˜òÐuykDÐÒ�!¢v/@’u–ÁNVpùÈ©�áGî8þ¯ðŒ_»núÊm‰Ç6£b'îS¦|?iÉàiúÞþ>�ýc&á4„ûÅW{MAFÙÐ:æ‡{g‰r•u“B0ê¤KËö^ ²95°X�å¼7×Ñ�ºÈ®‹3 |Ôl0…€�‡±E2“¾#¼ôu)ÕÍH/¿Yy8Û- Qu‰íQcÝ?qàÚtü™+÷äd•…S¦Lí®—}ï­t`§H €ñ,¡¿ÑŠh!D¿­Œª>ÄHv$�âl¾„Éi›%@¾ªê¢ËSñße©îê[[ê´égPÛ6¢8PÌKpÜ«p´Û3*ñÒñ \pÓ-U®úÚ�Ñ™T¾ |”_Th Ÿu,¦à—ííBn¢K]ü½6)5FÉi~¼¸åäFrÎkƒœ®ip ÓMª3•“‘Þ,#FBžPl3“îé4²a7À.V6¾MbÙ$¿ø�ˆŸ•|Õ7�Iôç@p§ å.d)+m^ù¨-°W¶ì‚¶"7=š«´½›á‰IÉ>êûi_ÙðKÐ0†§Ï:dÙ•ò³¬ñ‘ŽœTà›usfÞ«2RBJü1>Um…‡ë»åóU�´[¥=åBn²‘ Œ}O4{f:ƒ“eê0'z�ß@x ý’½ ˜ñù1¸ªVÿ(UÆP/Us&š——/¬ØÎúÎøª]�•NÛHc«)4i T„õÔµ$»A�#=°ÍI-�Éí3TûxXpoÚF2¯¯ql)šú^Úhð;´)yã\]Á÷ãTÈB?µÁ;÷™s ùìû%Û7Çø^zcøMÁ÷ÃwÌ› ô›óÒïJwÒ«õSɧÎÂÒCPô­�à W‰Q>´ M&@é¢qݳ]Ð>kÄ‚ý†âtSÍöf‘J¿Ìh”^'§´�ûÍëÓw5üI2ˆy†Ú 'â—Ù‚IÞ÷œì¼ânEíJ\‡ÏáØR·¸:òôt>N‘^Ôêq©q»5|ZÊ\ÏÆzà¿]µ^3ó\�ÙÐâc,»K÷½ùŽïKü@i`$V.uŒ?�¦#{‚F'nY‹«®9S˜öwþ¶Ž¡¶ÔF•È”j­lõ¨ ËTÎí,3ÄX£×º{‚Œ .R�ÒñÜ`Óâ.1�оy”HÐ+µ¤§·8¾4èãØ­öÈ�+Cý¾a4ª%àòáüg‚ï�³ê¿c‰+¼ â}ÞN ;y‚+c*Í ÊÌu8WÖÃhÚÊÅfÐ= M°ÄÁ@ÒüÚ§2æíj€¬Ã!ˆ3݇+|Ó�Z…#ÊΔ?LÖ¬‹O»Óˆ`¢¥|­÷ŒrˆÙ¼Óu®EÁ>Ç+\pM�°.'£ãŒh˜Õe&A’jÑ8k]®ä ú�ªjzúéXõ¾Í·§k¹Š±Ôf £G¥ :‘ðèʣȑ#¹<7ÚÏ$ãDfJ±2œÒâÎ.²è°»ž‚D‚ã? FìøÂìÀž)FH>MŒÄ$ÿ*{ªßgDÕÊÛŒÞÝYOÕ ŒîOÂ;‰ßùž¬à]‚ÆÍÊ’×]üD§º—íl3#Óá§ÝóüÿjRq«�I`•‚Kuoé™qìÙNô}®O:Ü^éksú’ß&gæ›Þm¼�Ʊjtôåk­"ÞˆDÉ>ÄL‰€fI‡åÑ‘I¡�p¨Q:Ò²û6S©]“ûhÔ·Ò³9 O'Y±R›Ey��0KõÚ�…¯Ò€$÷aÌôµb9ÕÔÓŸû6%3QVÁ䋽Ât´(9àÒí«$íÐäMd1GÐæ\3/þÓ…Õ'¢º+¥ ~½f)éWˆd%5×Ò~on°3|9)p˜h�,³¶ÏÅY‘YÔÉóÑ÷²’ƒ6q‡AÐÒ3PY½Ç>·[ØRÌÔ@YÏ!°¿E�Ô7Œslí¼p¿hŸ~;j“àlÍ8Và×z¨˜Ý0ôÛ$€4c|m7…!>NLÒ_v‰ Ü:Fƒ7hUvG(KÿèÝŽú“@d$aQü „ÛþŒuä�@z\lNyã‡ÎVâñ��n98¥â÷Ka±^ÊþÓŸxs÷àãx„_šdG[qáݲúFƒ{(y¼†éBÁ¼@¹Êÿ٠옱F ·$:Òý½~ sä`&;WỼ|_¯ —¹Â¾z½¡è´³ŸÁQ’�cŸäÙß“§ oDÎÝ9›å«ë�å, p_ìþE˜îq©atù¯±±—òœµU¥àÍã–ËAkñŒü§ŸÇ]ˆo&>œ¦Êm‘GÖô9n­8ÐMOC“Ut4d‹ κc‹ácœv  E€î [t“ú£ó�aJà �’ü-ãÍ.-D›Ï=ÆŸífÎ.sÛßqïÒ‡òÖÒšGÃéü?øó�Ç[O£³_Èi/cðZÙX‰k߉D RÃ.Aà5—b‡8UHÎýH1 yæG¡LÞ[ÒSâTÑCª'˜qé܆«À�¸s¨ú“@ð¤‘[zJî¤Z˜€ë?Ö²Yi¿°LµiòºE9€xn�¯oðoÂYI)Ýq­¤â·G›À£Ý`›8€J0¬�8»Ì*6Ë ¸A°òßD|Ç%=×sųZoÃëøü†ÏûKŒÓK‡‹é­/Û�ÈA©ü§üùÆöô‹7šÑ‡£äÄ‘}86Ò‡7á)h ›ÙÊ…ÅŒTêãUÎE°0éTæ±(TE²_¡¬æN¤N,]çÁÆ ÛZ•º­ ª¨T÷§ž�‹îýÊ9îàÇø#°H!#îptBÇ_U{uÿ(΂õËÐ`qðJ®C#Š%!ðpÉ=$œ ÿv1ÛŸüÔI?¨nÁÙ;�ôkn ÉÞ\z9ЧiÛX]!ÙÑx89¡É�d©—dêKIµ•UÎTWŒˆ,nØÕYÒz*7OŸð[;…pÔ¥ó4�Òÿùž<Áäã”Í9ä¥îξÐ.©HKý4»x(n�/×ú 2éÈÁ��øíªI¢žøl§(mÑàþ’ ‚è ]^´Ìq—B¸0aNARÿ¡_s ÕYOR¸„­Œçù'+ŽþªµG+‚¼‚tÕÂm»ÝöåCOðrÚV[¦ÆsÛS¦Ÿ;&TKÌ�;Aʵ Bÿ’¸Ÿ�ˆˆÚ$70V–4ѰtÙÖ9Û1+@�¢Ê�Eeà1Xíþ!=|hí$Xw%®ÄŒ'›10+S„æèI µ©±²ZUÃM+=Wä§/ïèq�µi|MËi¸ÚÜ'Õ•'ä@Tµi‰žÌÆ\|½Eè(w²}CךäJæ�n,ÆñèöÌ{üoUúÕ½ázuˆ¸ü¸ü*YÚÃÅô2Ìv¿%`{ %Ô¤–*ÔÔfãS57ô· "Æ “؈•àF•Nâ[`¶3Ù‰É)3µ¦Œ[E! ØB[ YÕU< ÿˆ¤ù_ͳz®C Ñà¾ìËyašˆñÍ*±#öçûÀ¬ˆúò²²š,`øZ¹ý0›;iK¶J]wDÃ+^[o‹ÇQgÙ·íÜ?kœKΕö6®:'tµÐ9 ,<*y�$K|^lª|ܤwâA.Ü_g9W ”uH—�’\�ߦïÂküØ*ÙTãbz‰°p•pC0{uâ-?àe^Wã/ëñmÌ�Ë\jÑ)½˜�Üé;‰Û£wRìeÜCn‰ë­½ÍˆEŒðʨB¥£Û#0fËr5H»¶âwôœ[U@}”…ãúopp0*ÈÁ.gÄ÷¸º=ð™+óL�Žç8k¢s~à”f)¼™Ÿm²!]gWÉZÔÉÎÚõºÒà$Uóüª…bë�I¬cÎëï+´çÞ(ç:N‚·¯$–ϰ›ÔÝËѪrS¥cj’ã±Ë8A9[%¸<ý“9̺KiÚîèáG€;Öç¦tBG˜1у¢%æ—¬?ÕÅo1:ý¯¿œT SæqÞ O6ÂeÞ™“ š\[ÿ ô�[›¨ž‘b[ȵ(|d©ú †I€ñ…›Ï-¶…IzCu¼ãƒcŒ.ŠAF_KÓUϯ“ þ=hï(¬ýeùÊ’%.b¬nK1Öuwè·?_g† P�"zr ñ]süïÎ`LO6À5ZOšÊûGˆÒåIU›Ê8¿Ç{‰NåI¸§n»'ôó Káþa'O�hP�æ[©õ²¼Å·è‘ Lsôs|Âä®r¼v&Á-  4:…Ûö Îæ.“œ*m‹ªQïû—��"¡E|Ðæ·“ï|ƒÖŠ‹Ò_PpSÝ:L•™i%iƒ±_lLQ%—x\¡78²Ð‰;KMvg¹{W­'ÒÛM¿‡”§=6�×Õ¥LÑ+oÑþ€Òu( ãÈ7"Äü½Õ•™W#×î/æsÀEXÒ�•ÐTV¦?WÚ˜îm�dÙàcDû‹ñúI™�8ì¿.Ä7Ù£'�È–çPÇ¡ñM+ibm†uv:®à㘢LU X\’ÛäAÞ²fÕÜ$×´þž üËiHº~~^–]gC{5ìù¿�4ŒR/ÎA=ËÀ’#^DPAP ¤¬†¦6æ¬ßœ§�òÓ@†±<·z`úˆˆè>lgÈ¢Ô^rÓ.Ó"H�T—°Ð<ÑcwÖü2�ƒ(©ÎàÛ(· Ž®š¦ îÜ϶O�%æfÚØãÐÆJ%qS<øßrÃeɦ—ï߇ͻ9>xÁ<ªãÞÇX‚½Ñ@oÁ1j¦÷özÉQ0"³éê¶$ÃfÓ%þäå{ÑØ‹+YòLk� Åú‘ŒÂ­‰žšã�{Yë׫\B ®æ$.}2€¸³Ö/·¯;íÎ˜Ä ßÞ†£.Cö1wá}.™vÎ÷oßÞ '†»W0ÒlB¢O>A6 Ðóóo€&þJ£¼Ž¼ù¢Bç„ÎBžÃä]û°—CÎ=÷�,¥d4ïn|ÉùÈ)UÙËÁì ìðUäe0�®2D“_œA*í²­Fä®ÕCÒ~mÉó ‚,n¦Ô{+>꘢»ënèPœ€q§ÇIÞÍNc ¨ú÷wSû"5© Ü“™2qäñd"ia3X:Óå÷Þ0`yʲ�0GŒNL�–@‘ë¨?É?HlYו.Å*ò½h?^Âãçܰڔ„dÖ×bÙ�UX€§·ð>;Žº.~£ße€¦ ¸*ßÑᑜvNÚ³á@‘Ú<Óò+ýrÚÆÚ·Ïví÷æž±ÝeÙDš,Þ‘*v¥Ý«PîÅHŠIþ¦ßaŽTD{Èin>í!VÛax»Èð®ˆ(ŠÛ@ÕAøt„(*¿ìÉËKo néež÷7%ÑþGÑâã3òü9"æ…"ƒ¬i7Ïî7Ñ�,P®]&£rÛ:;ñ¹õ>JK»­âjß(ÎÒØKvúÛ[„ ͈z!Ÿ ˆÆ§arÂg¹H¤I1:zU«ò‘,Æ�N_i+“ª þòUCâÄmÃfè/NŒ¹.'iÙ@Þú˜‰ à�1H‘ån°2Ö—Zs+w–í�„&~•HÊíÍ_˜ôÏîx“½Æ‡.rÝTœ¹cûx0ÅÂDÛTŸ 1þ-à-C�R§u¸/€c®¾“þ*×f Ê_Ù<’cQvg¯ƒ9¯]J½Ü+¡�¹œ¯5&‡÷3ròXÄÀB$)Ypžkq ï­ÞáK¥¸\+/ ÒŒ|[£4CØgè|¡=ó³Ò`¸ˆW1>y[€?+q³eEýüÌø¼ÒÈn æì®-¼=ou;ÜÂX­¦NeD d–DZ‹ínØéÊ!�—%“Šö›8#ü•“–M|ü¾QÿÐ"…‘<ÜÀ팋–÷n»tO€Ù2­æœŒáUî" ÎhóJµPÑ][Ø•™HsdÕ¯,-ýôØ2NH¡©�ýæèçM79Œ.>!Ý­÷ü g‰o]<¿ÊDAYa3‰< òÿ�s¦=û=4C-œ¦s†'l¯!D<½�¥ÿfôÝ¢ê³í ¤3ÊÊòL2-Ä„gÌá•ÿ ¡ ¡bïý÷ý·.‰Ù{êt–_ú‹2;°š,•y7ç»簭DZ%é'Z‹¢/ù/Öæ%’DO��Î3{&•þ¾#2ã!´:•¥‘’É·3é<ëàJ$ì—åÊüQÕ©,xqÂ/?ÿÞ:¬Ö²/ÆÑ:o‡!ë]Ö:\�’V(h5ŒH@r  ˆw½Á»ó$D íñž")¼ ù)ØšÑlQÆuR·_a3–î–†ë°Î:X(Ž(ç»mÒsäE\f€3 ŽfÎýkA¸µËe©/Y-§IöG¾ë‡™ª:2èÈB `…‡�œ5#õé a6”Ï–:±•k�}E””E"¢˜H]„ _ >×ýO‹+hÄo08I c%²gËŒ�<‡"§ {ì硊«B±‹'"ª ª´ÏÀ¼-0¼H'öÌýñ ²2ŒdY’ Å-N¯žÚOÊ"${n$\�R á�w§€-³l ïÙa™vJ¥ý°óÍÞTòYyP®:!M�}Ѽ [»óªÐÁP’&ØòyS^÷¿þ @ðßSø"ŽÐðî·cæ¨W›8–Þ˜Ù™ƒë}΋u£W¯¨NK’I�¡üêý¹�þÊ3Õ®2ã ä Äf?öeS�íÙ¨Éæñ¢ýiÅw>ƒ«ôîï•›3¼k—®iÀnívu“pc¬žJý_#¾YÁOo¿2¢ª¶#J÷“d =u¡iA: À‘*)${¡ äuÙ[¸ã_]{‚?÷Þ"‹q<Ý»þZߥ¶µXeivG}3àãu+µ•íQc±—'ûŒ¯à)Œl%y×#£–ÿü�6¶®(j%¦fïÃG=—8Q1ŽÌ JdbîÍM°~©ýL?F1†D6p"="h‰#E¤´�ƒ1vo>Y-éxòö¡ŸdiCØü=³GBG³ßªåÒ­*ùƒk�ô]ü|9`z PþÔQvó_«7ö=žÊãþ \z×±éhtú¢hç*nÛ—RAü5Éè ŸÄYÄ)’s‡~¬7»0‹keØeZκž¶+gÒÅÝѬˆsg8Y�yƒËÜ-á‡û#Àù~ø=[C!Y! m%÷ŽY VËÚ¹t[·»ì2—ñ¿žIƒ:h#ÎKâ+›X І”0 “™bpbÛJÙ£cšD5ƒ»•ßʈ©+Òrì…ûâ:1ÐÔŒEˆ› …É'ÎyrÞŒŸòp‰�LK0Ƈ|8%çŠR.½q.j”h¯5?¸ƒ¿@¨âoúx˜‘ >¾àÚfÍ o÷ÂêùÜ®7¹ÉVGæQaÿ,Å9×¶ë¼ Jµ¾êWʪ'¥Ô+äª]’Ý”ñÉŽË1Å)ý*ÎfTG¹SßÎe¡Ìø\­~p° zÒ{:áb£òù|ÅÈ~÷Oç¯ÂØL4UÊàÞtÈÅ™¬4ž³Ð1sàÝ&Q³ã=¡LHó8kœ³dDAVá-pë[ÉW’áØ·­’ªYfíïI·RœvyJ  š'äG¡F~KbiÄ‘váæõd[Uÿ¬‹—{žá†�~™úøn¸WÜ+SÛy#ó-§ê¶] ^)ÕÈÀ³}2¢'€•BäSŒOU“ºã®båuüþ¦‘yxŽ{�¦qö$F4W4ŠÚ(À‘4ËTL< &þþ5UvJ È‘KTåäZÝn¦…ÝÑ3ì$q¨ž+¬‡â°öÇ}zíEÙW†ƒçp-†¤1›ê ú3Å µ‹FÜ«w x´;´ö&5lý¹US¦®È‘>EcùçZRa.$ýDB9ky³$–dÄ6j]}áøì2ïxBϳ b´Ø¡¨¤uØßé;û®ýH¶½ëÇ�ç¤:º]C-çn%ÆzHÝž»tLI:Ü�T³ð<ò‘Â×ý«"ÄÈ.PkÔÓ"&œþ<¼ØhOiûúGÈ»ç-Ä@DAÝ!*Cq«¶}ˆ²ð‰ÔiT³_²9¡`îä~?i-©±Õˆ1Îö\iˆcœÂGxNð6ÈÞ‰üà²sC¾‡‹gSêˆ ÿiy™0Ã?µRÉcîy-|™¿¥$ÃÑ0¦Â½ÝK•¢i¹«³d[T¬Á>4ÀZe5ù¿¨uj:÷ 4“FµP<ЋôïÿO‘)´R�øOš \ƒ_~Z¬[ƒ![=*£Ÿ[=b…Ë‹žù±\ø²ø;êrÑ;æD˜p@ØläLùÿ�Ù,`¾�òò¡¶ šü!Oªy‚/4tžoŠÙ­í›ÐL÷ñ,Öc�¢¥…#Æ ®0ž…€Ž ]Ê7bo‡Yán ~SË5©¡gŽ@æNfêò€­2*‹å”sWгÒúzXÐg7 -#ä<¾V¤Š&^zKqõ#¥%œáÌɯî’^iË7u­*)ÉËäudܪ$Ùª‰™m»¨KúÀË´WãJ UìÒ!?¹ÚIcrq›�±W­„Û-4çd*§÷\)C—Tònƒ�#èŽúŽ>«ò¢ª9àvJÆvzè×ÇÍ&EÛ®*ãß4�­¨ºêöI.�™â�ARw*+`wÒ%9tóá°û¾úõ† 5�ôóô½õå~‚­�®§¬DXˆµÅ*¤ÿ.”?ìC0¨7º)¸t7->iµs‘-ðvdîèýæfÚc©$‚@¨Gs§„?È.¸#åæZk¨¹OL ùjöB¦‹­�ÃfTä¾j‰Í¨Ñs  RÑ©×K�±ðÕ§‹Q6Ž¿>Ýt,s¡ÕÉ)"P_[&AuúÑÈÕ£bšÛ?žnV˜öÇ.7ÂÅÇú¹8 g[¨·ˆG€²èdcè}h*ä™¶Ö8«Ô÷L²(ú4üRÒN¬#ÇÜÌ=�¾ê äv`zô/üá3VCëpŒ½w×dUèK8ñlwª< _ò’˜aV¦t\éÞNôöP ÷„yIg¶¨ö'™´š�[ Z¨VD'b)(~˜Ê’†vÝ=´ú�i˜&°zp´¹Uþwý ´²DîYxìÂ'Eo¢¼]�m]$äàHàPÂBÊ»À'¸P¤AÜ,mÏn‹&±Z²à‹ Ã©Š§,$ű.5ì(嵃¤ýü–©n°ült~£ã k‡^£X›ÀàïLv‚›Þnˆ}‘¡Ç³æ;ò¥<ûy3<Ú}3;ç 7.øj¤qéÙ)l R~a¨+#Â'„i1”‹ÏÈwVIªïÚ83/ÊxÑçÇ2ŠEýFíæÀO+ÿ·Wbjç¢Ü�Ëå ¨/-õ› âv²cÂëUi [4wlWH@ÿ~¡ ú †c™¢jr5J“zÑôÖ=E×ܤÇKy|‘ló};?Ò³Ö™±'� ãQ–¹ â—@÷½l×�ä0S«¶jй¨¥ð‘!ÉÂjÍ‘mÛæ5_̸ñÄ–}ßjLH•jAºïÝa»›º é{Œ‹j?¶y`Ú‡�Ññ%´Ä\°Ä»ñ‰äp}¦Ï¾Û—%±êÐÅ´6ž+ œtW5e¢cý’„zѶd¥Œzwß{‡3ù@d¿Ñ}ÏÝ“‡‡Jo0ãWd¸çß¡ÍýW-ç÷Hs¬äZœÂ/Kš4ää¾b[n#Ú< çsõý2¥†">‹¤K‡¹Œâ)t�zm÷êÙ›9O›&™ÖñÙ¸ü˜è#IÿkQ;ÿP"s.!,Ñ㋯õB!’1QÙä$’Õs&Ù´�V[Q±“ƒÛßÚé E9õÇ´q$>›\Ž*Ä XiÁ¬ì;™nlgsò‚…*LtÚÓnèæ‘†&5•¿D›q~hfO €©S›xŒù;NY-.SûÌ8|’ ¢’ž•6€/}‹J"S–»VÃ_8ã®Ñþ;îÙ»Þ‚Ë[õL½;8à9èx¢ú(Y'ýq³!² Â? ÁOÔïPzêŽx}¯_ìäç Kve‡–˜âñ2ÇM+3 9Ñ’ñ?÷ tF£©ÌîP¬ ÷Ý™àN5aÛ'£4ÜZ«ÄD•«—w•±¬Ì‚UGðP¥Ýh]ÒXUºßeáa~ëé'ßùx‹:¹¤3éÜ@²É�1>ÃB›âLĘËÎRb@ÁyRMN‚êðíðR¹S‡U÷J„…â‘”v™ÀÅ…™ªdbK"_ÇŽÿ"âˆdMvRÍ›¾ªJůUZË.|—ÞÃÏ8[å¿é­1Ñw0@ÏÉÈCêXû¡›Éwc´{¯òG›'Ð&Ñ�#˜½ÒœH€2Ùco²8=߆ãEß�Aè¿ ×51OB¢]úaOÔÎý8vÿnþŸò63&™åµU4UÅ{ÑiÇ_ÿÕƧ}C5àFwèÔ�ÝÊ¢NüxKÚU£Ðg°,Ÿ.áñÖßS÷ÏŸîýqâLKGO�Ÿ KÜ2èüCåÖQÃæ­‘åp~'F7³ðàÚcú™RßOÒ só³È¿8Tê8Þ1 §»² îE‡ë�R²”Ï(¦äºf…(B¶)eêÚäX±6"ð 2hù{h—T„~ÉU–‚ ¿îæ(bMêÁñ¾›&{+è ¨Pí>÷‹UORIôÜò õ[yL¹©3¹Ç§H m+ÞM¸z2Ö¤û| -ÉäOœ†¹D¡{44Sð®Š_ûÏ«ò�Œ|âÍà�Ö°'èÈtÕ$2]ˆìÎoG‹3h„úäÇ•‚{•Ų;ÃÔ:Ë›=B‹se±"�KÒn€Á´‡Ûæò^ZͬQŸ]”' T¤ …ÝTÛeN5ùãIð9jß û5k I¶B¡]MaÝÁ©�#çÓL¨|[ûýl²*\ûþ%bõú± âZMŸ‘ §ûS�E7•-GÝ>cè!쎢-¨™£©`ÌÓ°W''óY\rc4Ë&h†)*ÃŽ0~Ⱥ6dJmЏ<.ï¿8Ñ1)Ã÷5ŠnŸH#?­x”6CwŒNÈäá’Oœ£JZŠâì—n}–…K)Óuq—Ví:Õ÷³RþpÍíô)E:Šæb‰ ¥‹EÁgO"É<í·9 OÅÁÍ¡cê˜þŽ8*lˆ\“æÊ*c¾šf‚·Cù¤ú—SLÅ2eÊœºK G1 ÉÛö;Ô|?ÞA!ö�Ê}Ÿ½¹7¬7¶èe¨/MÌÛD×cd‰½Sظ¢Ý4YEŽz_VŠ—?Æ)S9—´QÐþ9ë}ö¥©|rBiˆed Qm7K/>qnªÒš.ijz"¡€À‰+b�¸ªy¼ìÉúSh#ôƒ.�Rk˜ ¸Â¨ãéx%é Óí»Ý2*•Õó€IÚ!—²“ÅUDéߥèǬëñ§›91ïWÓpìæ}ˆN¶¿DÕ_ePŸ éÒ‰­(usà“ä£|íºj/&ÞQx�IMö£˜5‰üï<†Ä�êd·•‡9:i?sûÎ /cõ4,!}Ò�¾oW̺zP©é¯K¥yõ)p¡êÞ&$f1[åX_þ^LÈ�¸Uÿç×�7Œj¬7W{®høØÛ‹} ’XÙøƒ9Yk(ÔN ç@\õ"MÞΗ€Äò¥˜IÍ‹À­ìÅì�›1Þ¾tŒ‡ÆñÿŠÃ…(Á¦KzNaÕ9yŽ…«±yfFìùÍ •ÂuÍM)e¸måö§- šèæJJŒ€�m'ÄUýŸ{½âÓ“‡ÛSµ=d•¯9½Êêpœgˆ©€ÿ¢�¡íbØ^”ë>áÎòh{¸ªÜ'¤unÔÄi ±¡x�¶«â`Ï�«Ó�U‚ 4ž]qhN“KU¤&h(ÉgÒ�oǘ¸ggc8°²˜ç¬“˳P eklAC²´ý’ À„ª‘Ïo‡Éü6ã´§Åïn‹Ê‰k: E÷�¤Ô ›ë±Õq|ZÎ�¿“ÏE¯/ã‘$â\jV“uR­@iAÛwLj;pxÁ”ìW”È"µó½ùPç/wŒšAÚ)?¬9xB±¢%m2-pwNxÔâè%¾CåMÚ¥ÄOÞƒ³ ›¼Y2¼w~V_�Õ ²y�íårlxyÈš‘—J•¿ÖÊ¿½þq{KÁTa¾zˆu…ðTºY¸#D¬àGôD±"LÕ´ùŒ´46ß÷·¡ÃÖ·z´è”Þâ©úâàiá%¿ÍÒB®%•X@†ª$ÄÂ(ƒ˜˜Ä!u‹7+˜¢œG7Z¥“š!‘4cyü£$¾*®2‹jÜl÷ѬP¤¥ü¤�Ó¿'´yω$<¤ð’Ç„->ì•ßÄФš‰[zœZDOótïn‘ E%mZág�éhCc]sÞŽ«\5MÄâ�»È:ÇD"�ô@µ U4ÆB/)ßñ:�Ã'T[x”,3ÌjJgS‡‰Bݨz㣣‹0ŸR0æ•Òt”-2†¶ƒy{fEi«»iF¹|’£!xcòãR•·v€66L7¸©¿òµRiñ+eÔÏÎ×× #3¶ò/Œ´ .ƒ Ï’ÉA§nxÒ0ÞÁo‹¥©z† —=îTt {u|´rr÷MK7š[˜ªÝž…Õ„KvB4-”Hb—$e)ÁÅ–²òTÊ„ýU„Ó˜‚f¹ÿEãöÎù\Sò6 0|SÙxýsƒûž°·¾Û¾³š¦'Dùó<]y¡él*ÖÑ z¤#SÎyi`À²¤»¼Àû,.ä ¢íHh…«¿±´¯’=ƒ‘ÒL…Å ò`-5\(Ù@]ÕZžcè.èÑF Í#�ž‘ ?MU,óß·ý+¬êã2Íß_9~ˆ��`.šÐ÷ÂÈÝ_I&FÚˆÖ.0Øu?húD~]Å›­™½—œDt¼¿›æxtôvg™Áp{'ßâ@N ‡ ê"2Z`íÔž9¶§7p ‹´VÞSœs1£ ,¾w ø¦Š Ô©ö¨Ò·Û ~àq‹0êö`êVƒâOž¼D_P ×È–úç»ùq,šwl”pŸ%ÖÄà)pògZÞ«KReõÇGCÏgsj¿þÙE\ ŽÂb'öA�äô©ÎK…V))ÇgÊOü#‡hqýõSÆó G¥q€±ÐGFðëû:�R‹æ"ôOú‰ ßy›ÏÅ"òý±’Y]˜2ãÂ6¦X$Õÿ’ �gÂu–¹èÅmLds–6Ãõ !âfl¢?eédûÚÉ_ÒH8Ë&Þ]„N­c½‹íÁ¨DWèÑÆo#—b»DwKk�}®ðŽ… *ôFmh&TžÄâÕª½ƒH&æ‡ã-Å·%ˆoÆuüU�ÇLjîjRl¬ò£Rvî ÖËκöOï€iß}5úÒa ඃá¾ÎfÈ�ò/Ã;�U˜ø‰ç Ìî;9PsKqÖKß‘fÁ:Cëóz±æ“�;!’b>7ç½ÌO_™J·ð3 ½É¦Fî[6À�\ì,”îU'b‚¡ò-qŽ?§Ö.øa ®Wm7›„0;í-�Á[µ·È�‡+|ÅWì,5>U×ßìK ¤ëU²°ìo·'X5nOA¾ô’Ug™ÝŸ¢ã)yŽœˆ>Gåö]ï’¼˜Q  G2œª)¦çòú¡Õ‚B—@Úæ]øJš™û™Ä¶£¾ðqX”+H ?ö"{"ö.>ÄÀ ‡-4j ¸±s>Ñ·|-%'ž6Ñe’MvpáO|çyÂÙÞ!†©íƒÈÂá¶2àèI‘'ÊT�*žyv켃 ¬·#™<'"ë•#òó÷oÏ9!G+ ÞØà¶�‡[s`°çRÌ.8ëÞÆ�ÁJŒŒ•Ðuª§Xo¥P{v”uù’»};…XéºÊ“å¾ë]ôQ’Ò×´ ŽL,6G!ÑÔ½uJÈs�.ÆÄ{ ÝÕnP¾/|Ík¸BˆiŸävä6‚Ï ³~””³û$&Ûa z#¾,ƒ];YT›–¦õáaúó±³AeŽG�,‡tûP¯Û"¤�K)`øÐÐ盪!ôð Zn�¸èˆPLÀˆy\k°e ãr­`ò9–2sý ãR+jI’°˜Lá…æñ² ;Æ%÷ \éãÚ’ž.$œ?Íî¼A(ýø¶b1÷]m Xuºe:\£îf6âSa dŽ*J%‹ ­mX.%œËÒ†¹ØNQ¹ŸËÛ;êÊLäj �P…Û_UÊÆõ[*G!¡‰¸)¾.`…#¿òä+ñÏ TLb›· 7"ñ̬š±yJEõ—Ü©ÅÎòÊ&+:ëH„×ÊI#¥rq2SM•äè*ÿdê³44Ò�!7 û ~Ý®ÿR´%nŠ8|£\„¹,FŠó<œ\à�¸öAL9h#¤šBºíø�ê½…³ˆÆï$àm·&`bam龌Y5úé:ÌgU$.šRT!Þ`uÖZhµ�Ï~<Çü;<ïU‚f?#íoÈûôØ'¹“c¥à§AÕà•EQ/IyªõýUzò1 �’×v•¢täŒÚ ËŸÀ øŒk?Wy«_OéS�Ž[ÞÀ�XÉ%~ÙyêÇ?©–qŠ!¦_�·ÂA=9þ×î0ŒÃƒ¿•ÊÎ#Cc êçüÿ§h7Ö.ÆÀ×i0?ÍT¯W æo%Ýꨢ („É AFï‰$™;ŒP†T3¥$D„ŠÚ ÏgJyé¯<Ñ¥nT®aèE¤ÃÆy}VÕ1høoÖãï'1R|–Û¡©F¹çå«ìíOfÃ9w¨’dÑL4Ø ´{Ž˨Æg¬‡&qØØ8{%Å›Ä0[KýÏ�@��ÇrxôF~bðR –$ɺB³®y#«A¬ÐIy¶–£vMJ%`ÐO€øCúðÕ]ÝòÀ5ööwéi`€<ôGÞ?-Z�ÐQI›*Úüv§H£V?˜×ç/E¼ ñÑ€o¢‹Æ�Û‡Ÿ2¢ ­ûXôbQ¥GÇöÁ*éo{é;¹…eÒ/yúÔòPI²€/.…AØ‚û™zžX3å;pcZ3­óç„.oú¨h6›÷^âKÂÊqT'ö÷¿8³\ÄòV@ ÜE=ÎQªrKëÅ ëàt3�í\�bhÖÄe(‚_Bôì?ÊX™Î�+�ú7†‘ˆµ]O€çÂRF¥]‹Ùg«Ð�G H±à•L‚�Ì�BƒÇÆ•R÷‘¦Z}D£ †S}!D˜XH`°8~¾®h¹›ìÎØ¾86ZªðÐZÇͪG‰è[ç#ˆ—W)Á³\AïñÇ{¢xlAP\2—]Àüì$7}¯os~§­¨ëݨâ‹£R@hNÁüÞš õë�ÈÛØôÿäq< À’³msÊqªƒGÕI//+zÇEèÖ €[~gUˆNì¢IlHí4ú]4Xð¶VðúK”垈·Â&¸V¡õ$ …¼¿êظwõO‡û~Ý�¢nU˜…�°š#Œï>ê$.k® >dúz�#×a†"-4ÐÛ½_ŒcLÀªh·ê,¼vLeyÏ~ÿ{ç]K Àœï*3šZŽ·ãê‡Ìr:§VcéŽ!ÂÆ†-Î'\“DMk«Ý÷¿¶ @òf€“ß§¡�sŸZÈu^ƒÙ±Ä÷í–6k<–©WÏú+›àÉ+÷±¾0z0¹f*£t°?:_ý‹~ЫÁìÈ&S6‘Œdžÿ׫õØ‹ºôÅek&=p»„æ�ÿQVñÅ·¨5#�Õ:ß4‘rÓHr�‹ÑέÝüñ®S¼V)5Œç“dl5BWk+{ʦR2`H»Ôë )uñÔ&V}p^ÿ¥¦PÀ”k=9Üäþ€ÌÌ5~¤F,�²±Hy– ¶"X5_yù™0æÎ[`Óì>?FÜ׿UýÐ÷=\:ì?‰¥¬°Ûs¾;'GuÍXÅà I±:Î7¡Y9@#E̺\–ö}Toox{.ç0¼ùXô2ã¬5¾pœß,ñ/; n•çF¢§ðž•J˜Æ€Rß�'•DzáwyÑŸû”85¬›)ÂɧÅUó¿4wÍ(§Ô=%À]æÏj4µ ~E§r¨}¶_ÿoýl·¨L{7íµTÒƒ±ö _¬çžMnÁoN¡qÃ]3èˆ\ «Hø0 ¨3/v6©Ä{5�ôS:ÏblŠ –Ã_µ¿A„ZÂ,W–çˆå\ × RÅS®»þÏ— `Aæ>ÐnTÓE#€çÕ6ÅÅ?S P0xaj3xïËØ0´ƒ,'CJ@TpU‹pjèk…åÜiCÖOäëpÅ| ºàƒ ®ð€ÈÃsk³�5ý_8½Û°¾óoÅãþD0�¾ýÙÇrCdÐÚ¢ˆa¾ Çù…ê`RÄñiófß›‘úd4¡vßE²¤¡Ã×ÐÜ{ç©�ý�ïêÌõ‘]²ZÈW(ÈEAŠ<9º‚o‚� -CÇ!üëÔ†¢'„B‡(»îÄÔËb†u.ãMƒÒfÓ†>5Ä®œQ8Û*â=*¯˜œîH=½„¨ÌH�ka=5�AGlC0w«j��„\Õ çC“ÓplÉÖ@dÀŽ”¾t$4ǽ!{’¦Úâ“Q¥1F GçrS)Ocý¶<½·^á&w_ãYO\¨t·æšs ‚ôÃÐ ¢ý}#s”Xî²LøŒgÊPD/¤°çóBe 5S-Ì‹q/›±¦ÇîVÇ¢%Êc–¹+>‰G-¬Õ˜oKЋœ•?‹ËÛ6]ÉkVºCeKåa\µ·+”ú¹ËãÛ0½ õ÷�h;`ÿ+^àÊ ¡!þ0ƒê¨$O¼Byî*•[âM¢œb’zºUTVgÉ"=ÿgJ¹{$ÂNÐÂ-Þf6ùVÐQågŠxC¯cêþ«MÆl”ŸŒì` Pï´¿œÜãéšm7H«„²•D?#ö­$°¿ó`9�F³š­Nñ”9 y)øÐk Õ |Ú€ëhƒR[—þ´¿ð2àp‚¾GøX˹âßBdy%Ä‹s/xè&“勌4§¸ÊkùN ñ™èeÆ�Å÷¸ ]_Ü{Rê¦#:‘U6K’K‡-KŽbé^ÜëÛRoÉTÆ"ù¬c’5É×ÝRUagh›û;ºVo—‰ $ XžTœb7$¶9'mP— c¯’"KïŠ3*ø‡œq´�ag’bqt@gÒ¿°ÄÈ4ë¶¾‘‡Õ_ΘZ›º5 /YëúË�‰üü;âÏ=† nÉäö8¯¶>ÐT¯ÈxÝ&B_]ÿt特Üû­åß—(‚»SÀìNÈ“u©}ËI�Q>e[U²åˆ½c7:§"\È®5{=âSýkÝ7&°m(ÔM­®Ô \¿É(á» |™&!,PöírÅ|lîÓ6ê2�|ªç' ±9ED.ìÞL0mzóòÔô( +ͦN­=a2½E—î—½=Ó�G¡ÀuèÉ�õ¡Å˜wdêib3ko'Qüi—‡çLÒTýWÇßêNvTi]²C7@^›\bÀã¥LëHc·Ç-4àÖžʌøbôÉÍž/y­<§4Ö;4\ÒBU´«—à²Sõ–—ÌÒù[&Û‘É…pƒS‹8p´=@¾™ççâÞ æëþ¼2@¤r„ä­I Ù=ØV-½(†á�Ó„5È51§ÈÒÿÎh­r"pÛhºµùzý>ï�ñ*_ÂçvðR ogÙÖî�J+Ž­}Ƚp?¦õ]‘+®eiõ1˜¸ôúñm"öÓ´rzâË=£\ÛÄëÆ^iOd³éjÌÙ.BlW‚ ¨ód­*¹ß);îž3—ÊÌY aœ mò+¼µ"®H^/@Våhzݰ£bÇü¿R7­Y$ÖÅuIÛîrPè—P9÷&|FßUZ"¾P/§.Ì/ §ØR¯@·<Ù3.ÔJÃ(Öñ%hù)…òÞV'€!{Á§7‚byµ„'ôY ÷Xï9�ùŠœg¡0%`:·2—%0>÷±%b=Åkà·ò£‘ÈÞ~�­ÁvG�ï0„°csö=%•ÞŒL.#ž�Þùí$o�I—®kÝ#ÕžŠê¬ÇoÄ@/±ˆ¬0“¾‘Þ@w¤„FùRza%OÛF+ÑÙHÙ³²Ëi¸ø×1™ª@L5’ó•�Hþ M o¦õ“·’ÔXØížý(àSˆÅ‘•ûö×P»$UÙq«¯Œ‚Û—(˜P¤N8­ØOU¬œÉ œ(bþUÿëj¦®Z½]’ÕF3ÑÆç8 Uû¼ošCB ˜wüÀ>OÓ2»a{£ÑœÅl&~èÝœâUANwæê�«yµÂ|F— Dò#à…×J)ZìË lru†|ÛÊšü¢©/žBÔ©ò¸„e�F@*,©·âàÍ“NZ$¾è5°y˜ôˆ"sš ¿Á €v ­~�öÈø� ·¡¹pt0¯…!]ŒÍ®Éø®-Á‘€ÞqûÃ�OmÏêa>Ã4bóúfˆs™.ÿ~ÏUðâ'kdžð®+²M%·¿w«{¼Èôbz¸lÒïÌË8¥·‰eCäM ,3(íî*9/Yg÷UmÛû•·In¼Œ Y´=òð£µT2Í Ï�LÒ"‰ž!¼®ˆ7ó²’žÝUTÁ§/•IAüü#­xtOª7ýø÷¨Ô 0å<Œ¿¤˜¸Wt@Wúé¶4Ï·QñvÙí'Y vÒfN&o)Ø„°ê%ê~Õç†ÝqF1ŠxãLjKHMÅç†mìZ°Œ�o ÌmÔ#ÎoHõ©Ü.]|Ä.­ßÅ器tP�Úã# �þ<7‰NµOVl˜>áw&ˆ~#¼:Õ¾µýò(ºê'¥ìŒq‹çFï�ªnw ¿ Âwòáì=¹Š–ódÜ>4´°–Ò/ "®Aßï4ªÒqÓÙ’«ÇÑz}1£oÛ>É~õ„¾ü�AtÙzFí7¬+®•ïÔµ£s~—¶áx=|}&‘a„ F(‡Öå©mQy/y“‰ýÞuªyå3!Õ5›ûM”Õ§½?=\ö@�ºÚ„‰ZÅRªFà,ÙFi£ãf|Vj e“H6 <¼uŽîÃòyב°~©F®5åòxvÝ^à<Ùuºðr˜ÿ�*Tq­ß'¹8yŠ €Ê[= 9£½ciRÿõn{À£ëð�—Ϙû(Œ÷½‘>#<”FÐŒf³†ìþ¶ñÑlѱX²^ë"ðªÓLz>—x C˜ÛÅÛpñú öä°¨1¶Ã¯×é<³V¦¨<7ö°O;º�=�8ã\ªÔʘ?ÖmÒ.{3…Ul”%´ð£øê;ø~ߎ‡çî´¶SRh0»�ÈÁtëéH—ö]|Üã…~ ¯:0®™¯D«‚ œ@]6N�§™÷RÙçÌIoÁ¼�'+r.Ž„]ãõÆ×òqâ;ŒœÆ6,R³Ûÿ:ñáR»6šÀËšíq‚‘¦Ÿ,à =K½Ïä ½ÖmŠÏäôïÜqâ"Œ1�yCëæå'B„D�äì�ªñWèB;:§ÂöCG@l_N£?`&® ŒÕ svpù£ªwIŒPìÕG}Sÿ�(§ñm© €ˆö¤ÜZEx ÂŠ‚—ó÷Mìtï ¾+¯ß€‰K/eš#Q""cpŽú‚5iàÆU –‘Ö­I|8 Ûø­\ëϪ{›g×@Û{RWUCÆ4‡]œXgú&ùG\ú²œž´2-ñò�ËsÖâ{3b8*0ú¥0€h‰¯ VðWÙ*¨0Vñ†õ=˜ÝÊGÖJ±‡â�‚l‹ –{Jvð¶ûšÎË6®=n¶ßèFâ;¿ýöÝÊ\§T\ça/hGÉSïÐ^é`�çK[.ÂHnåäà—0š}#4Äçîæñ ß~ÉæL‚­žR±AÞ8RœñÐäÅ7 iÁ}æ4ठ­ñº Ç'Õɼ¤ïP’×5 FÃ|n�¬‘qèAÏäNœWn²ŽbëЮiá|¹Ga‡ãV¹ô ç†?ŽW®1ÜP‘çÒÒNåCÊž�Ýg²÷W=f7àUv� Ù©ÌHag2ªH•¤m©¤õOÍSÁÑÅb$Ó«VýÉ™ÕIs¼à˜Ü£íéH¿‚hÌ[–.UÞÎõt4ž"(ßëöj†2�4á׹Ӡȳs•|]“†qŽ¡¨ÎU)¡²`2 ¸û‡ë‰ÍBktŒkšŸtoÜÓ‘“íϰ¦ÜsSäƒv$®Ãn�©¶ ÐôžÕ=k°><èLÖg'íÆM8ÆPwh ¤}-fË‚£¤Tmß’Ïݰõµ%¹ET’[^¿ [ä[©ù“oðõ$uè¾²T!¿MµûhÇAG‰ñ)îŸ0ˆ¯¼Jÿ1Ì9.á«:ÃÌpŸE % �þ«P‘)ü!yæ<ÕÙðIoéa‹. ÏýpŽé›E[¨F¿ 7eB¥º…lëýïù’)þ/# éI ¥±ß›2¤tÔãÙ7"3<Àê9˜¢¯¥4öÔ–<úË©Ž/§%L¬Ã:÷Ýʆ´føëL©eZ�?¿IáµrìøoÒoΠÔ}/Ì>÷"Ùw“· UúØ9wš‹x€w�nÀ*M9´. ýP6gCë£å¿ô §±i @i½¿T!N±B/ Õˆ34ù!of)ÕÔi^O¬Â³t–ø/XO„Bbm‘¶Â¤¼ö˱ÙãØó3…;Ìyh§>Í`+x7…ݬVGé/VÜpyÄ’tTíÛ5…TÕ`+œ¯N»º{çN˜Œä‘]b+s¶¹…p+ð„ɸ½ã½*�<Þ|–¼�6¸¹œYQk~œ)ª¸tÄrØ@Z:ĦüµÌŽ™À µµ•ÁÛîämg–ƒyPaJ' ~„–ËÚ±ÑZ®¬l 3Ú8þU圷¡9¢…½vìÙÜD¸hôBüáhôæV=&å:Þ7o?ލbÉ&üÕ¬H¡¶·0ßeÃZö­�µ Y�4cùA™ÀÙ†áK2sAee+@`;£h臡Àó6g²_­Ïökkå# “˜lÛáŽ.3"% æ3« SèbÔü&«ûŒ�úÆ „éwßIH�Bpœß=]ïñ;ÐÜ,Aì¦Th«y[ØU`kb�þ _,eʼ4…Ë‚¦�IsØ'ŸžF�ʬ‹°ÚŠ€Jg„5ê£è—›ªàš&Ê¥ŠëÛÀÚýG2{@9nWû7p&†Õ²Þ±¼¹”šÊ×fb$E1ÿÈ#ùrP-+Œä`€dõšh¡Û`†œa²fņ‡ºÛ+ØÐgûCj –ë¤Ë%9]©x’�œ\$/ï njcDÐÕYí¿êþ͆Mª{|É}¦�ÒÌ„r㳌©@kÐQ£ú+ߣÝwkë4^Xfd;ÝQ8hP»Ag¡&ŸÎ¿’#Lí‰ò­†—n ÊŸ„RÎOº¼6¨]05“Û>¿?ãþèkQ}ì1�@Ñó7 q‰º%2ËÏNGÿ×Iò�<”jâ—b~Ð\}‰ž€#JUy¥Í_A»tÀ\Â_‰\€R+NÄÔÒêëR šÁíùúŠ ‡a ¤"ÝÎ+¢ù:öžµëÐä¨ç¹ÆÌ’l®vÃF0è`ƪkjb¾|rH´âË}²žÆ ƪ„Z‰#nÉ€þ¿Æ¤ïEÃP õÖðò‡Öû‹•„îMjÏ‚±ÏY�AœÃŒ’Œtv: ‰|œ]z˜±q¡m©éåT×ÍîÀe›É�«v�†\'åxê{RŸyGß¹\xB¨Ç]‰£ß§uÙï:5ÚÊcN… ’Hø‹&iIPÏÉ;:Z�-·¯�(Ë8ÕÐÂælEÊ� Ü~?Ùòà>pb^Øu„#ÑØ8³Gî§£HÛJ]îÂ6Ž—£1�i”ÒB0.iõ¡�á/Y,B@nϽeÖyHC|ïpä<N]�ÆGÊœŸ:^Uå°ç•mã(×µ‚¥¿‰0mØu–-=GÂ+Rne¤&u1ДQ\9îxÕ Žÿàá”?£©ËÒB¼°ïâü¿ëgp~�¿fì²ã«³p*„LÖñZu“‚ìHMg¹3Oý{›þ´»° zþ‚2â�—@G*þ^Äs°L測Å›éÔ ÑÜ�ÆlŠ1à†Xçc¢²;ú{æ…K@•·°@¬‘„‹ƒœ&–²ækXòë?mvN%ºM�nG DgmˆÛ'ÈÇuí�c¯Áê-œZ§}ØxŸSJ8+gkNQP„vþŽ�5%9ij¦|¡Z�™7áÿ +ÜòEãÈÇÖëfÐòÜMM¥:)8½cî_›‰ƒP� Ô7¥Ú 3%omëË[õ£ |þ$m»LBä%þ5‘x9&2¡[Ú¨O°�ú¥Œ7S)ã“@ÇØŽ-}˜|¦xŒís£�¹\[U»M*”é‹@,ߢ‰ßÂý|Þ9mFôq–\Qù]ï‹#k6ÅBîlR„n6׳Á?j1øµd¨0BæÔ¦az®G µßþpç3ØÉ¡S9wÚpDi‡•MÂ,ËôA�ؼ´ÉÓZdbƒz“:²©k´³µñÈx̺ø]áKõú1V]M�ÚÏ|ö§ª4ŒÜ9î{ ÇxÏn�A­¯ŽS¸‹›ê�[ð¶¿´nì&Êürˆ#‘qjbn5êd9ÔôÓ]èoN4´Ížpõú D™òü¶Ì×yȇáÝv¿‰�Á :htÞHñ¹ß0óâT«=|IµÔ#N˜ƒËþ¹­2È(G¯®z�—wãüI?]2ï%ä<1ñ†¿ Û”k�/OšÊ@ dgŠær‡-Æõ¯/ NbN´Ø6?%àüŽºE�ÎŽ,øí´P1øLØ»«6)Š~ŽÚ4 år¥\_ÔOCœ>£Él%yçkº//›òä È0¸3¤÷¿·*nK�öÅ9ÀÍåK]ßHÁÞ‚ø‚ŽÒ4øÁ]í2 ÐCÆ+0È�ðÓ°‘¢sä%"ÌLB«dòÖ†Á¬R=tƒ8Ñõsð޵tû&¦’¾¾ãרFŠ`S·YfÔgÀèñv¹¼f¹‰¢±6)¥²ôÓllÄÒhCÏc¯‚ôõ9BÝÁÉÑÙv2iòû—W¤Äíyf¦å„EON›�ˆ¸àÄ •h={¦žÝ¤4lØ‹·9«ÒnÍN¥¤²v'G‰ d-òmõ+ LÔ"/Jqè^2·Î#­*¾äªpoGÒÒñ$ØPÜx^—õ ³  ‹¡¨CìH�ewÏåT�K¿3&–G È&k^^{nzMJkzÍ?5h"Šþ§ò=LN%û<2#–ÍݯتpH®ÅÔ‹bÝЕ40I*¸ÚÅÆ }ˆÛ&ò")•¹L‡h5`üŽNæ}ü1fË4C¦Çt¿„zµŸ%}Š’¤¬Š�‹*ôt? ó­äxSƒ² ¦ÞE q�sÆm€’ìÙæªM�èôÔ«ã›lÜ�Èjpˆ¿%Zoí=L³ßÇ9šçX4Fˆ�·¼  ý~a ãÐFKOtÚ¡E!šø—ÜŒë·à“Lù$„ûK÷®qW@ƒŠÛ&d÷ዱcëŽF<[ ÐZ@1—ºšOÊÃYK¾ |ú”*QH}rš´ÊmxPDã³u`Íf ÝúR:ݘrSTé ÕôF,ûs³àºà—ñÞ"Fü}˜÷mú‰¬AQ㳯ÐÞþCpÀ`3ùßSõëÆ¦…=ÄlE³»Ç�XBÌu¦£KÒI#a)@ CÇ;4‘Dã“îæÅâ€(©÷J“´¥Íü “Ð `Q‚[õw™¬JòÖ…–øç{H®¨Øiˆ­Þ|ж¥Ÿs¦¯ñ÷Y8ªG¢y5þ‡\‹ËyÄ¿p…×S,•g1òÇ—�¶O;¹è ?½>HÏZ¹UpNÈñô–ÚÙxËXAò{éåuî�÷¤Šë?á&Ö|Þ]fÃv’;ûðÃ�\‹ré¤$½�bY&É”¯›× ¨ó’£~ŒŽ7ºù‚¢Áz«mö=U¡ä é\,WVÑSÇ àj‚w†"ÆÇ“óUØžØ%®û2×X×°Ð27�1!ÿ#€ZN½t+ÂsÔ‡•µÊ˜_±€K½ÆÁ€0­/ÍH«µúÌ¢ÿÿI�½ý”2ä‹;ÙÕ²y¸ ²`lÀ—f'ƒ»<”…Qt]øu?yö"›SŸ¥‘Y'Ö¯&4ÆEÞ»T ¢î­f �vDóÕÊ!iµ”8I ÷ NgÏÅâÂÔ¢v˜j`̶:4;H×|?—z©0R€«îNbQˆm†Ûͪ�Š•õ­?ÜPÔ\o±¤6 –�ˆ�êukkªÎ„“^Ï=NgU½Áóísai³�’ýŠáà&¡pˆ:í¡ÓÍá–ŸÕ³´è´š:Ìßl¨ºŒe5•›þQÁ𫸲([&õbXZp£®ZÓZš^£óÂ?bþN9sЌɥk7T�Ÿ÷:—Ú¢ïœed·o|À‹WW¦êɉ«>¦‚žÐÔî@Ûí9¦ÄI$ >å꤅}`Ï“ª²1ÕÀ�B‹ëk@ã×2l[¼"ãýz½d1�ªçìÅWàï%sª5¸þQøOé`üqvÑÃ;g‘½éLòó‘:ñ”�¯70 g'(‹EÕòÑšö}ÌFõÿÝì®7aÎ!!® Ï9tÉöçû|Çz'&à/Ó 7úÄ#  iÒm-Õna�6EÉDñ÷êâ`Wd IÁ%–V×qØ8¯å·Ž?Îh]ÁÀK™ÚGj^Z´šfz&­ÛÌæƒsàk.1¢Tøe΀kODZÒx âàH}¡óÀì\[ºÌ‡íá¦pæ-.©XˆÊ�¬0qÄ+Iè_w«w®àz(Í…-·ÏhŠ«RY“Y@¢˜È}^W`’Ù¬ó’ oÕã#mêR¶'*y׈űýuA <2ÊXl�ó¾Á¥Òÿ…š'–¦û3÷øö�i.‘•,_œÅ sÙ^Ãðw]&®…?ûdXñÉò-£D€è>'Ç•ÜiDÛßõõ„[‡†¡ÂC•ÇòNж•sØ6�Åy¬27À^Ê=ÙÖ -’ªq%=Ú)™ÑzãüÈ0iÒó˜_Öý«˜ÂܦG¸�Â/’†¸µä®ýR“T„¦Þ‚Ìã''o%°& ° %ݺøn/yßbº¡ÙP[ü0rt´ã�ïÆ>ÜÌs×ù;N—€Ü‚ûBqÈ&Àúî€wMæ‘â„? RŒ+Í©—™_­þV²ÝÜ-äSëÉÄ�¨¹,Oû—·ÝyÇÙ‘  æF¨ö<Ê#Àu|‡ ]%η+_\ó¤Ø \�Õ¢ 8ÓëŒG`˜»�V¯x¥ähZÐiÙ”‚q@C² ¾hÄ5Û%¢Zá +0øÈä?}{TÏòI§n�ÂP„¢­jPSÍû¤2P�¡É’D“Ñ\g†Ê­wi¾ �cÍ’^{±1{? ’˜]Èã±qÙ—¸É�_&ÂüÂSýÄýL\Gh6õŸ¯>£ÁôЉötô•â`Ó8„µÄUGŸ(�)¤ò­ª>Ç®ü¶„—páCå!h‰÷SüÍÊŠ@‡Žç ý»¡ß]Àw sŸt¢1¢ö7-lm$v¦ˆkh¶ßŽZD–-úN¬/­Z¶³åõœV¸ëͰ½¤ýÄ>Lm½–zúb„¯—žTjŒSwèå4 áѸ•‡"ÒqéYñ~Ü�LÐÞ—øa´T†}òÍŦSÇ®é—ÉÒW4¼­,ÊCKh4ê›\Ûû=à$6 Û{¡¡þÇk" mÉ­çG�!ÝÁ®¡Óà: %Os«?*÷ƒ´i�dŒÙ̸wWÙü2Ù£}v aÀYªŽWÿŠ›÷Q‘ŒâÂQN„x[ŽJÀXÑÍ>ý’j"XYKCjãÛI®IΦ¸àU©ŒL¡èf>zZȽ yg½œ�ÒzŒ¨ß8ë€É%»©VøwzzÓOPZzëÒ åJ×Kä-&¨£ùc�Mþcéœ=é r¢ :Å¥…V UsîCâ¢sêÛÏç´§úmõÇ3þ‹8àûQ2ÜUÊ™€S«àm¯1W@±CAU¹ê�æ¹^˜ã‰ !`5*Ó"¿W« „vB[îNìg~~™s¢–åjâéTä­ËF€Æ¡¿(˜X÷רã)Â>�<�)yD—çéäCìs¹S€£èTã¡×�;@«“=Þ#ju�Bð<3kã±b™.‘¦­ç统Jün™i0«‚L9®„°åÏr˜ùFŠ=›ãàwð’fಀ°ºÝMÄ�ªVÆš*\>û÷ñ9¬º¾c]óìV ÿ±ê}-îè·ÒÔÅ{󫉀ÚÄÒ¿=äš©§SdmMjÔÕ-‘)û?Ú{ŒH¿]”�MaÑ’jÜFhùñ7”÷¢)—¿ á’Gh3¹®óVÛ¹7ùJ/¿{ߌå%ÃIAy*³wß‘W_«ã&ˆ`D¾‰í8uƒ“ò &�è©’eüçäÌ€_â‰ýË57AöyK'à¦ý%µJL\×ñÈ´´õª:¯°ùÐå®ê$¿ :c “•Ölê°qŒÑh‚¿<¯x„&«õ¤îJÍ„ 팸ÞV+´Éð»™…¿í*óë犃ÈÓ¿®"¿ÝˆÊ)ÒØl!&ðÚæÆXV)³<ôó`@F]H�ìe¼ù¸NZn¬Ç$6˺ c¨µ4ÚMϤ>®+³Q-¡Öö«Ò¶¿Ö_P<ÝŽŠ�CÉ¢Á+?q‡øö[ÁO¿² TÅœðfô Œ9Ï+Ò \x澈yn.Æ…GúO'Æ­(iÜ®©˜‘iÀaÝJ¹H[ùàf»—·Z>ã•v(z=­æ á±2ö$%RP³û¹S¦Š…ùæÙ³,çe°%_-Àt5Tï†Õ° O³I‰“çà)ÿj›UC·’¸Y/�jh÷+^û\µ¢l;e‘6ñ%1‹•’ц0õÛ*¶Íœš7ä«Wyšû‘¯Bê{„6‰`0à|±ÓÚdéøÿ^EM�aÿéeý‘Ó�åª/©â =Ù7‡÷‘MV0#p<•Á<Üô[Ò\„CÚyómëÅ”6» Øw]»9”u YhTKÿ(%öf¬\ÒÅKáÄpj_ÌmÏ(v†­ÃÖ×KˆÜO¥kTëàžaxÅ^Ì ïËŒKÖ¥Zÿ"[([t‘EÀ_*? ðk�¡(Ë<Áñj�,¿cÕÑôѾs<¸«ÜáÌ’HåTçcÂ_Zµwf3PqÔÙ3Ç™ÐÑý÷vóÈíÇK ßíž×ß›¬ü ²|ÆJrî~ ŽrÑí0 šHÛ�v·‚AÃ^Ò¡§H ÞÀs¬ñP›yû4 i6„ï§c`ø€3g6èg•ÛÜu§¾ò£ûœGâiê°ŸkX ®ÿ§àz=£§õ°É)1z¢ÒóÝê,ËêÏßAÓýBö\B´ÆIí¢âéWðiE°Õó†T›vKÂÝ´¹ŽQª+—áÀ)Æõ˜æÈÅŒÏ$R9ü&E�ãŽEþ5¦¼Æ‰ÕõÓ_ð°Ã\¸;t’ÖÉÉš¾·ª~lø�¿üÔ*iåN�„7]þjíí'´›îº³«ÛØÓnŽQÚ¾ªP( \Iñ"åpFÏÝMÐä+(j— æ5ùÏexê>+7–pÈ õ†Ñ6ì�Yîù4¿c“bx®¢Nñ�B.šJj’pA²î4¤ü†f16È›—¤òL¿Ûÿ¨âÑ‚,7çÀÁ MpC ×J8 |�¬û*oYŠÒAOnVoã®&x¯Ì¨�›PçÓ‚5UöÓ¡q¸‹Óï� c~}Ío:ïJº]ÓŠS7X2Bå&+„2–'û QÉX²?üfVµ·ËDŠüÿøº5ºr׃ø}êáÃðÜ8FAöŒœºNÜ0ðOuê#�^±û¹msH­ "ÓcFÅš-­c¥zHËÊ8+X4Ét­z&ø=†™ˆ‚jÃMZVEÅ rë£-#°è¶ó/Hg€›.‰Âa@±žM†ÎòñITXÉ—n?÷ƒÀ”S¦ÚÕ¼Æ�!¬ ¸a�]o)eya“÷?XYIaGž«7ÄÞÎ?Å–¬‘&¥•XãŠÅ¬¥ßX‰4ë¦Õzäë\g…1ñŸ”ãµyÁͦŽôZ7íMÊaƒ 3¡âûâ€ûž‚LŸ]IôM#ž©M6i7 #¥¹g©�Ì®�ÚSTD¤¢:¡Ìû$Wðtå<œZˆ¢Ùt 8õg1p^ʱÒïãº+~(ùd§à³Ä.{î›ÏäWÑ5�Žý¾óåÇ•zêaúß ¶0Ã3ùrk$Mg/‡$æ]è_×€p½°šÊ"¡àî ûil4äüK¾&ï•+*M; iÊ5æ0¥OL|¥x"/uÌamï<&CüÈcAìb—2ˆÏôÛ�ýZ„í}=×@ôÖÝÒÇè’ke«êâgGÿ“3µÛóUjÊY|ʨFšâR�¥a²ìן¬ó¯ÔjP¯shù©n—KØPl{¤îº ö¤…~~ü‹›ƒ¦ÿ>pŽ¥ýüø¼í{&å6-°r²Sº»…�ðl¢ò7s%CÈoPº<~.mNµAR)eÇ÷XèÁhšå kìZ|"Ð65¯%ßÞ}=ûZQªÉ…(šü¦�aèg$I_ÿøÌ×ñ–>)TßI¶ùÕ_Íß'½¨ýÁš ÇGùj¤�Üd`ÖüãP¡£’¬ ‹æ^ÇàŠ¼ fÇ[&çô~KUÜhfùd8èÞPf\Jqý—×]‹(w®ìíÛGb … ¹$3¥å£ÞÁ, K6Df@žm»ºŸôÅŽ´´íO_QÌžÿ©ÞÅ@„Ö=' ®üEmѽĩ2ñV&�,{U h›IÍ'ÇË.ºMø,W„Q¬´t~ðü'GgäHžØNR¦*Ë Eu[=”èJ"Ð9øZ½*�ŸRkÖÝŸw²O?¿ùˆÕ(Â߀ XÏÜl,FÁS}Šö9r&~Žîû½b‹„©{ÆÄÃËk2|&f+ÑD©F9Ï Í‹ìP¾)raó™' Üq¯ˆ;˽B :‰ ’Z&Ÿ÷žEx:ŠÀÐogQ æ¨Ùþ�`m( õNŸ–‘jÌ©º³ƒ_Ð+Ü(-ëd:À¡]kq U�á’#m-ÕRЉg¨0Ø „°¿âŠžwº•©‰s~FÁZûc俸ùy�x«ÀNÖ�àN‘Ñ–š+¦[’Äp DþàJÝ}ç7ˆ‹8¾½[©“Ç&+6�%<ˆÑÝ‹þ‹{™)tî\iÇóõ±+ÓÖ¤üOçÖz´àÓ‰)¤Ú9µá_ƹ<Þa¡ÿWGÈ%2àÜWš)}]}a3)-‚´,d òa˰Þ躯œ�„åêÉ™+•„«×âmÆ›5`“µ�½Э0ØÑBésÊvñ©™–°tM¾žéT¦Þbâ ‹´ E8MÊ%­åzð�êÃàì@ÏyæËè°½j&"mÁ=ïØÃc÷Â&›TaI^©[lú¢P c± ªˆ°¢•cYQ~‰ÁÛfFFƒÖ±ëfÉ_&õ™Ð¤2èc–9²^¾Õº$“÷P@[¿©hFÔË\ÕiòYõœ!£åEcÅ'°k,ÏYá�ú*ÑXý ×ÿ<òÐøÔüüÛÙ“½CµwxN?½«•¶U2DñöÊvÓе­î’#g¥ú£fΧÃ4›Hô™\óÍ ÚÌŸ¬†’åæ4«ýÍ(HðŠiòÖ3qªÇR§�s(xÿžð0#[yØ ›È@ˆ1øé,Kµ:>k°c®ÇWXS�ZŒUœ¼zßO¿Ì;°ý3“`àÙ,´Ÿþ‡ÿ0<Æo³×»#Ís…�‹ö$õe0Ñ Å; MzˆÙý×Í&a ´Üå ó7Q\™ë­QÍ^G¥­ÓZÄÇ+/-Ô�Q;΄íëÓ/²�C=ja,ãÜV�Þÿs“Ú¼‡ ô'o�¬O([»Ý‘ :©reÑõ*¶:^P°³¸Ž’WÅMi'€ý—‰àÌ4*¿AßZ ‚Å„b¿ÿýfN9æ¡ÛbˆdcX��ÍPªõÌÀp±Ù/¦˜®ÌÎlm‹Ò¥„æCºhU¬ÖsÃ�B¦îm³�º‚m¶¤!ÉžúåêÎ}Â!ÿVÈ<õà‚Ù§žNê{}BÉžQ} ŸõKEO3Ò[ÅÙÝ·›År•ð<‰êWBö$•u?š)’ôì£,þJ ˆ€Y @о<òËoןS¶hÒ�oûòå1ª·XÏ/^2Êës‚’ªj)»ZÌž�b¦oÙ_ó|ûUG›“Ò–21Öà ޴0ªeS!„TÈ;òV!9·�Ÿj�B²']Qõ,ûbs|09G¹¤ÉiØv” 榻>p-RC""Ü$  Êuá„7hš�ƒö®9�aC4öý`ò ÿ?ÜÖ’Hw¬:I¸ëd[6Ñ�4ív÷±‡þ±ŽÐ"\ØÁÖú†ípVÃÊUMÑøzäµÏWò¥Ätû‹ßüSÙVx"Þ”R¬¯¥d2Y Šú¨„7”bæ¼rl�±t,C?!Te¦.GÎà§ï²^cçµ ¿åæÉýÞš{úÏz>Éýn¨nf7ð¬Øreš°ý ôÎ#ëÆ<¸ùÁøÛÀTýká4Nð¶ÞW)w wEB�u ÕÖiZœk–ŸüäÎ3ÙËÖû~ªµ„E;T’Q‰S›+iì Û%}òFMÑeŒ{¼åUb@P…oNÿ·÷f¢#> –[„{<»[‘ª×2Œf R¼š¶ö«" >òux€‡P�~Í£LQ„­ò¿É»ä.øöJQQ`ÃC¯‰Âå<ánäâÃÄÍÛ…}nôhâÖ’+@ú(¨•-]Þ:ìTÆß ú2c•ú»Ž”zHõ‚dò-|‡ûEô%äcB­�ÒÊóç““]%ÃæXõ Á5xÎÁ�%ÖÍ«ÂÄG{g”ÉkjÂÊ ÌŸ7Êì@öÑH1>}¸­3C›Wݽ肅W¼Î§§>Ôàb»¬:aŸNC“Ò/Êð~xP¬¶mýÑ-¡8ÔÖE£ Þ&6»¥\P†³Û(KCãä/¹â+µ}±e+&ôõk)wöFØn”w¬É&Júw¤–ÿ¡Å"“©™$wµó=Qi\\cû"Qeëw]±‘y�MŒCÕ2_]\¸ 'ã¼ðê(þ‹aЬ¥ƒžT ?úJ.wà€²‹Æ¾ÔÙƒ×íómeÓ6‚¬+y‘v‚Çø*dÏÓœ-ð)¥¼{�ñ}N¹E"Lÿ°ª²”ÌNÙ±ÄïæÈ¥ÿ–š hVÖ×.�o(ýí¿ËÕc½:uÚjèv¡¼ÉMz8¶Ö ' :€½ò굉¬òùëšžxZß·¹¥³ÝàÉøøÎ\Æ*¢Aøñ°¤ç?Ô}¢ò—͈¡Þõh­ôåæ·¿l )D5“%¯¢­åÙɈ ØãÃ�Ò÷¼‹X:ç»Il’÷]îØ ÚP×l‚¢�«±aSõ±A<­Ç­Jì·SÁ%Ç:�|½8¦9šâ—½¡äQ¸M«Ö)“†ê©”ðœug@X†¾dË2† PŠùÒbÇp7ÎÑ„¦Ê¹á9vxWj‘€½·€pŽûÓ}C%ŒžaÔD�8î)y:ÝP#n<Ÿª+;œŽ‡5¾#“¡±ð¯h6‘þ�äõ1 Â>(mÕ–²1Q9šÖ”ç{Ê[‰Â9˜ñš57£Öèñ]«”Xœƒ/AŽG5í—ê'C˜¸:<<ÝY\COX‚ž² òÖ5‡«¬¾êÖ¨nWÊ(|¾²Iúá™[íߛВ\Ã4zaDBµpëi:™–l 8!ëÙµáÅ+ês Í¨�Ä÷(ŸN¾àbnÜ‘6˜5ýÓ»¬_x»Œ1N™Ê|úmÿùó†·ë~|~îTÒ/ ¿0�1AF¨5ãRæM Rƒ| Ç–¨’§á]ùÁ¸ÀDuID|ºÅ4F½¹+7~Ê«[·ƒHYÙ­üøŒ¼Xg 켎+àæ‰8ìþÊW\rÇ^=i¿$ð±ÕæƒiÊÁƒÄ”ËY€rØÐ8Á¼,èÊE]É_XÃt!�ÿ÷�î “sZ´äoeÛ–9x—D@Éó¶ aÂ_Ñ¥8A>TÞ[a; -Lïò,&¦µ­ßÆ !Cêòõû¶‘…¡uqQa¯V÷Ç«‹Ç>iž !c�veØkcŸí.PT3â?ÛùSñ©}IX™ΗØÚÏŒƒ)£ºdIƒP³Ëö‰íâb äffûÍÑn’=´G08JËèšì·“쨇¢õÄÕrEæ¹ ÐÂêñ�X0sÑ0f¢o,ÒÕî>’ШTÁV¨.µ‘o@¡ 9�ýM)•¨•–H{ä#ÀVÿãÉÄáŽÝWí|ï«^iuáo×RI郥šLuve-Î;n#ÎÈ4O�(†~û³,mMðŽïýWb qÛ#Ekü†3“cDyxy±€\3œB1›PLiR9MÖ½… |Aã¾Hß’¹½ã@¾¹dsrˆKü|†}MâK•\œcúxÙ&à*ŽéÙë!PcD�ûÂ…;œ¡AäÞRDfð¤ô¾Y€ï?mu‹Ì�ÇW ð ?N•£uÄX‹AJPöCF^Òí|.W…¢†39ÕªŒŠ'Ž8?ë§…Êפ1Údk“òäÛ¤sfÛ‹l_R!y؆ŠïŽö”† ³†u­ m˜7 hûºš%³g¼µLa ïúK¬´M¯ªÙÍm1FZª_›uÆùŒ rì^¾M|‚wR Ì;�_Êfu¹GÄQ\~b‚´VW”KD‹$§Å©Í�ïúUÖˆc<îXúoðš¦Ó“Aü%âffŸ¾NÖš÷ÄðÍÑŒm¶oqßÀ+ý âã;í\¯h@QÌwͽ;M‹2±ãæÃ¨Å8§b>M{¤9g®Þ)‚; ‹¾ð7Mí˜ßÚGs¹«0ŒË)nVDŸD7_j Éø9½¹ð§±¤0zy€£%D?¸x9Rƒn³Â]|¬íOMsF)mß–Ãô /ønt ð‚E-”³6�Õ"Ñêgè¡Ý<'ÔôSÕA=¥ÊÀϨE »A\I²­µ�¯îH¡$oz3ô{ݹ²¡°)]¦Ð9'{Œé¥ÄYæß; ñí©>ÞùžÃ#Þ…–¼ÔˆÆ¯8NKCËè)ÀFÉ´°@�=Yaé/‡õ=eÆõ_ä Ȩ¥#¸’i8šL>÷xm±Rç²ûÄîôñö¯£�ÛĪܮ ÇX ë1Ê"l_=Ž6Vš‰Ü þ6S¨iÀ��øòr\}k‹iŠ5,hÞ:#ÝVŒWÑý3üŠü|v`ƒ‘*#Éרöm=‘žØõ±Òͬ·Ã#9é�ÇUâº�>OÈ™i4lçÕŒ™ÑR'·‡.1ýߎò3ÐTOîZ ¨Q¯B kîwhyqK¼¡À$C„ÈoZë Dì 0jâçÐeŠ)æ7_.G‚š/-ƒïò9±þ¿O½²Ìp¤ºÒ®.›º¾•Ûg¸³•¬¤Ø¶wÔx­ë>(3†ïèÖ{¯A0c£5½•ߠ愆mp¿òÉ _ ¶ˆùd�Ërç ÙªqÙ-Ÿ{<ôö >$ø"‚�µÆ½ÂÓ[iŠ`œÏX†�÷\*„xçS\8˜ÇÁó>‡&>$ÙÙÕ�­ò\øîÛñd\Á¨&'b‚‘å¢Yošuùùö³4#¿ãÞ–`þf6³ø…Zn¥ 2†Š:› ‹~œ«HŒLµ 7„‹4†mw¥)oÎ’ÿpÓ>�pÎyƒ�¢˜“¥¢ $ø)ž]+­5­Fd&^æ/ìrÌ Ž4qÐfÙªFKF¢zÝQ\ÿ5·ÖP·æ\_ˆîw”³Oúà dϾì}ckjs¾ˆútöÕ}H�7sCH Ó•£0^‡Ø}(à&`ë·§ªÆèt¢•Ú·PêÓaf²�öþâ… ¢µ¬tãZWôöï�Þ„“Ž• íùf¾ié¬:€aWÙ1œ¨ŽÚÏ_…4€,*§¦Ë‰‘ŒÇï†>.wf= œãÊHUÓý&ºåƒe¿@ÕrtuŽÝo…�Ôæ:¹NdK!«jÏþêënÚlàB�÷ ìþÕuþYß׊ «À-{|dD,²hݲÀ‹÷ËõŽ µG-j]U›ËÖBc~ˆ@â,_}ÍÎliD5¿¿á¾²ïôv pjd_´‘_轳é ö û½:/9ì–r Cì&TáÿJ>�åºrPÃÔ“îÒ±¥ÿIþ6·M+>QJbóâ=çz€@Œ½VÃZù˜Ei�®µö­>˜—ÂEÖ0+eª³%ù“Û+4Ul±ƒå&3NÞ»Iûî¹ÇúÔ;%¯oÈÙðÛ o6âÕ/gµgH©([œr¨­4ÿÑŒ¦e£ 'w竆“L>¼6ÂcŒ(í¨(»$)PCÄ»þ²ÖÍ¿©Ø¤Åÿƒ+7²ò+ŸœS”íÄŸA x“à{=éHœZòè±*x¡d�3‰w¶D‹³VT÷¼¥hñÃ…&_ÌñÉ—…cÖíë›´àC øÏÂzd«À–%¼ÅZ/&7Ì5ù†¦Ï+Š‹Tÿ½»¾j*“ß%‡Ûå”íƒñè•h´'+1 ® 3Y<:J:ˆ�Î@Bù~ÿŽ®¤s£íz¬D«å‹K«Ž„ ±øA¦ìâ ŠPð•!XúphñgÆm-ê¨q¸#œú€§v�¢Éz°`R3ÄÈ,…"ë 8ÑöЊ{Ø$IñØ–— $24‡ý£,axËï¦$Èÿ3¼y÷k"ôåõß F\K,ô‚*õÀÜâ.Õ¸\ë Ì�:Dê[àzPaçiÆnÝä$ê»Âz^Þè['k7}ga†p´»8CeiAiøŽÎ‚áÈp¿}�u¯Gl½?V«ô,Ž®w¯2¬Í=®½¥iµo…a*öÈ/; á…§lÌBííÁh[R½1õŽë#×ÝýÊN£‹Qɱ‘ ZÉWc¦S·Æ2e¹CÍoŒþ¯ñRw«Åñr·Â»4®P˜%,e%£½FðéþÃpñ#EJ ûhÖÜ‚×Úc:àŸˆ,ÚNlJRÇnß^"RUÚÚÜ8%O¢<)»¤‡yòšØÅƒèX&¿g-ŸvbCó…lùX\@׌£\fãR]\ø.˜�Ï-¹Y'‡­®¡Œ¥#=¬v+ꌺÛæ,Ë~ËÝ –ßVþDÏáàÄ¥¡Ì‡ûÁK?/ÄÀλN)°Ê³:¹Ðdz˜�Ú®ÆÑ@oξq�åçYØ3ßÅ÷f¯¸n ™©±v¹­¨<®åÚÒ?{·¬Ø` ·›Dj¨H³Ç¾×]ˆPÎÒ«R"pæŠ,'€»áÑbçëtrÃòPx‘è7#>Ç &Aáüô”Ï÷a¡âôGÚ쪫2ðÛ±w9”Ú`¬î‡OÔ¸ã�šÂÝG RL"Bk½ÃÀsªÈúDTo—e(©[ãIV”P¶œõâ´"·wµUõ»�ÅÜ£ršáþ-6ã-E[×+k�ªn•ð…®$$ºJq³ÆÅ.ż§Ñ¦ ¢P±ÁUæaÛ=ŸKÖDWJêâƒ)2Êy À ]³_—ù–|.Ô!Ù¥‰¸C˹¤î)0óïE<Óûý¾ƒÓýÀpA©3éo£êzå|>{0­XÜ<¨eÆß©yÅŸÈš ¸£š5&/»ÏW”¢±Ð{5î ÆGa[Mìn…el–ÃS(�îV`îa�L¬Ú€ #¾¥G·�¿šð `�½ÒÛ6ƒ„™o»=B_ ɹļ��N…*”W�\J_…Æݺ£^Øv�êo"È S¯‚M¨É±à8KµÌ[Ž•ªk­^QÀwÆ{X‰t>xè{lŸ�YžaºÖÐ×QpÉç gŒƒ´–؈A=·Ã~h{ÝDg`Á¼÷2ßôKáöÿXJ4= 2©N}°q˯Rt /0å½6Qì•W}èH`O)H«I#¬ðdª£Šö³QXMCüÆGÅä|}†TÄ™Uä—~gcH\QµñéÈN$$�ŤÈËal°ÙHù©£Iš¢…q>#ñ\uòÎï;¦‘@;¼Ë¬y²žòLsôÑ÷qŠ—ëj;é(ÁŸ\{Zü#IAÑx2ª Hˆ½FtÛü>TÎVË„¿•3ž²ó>¹Æ{HƒBíj¶§:íáÙì®TÜÚßÛÙ½¢e™�Ù^žÄȃ~�%º�eaÌÄsúà ŠNÇ‘i#Ð61ätª½^cV¶bS©‡‚þ]CHöwZ4c–k´zºñãÆlô|õà5!}8"<§tnÆSçv/&{*�g™bnÁƒ‹ÎŸšzò¿0lai‘x+«Àµœ¥þ¡ˆ,çdß²Tn¬N[Ì©¦u5h2û3 õˆ2yøªÊµo`ħ ;Su—Äáµ´¥õ7\9ÿܲѳßOj æ3jr¬LŽÑ¶òm Q`?’Ðcž?å½O¥‰°Ò~�·$•û¹vHâ±~Yé‘õg~’?#©Ü_e¡%Îqñ©àMxp�ŽÞ—÷Ëþ9TPwcÕ³FX3–ôÀB¡±³+[!f•àŸ.R.M­¤Œ•ïRÍ º �²TÍöÎmÏ/ÊûX­¬Zá8.gÐôõØþ¶dÙ=tÝqd(®":�”‘óÕ’ƒÑôe?cîŽFdé;[ ©K;ä�‡qíÍy–Óà÷&ŇååÎcÕ•êøÆpκqc¼T,ÀwCY1ñÁÕÖHli´ÔÖ2&ÿ>;@²éxidÚ�ñIgOjc€ô<@÷XýK¬‹nø]4y‰½£�‡½ƒ žH×°XÔq‚ cqüšãÚ¡šî‡àÞþ–AQúfÌkXÍ*d¡~'vÝ©œ‹Rß^Iùâ@¼„fœ½$ &Š@åç§üg·oÞï\/¹¾¡Ú �ŸÚz²aÈA!³•Þ™lü”ǧO!MhkÁœu­x‚�H ¹CO„´_©Ò�Ý•bt�,g] cWÒÈÇrÌ3Û$Ló­-ë TÛÚ9�å IœGøνC¥ —ƒPç¥o¿—&ž`O ôøš‚ÇI]ž]RÐ`Œ÷/ž0‚Æ!¦0™Âñ§ë¡D;¸6%ȆK²søÐy~Œ(hV‰ZSPfS»�ÊÇž?$@­Y —èqÍÒ"°J�wU{ˆX¶uÆpÊò`~Ú†ó+.{P@¢ÙB¶Mûl‡R"™+öL »¿ìBÐ'&eT^{~¶§ÎË>ñ�¦ÛLÏÀœ6Û½„¥¸§n ¿–ñV¥K‹QªÇDãçDöò-åM(d6ÝÒbGl‰CòƒFµ6ëDûô�þ7À}ç’¢åXLí’võÿ'’Ïúh"òÐXJÈÿ§å`bÌ6¸3|§0öï®Â öDÇ · é(Ú¨IÆê²öå/È�‡t²¹zÈ ÚM¯ò@c'œl`àV뛊ª7<9QúU˜5ûl ðß�Ú‘0G )E?Ñúä¹½]bm³Ç!åÚYrе�4«‡3–7Ûh?ÚóC$pkßy�û œÉ¦¬bå,r&< Ü.e‹M©gº¡¦N  eeX„= OÃå$â�›ÃÔ[Cj½ù‹{ÆÕâ!h8Œ»un”ÎbJ¬±}¦C¥Nš.=�N9_Æûdr¿áç2²ßx¹~ }$ÛØ;2dÆ?e¡ˆR Í ã@5L,+î9�ý&=Z±‘'�/WíG –!`ówpL#±åU‡È J¹hËVwHî¢�ƒ�¤~ã—á¼Íî»÷G¼O4~ʆÞl׳E^É…8(ÂÄ<—,ÞEˆž®‹HSí1àÒ0ì0&ˆE»ª9Þ@ãò½¶ ÙGùV×zh[“ÌsôÓÊðBbJМþ•&e;;.úÕT–—^/f›¡*.c°%ÂtÇ'·Q‚z&LÁ‚²šh�YzäçãhßaÜ¥­Ê|ß÷À‰Me.#· sühÜPÜ�E5�és–âL¯ñÎoÔM„ù›Û³Ò¾‘¨,…ß(.Ô¾F9¿ËHñ²=Ψ¸,Å©vAê¯ „=+”¿j@�Šiçã€CLè|?Á¡witj�•‡VA*“�“Ü’LqŒð›�U­ðŒâÍ, û|<¼dÞ~}|I\žêMVy,4ugÒœ„Œâùª.†º*GS‰!.§tÀZúEÇ´·ñ³?YTrÂWq^¶ïú³Ú=AŒ§žŸB˜ÃÙÍbZƒ÷8-Nh�摮ɬ+IMh£þ½ú'ÅÛVI•ŠŸ°eñ]–Ü—+?Îlgu¶}"û·Öã™N‡åÜÑä–‘R3éZ\§sqôW “¿Ÿ÷2\O }%äOÁÎR+àié[ãCn2¤0ý‘€i U6sƒLŒ�°ž‰àKÒ6S8™»n%£¼�­— ?<Ùø^²JéÀ“�ŠÃK‘&xe_ó»räêK9ÚøòÈ(Œ6›»³mö3º˜Èú&-RェÇFÙ½¬¥Hoå'R_XÜ „/-BÝŸßÑÁB ÍXgCn™‰æ]½UþìÜRþ8v>#Þ’¯èê, )Q|‹N,§ôïÍø¬Xí4I*±úãú¼ú¦ùí·Sœ°j$ÿ³‘ýb‰*³�L) X¨²¥×·2{3g‰#„ÅòY $0ز–’‹ ã7nŠl“´Ë%�E¸Ó¯``)x%Ißú€¡,¨B‚ ÈÿS÷臜§5ôñ¾dÈ©?QqØãÌVJ¬?zcç$5)¼â®ÎÑû„ÈJ:Øe·ÿÒª²BS:isÃ.G§3¾q�·�Ãe„'p²�XôùRõ_�»�3ÚB7� ˆÆpž‹gj;âfóúíÝ¤ß Òp®ÚÚ…÷õWÏÕÃ3«©²Z�™¥‘3[ä`¸ÑF“ð)|‡ ØY"BgÔ"Ê?À­·«q3A@[ ÞÂñQ…½ ;Ó¬m‹q×Iíz,sm¼Æ„rMòo5¯Ë²˜Ì”€[ Ôa�Mâ% �ú/éÓ¦…AÃyV“ÛKbD7s[ 1_Û±U þª ÖÅ)�üà!a>ÃéÖcz 0ršSòÃ[vý üùv=`—>Ùpch¹ÑHl� HørÌãwK¯i„°ûN(º|%¸+ +BÌ ùT—ææñÃf0ýg󭤟ä.¡sÒ«ósc½òož¿ñL7­‹X'½9íÎ,„¬pæÉ�k IE2üc ÖÒ5-aæÆåx�wði²6Ó—Û»ÛãyºÐ‡ŠÀéš"Ô5íßÑî9õm�ÐGÍćw�1iý^lrÙˆtÄYÙ¤^œtgŠê'¤Vë;�þúx�Â4yÏlvÛ¿Ûá!dj©Ë}IÑxj S-_¸»&@ˆõ# ư…"øõ¥ië~�äô` Ýê#ÃZûÀ^¤ÂN b­#âŽÙš�Fgr£5â *¤Uæ€À3_䦯ÁÐë,»}¦.hùY4‰ °ñÔÄMç8íÏÎåtDYÐïŒI8šM>ã�Ä}�Õì­hÄ]™¾ÛEíáuS@¼·SÖW ¤]@÷�quNàŸâd£uj¹·Ðà4åoQª?€Ð Åéì?émm°ü̃| K?¬™Šø3Xº�´¹v²Eº`ë¯Ä൙ ÔzdulÆÝ­.¶Ö™)ÄåÂðò€í™]ƒÇÖö£ØòšŠJXÙ©ÈSÀ%XA²\äc˦”p™��µb÷°Õ 9½Ò|nlÞ€^u Z¤æú3ú>A¹fìp�r‡»7D“â±éÖͺãrÖUKÈ—d0g CN#ïÏWÓy�úž�õYØÔ"_©z˜áËM|_(6 9´wdVÉELâØ".*ª|½Æx“ÍNâ8_a^"ú柒=xx œÿÞpéžÜÈ' >å3YàŽ'g®=øiPª='X°ô]´u–]�#ÂX¶Ùª=¨»_yoÙ¶i ˆ(yIi¦íPÈwÒíÿ®]‚dGÄó~9GWŠV3Ù½tà§¿™Pk¥b´:Û=¦ùQüËþRãúÃ~4•Q¶ÊõRcjùÛèìÁa>i7m涃gzú„÷âËü¯‰´üf¡¼ÚxòKRNY«¯½+º% aEi�ªð'ªZåÜ�0g,œ«yjÙ/ƒÚ~s3|WèoÜ�-ûƒûmi�²\Ïw7Ë�ËP4fÚt€,1s‚WD¥B·í’z´©Ë k­ø@ZÌ’ í;«¨è^G‚8-‹™ºr÷¯¥»‘>;£@´lhúGüܬ|â«t© P¯Ò|Þì1¨ä8¹&ß[ } ²fTœ‰²h« ÁÖ£Þ/Eè=ohÉ_@ŸÁw>úw^þŸÔÞðù\�W?(³¬F¥MkX +b¸B$-3’ðZ Ý>â÷jþˆ®.9~VÌÕ›ï~ƒhË´H¨Bòb%¥8ÖÙ”IŨ\�Åb€Ö ¼�ù†IíBÑ4’vzï8ø“SFñ«…<ÂÚ?†Æ»®•rýÈoµ ^UÖÅ�P¸B¼ ¨Å’õ\š¨Ÿ„·…¦È9ž¾h åc"3Ʋ Rÿ¾Þ#˳Hñså0j`îCz'Œ†îY¨KÑ,­tºÕ;O�PÂÂÓÒÝ0—ßâí>?Cš:. s,¢³^ò‘ò’ì]ZY¤ijanœ�S–=¾ãŒ¦i€MÜC >„êO«rU”iä ¦Ãûg†z±»Räz Ò´}nœÛÉsK&RÛ£a¯¬‚R×m<ÁgK€]Ù³Q’i$inXÆÙ?X‹üŠ_¨ ’´œš#. çðóÎÜÂ)RÌ÷‡y¢(¬M¨â2ä¼{èÁ=s£: SWÆ1>šPz¼Yë í¬6�1è8ÜU÷Ž´¬@ à‘V{ô$ù³¨Û\¿8œ\üjËX‹¸WTŒˆ5*%³ ž6mŠll c’¤T²�õ!5™!U•Xô].‘XR$å!¬ëö¦G=iý“’ªÜ!4܃/ÀÓT&öeÏ$–¸×ÏÛIÒ†�>ɧ²y#¯ y™7ÚÓW~ªÓŸÉµPÉ2g¨é.Ë|�4‡NS—¦€^ûí®ÔßpØBC;Œ†„‰ä‚– XèÛl¨{EææTR5Ûècwe°jäÒ3éz;8†VÝøì:7úê¯+†N3õè˵<÷98¼éòÂ�·‚R`�Ô&>ÝÕOº^O¦mÀQ)éfÇßWç'*ˆi¥üãÕkŒ¿A¸X-ʘZáν�zìµ×�´dó°ÀôßT[½•Íä‰Pý³Á¨ØQSÉÉAÂðª­n‚!yq¬Ý‚C¦‚–¸ 휇,iv¨¾·cÛ aäF9&Îú”ÊGi¥ÍJ"eü Òyû%°±c”Á  óî_øí»éÈy膯˜wwe`ýÑ‘…< €[‘‡Ñì-Tû¯¯"¹fsì#mk<y¬çõN´;¾š:›Ù‹ŸJ:Ì‹pÿ1F¡x€¤  §ø�XMyút>L0æF}Á=­þ¹Ús`ðæ¡‘©l,á&ÇI”VÛÆ ð¦WM2d÷³KfØOïÔr¢*Èvvík É'²‡-;)Å+#�m}Ëû—[ØóÛ mþ‹®¹Ls„›Ö÷·þ:¿A‹mn&% y¡mH}84 I ðx}É:ý�8Ç;@™eêùÀNDÐ9šƒ�Í®”;·¶·„ÂD<ôN€V.¹^\ÌÛF%‰6)×åúO_c—¼<ìuB�l¾£I1*oRŠ~ ¦Èi‡§-2J{SœÎÞG@An€”iä;&ç|ù‰Í¥:õ!y3C�²-—-…vùäÓÑX®­ß—q2èzÏ)”\ V3Ú”Ê:4'^ÄÊñ…Ç—’h¨¹ãA»Ý·�‘Â÷¥NO•O÷®“ žÚjîçjr×äÒÓ,ÕmÀ§�kfé^*[0â8’-'p JÑ\ðlš15¥J !Ô̰›…ËÊêïê§>Çc¨Î`çãå@¤¸4µCÃ+—<–»“ë= BZY€Qº^l,ä¾¥k/WD²(ô±x¥ñ׌a®ro6åêEU3¹o�o펠À`0<ø™’¢Â<µ·P¼Vyºìcdœ¬”gh�üPÔ_ÄÕRb¡€ò¶Ûµ‘ü ñsº#¤1‹ à;�dÎ�hD–ßï¡V?²L=k6 "­p´(nQ&cJÚç‚ *uú­Ï%Û.Îâ­ì=¾Í3å‰D ~LÑt¾cëmæsr¾7ü£‘íÉ›e”14þµUP5N¢¥ëC�¿öu5JѰ]¹”ÓTHCì[¨»@§}]Œ˜ÎHó‡?Ú9"kYoM6úcH}PÆþѪvˆ]�mp óßEq»CÉ=™ ÿ“RSd@ˆK¬ìwÊ‹®¤ÒÚ`~+Ž!ž”¡å–á ©INÍ�nûâg�ÁÈÓg‘•Ë­/D,yÀ §¬:mµž`úLîbâèß‹1Æ)êÖ³”ôn>¦Ò<È„1^ŸìÙ'›,² Í›Ç‚�ž}Bs&„¬ßYy¯àÝ$\9ï¹üñMŸà Ψ1ïµîߌ• ¤‘R´¡¥kR‹a‰á€‡E!ô}¾tŸXi¼Ü‹­vq 8¶€ºzXW»àxÕ•ÐÙv¾-õô(Ò�Š!jùI‡Zh,8D¬š¬F\½zf ?1”Æ€#Ùïy8í‹·îõ Œå¿d]ì®bd½þÇ�®ìâ.F¢i>^ƒ�)CûÏHԛߠ™ñïu’¸“æÝäÃ’ÕTzVüÚäñ3ªïÙÉ^^]k/;„Ú2åq|y6X5C ½{[#—lØõÆä®lÑç¡äZ)Š)»cƒ, Ú/DÞuÜ€E&SK,tÜ×€…^5^y>e^ Ãa*V^'ïHÝ|ãpx¶Ôµg{oö:Ì3H&jVݾ Ÿn`*ó:ð!W BŽ3’RŠÔVV5P(U›Þ˜˜ÒJÎ"Yé¬O¡­¨÷¾Ì±12r»Žq;·–Æ{:7Ž.Îãx´WMF…y”Wh½9ß»AAVë;U§¸RÖ…/×ó ØŒsJáÀ{†2�u?éq NÌ4­�´},ã1zƒdÿuŽöˆ ®‡½v•“gÜÕ<4L VßkzÉ Ìp‡cå%¾}™B˜ñ@ Ý‚4Ð7ÍOk“$å|g;•¾:nÓJÌ�/ «ÝþÿuB!9ð(°¬�÷å_…ë®*S ézϵ�X&(b)àA$Ÿ_)˜L6Ε$¹r OnÿÑS÷�ߣ½(j¦¸d([±oóZ7˜�¾M“Z¦�µþsûì³ ú’mP³à‘³R dø¨½ƒ__fìˆ ¿”µRÁ~ÓÂ>:EgI�w¸´ õ²OÄýÜWâ#W„M¤4ËoÊ×h,&l{Æ=â*ÞY'ÐþcÅ´kªy˜Ã»åÚo ŽO¨sß|iöüÜæÑË8ÿyêVDû’œdÀ«6 ØŠí+4D¯4Ĉ-V)7ØÖ£Z)!rÄ¡š2.² ^LY|¯’Ê2ÂÐï¥V½iÚ@!|Òvn½¾—4c�½ü!¥øÍ\')^Ü’‰áp$hòsñßn�³Éæ¶PñÊôc·´YxȲĊ:ÙÞ¼F½çè· ˆ$hˆD»ÎÌ* éþJ)!׋eîÿ�KfM3"g]Û&Kƾdh§î)t»ì‘jXäC¦ ð×ÏÈï˜qM¿=t,9I@4VàÝ Ëñ³ÉƒañX2¨BAÿóZze˜�¯>׿¯àÌÝ�ÌéªTðs2Ë,Ñ þXò¸Ç€•B=Ä‘º•a:$��PO '4ßù­úìNÐÝË„é‘i†c!ô^"4½ËŒÁkk�N”bU8¤‡Õä$# �iƒhl»SqgèÊôFT4Ú¸€‚²³ãÚÆ_j³sÀåŒt‰QáJæS°<°ëÃp�%³Å`BÑ:©‰{;ZÆ0ÈŸœ®•5±ul·ü>¹þU9…µç }µyƒ½lÕ d‘8Ϥ¨†¯š¾'ÌÐ`™[ù¸Ø„¢è+%†Øo/´3)ŒìB.5¯™�>òèÝD]òŽB“#Ñß[0-oock´ ÁíZÚzÊiⓚt“Тö…S`»È‹g½ÝePYç%>PǰÏ)�)žpz¢%OØÊ»�Mc-—zÃmáe§ðƒ–ÆÐ`#¢'½î"âpØÍáè`Ý!žÓøj^˜ }³/4KxùT I•-ñæ2ˆÉ†¥¬fÿjG‰ÜÎ9Ý^àò°E3;® 4b¶$øb<Çvîu§}œ?Ìiý@ŒÝ&Ò‰yÔM$?õËôq-n¨}ã<œy”!Lª?«ÂñQ"�m‘ñuÂ�f†”Ý yGF C¿÷·mìÎx ãÌñÚ9ñ@X!¦$ps’ÌØ¯ãQœ±Ã*ÿnаKa,V°X‘f™*lñ[1+…\…8sŽ.B‹L^.äl7úBŠŒ¸1d cPÀ6ga²^/Á½½Vûþºo2#K?»MˆÁò£m @J«2ˆGxòNôšÛBœG²soæR'<@%Õº†1J°ëÖ¤�×*¶B/Âtñ¢åE–©`ï¹kTâšqYµâÁµ„¿E@“þ.*º ÕD$"é™eeù¿9…¥ I‰ÿ%dóˆkVå‘Xæ˜0z®+kë£AË•~ôŒ‡76Ùš>Y$l%ÝoˆLQ—F«‰�oÚ¶=O~÷ü²Bj;g–É=ðFç¹Ú®ßdqŒÍS)»u, XÆßG…ý‘á›ÚÇ¿vÈÂÄó˜C·’ˆ‹ÕØEÅT³k‰ ¨m!‘{m;íîýdiÕ˜C%¿È<¯„{®gY”qÿÍ}ûéSʺé²Y^«kðër«nR÷�æ ÷W¤SýªöÛ­ýb¹tQ˜ßa}†;f+*.lzO—ùÜÁ§l2¡¶Ü8ö±ìzf’/ à�9º¡÷$¼šñ`3Œ>�¨šq¿#휠‚SõŸšòÍZÝ6 6æ0gëH`F©ZPRšÃXÖڪ°Í�ÂÉØñvá•úìÆ…#€¡ó_Ôû3³rÙdWpƒ‡–Eø=¯cgµ€Ñ�çž X0 A<¿¶]~¡ÎÁ›Ñ‚–Üže´z^æj¦³†ÍYe³ÌÐ[ö£O 4ŸÔ¬éÝ_oP޼ûðdQVÄÞ1Í]í9/›No e¬ÄÉùT±qpO9æÈÐ ÃfÒµº�¿\}¬¬ZO…Ü€9AH�EüOðõÍ*µË¶‚w~xÈÚã!Ó]áúÿ�:äG'é²û'+«ªâ]á2»EÞð"•;¢Ï4æÖÏvò:ÆIÅtþ"ú-H 5RG4PŠ  vö¾t~$¾ÆåbÊ}¾ ¶çåZ�ŒJõ'É6]Äfñø”FÓú”DÚûÛ1­–°ƒ”^ÃÛ_ Ùï<ʈY‘ß°,F]ÂçšëaåzÔ=òË=u%øGB?1\}Ê�•vè`ï»)8ƒê(ÝO ½^Ælß™¡Éá�¯çLÇež!æ‚üÝlVð¼0ŠžI ñ|­Y\'tf¡úöîf€ˆÔ`¹ú–œ…éUY_p(Q0jëÕýᯩôaú|š‘Xå#¦1Ê’ãânýŒ©Yi¾IO…æª'ãYBtPnb‡×­3�úq«òy:^A…Ý�<ÁföÁ]F:‹¦àûsÎVq§Eq”f�gì  ­ÖO�{Ñ�ërÉá£�o‹ð4i%¨ËuçýO¬k¼“3,b¦�ÜW úù긬‡a)gBýsS7È”†’c\¡ü€DpÆùo”[WÉ+.t]bVÏœæ„ùfï+áSpè¼-àKB…ÖSÖßíz£À8‡~ÈrÂ�p~-ùGÕ ~c ,@<œ( <Êö ?™C;,Õö&ÛÒ_¸Oð'Zm iÁz¾™ÆFÕ‚ýª=ºO|ÓÃKoã2D…ôU댱o—� d9¯´Íg �rùUxÉžÍ2?1"ª°nk5�•hÕÑl’»S•$í౜tÊq´þÜêY�x¥¹-šl" ŠëGˆ%Ëo1KsH»µ·y@¶‹ 7÷ì.«ôãn�ÍÉ4¢…|Ñr¤îe °‘Øw™°¯Hl0о»Lè�´´Võ.ðçïÌ­J o©–ý‰è‚ô]å¼määí›×)aŒä÷\Úˆ ½¹UD$d%äÃ7)Gž,�ðü()“E°»ºg&¥Ô�Ù‰ŸÈE±�qx«F S&N÷{ÍÀq'žA›â—¥·jÎK¿ÞÉKÌC~ýb$1ãBtŽÂ‚]7tx’<öyɪØF5­(ÚŒ¬Øõ(>ûÏN{ýΣ9o.FÀ_éMÃé# ÖKí‰d¶4´‡Væ¾ð•Ç+ÓØD9ggv‘ÏA¤'s,'\¡NnB‡;û;´WÓjGD, ü¤™ú¥¿å©ahù%^$øi€iÎðÔ‚�(ÐÏšX3x©VÆ«ËûA›X‚N¤’õù [Cwü„ˆ§ýzsžöµâ釸Þññ_ÿLP%ɆCBÀÊ�è;É%Ÿ5òÛ´ñ3¼¡« ÞHOõ›î7È3Ü ³4tÍTæΚAÌŠJf®W™ã z.ß’ì‚೩³J›œXóS/-G ¦©îËn5áeK"xw𢢅O: V=—¢+þW´=›*í'}„nÄŒ‰5iR]¿¹ “½:[WÄìô­¥,Rs3ÖNðÛˆw1¥†”‚kÜs(¿Ž$/ÅDσŸe²i�H:¤OÆÀõi óÎ¥hMEÂʽ³ý×£=ÁÕ6øjÉ/Zæ]àù2Ãa³è\ðF¼°i[Ü4I–›',è¼bXlå(ÛšžÞ:]ÂÄ™‡+uøóÛY·Úyœæ2 …+—ª¼Žjî8æºìL‹²#n:´FÞØøŒàʽÞg•$ö",&Ãsmûcgýz ¯ìòŸ‚ ÌÕ [ö ý)Q¡´ìÉsßô¤9ŠœZF˼˜kX(a–5(_Ì­Ѧw¬³¢Ôlÿ¿Ã…V®Î³ŠÀ `a£CNHû])ì¾Ò¥–T³€¹ë.õ¿‹·}˜Ó0üÔÒ§—}µûB…Qf$3ƒõ–-^:§—?û[{ðñ©!]y²" E�×…P3È—œø]¨ ª³â3å> Gþƒ‡äËwG¥ƒ˜¼ÕYõ˜@†l:½(§Y’DÅ×Ql¯úñãdá¸Ö³ƒ¼�«N²ês°…í"‹[¬H(‘»r]9žêÏ ™Èϯa_x˜$�Ç�éKý&ü¼02s�‚24½Ží…ô¿‚“v‘Z±ß;ï÷*]s_€';WúÍÛHGè§2NŸØßºÜù¢Kw>�Þï€.×þòÍŠN|‡=¼ ˆlV4ÎÊ;bðd±ÊT%TXËÁÓ~D,<`måçuˆ»ç8/fLzЫ¹gòdÂÎŒ¼8Ê�ãÌ 8¨Í\…�[¤·}ùY¾ç%oÂí¸?…{Äh™A·ü›Û‰jr§ 7¬#r¡‚¢ÈµlMX²R$å OÁùõ›!Kh¸“ÞêºÄO =æBf1Ḓh™KÉÜ“–sô<.²ô÷CÖ¨gjC Ãô ÔZÊ3³Ï¯vjiÇŒ`ÁÚž3»…MQ¤ûäë°�f±ÅÌð!À½ •œIÄ 7‰C¢²¢àÿß2â¾Ç¡"c ¾ì®5M»áe¤{ü݉Œ‹s‡:p¸”ä¤~¢-pÒ¿&öU÷œ±§¾H´ J´ŸS%ôØ<ØÐ†Óõe¨›i4Ãøˆ{xM‡*�˜JÒ�Y£A&�JÀÁ¾O™j‚’4†löÕ÷>>cô4”ªs`Ö8ôò… A@P.ÿMS²€¯¨¡ú¢gÙek¼²XêGj·ŽyÖ@ÍNÏ …äP ô4ýJö$7ðÔª¸ƒ5"êæV'#AÓETZv Sjª6��B=C›ï§ó¿rQƽl™—oýEÚl°¡H–n�p,$kŠ¿³€ÑÄ•-öÄeðŽQe‘ 9ÄÇ2¸’¶eµËÓ\­‡‹lT‡CíÑ!«Šþïô^¶ÃÃß›ÌinÑì&aFǹ !ßÍr‚Éè,üÅÝc®@7ôIyæRš¯Á^\DãVÈvÙða»’æÜ KZàUX;*ÆÃôÕ³¦‹8äfµ8i¯Àm?r£Xàââݱ3Na¸Ã\ëmBDÒbÌF‘z’ ÈaÜXq‚†?LˆÚ‡Jͳ uÉMŒ‡z ‚tÄè¶&!¢{u�ŸÌ# n-Ù;;yo;È•I XÀ5¡¬kªÛ»)^­ÍD R/íܸ�ðD.ƒGæ‚·á@Q| Ó5p ®|BJ³Ë�òú ÍÉ^þX¦Á‰DçykÍh?ÆL€ãæ\2ÂòD{1…ß[O=ác‹öÛ’q†Ò”{¤7PêæÆOƒŒ–zšpy‡ä ¬�7Z7õå,åâ¶KUðoÂ�Jð"žmâÝH‹22Ù®?ú@]ˆ?]Rm8lÁã¦'¸Ô^Øö÷'Õe¶¾öÿîã}5ÌU³Äzÿje-ÃÇYK‚‹ùÿÏÇ7ª‹‰z#+ Êa`áäÑ»*®·•ßåú ݲ–aÞb]#*ãÞé ŸÆ¼rfoµ¬a5&Ú|㎿HÀèíÐCæÊ¨ÛÙ#WŽÖ?ü,PG}Óô)CÌO=÷‚Ÿp|ñ®˜ÝLÑo†Œë:Áe"†û÷ôü“÷oÝ2ó’w{ÁF&u÷4uï/ù]#'Wőઃ {ÊumÂâÑøó&¸Ü®³ìêP!(TÎñBÁ·JÛÀÀý"+k4�2ž\dÛwÜx… ¥S �¤“×é’M¸?Ô –ÎD³ê¢¡øaö�G0Ê0¢ñ©`¥zØ ¬ñ¹”ǃPì;öœÌ—5ÒRB‚ºµ¯’¹»Kp°ˆgÜÈõ¥ èpÉ{ªá )­Çë¢-5 ÁÔò(µ»s_´Ãv‹×¥â¤6Z=~/¨}úÇäû×|‰[´ÐÖ�kî…mï<©~&?âû_í7ÀD;»AV÷Êj;V@ÝRÊ�`·³oÈ®@ö±¸@Í.ÛèGA�æ²6 qæÖÚÚ 1²”€Þ{¸Óoã�nÒ©;忯û‹’sNã Y³cÝúhª}�‚SäHž‘Wòïø�…r;Ü’l€�Î5ÿ�'$I¿-ˆZ;Ž’ýv(Ðû?r/1/߯ÔMá·^7[­tÊèaâ»íÅáú2¿¥Ú;¿~Ɣ͕6|%BÉõ¡iä4+™ìË\Û2„-ÿŠË½ÝCœXÞÕã¯w3®Ó{÷ñTb'Sº°ù|T˜¾¥jp'¶U£pØxý5©&ü#�X;¿�ëa:!ûw-�…,Åô¨ê¥v*€mÜ‘KJ‹�\vwhzÅìŸW@íJªg¦‹›üÁ¡¾ÓDb~§#tˆ6»=Ÿ‰ýáq¬gÉúÅ®>`þ¢ŸÛ•J9Éolä'bEUßîk`Õô2R­Úpú‡5Õíî©â0þö7hqÜ gíH=ÖeˆǶíEK�ìS³�br}mLGIˆj¥žcÏÕ-3&AÔ6>… 3°ºöÎ\©3Ë_‹v/v)^D$3 ÃÊ…^¢ÁÌs¿¡âwúHï ¾* ݆ÓâZt!ˤ™künõV­ f?ÑЇšà¹s©ZºÂñÅØŽ„U‹À�‚.)\@Š;4êŸ=Á�¬Ú‘�aÝö ì-³îò9ÙÎ+CàŸpöÝà×"ðÝéYþY#ëa8€§x¨Ð/,�I7„=ˆÔ ¶9S%Ö¶y¼ûxuW¥ÍX¿>kýòî5{A~ßY­)¤¶C™^§â›"o6›UNí尲œöêP ÿU߇7zY«v¿çœ{Vèx§±ª±l“í7N„¦ÕäJCƒH?]ú2ÃçÖ?fDQÒu»ªã.û ta³ã64'ïŽ<éGÓè;”isªAv‚Úú3ù¿«8‚ôA¢ñˆóyè “ßš®TÛ>ì?oî¢0ÖϹÞ;sP¤…ÅçT³îùˆO e}P]Ä‚þª›ñ704Q‘—Cê@¾“÷=¶Ð ð²õ6ëΨš$Ý“± ZÜž #ŽŒT!N_šJÁC(�Ño¾œ.² H�Uz”w�v•ý“wŒž¤Vñn“>¼6ºßIe§T.|ÒÁ¯#öÝ%ˆÝïÕ¶j@´ðy;N;6¨QÊÌÈdò�µ0õ“ã€c.¢Fl¡Wej¯.ßOÌ-ü*ô8®¿É,Å+¥Ì³a§x$’÷«•í±®uNfƒ¶ªÑà‡³"ÿ¸%U;t~«`IËJ%ò¿+ì¸âòwŒ¤DJ�…�¥]Ƴ#v*¼\…é‹`^UqÐ)6©‘sZ³/^7ЂÐ'ÁÃ[ßMþ¢‚ ú*xƒÑŸ”6KBvœ2zÈ iíÚ³éÀ[0T­Žø’Ô•‚ëKD&z�VÈMT”Š9bä k9'Ne$�X­±¾[ùÀ³«8þ§l|a7¤’͆A“¬¡eOMÄï\ùÙ¡ó(g<à”N6•¦óè9ëœpˆS¢ÿûë^¢� ÖÚ“ô2ßú·¨A5$)LŠÓêD–lQ… ¿Ó‚z‘‘Œ“S s´v"z7ž 1k¹`‰LbÀÕ¨h”�HœKuôågÍX°ÇvnRç¾¹XÚPžIqƒAjç›ãôÐ5MÌsÀº  ¶è‡Ïq•á_<ÃãèÊ5×®º9�îw±¹{´„ò‹ÔÑ3¹ã‹G+¸²ðêåó¡ ðÁ½÷Ÿ¶è†±6*gÓÿ᤟N.5¹nÙù‰ÍiyMàŸ2Hãüª�¾×æì�ÆÂÌ`™4_ýŠš OO{2è 3`ï‡sq/ÿüîlb%K#ÀÁV¢£R¡®P¢ÕŽžW?kžÈ±k¹Æ(U÷”sÎÕн a2„8̺ÏkÒÒuÐÌí+ð‡Çñ!:[ã®æšÊÌßJò ôûR·WRé]h¥†¿Ï=²ªÿÅ|±`(²ç3ÂôÌG-Mq^i3?o˜ÖÚ!V©ÛÎÜ/Ð'fwôïØz]BÀ~Á¢J´Ôl®2­çŸL«¶¡TXµš�¡Ö?£ Ö¿Bôj}ÄC£+]0ÈTG殆ؚsËœùyᯟâj “†i™“‹]-³û¸¬B9#é¤cÙÌëÖÖg ›áð¡ÿ¬æ¦‰�î÷ªX_÷rCXš¼ÚÕ¡ôÈ¥ŽÞnL®Eá�ÚŠ/-±! å�¤wšÿ’±‰>;©’ÀÏ8$ÁPÀ]/(ù…–C\‚¡=/f/ÅÌœ¯XúŸ>;ц€Mïl\Ü-tìß,F#{êqqæ Ãë ܲЫ@süê|è&«c^^2!@&4Uè?Q;¬>`Ã$̱[‡ž›í&ò"ØÐ+˜‰VÍ19+^N_)K¡T…ž�aÊ>Æ@|]|¹é¨Ÿóá<„^ƒ­d[äß»çÑ÷ͼŒÓíü $5K`f Dä;þó$LXÓ¶™´¤¹ÞÈ{öc‚qhµ |·áÉþÑÝiÚ0æb©c á†`¯ÿ"òði$+厜44°‚YQÇ%ÚêäÇ[?u ¸ÕÔ¥Jÿw °„ìœnuû¢l—•Nnöv%§¶žCÌœåþbßVØÚn˜½ÿ8‰xt”Mè�£úþvéÜ^û˦«àOo;á‡j#¦hòœideŠ)‰í )ö©4>Ì@†óF¢»~\ïc†—h’ eÂ#ÌyY^}¯És‚‚µÑ¹ãhÇÖ»¦nÞÆÃ/n'×Yõ<Ý—Ô3Wm�ANÍ‹Ý_“Apþ=¥û`Ù¤K¾ßãÜ·Jj½E‰eZPç,ÁNu´³,4ÎÎ)£;ŒÓí þW#¡y´ë JÕáæ•<§ªKîkŸË=‰îþ Zª«9?£ø˜oÜ¥¹ZɾD»z;¶¡K°/�7`z ±­Z$øùp×ÖICÝR˜>J„=‰;r› ëª 8à7^èÎŒÙ\óvñÍy"ð'Kl®~WÒê곈RϦ�°Ÿ¯B`>?ä¢Ç+ UY¸)¡RÛòÊëçËÔöQªE­qH§z?\)¢þ$5€Òûp»`¢e]³{I5À̓«Êq-Á¬äMM\¿Îtq¼äHâ—í§ò1B´VÌ,ÊA{�ÌâªÒh¯üm�Ê…J(¹ýˆRÅĨ̈Y ,”ûžw�³h&�¸¾<>hE£Õq�'Ùqâ $_IQl¾ “@>€Ã‘0b ©äNÏ mt‚éG§ÏŽÙ 1€s–ÊÒ,”6š{Žg"Ü-CàÉQY-HÂ’~k¨ÁìF&–B¸téߢýåa¼ƒ)õu%CY�K@íê{ ¤é?\/ÐÙ‡L�#"B+c*s‰t!éw%Ÿ³cèýþò“Þ`I¥yò{´ç&;�úD§p©àÿw‚¯p+Z�Ϫ¡üõâ2ÁP™sÀé¨Ì:&/ç‘-J6$¿þ˜²zet°w㯈›^jÊ£x Ï”[æ qu^w02$ÞˆLº[`Rë îÅj>KgbÏ#ûê 9ò.Aø^ä·`ª¼WÝ2›ôgþò@Sí7Ëv<Ô!…»U¸äÁäÕ8¶ßežt,ÈæÒ‘yBÊ‚l`²;0ùo„Œñ1¨Gà1¤JøkIýãÃjz&„sK¨åzüå6‚ç{Æ·˜Ô\a£L†¬Æ¬+Ü}³¾£ìJ•gõÃlÆüD=²çÖæ wyVÅÖ¥„ؽ3·`“³ý!s×õ!:oÍ"ÏX½F¨v"h Ú-.Ã.¦cFg©–8ÿµfJ5Ð.Âýû†G’#½(‚|’ƒûB,Ñ]! ]ùaú|žBçÌ?vDFâ‘Hb B¦Ùâ<þJ>Qß6(ª\ �õD�ÂøÌyu@‡†ÆçÛ�ÕhYc{v-^q*†�ðSÄ¢/äÇÿ¼‡¯bmXÞ$Ó ;N[¸ dÀ�/õ´‡ÿ‘Xq�8Òè‘Åd›ò–)—ráD++MÄ{ÐÇ2TBŽ}Ý¿Ù_²Ëpg–™G� »Ãlo×¢¹bØÿyBùâןr1 ÿÍŠ'Àjû!7Ë/® ˆ�£.úɈ½§¹î ØV÷±ü¿ã²5ôœvƒ”vzn &ÚÕE¹> vÂ?xª¡¥¾ UC•ꑯª~\yvÐA×GÄ“Ö}8¬(sc` -îÀg[·üˆ£"Û×vËðÓ±»ƒ¯SCýᆿÅ#å¸ê¯’oµŒé›¨Vì× _[Å }&6’T–¡ÿÆGÈ?—:‹_‹ŽÉL²U–"ïïÐù¹£A”r‡¤šÛGµ¿´56E`òé's«/øy-Ûü0–„ \Ü¢e(>¹¦âF>ìPÕMœ6@Os¦U*ø½^Qc ç¸Kƒ¤ÀÿÐ3nÛ‡CS.ú¡Üº:6¡ø¡ŸÁ–Uzº¿¾íD6Ksƒ„<#44?¤ÚÑvðA @�®«Ð[5‰æŽ F*à¯2›Ötׯä}BTà»Ð‚#„™ºØ6M œ ëí)¶ú™KÄÜ›â•kΙŸ?rÉÑPHÄÂ@>òU£V$ÙYä/‘ú½–g.Õ�Úø”.€N}·ËLV2lœÓÁžv×wÑ— ÉCÑ ÔÖyÃ<»ÃÚÔÓ[Ù ……=dm�ËÔVKº{=8º?IùQ;,IGg°+äzVq©Æ~¯)ë5½àêìßW¥ƒ„‚˜™Ìæ;v¡<€FBC5‡e¾Â§ìoŽÔ´žÆmÛWµº>8ø—© Û ÀÐHW‘guík0!z±á^Û÷aà 2ÛGBþ‹xCè¯L˜U\^oV UóЊ†_%¨‘†èºðîkŒÉ¹5å‘÷ËBôS踑ÏúH/Ú³IæäÃn¡6VVª :îë~A7äÁŠvèî^SIÎcdœ_“q¤Ø‹Ìk¦@dÎüOü×ÞÐ! «üHGÐàuø�sÚ);S�‚Ôë ,¼^Ãöœ•ÐDj"ÙRµ{ïMJ`í’&  Šˆ’±þº”|Ænå>’ÛX5#%ò ±àÕÅ5s�ÏGJ ¨OOŸm|ó£ ê¬æ{i³ö\R(«[EªràrÆ‚Jx•FráÀJ¦nÿ=)…­ÇXÁÛç©ç—·*õ©Œš—ŽãÉ;8¸«Ux&h¢›�c1I IÒ!¥ŒXÔ„5_õï n y{c£çòN«ïò2*"ý5$šk8´vÒÀˆ/[ØW¸U»£„ê¶–‹<^“˨oÖÚæ%Ðf!ë@ˉT{7‡�5»%€es‚@}ÌÇ;F³"‹ “Í¥æñ*ª©l¦=A ‘˜ÄU¨ƒ)¶Êâ;šT'<>Ý6ü+æìÁ+9m(œ— �£H5ØŽKì®BÌ.Ÿè´Q>µHu³�•@¯Å,¯è”-š MY}º�ê_ˆÓ]|êU+@Žž;ÁKÒU�0ª¸ãho´IÒ΢Ѕ‰É îîKÄÜ–Pã­C™W¾&�f*@;àtßZìˆqŸˆcyy2.‹&?Šê®Ôu5~—E'ëM>›M«u‘ãÀ¼æ¹ÂG$Qê±¹%3;Tl¢×ºz<ÃßO\üÌ@æËs¼+ê~¹IOv2ùüâi (A><¾N Œù†Q~oÁÇÆ8\šŽD/Àž A”ÿòŠÂI±“[ KU‘™ÂœÞø0€j{¡!€ ºŸ*j¬l ­Ly S–fÉkö„K¸u;z†7!ô)÷9ê§rÛ�4ø>lÀ™ý·Ú¬„®éõ„øç«BžÇ ­Z€p ß+8»ÜX…ã)‡›SÏ3‚™äKô¿È�:L ^ׄe^ lù?�^ŠwÚÃn`4 ßk*À˨¯,` cèNxŠ–V4xXD<¬ï]±Y·x÷J$‚½ "ï�â{þ‚ð{¼ö äÁÕYÄ>RgÅŠCÉêZGÓ; q„~¦—BµéI°•þóá‹ü±›àì¹fÝ ¬6OЬ�‘þ‡0ÒÇ:¨ Qà–𛤦Hâ3-S„&ÕÆ‰?ú6B…y3¨Å�0ÙgÇ&ß16µˆ¯Ý‹¦¯¦�[Íó,èpʯbYEº‚kà%Ÿ��çCžqø¨rçWb·éÊTâ†� „“™t-�S ‹¼E�®`ÿ³hå08d×w07ËMÖö3Zߊ?¹“›ógoŠ‚Å/šzdk,…Ì2Ê>†Â‰b·5(; µä¢�²^:ÆW´Ó]û*ÏiÐév¿àôOˆ¸X¶B·<-�ñ¥�¬´Gj7�ÌwæºfÅËpî§g2‚è©`B ‚ÂØÚOp"°«x Ê„òT–y &©TMoµ û ͯøÅSî> ß<ð7ýEL w²_„(+¦ÌNâ´`îtϗ̬?Kµ»‚ŠRkç&JI¦Ä,K’‘ÖV%XR�úµþ�êÁÅŠ5Y¨áW�”‰T ÏàÊ@ójÏo×Ĉ �µt þÍFkZÁ†„dAfGÐÔlþ3"à“ZäÀ©�”3XªÙ«Ùrk#Ä(GPKnXtý‚6ÆøeŨ ÀšÆç65`ù8Û•œ™äáPBZjÍ’H=qŠ?šêg§|ÊÜ£4F�Е2n§ðò/™¯š¹zÙox²)àpÙº$Ìpçÿ‹ÔVˆYµœ²$ñÑ)ôÕbe�fÓ×r¸Rœ»ó‚îäæèí3¯SÝ»{+÷§¿Ð&¹ŠHúã´„H¥íg3ò¢™YúëA -<ñC³ÒØÓþ+G÷G S�1.aI nª>Ùky¼´•c% ã÷ ËÈÂôࣾ©�c†YܾG´!E €º¦ ®¥¿ÿˆ.\)YÜš�~¼;ƬÀÀ+Õ+K}ŸR|t~ÄX»5œR¶øÐi¿úº @bi]ÜAbƒ,{«f|ŠŒ’P=Ïž³c�J’}¼±š)p±Ñ»½|Ç{�D˜Q2òÚ¬¤ªˆ9¿Ôª=ì PhMN Ý‘ézlÙ& 8¨5b öOŸŸ{K-û)§¤V‡^®ÿ¦t K«Yš$!Yz”Ã’fÌ^~ßOì }ò¾”ñ�_ŽD�§ïéà]“w³÷-²zS® hVïB±oÖ˜p¶´ä#¹±à‡LBXÚ¹ÀùhA4TïÑxS 9󆀒‚ãE5]sî®ØœúñRˆÿy~Æô§šÞ8P ^äJbQ�`?Î]^b§@ÔJg—Ú!LeµB�nõégnR©‰œí¥×YkoÀ]l¨GÐk;*è”tÉFY•k­=g©4M˜~¥„£¼šUÙYñ Ù<œ�Ók–÷¡1Ô­¥ÏR2 Ö–««Øf\‚1“œéË£‚[ý�/{9,üÊ×4iÄzЩ¼yÎ#\þ˜“xðúø¤G;pº’wŸ‡OÊîœâ< f¬Åän¤Ô7oÿ#ç/šèë«ôSµáÂ�èÁKñ;ŠqÕëØ1R`Ð@ÚQ4´ C±9ؕʓú_~¾Ã{ÁfpâS;……Õbj\ÏqüZ²ÝØ�Ô— ‹“Â]›$æg1R®m­fm0@w€ãõ�³I¶Aî*:hÃ,\, �ÄÏL–fK=‰ ¼Ç ‘TŠfÄ�ódìháK©“{enÞËñ¦&È›¬ËÛಀË\6™ ôŒT%<þĹšµ2igC×c@)>¹ÔµìT?sÔ—|0Áä÷ÀÙ”–ù¸ßšl1—è.ÈW.çC~Hí6²§ëZäÕ|Use•å®Ü®+Ú²O”lÆžö\ÏôåÀŸ…ƒ?Z ÛíãÒß|ŒeQ¼’wW�CõY'¸ÁL.*ÌBàW-×¹U`�ȉ=»Š'¨Ò…*�c-àý‹C™åÑãMWbO+ÛLùçjš™ü”ütœzÞ'‘!œ¤Ä}!BqFôVƒªµ´“¥4¯ˆ\¦œV²AWø(tC(Ô´ ™êáÄ4� [ÁކqÇìÕæÅWã–Ds==—OBïkŽ R}?—a+lR¼Š¾QP4!kBÓÓ<ØqéÉø¹öHSj™µù0ƒÜGZ6 uÑ�¾J1z%`B6¬ëÛ/¿Z¬·ól-°%\3óÿ¢Ëf»Üh¡TcídN8ÈsÒæãóÜnáag‹ÚÎ�xhLó=æ•5:ÃØÈ® Yé䊜uó(}Š/&ç{nŸx\XD$p£;�]ÖCéÙs ÚoôYûŸcñ1Ð?9MÓž§TÔ�Eëo•�Aù¨D—ѯÍv{ÝíXç\¥ÕrùEVh¦(Þ3Võ bhË8Î&1‘îƒ�l·Ií/ÈpnüѾ4TïÁ‹E ~›;ÂØÑµû·MÌ‹®/ßã·™õ·OÜC%mV0ߤ¦]†Vúõ‚õåˆjPÏ¡^¿`ý‡­"äß¹ž)äïGÉ‹t7 Y!Ü“¹¦Ÿœ¡�*º 1É[h<«g¯þƒ^œ£)Kp ßz[� ®P±ògßí,ùT>;&·/±¡u°CÀ A¯Š0¦BÁ¶×»�ŠÜû °3 ¬{�ŒÝͳA³óÃÍ ÍáÁ9o‡Ýq‡ÿûùgÖÿñ‚ui'ˆQÇ¿)™h ó•ãÝös9�uTj™‚h>aÙ§'Î%kÔù‹ÎRH!¸¬Ëª;üy ƒ¿¯��ÌW 1ÀòCþ´®iŽ´>&ãN¬ò´qL§2öEÕ‡ }´£yH ‡´jéåðè@µ˜t¨è€BÀ>Ó|ÀƒØ¾)XŒÁ40:ô§÷ykHArɼ7Îæ… àg¼YOÚ‚ã瀯Ÿ–z•±‰^T›†Úi±d-¼Z…/¾·Ù“6(…ÌÏ}¦V˜l\†þÞÿÀæ¿9ûÖ)jR¯wPÌЕ÷(&„pÚ¾waéi‡Ø<³ƒXg,‘¹ÏäØƒkUÅ7¿ @6"0œ ?6±Cþ-_ÌgÃ�LÄÖÚ‚€~ƒLˆÑ‡Eõ°’³ÂJ"ÕøåéookTÏ%×ÄEϤ,éLñ‚MNLQ+õÍÿˆýêc'¼WìiÔé»óñî�!u>’�KVYuzÕìäÃJú è«Tbº„¨8wØÕ#„åæVRùkSTëãåXæ9™Üˆ#¾ÙMkȨÚˆ±‚8�RÜ‚�o¤`{gdSÕÍg�‡O…¶„c*Ã8·ÆßnÛ˜8.u„ûKÉ�Ð ¡{#�›bfÀ÷%zmCÐ#;W苈‡yZûz'Pkãõáó^€x y¡‹HÕÍ,ë ¤d╚`·-Gý/CòŒM:ý#Qr b,O7Ì?d1Ä3øºg8çëöõ‡t6B„àÿ}™;QyÉ�¢ù¹h¾k¤‹_Á=/ï™Ì¬™h$'…ª|¹½Ïšg¤¥ÅP¼ÑŽRúMå×=Àgá›@öó®€ËE·%’�/=×%³Àû4Ü<óìwdc;ÞÅW 4Ä ¢)_¾¶/„aø(E¤Á5Ž Üž‚Ÿžy?rDô«w:_Åžz¯ìPF¤‘û[æç d�Ó¾‰‚B.Ðs~eøv  ¼Î„v€™ˆ—B³v4Ù�¤²´4Ô…U­F°«X¤oUÔM:ÖßéâL'h•òðÞdp./Èhò¬�àñš­žr=Õ$]ÿ0ªÿNuÔîÓ>c"B= ÀÙ>¿"æå=zÀý kظJ¤Q^ѽš(µ _>f´©þôˆR{J^´¿·«Ô¨«g, HP?šWÀlª]dÅÂ?ÜJÝ;èXÖ̯  P„ú–3ñl©vÀ56üÛ/3\>ºw/a\jî šÎ¥tä¬Ð�ŠøI_�ëYö¥ãcUál- CÚ�|�g#�Ld¸_¤ÈÑ!-ýÝb…êU�Lßu3ªÒ…°–¼Ôsäž¿ÎZµ@ÔZSñe † ›C‚m¸€ÿOc)ŽÞ¾PÃîòHbRµòP)z×�¡å}¶ÞC¶ÚìÌzìþLOÆöw�¢l+#€j–Ý»[¦Á$ª¿Yj”Göxõv HÚ“á{˜„F:Œ—GƒÁ¿Î�ð+)ä 14þG¶Yà‡•\w°,0S™gÚe�g+Šûfß�ïÇQþ[Ø×�¡ßÈ1}¤_¥Ü¯버T :† s."‚÷>ºé¬£ÐR£R„/\¼-"Vwþt þ«•hF •ÝÏ Æ—ˆ-;gÚÍRë÷ ¹›lªw¼‡ Óò¡éÁ’êû¾ð2‡—tߺ ¤>Xåžì¶ESŒN)Â\ÿü¤ÿayà52ýG¬ AgÖ÷f„¬ ­ä¯,¸ã@ ì LÀT©=‡cwEÖ4•×Èù³óÏmÿQa!�ùŸ#¬ÏS’û -“®ò¨' AçÔéZ+5s'ˆ€­?¯@Sà5‰¢Á¸�^µã¸†sŸ$}¢ÂË2Ø –5÷z¨qúVu8޵†¶Ê8R�­ßGpÞk(ñµ~%(6eF R`�ŒÍg/”Œ¡Éu�M~îæÛf1“1ô>õòA™|mÿà´ëH¥G’9Os£—òt2xˆvŒ‹Ç ržè| 9TûÁýœ›Fx\ø¯ØÓryˆašèR ug㯹!§×ŽT¢•¶<탈ŸðÙ'\ñ\¹D¿.÷Œ¥‚˜$€åØúy„¶w´x§ÆD¡ojF”î&ɘzѦÝ%thW©%_nþñ:ýÿo2½A|ÒÛ"6•‰*ƒÉ­åÛ� �Õ"1³Á¤ÂøÅzðåmÓ/¼ÄýV|3�©ñeò9ÏÈicNŒ­¹´%"î+ÂÜÑ‚{ã‚3¶ñA ¯kÎIÒ�ƒ¨d¨ ìxnÁhÇká\ =sÞUqÛ\¦ÈAáì–ã!ãØ"·ê–õº÷d…o`–¨\ÑZÊŒ¡iÒ�@Ñq[�˜$ùÒo³õ€ ØÀ\æä Ý"úŸ¹fLJ›EIÅlmZ±‹ÿ@}ùÓ˜qö›$ªÖt½{�’�¨|�r(—ÿäÔ[ ²™ÂóæNãB?¶ÆUªj~sÆ2¾3X¢˜rDÂxóê—‚´ÅB%ç¹þTÁTG wVˆöë¦òø´’° vS²m™žŒ�Bÿ|îË=ú,c8“ê ›Û>Kó1Y]w$*ÈiªX¦ŸšrØ!ù€‘¦ µÊµØl©:EM�±êRoÁ¬Âj:@RZyË ÷¥l€9¾=SnS^Îü«.Œå|:·¼öNB«�S«Gã_º¬ ºäRè¢^76W1cÿú;hŃ¥Ÿik™à½m·–Á:c¶:²_ßJÞÀš7£ïe>ú¡©³¡áî–ˆcü^•þ¹Y~Vó'23’vž£/Haô8PTkN‰P°×e¸n…Íê xZc­ˆCA“ã9Qj1ߨµ‚º“-5fà­^ Ë–(¿Ä£®Ô�|+£`)ß ‹éÆq2ǽo/&"×-üyß4²72‡ÕRoþœ& =)©¸Ãæ]·�>‹ŠknøÞ¨˜[«¤ù‘€u¤qÖ¯P ÅHk1Äg¿u+&¤ß I•§66Uø¿Ôº RaÓ;mâ±ê赯1 ëÁ" {_‘ÁNääß²•T÷¥ãE¼æj~›IüŒ¶BÄ Š{üõÍU^Ýç‡J¤¾¿îþhæ?/ÅvòfÜkÍâ³ÓþSˆØ˜w ¹š€=­b½Ú�ÃÍ@î ëh�— =½ \K®nPÕ÷ÇI°kÞæE=Ìcæa €aåeïŒ0¥BÌ7�+€4”SE…FA© ²øÒ'Ù:pw&…Ôš€r'ìt™žåÀ©@ôœP>”ûú±¶®�Mj:ÜzypÔÔÝ�eÎfDd÷P®ÌB__—UÝOàO‰>«€ P¥ÖQ×LÛçËÝ *5Îõ¼�D _D]¥R ¬tpo±æ A‘Ñùœ÷8ºÑaVS¦°�÷ƒ‚OôSß<=n‡gðLM8.‹¯YOì�‡s ­PÃò“1=ta h™—•½UîÞ‹l÷B{ ž!鑚^>ÿÚ÷s×9•Íæ«¹ˆ¿†‘�k—«&0wgL Á�ÎÇ•Å6 Õ.lÇ¿"ö ®†Š³ïÝí‘ÝÖfkˉ2SÌ}¶§À‹®gv)Ÿ8÷K ‡NnÇ+¥pÑ»$BÿáóŠFá _èƒ)•¹½‚y'¹%¤œ†¡³¬E=4ƒzeSÞM§t sɉ]Ão¸Wæ ¦ý©Q×…ÑY˜Õ¯.' LŽ•d jèÉ�+¸¼Ã ±VJú6@à(åH=èKºß÷"gþh«“¥âú¼&ÖMd Â\¼U@â…èƒ..¥´¾H]¨õ2Ê`�AEh¶˜ÜIê4}М/mónt̆ñpu°´B÷ügy…ÐT @ÈŒ2cÓ+â2. M 4{Í?¢Ç½öJûwÏɦa¥ÝBÑÎû`¬™ùùÜðÆÄ/Í>‹%ÝŠÖ%BEV»�r÷2´šé°59Æÿ ¦#ln›v±QÔ—|S'éSÍê•»#·ÿÞ)uÆ ½«ÀžY8»n>7âX=’õô¸„`ÜgzõâHî%3Ÿü¸nȰò{!Ì1{ýó÷¤)æ\àdÛÉ>lº•E“½6jÈB Öê‰*xJ��`äúÙo�¶ô•cÛüú»Þ ʨçrŸŽ}šXT 4P¡=Œrµ©2m² <Š×úì¥jÆ_ €Ù}O9ßcƒ‰jâÚ¯f¬˜hÇ�ˆÎf8T�ÃÙÄuó<‘šÝÿ²ŠRg`•¸õ¤/—¸ÓÈK·�Åo•Èý@ºѶ÷ºUUû#šíÂmxçècç`ÌOGh#5»±°,½ç¥•ß1¢l„gOóX-^ݶ(y¼›ƒÛ­a=g&ÿUH&ÜÿÛ¿Kpµ!žËü€ «u� ñþ <6�!¿äBNÙ7v]¸ ŠUk·PN©âˆâ@lý7¦eÌÓ?]ä4Iš·üy¸f� ûáó|Ù­_·ƒ¼hCŒkL—ÂSîÌùÊwçß |!ø[áÑ9Nî™Ö›ÚÌ5íÅhx¡;È+ðŸ²×ñ©ÈÏ®¬õ2êËÝá÷”(¼˜·r­ý¯`‰¨Ö‚®§­ðó|Õ"5,��å;–[4ËM›IÖ� "RÞÉCdÕX¡ tnuiþËÀ¿ "ËÖÓ<å€{r§¼sà£.3Ž)£Õ­((6Ž¢Ý'Iæó(œËÓdïrÏÐl“Šò0‡Piy) «;Sm*ï³J‚Ølµ•4t€ÂæçÊ�¸Wrºçs4/ÄÀMýÓE:AüàV›Ý:ét­¼è©ö¥Ûø2V-õŽp}ö·G«f·-–‘7¨Í¿‡IÖ—A$¬N½ÅN¯çÆè1k|BŽi¦“óTÿ 2¹\±el(Z�ÐSqA³‡LÕ%@ÒØ77Mµ2‡¹Ú9Âçañ4ºš\‚T—CûA%VͺA "±@JϘªÔÖ�!Šœ sn46fËhøüfRÔ´/õÔ?ž³×{òw/°Ù m*!-\Êf™§;ºQEa%'/ñbv"£AÊ¢_°O¹+ñ‡·˜�¯É_µv,9í£É�—^/$"¬s¢‹Þ’ÑFÎÂ-‰ñÕzUtµ–Xõ¹µ_èuʳúI<][Î'Q²W_Ùüœ,Ž[1 �K|Š…ÆÿP)¯…Ž„Ò§ê©4'°–=½Wª'D&Ì�³M¬F˱¼´ÓßÓpðr�Îoµá…Þ=÷íl6S…(ôj·k˜ÞMÉ>–3~ÍÁ«mwuÙv<ŒÇÆEl"&¶2Ûõѳ³}KñÔW¥ ]¶$AU dBèÆP¼[ÛÞ*V 'ø[0&-ùuv80ê…Q¯ô üò‚lˆC…C£l ì�W¯yBXÒ¥#~·<Ôè,Íö¹­�+]ÛÏ-³¡è%áGN4Fï’<ѭذN\Ïx=×NâP‡)šÜˆÔ‘…°‡Ô¯ ¥6kßû0URcÑX}ÈÉ›¶N!ØÞ)6k«;º¬]x5�OÉ!ª’±�Ëšìe$„ù*Aòƒ9!oÖGÝBïæˆ›Ì4ŽØê«'x�Þ«œø¨’ ¿rÄG(umÆY��ðʸR~1.î�+DÓ­·wºõZ‚šiðmà=;÷>�ím=™é0ÛGæ!Vm ]Î[ïöf˼>TÑÇccî=Ìþnèç2mÐãHBîs£.bíçœ'Œ$º K°Ž�wÜL\3ÑÃyKJþôrÞÐeó°Bª'ƒ$„ók¨�Æ#ˆ¦{w<Åêê?¶]SËçv ÞÊŒRáùЮ\;ðR™½ "½ØöTÖ\•9’çR3WÙ7\Ð’è½Ùäöp$ýS./H-‘ÔÔˆ b…F±¢Ù¯•B¢ ýÆI©fº“s¹¨#.ºç¶RËÑÜñÐ/bq68$¼!¤œ7ðfÙ,h1‘»ã.)ºÖýºµ’½>AN#÷|ÀV 3“ŽÂ¬õq)Ù`®àŽžÐø+ŠgbΪ[ ýÚZ9à·L4º)K5±Ã<2�fóåìR2ÙëÀOÌmq'‡kb¼áççWrêâsû£†LVÔ½ÒÏÚ´+7Qò¹ÛÉ{kæ!ßÈÚ߬^ #Îõle^‘SšR—)¯{¥VrVªäý®*ÝmAn(Ã2àŽ­¼mË…¸cð&÷ãÜ6M>±±eX8¦2¹¬Ñ:�^&Y:.ŸÖ˜Ñ,'@¶KÉv-bÊ�‹s!µÉMàaK®Ú;Â1Š1¾¼4äÊ^kÖFܬïuŸXM]_ßGöµZy¶µào×#ŽÈ¿J†±‰C*füÉ�up¡Údt´WJrlΔ�ÕäDÚE´™?fªý£ù‡ìé]”5õ„cb �/¶j@Ïzx‰?éL®Þ:‘M‰‘¨-@ÆÞ3ˆ=cÌ?‡�âó´ÕÃ2“® AúZ×Âß=ä۬Ђ~ÊË_m›½ÃÌå5øõkiùÙ–[ÒKRKíµE K§Æµ@­‰càE:Vœ×â¹a‡iHù6Ê(NnÂá7M`B9f%Óº÷àì  (™PÖï˜$S�—« ¢‹nž>¾Æí�èe ¿$bŒ©ò÷ðâÕuÓºìð_›¢WÝ”_A©ÂW’u �xD©KØsåÛÑ0èTɳ"=6¢(~Ods»¯ª‹> vSÈ-üsJ°EŒFÞu1Ò Ê¥Cg|óR°™0¦ò¢ÛoÝ !èTæê«�NÚ¿çÿ‰\ÞZÎkЀí þ®¬OÌNï°ßÂÖۜ†ah 9®¯�ö< kšJzí þ’ƒ$XRÁ§Î¾ƒ¡Ïô$å°x0¹äü¿L«8i(D%o"Y&Øœ'¢ËïÂæw Ù†|Ïai‚uw.Q’£ÇÕ‰9ë¤ëß™§\¥�YÔ#˜ç—S™oP)ÈRþ£úŸcò¤Ò!úÀTFѸãQΤkÑ£f†ü‹?”Ý» ïHÿžù±Ÿƒ¸dÙ^¤àiðFðÉØ!¾ìÊ(F"¹¢ÕåÓ-My ôêÑ$-£Ï QaMU]LÖ¤M‡Ÿ¼È@i¨<&2šU2LN$>áþð$‚,Ö9툔éFåŒóÌñJ·®BË&j¶“‹+ §!;ÈþŠÝ‡TYàÅŒg´ ÏÁâQ�ÆØ†jdfY/ƹ£ç#0Ø‘˜©æ§2©¢!*³²­ âœÀç63¹‹;¯"ñÓ~7 p#œ+=Ü»?@Ó½O–acȪÅÆ[M1 ŽˆÛ£&¦Y5a!‹)›’`’ä¤c¡XTäº ƒû¥w¶u5±Mqa[H‘åy\P°Gl‰†Œ5È`�Ãd¬þk¸^1íœ"@`æ1Á7ƒÖÅ‚@c)&L‰JF®ÍwÁC¢‚nðÖ0’Pü“Æõ2=‡MôHIÅÝÃ�ŽdÓTËr~H’…1Щ–ea”ˆêQýz¤sŒ 2nìÌñZ¯¬vuâß´@'¼ôÇd’º Ž€·åT`Aœ—€«vYD�x¨<ƒs€3?©¿ÄcvÞ.†� á=2yȰd±+‚ä±ûc;#›VTç9æÎ¬ÔE¸'X¦_M&¬ÃáX±¥ÐKH¤„¦õ1�Í|éR}Y-ñÎ^;ébä#Ž #g¯¨ dƒ9 q`^,ן'£I SÕ³î�„É’=Ùɬb ï:á'6‰� }MK„‘hUŒ /0æa“^[ê}ò¶£4¥,\ü€vè½È½@ZbÓj—‡ïn½3o±0t»-ae�µTF¼4ôv& sŒ4Ú 9q]è¤eL¨hš;$x”h)Áï;¦O ’é8ž‘·�®Ä.�™5,‰ ›ìÇe›*L˺ޓܯd$ß'b$±Û‘þ„Âù-¯G¿>òofØøë$›yJ/kÜäµ\4¾ £=ÓÓjIµbt!˜âkçZç6‚äÿÅg×Ë  Ð¤ ìÄÈf´©}$w‚V½Ì3(µü3Þ$zˆT/ –ˆ˜º‡ÿ0®åP§ÒýŠîÔù ³MÉìN™C>╦szG¢ӪÑ¢µÙj¤ñ¹G!ëÄÿä4a\a4n„§BË,»7òë�êÁ$ /ôQX1ÃÂÊëÝÇ�–jÙÆuò¾Z>Ÿv¶s›‡²¥4yZk8Á…|Ô��Ë$W–'0«í ÜÅÜXõR�ÕÖ®iú V*Auï'YFz/~z*’,Ü¢„r�Øë55ˆþb¸¿ãТxà`!¹u8–h3ùF[�sîë?w—3Y¸©…�Ôšò¶Ìõ#xé¹y`Ø‹«Åã‹›r›¥8á½jþµöb>‚±6i‚­¿júIš`ØÁ€¡SYTe9Ç '¥üxŸWÌÙ_†úøB:²-Ñuƒ·ŽÑi^lnüÂìø”*D)5¹šLìÚ¼û(®ÅͧK ¢äòÁ$Sxx�pcü ©ëè^ÑCkÔÆ?N´Vo—åÂÙ2êŽl&N€ËlÜî’’ z 3ô¬¶Â…ðkœq²a Ùh-p¡Ïr&w RZx;‰S+€x"€Æ÷ZúE¯�历þ®t"O²óœ�0ûÓ%iw`½!�y‰į¼:MŽÒmô)ÄîàÉSäwŽãØ»,dNÇ×J ­iÕÙ�ÖJÐJ„”'57 bfŽÂ}ÇÕnÉ”8�+‘¿*½^•öé�º“¡³#º@ú¬}Çd‡×JÅx»TÈM+naÞ¹ ˆ>XÀ‡^jøÌÞKOœ³_Ç„�²3üuúÂtóbÌ#å"¯Éÿù°<îY˜½÷*x©5zÍoÎf••EÚ`¬7 iýÿfר�%ªÉ{É4,k�ŒÒÑ‘ïö�˜ÖÆ',ÞM�­�tYH^âù¦<¼^‘z$‚+�Ȭí3 FË2åQxVEHÇ‘m�Äí#Òöúnò‡, Ïš{uï+tßC§€QjÙÄB6]v¡}eèIÄtl~QnàÂk/,ÕQQ¿ó»!ðµä[ óŠ¢Ç!½øØÿx�r 4 ŒE `\ÛXÿuà”ZØØÑ7AÃÈïÃäÀç­o¸‚th­l8=0c³©�ëk3†¤£ÝÓ[2 ï]BBy”½4G¼úÉX‚£Š¹79Ô�c ’/V¶ ¸tãˆjAÚ$‡'9©§ì4 Ú¼înqõ³™ pTwm'[ÎBŠ€\ENqɉF{ëÁoâ­0¼3&Û�ìà�mÅ›¼j›~äŽEë ý÷wœˆ6²˜†¦ê´~=$›|‰õeÚc"„¼—gy8¨$n®Æ¢.™†´IŒMý `Eïù·ýWÚ€Ó<[î¾É«ú_S[7°æ©…=¦MÀ±…ûìØÄäÿ{øòÒSC˜Q?½Ò×SËü�(¦Þ˜8Þse)2ŠË`U¥ƒãù\qzbÏ”ßðæ¤¹&³½ 3A)ëAœvÜ:T½,}r™Ï‚ÌØ:f9ï€EôŽš—ÓŠÕkD��H`õú YËË×ÉFô¯ë\Ñzã6Ùýä:bïègÖ˪4zɳ&Õl¦&2.2k¨ºr·ï‰ ¦zŸjÍþµú#Ù'žYTYE”ï%gö¥büCP`ož=ŒaŽ×‘ë-ÕcÙ'P_Oy—×ÐaÉ6x/ä aÚ<øúñLö?Qnâû­%“Ã*ß8$h€Û~b#¬5[i«[_úó¼Ê[ö(Aò–Íý )¯“J•2fò‘~ q§*ªÈŸ…2ÞwÌQv§Ž°ø]Ïp%÷eIMzÑDcÁ€ç¾“MíÈœ.]ßí¥äY{%Þ°˜txšoìfܶú|ïÌæ|àwñjË÷3.ã(ìä]âu5´œ»›¨­úµeÒˆ?$>>Âõ€b¨È5”ýla³)£ÃÅTÍCŸ#ÌGb�ޏ›Ç,Hbëõd–Ç#¬5»Ý¬…&ÊÙ»LK|Ø«ìwl?ˆŠ9úeâ‘# –hÀk6ÁŽàŸÛw"šç(7(Å¥�:Nÿã_) M�vT ©o»'e)­ˆþ2öC‚Ë "ø�±``|K—‹ð‰Jæv4 ý·Åp-§tÖq¹„?e)"“È$@qH‹?Hö‘õù 5Öj‰¬Uö E¨å†BOÐ2«á“},ýó °›p¦Çè›CПÔQ)ƒùÑÚžf?àr6‘×Yö<ˆ´Ô$ïØ/xm´:œaÈ‚†7\"Çí#Ë@�ÛÝþAwì XÓ" dü‰ªèðÉ›Ð�"c¿>PvÜS­›æ$µA jr¯ò—ye¶-u‚*PÓ–¯ÕïsйÉÊ·� \¹ÖÁï…&Öz¨¨²Úäósˆ¿ÀÆ“ tF7—ëU&NÅÞ,¹ªüxÞ3Œó’Pv®*:Û!© ë¿ /7Gî ý(`²ùæÈ�bÐGö8Óaï·ÑFõ¼`ÀÀpk~i¾å%×ÍqõèÌ�­é@ œ’„�¨Á«i4Äùfcƒ#õK�œs‹¤¿Êå)p Þkí5SRC”/ çII=19!ßÞ`õpb%JKضZ‡¹X ºËNdMÊ‹©Ÿc… ØØ^ZñŸáPÞ ¿bT[ í[5�Èøé�1R·©ç×ð³§ F.Q˜PNŸÑô–ºgÔ›kó ”yh®�ަ§Œð“šY M€ˆ%ýë×¼pGžEh(Î"9ÀjÕa3U _ÖË ýÇ®]¯rÝ}.‹|j9ïwå¨�¥V_ùµê £}ž#+ð»Fp(Ôù¥ª¤“vûGð£V{ §Þ {E䀒�Q¤÷dC`êW²ß&á]“{Œ¨/Ì¢y‚„|t8Ô€áu§FõâÌ„®�£Å¡N)PûaêOçð6µ\]¤v¯¦`’Ñôõº'+cEé$Ä剧°šÙ"¢L…¼P"|� mx†äQË.¢ÅÍ�ê²¾KøÐíÃé¼�’÷Ì Y.�%û ðÓ}Èx#áÓùê~3àÄtݸ|�u—Ê¡¹ºiáBÆø€×¤YB…iüMIçÄ–E*O�!Ç -¡Vò�Ó�n1{Ó ãÑU’ÈWSúÛ§µ{5?äWó–�Å�¨0ߦ�§¿Õý|øcuÕ³8åC¨0¦ìôðµ¢MurM­G-Q„àöAÉœ5y¯jÂ'§¥Ù��Úséæ×@ kêa7§?H"¤Š2m[)râèB«¾_ÆÖëÅêÝ”?+/ÉTÓ_�¬•*3¥JúÜa°‘tãýA[Y…QÌ”z«ÞÊ4“СšÊVõ¤`€Ooì4@°$ÞÜáq)�ß®(Ö  Ãš¾`ðÒ´ù¸ &7š’§@ B†ì;g¿• $ ž‘ÖœEÖë˜4@°P7áé L8%XÒˆçìŠAL\öéö 4fÒÉ.Qò<¤¥ÝÛËÖP± ÿ7DY‹/.šðŠÛT«)”Ÿ%NƒkDq@$Û£v�q{dfé¡Qi‚Ø«d„_—àc1"cá½gÎd'�®ùüÿ5 – ÷Tr�BkëZÁ#‘Þ:ùayž+“Ñ·$Iœðîd‚¦noºí·2Š»ÌÙ633MÞñ`00T\n#¢‘<þ´Z³W3]ïnªmY“ü¼Þà[5�&ê_Û+ÈjHÐ÷‚5ªØ§ÈÖ…2µÅć™Ã*˜ö+'÷w–iï~Èd¥+Pq¦¯S˜V¨,3“éëÁ$†92ÙSjwAëˆYêèÑC|xC窅ŽHέ�»{Ù‹á{Ï:ÚØËÃ-2X ý¸rMíj áåóYð¶âà;¦Ú XɯG4/…8Jf#CUZmu¶Ù�¯ô5A\·ýÊnSX̺°é¥Í4»Ctü‚öx¿{ÇHÔa(¸Ó=oõèžwÕÄ=ü q#Ç�½Q‡ú•³–ö•ß'”³Ì¤IKI'¨V®ÂRéîdç”|[¼b Ð>3lTn�o7w ]�j@Ä�[~S�» �ú¶®nÝÈ·õ™o)×cmÈ–Þ Mu $ˆŸF«ûYé÷K Þ£Πpw¿!œä@éÂDÒ)�³¶iïÆÑXPh›§,„eá a³;ž ß—ðŠö ¼’ip&²×EŸØÒ¨"ùÍyŠ/ÙÊ‘ïH*Ú²…óc.R„ßv�é˜Pˆž-Ýïa€0ð]¬È�Õ‘k¼ßúaäj?äÄÞèxà®$%X\ãk“Hä{�®®¯§Ì =ªeðUáû¹ ÷ºmö��x>}uñ‘gæÁªJÆý¶,ø³ Ùzú2�ÑÚß•’ EM$áþZCη‚2j’#Qö4ö™Ú Cìr¤GˆUÜ¿Wx•†¨*fæ ;^ÿhÊ€1— µÐǯ²ËÂôïDϨ°9p¬��Vy¸=çW juD”yáƒøŽ•˜ûN0¨Ë{ô¥XTU‹gDá�uï[¢ž,W¨©äïܤ`¥|^‚• <I‘%ªYðΓ£D<Áõ¤�™<°y½P/¦TKyƒã¿Ÿr2,—ûþy²áî5P¶ì¼X k¡äãȶ­Œ6è›yu/_þ+»ô§ÅhEàˆh|Ó+C±è·håk—3�k Ò†­;¾x³>Y–Ýý ’nбèWÒk¡ãz��îÃvH`] ^0VAá™Q¯Zd+P_ñÊ”ƒv;�ØÌdžÅN¥ÜáÆÕæÖÓPiË—ú}ùå>ö9i‰e¢’[ТûòÞØÞ_½ʶ 忨\°èêa­ø/š÷pð]ÌHï‹Ré9Ü•�þ-òå2nS»›p£üÿGfã-M^Nμa œœ�·?s\¡£Òü ËuG*Vdü‚ç.î`±™º¹]òðë«€gÞzVEüôðH;„PöËi̲ï€wÀ»Æ™ŽÛ%%Qþˆ––ù¹–›(j©al�‘‹ò¡�k  ­‹‡�LeX@‡”ÈíR›„²IƺÐmÓ"{““«”ÙìpÎ/Ž‘á7M¢x¾ž? <˜‹Îý`)§NÙD’Çbá jñjRo'³¢XeÝ£šAYï¿h†y6‡œ!Ykss!õk¸¿[3¢´÷§S0s¦• ˆ�gð9ÊýáòN&ØÈW•KÌ–#z:r�kãLUÓQêöÏòR…õp¤§`êZ‘uâÜë0®únS¬²ÁÌnCHÌP,¹£³õ;Nìábÿ5ÎÞG‰+è3C(™rGqõ¬–ÌSõ×XyïYåÈ6ûš�ö·ÝWÿëî l|ÊÏ\œ,AÜ|IBÆ™Wß|öÌÿ»p‡ÖlêÆwŒ®ŽÊE}@uüK´eÑ7s“�KKýÉÖ hJ�ÒùvýÛô8_ ë¤ÞŠ‘7FÌ4„(\ 3mOšEP7!|‘ùݬܽOX¡�}¼E߆ æYïчaÆU>âûÓ; k9Þ‹€÷ñÄøiäŽ$uoJ¹:„rµ*÷”°§ J ÇäƒúB�Ù˜0™–æ*“÷T[t6œô"þÍkþ(OTV7aµˆ"˜HB{ïÞˆÏÕ[·ÛH[Éçêøýðæç·pž[RfkeÞJ°ÎÛ/pÑšÖ üSwî¢àͱ*¢b`]Ƈw-ͶÅ}„„©��ÉèN•Ä:.úé®%‰¶(à3ðarí£šqŠÏ¤Êƒ[í…0¤ÀqGör«¶J� dYC¦ðwOÕ‡DÉ®'&å/…#èPŸÖWpUoìøb݈�Ak Jk„2©]èP‘&oÄèÁ–W˜ÚL>Áª…uf0î®NœmÜM$”Ü`éüeæP¶ÁGRÂ}¦Ë;²¹=3¢sÐ\ǬÀœN|_æ�ÿú ƒ8 ­ÉšË:ˤu„Ù¡¬¶ÂÄ&¤Xj“Ìnˆ¤ènCBbºpžÅ¾g�*ƒPAX‘ʉÃñ›?+~˜TV¥ƒp€ø lfòv•wÁí.ð|«òÔêÄa“Ü$[ù÷JT£¤¨«OÑ>vêÀÛY�Qië©t�ÌsùŸÒÔ‡�)ØO2�.¾‰A.íÞü˜n_…Š1\y ¸idâãÊ.Ù&Ò�ï¢ÙSÙ[Ìb�ÃLòF�§Rœ5ì�.8ŵÝWCæ•ŧ×Hú8áϼnH–eÎ 5¬ jmC6j¿Oà8ÔÓ/®bß@Ih«x ÁbÏÖÞSíø.ã&8ØCŸ�_ C4E§:œ8£ËÖt…«3 miÈü½[Cb;ILЂ�O‚ái²/æ¡[b »t;C]¨Ž‚®Êîe*ó1Õ'Ÿ·Wí[uRë+΂B›s÷2´¡vI]µÞ¾:ãvÎJعk«&"rŠŠo€ ¹|©ÅòèMyÌ‚ÊÔ jëÏnlr@+r cU»lšôÜó%åx1Ëxó›è±¹D�=WÞt%•pþé~€o�ôõÌP˜F]3Ä¿ÜhmCðè¤èNâ¦K©\:ðÆeq7œòOô�Ñ‹»®ó°G�Xî÷ö<Û �åË1Ýë¨p""\Þ£®íyoB@רÉfÇ’šZÕÆ‰{ #–]F0µ¾35ŒC³$» "›26ñ°Ó´ÌY`ϬÄ!ûÙ9%ò!T˜ˆì‘É()˜cWÐMú+xLÛ´Þœ}k… ¸ŽdJYô<ÒË  ¤IÊ8xšGÕ˜t ¡hlªwÞbò©¢'Pi¶>öÑíó´8Ûx}· œ‚^�¢šqô‚œÉ#p¬ågò?*Í`oýy+ ªp/"K_þa'6 £‚�ˆ6®â8zØìæíãÑ2öœ*Ê'Ï÷Ó œ•ˆ®ÁÆ�%r(=[Ú'´ »|úèr–F΂87%`Yy‚zÝDï~+rØÛ6ßMÌÁÚÊø ô‚’<Ä$æur&`3OY¹£ÏÖl•VÆŠj|€˜�‚^Ñ¿7œ¯\H%ظíŽXn×èE½6¶«„õo6Ö‡AÓôÛ™4J6Z�Ösµ“Q&6}Ù/~qVAèÂ'Œ©à"{§;ÆqsxëÂzŸŸ|¹¤“EÉ é÷»;LêÑí î[;Ò!Qt5Z MyŽÃÇ [ÉŒšÇv–D F0�KéÑG墚½ƒLu¯òÅ� WG'_–ª²c¡4Ï¡†û=�›Z]„ ¹ñý!n‘KðP½q÷P2³D ¥“Žü:ÕA�’AW^wÿÓ\‹Ct«þ*VeC¢ŽKv…¨vuЋŽ�L´Ó˜ÖÞò-åQí;ñ*=²Y[°÷ŠCÂ¥Ÿqc…~Cªzš”ª8f¼EÈÜ£ -œrNí‰b;Bí4ßÑœlÔ&f !Ÿ=„�«¡¬2‘ð² :‹³& \DÀý©»žÓ¸…"·ƒŒ¤W@l†OÇüÝb>í\ÏÔ�×\ØÉ¶¿ðw%lhõŒGÓýá]GÈiÆÒù�Hƒ8é§õÓÝmĘÃ>šSú‘0ÅuþÂ_H†&(àYOðï“%ªê ÔSTß.#²ãÕp@„÷@%˜RµÜò ?IUzÀáŽJû]Q>ävÙøR´žæò×\ÎF£¡ŒîPÑ{-ö.Æ%¹Ï¶ÑV„,ö3So—(÷ hjOóv®ëû›Êhå`W%v¼orú0| &~ß÷«¨.ö0¾qÜ}™¹ïÌ”à4/-·o\ÿ#Œ˜Âîî�:¶S˜¢¾}™4Øï; ìXáÍôMi‰l]^a]XYÆ•¬K½d“’ÁV¸Ä’1jo©�†-³(À[xµ�!u„JÐös·¶ÞMjÑåÛYˆíeþV·‡&�èdB’£0/Ž Šk�ò™cSVç‘=Só‘¼[¯(&%M•´Ÿ™õeÌ”D&.3ë 0‰�jDÍmË�«M7JYS¹ó@Ëóï°;î[·�¶,OõÐ.iâzcMPYååan˜ùªx-Ù– Ù£w9‰ëSÙ‰Θ¦!²-%JØ…X�9„‚ìØ*”jŽÞ;Éác¹ÿîw_�yM9ãœ^_ˆTÆÌJ ‹©#l¼Ú¦És/Æs8*B°�P/A-­zzlÊSŒƒ»ýÝ•n­Þ_Ac«5s!Ñ€Uø~’ŠŸQÉúÝM¿|M ¼s+Q7ü‡~¢UDÕš,�dDJÕÈ•ÀãÝ£�ÏÚ]®‘;–½±èÕª£±GŠÀ!ƒ+Í1륚;­í浂ƒ§’,”Sõ” »&s\u¥vO(¯¹}ù.¬=ÆÀD®WÀг^b}1FU Pˆ©IüJ>¸¯0¼˜�‡ ëç-èÁ¾´�l,±¿b¼¬³}¥j¬œÐÇTêÚн)0 �jèÛÍmÏÈg»ú3i�7Mÿ~ËŒƒøR$ôÍ©œ dfHî„©F´³ò0éNxEºE.à¹^ò¬.ÞÔWBscE­¤H¨¯0î.(�.Ý—ô “2Ỹ«2çlCÆl>Yð ¦iAyiKw4)®¥= >w\záó{q•cË•†á­FS…ÞÚjÄùÜÉŸÖ@ÜX3ñB]S5n7­Uœ¯›Ìðvlœû‹6‰óħ­L÷u¥ºõÖò¶“UÒ}Ĥ�šR¥.Øjp qIêBýË0ÈtßÞŽ; \¹s aº¬}0ˆ3п<ß7]éÒX%m©4�g?¶¿'´BÂ{›ª¤ržºÆGnžTŒ�ǬÒò“ÃlL0»þˆ>ƒ³ ‰1º¶|$ƒ¬VÏ7£è¢„øF@@L ì Å$º^4�X5X²Ýê┡³Ñ [MIá߈¤Ýý v`Ê‚½s½[„ÆußRÚ° !i =_M>UuÚø;S˯à[ÂŒ�P œ™e-„à¯æâö CL/Û÷ì@9û`~� 95aæ}¾F¼ùOY�[¬…\¤e(ÛˆHUŽù„~áGŒÀÑ£º­ðLß#0𔽯ƒ™+èÏq³eá€:ïâ˜+ÚKcŠ:GÎCƒDa˜±J/¯šH�M÷¯â‘q€”}l{­b—­îp Ì3SüŽêMs�^„lâaÂÕ×Úû�–lðð§”2Y 0/N“²\Éf™*³Õ(Õ…I&ι”užÆ€‘ÔZ½r£@ØQ`ÚÇÏ÷}�2JĸÓ'¾,Û3.D?„OQ6~Gf{1xh}Çþëpµ7Ç€(îÇãv{ù¥¡AŠýqi“~èbÑ+ÆMŸ‘s&×ÞOŠW€ÉŠ[e¬¹Aþn÷Ü0¾”s¢4£fžK÷2¼`¬po68¿ýÅC½|ç¨ÃWóÎàÇŽEâhpàyœ\òæ;D$wïøw")B�Ã(>"? L³4±ü’§:qº¶Ñäê 6pÖ™‚¸´?œpþUÅ|hI;âMÏ¿Œo£›:ßUxQ'S¨i\s€_ä¶Ú—M7ö!UÙ¢·Ña/PÝÁ‡v5äü�¶îƒ¶‘Há¶PŠ ÓÞÆ¤·÷3X×Ê?Š.2ÕAÓ"9gX$ÛŒi=aÙgDmÏÔO͹ "-{ÄPüaâñ¾Â1ªhê‰Åð'6;$”÷÷Y?±L-ê™rn–½*Öã¿Gí@Xǯ¯ rþÔ,²þÉŸ}ÖâcüÛXÔŒî–xF&¨®®›²lAk°S£àa˜ZÓsSÐpÈ«<*4Í£•tÖ €,4ƒñ”Ýs¬«Ä‹H­ÕB ÓØ+«žÑŠ5Ç“ä �rÃÞc�Á¨tÕæïh3¦Û_ï36Ì�syâÖ·vpû—2 (h{%˜Ýg«¡j2`�œ'ͳ:�÷·úÌ^ÙTØr3p>A|‚­s‘oØ£Õw±-k5Ð:äW´š�‘¶ULú¥pý0[KmÔœ’^Åר¡q`V“†ÕÇòJÈçàý¢ÔTwmóÛRŠÕ$†óaŽ<±Š'õ«â�¬ntÀ©wµ ¥ K•Ï0âZCÛ@~õ—RœžÜéíb²Ä‹rG÷VŽ*{¯Ú€ ^ŸãÎlÓèì�+ç~Æ$lž^[ðdäsªŽÒøOš]xpƒòp•c×k´IóÄ'óÅBPÙÅ�ålå)°D2Ì#”«*s™Eï½µ»7ΠQ5�nž4 ’Ï94þI’^qUçÂ!Š+3p³›³‰÷¢ç†—,�{šU/F#(ç 嘂)5mao‚d"ðžQw1�Mox$Á89 ŽûTÝ­(–B…kk“VâWLòdÍÀºñàÒV·Eòs�¸Ø’Á?kÁ%*Ñ�‹®4}û~ ;¸’•Cé:NœDë§Ô’…4¨ä»Å[S¿¯“‡¦�Dn Ñ~¿e&œþ¾iì;&Uße?”û¯>Ù€µH¨  ‹)MÚÂõÇP–¢êϪ]#�ª_pž<9‚üª[d•sï‡Uµ]Ɉ[éÖ”æÆ…¡Ž _éf»‚7•‰º…Å )�×"1lÈü}�ð“KâA�ªŸxÚ6‡æsñSQyQÇÍQØE­ø¼#Ns»DzŽôquñ=¦ÛŽ�"îÏàˆé‹¿Iþùâ¡ Œ‹É öA!U=5ô§áPi¯d±‰¢ô-IþŒÛç[º³õ;Ž8ÓU‰•8pc¨*‹Ô=ÇÒ‡–—ï]ßPkùØIáâðw’r>EÅÝ\ZñÀEàÙy…Ùâ‰(L4üd?ñ ]q‚CÅ$È@/¡3ªÖÙpXwˆÍË´¡ˆ¾ÄºhŸ¦¦Q�²Ôæ×qsA§¹Â� Áª—¯×Ò ±ø�ÚÂ5Q Íg®°ýoðkè×Ï^¡\´�ac¤ø2 Y¹5s�'\UÅ\ÔQ—{¢øz†êd´8¤ûÈkÐ<·AIJ_D ®~öÈ÷ ‰ïЄõ€õ2¾!Xu‹ØEùdø {”ÔY8» Ñ‹ÍŸ_ì“§\<Žœ|Ý3*5悔§¶…›3#›%ņ“åKcmæ1‡Ëêå*¬%Ú‘Å×Tt ' Z H§Ä‹ê„”GŠªŒºOB¤Do6µBE/–nÂÏ›ž.»uýf…Q½5v9ÄÁGÙ<Æ�¤LØ)„îŒâÍ“‹[½ü�¡´¤ëƒw�ôÇ€¹ã`C+«Š�æŽUÕL–ļ„"…†¡¨’cJf¶Ï'Y EŽ#\"¿×zOêpøÒvÓœ1i€�è¹ì…Ïu'û(‚š5/é^-·$×rþ<긬Ï#ª¡ÿòí_¬ãD⨯ÎdS¿)–�rPï¡ ¢Úר¸;Óá¦Zòr�ƒ;vKѽ†³NÃ�?BªP£×�HÚ¡7ñ~Ö,IÕÿ‚½� þ¢ÉIÐ�>~™ÏgKK�P† C32|ò4:FãR‚þÎ =$ÌäN_¾¡ÍAŠÜÒ Kj¦ðÃ6ß«DQôƒ{ì¥<[DøB³Å jz@E=¨b)�¹´YÑI�QϽ?�`[õC»®ÛPj7_Ú‘D c¼T:ÃæËʦÉ2HCq¦À”µ‘íjÌ{ · ´­'3Ë+�ä^ìho{%štG™�Ä-c¼�2ùœ¼H)ü‰Çq”¸t�ލ�q!ÔÀó›óno†”R>zB`$ªÆ™FúDoRY6”®=+x#ãú‚¯…ƒÒÇs ÏÕªpòϰí\ì–ˆgÁØgÛ Bj«—àÁ±+þq€³ÓtA°¦šM’�Õ—/3Ÿ�MXÀ¶ñ&\RP7U bS8“à¡òHð6œG_!s4h7Ñ#¬͹&ÔbVÝዾŒút,G ž(�ˆ ‚æÄì�Ï]ȿڃL:þ.úäà4¶8w¥n¹O¿¨G™�`W¼½nŸíÀ�Fy�BïÊ­õ>a•ð‚@Ø*"”mE³ûtKðu•;iÕ¾°Ÿ×ô– ¨Tôƒ|ìo=¦S”8¸Ÿ#¾€Ý×íœ�yß<ó^Ö4ã·ÙSþ�ÆÅ_ž¡MÍÊÿ¹‡÷¼¼9IV�ðF¶€ŸçGR@Ï ¨Q cwƒª­ íÖ 3ˆdÉ؇¨L'”_ŠLGéð Ç€¯ñ¦:beë®!q©õ„!ðˆ5©­E«E/¤#CgÜf6±DHN™% ðaW•NûÀã`D†Ø‰4¦ƒ¨k૬ÓSGn¿>íSAŸÈ;³°«D$²Æ³¾pV&kJ?ÖLÞþe”T8Åd�½¿ƒªó­öNè*&uɬ›‚’ªøag5Núz&û€@q©_>8xXh…]º£2z©e=ØÙ�¹(¹Èæ,ö~Ú�qÝ‹Šê¤"f¼.p:šíUWöÇó²ÎF9‡+~#¦¬`Z(\�6ZôÕ¡ùÏ%²ßQÈ_¤ñµ‹49%Jîÿ(4SaÙJUžcË©9õÒTÈ1–¦D%•%UK£vÀYqáÿ¾}©†S`&¡€<†eOuªÇ¿š ÎýÞJð»Òb.ӹȾaS¾lež˜q¢‘Ô¢|묌‚©_²yÖ¼íc’mn.öààƒ�Rh@ÖVýiå×nUC{ñSªÛd¹#8A¹Ä/»8»‚¸]›ÐùóðËÆïYð©ÌôªižÊ’à^ÍB@kþšù ¹áa·ìXóÏF;ç¥Ôæ.3èAè'¹Wá¢<'�í x³©¦_šÝ,úrP‹gœ äbmÄp=øô§à?¤0µ¸<ä¸Ó¤§¦ô‰vT¾ï>_NY ©ÑœòNW9Õ¨Sf8«€œþˆ%’mEÕØQ >á5SŸ] o¶‘½`<åºý1*; ó½À‡AvÙ‹N�5‚¦Ö軳@'d9ŸZ«Š½  6$í“(‹bÄÅ׊ÝóÍk·¤µ¯aC§Ë $ëwÛ+”Г‚ãU\�™8ü ÷¸´ùçÞ¢4%[ë(F*Ï´’šÃ4ÁöÝe|ðÿ\I¯VR$¸ºQ\ò¬AÁP A\Š9ªl BM†Ô¯ZãVvv÷Ñ›×_a•a¯�׬Q‡ì†ƒ„ÝSñ*É�6õ½ < Æ¿ìîYÎØïwÂnÑ ¡‡¡ É6Í»\€ îmš~Öý¿œ2Œîý\YòcXU™²¥jj¥üÏj¯P¼¹£õ_¥ˆr›ÞYxLßìkßÊ‹D§«KÝÛÜþ¤ˆY4…OÝží (û8°XäÀ[Rõž¯ªhcp3§ÏïWÒ#ÛÙ™n¡Ž.ÊGoö³Ø_S` /–yaxNh‡“xæ_L¦Šf„¤)ŒúCΛ®CBjÈzSF*4.X¹�©glèí�™NÕˆAÔ¹°LÐQÔÉ‚˜ÖŸå5# #�½.§É°ÿL³E^²âxyÿõêòCçæ°�¢Ê°¡±CJNâSô ÊaåÂþ;ƒø 7kªö;¨R+ù$Aîñ«½ºù ‡Âl5g‘È[þس�h@/¸ˆÚ '½Ê™V³%ÝÃ~­m¨«,$‹(×äëëL§ghY#hF Å¿jF|Ä„pÌ`JÒzŸJÚþj;”!}°~ŸÆÙ7Û�Ù3—À‡…³özŸ4Ë–ùfJÔ/O?ÿÎY}nšj�zÿZûK¹Y\E#Ÿ®‡í8ð"ÓˆSƒÙdˆî×…€!›²ßmOóõ¹c�Ð$üöC1ReUOú×>O¨&3ÓßÏÞW¾ÐX„(;3ùŨTV†‚1 /Ãn�?}S³å Åá0„(ñ%³±"]ð4îXdg…�´EìÉä´eCz$B‚F#ÒYõ‚t¤¨lsºy6údM­T·i;p~µ@²XÝ‘óí80Ô\ߺíÙÃ?”·€�Xi¬ƒäazÝn•¶ÌAtF먊mOú1v'|dŠ­h¹³2¥U~ÑrÔ9f§ÌàIø�892‡ CGKug:Þ¨/K'ðnqVuXvûÐÍÈòg[4]YMž(Us5Ö ¨‰{zçèoaÁãÅQ‚e»íXk=³j‘\VOSá$—üÙÀo˜½ævÛ"CÒ¤)©Z’ø­«f( ·EÓzs³èÔ ƒ Áˤ ’¥hP+B3Øyn†CFÝU”Å-oµøÓr¹©† r û»dЊñhîexkŸu¼ä“b‘äQ®¡ ;ÚÐÇ•ö‚k»d”bY#™ðâ‡=eò³G\†kWSGNßžç$�›ÁÓ—d«ƒa¤7jº;H�©±ÐWwæùø‡¶€)Z¼¾óÊ ÅF]P~í¼ñÑg±›Äcí 4„â½ö5R._8™æGÁ_hÔ÷ÒÁj�jª¥-+Ê¥�†4…»K³¬Ù"´6dWQŸŽ “ÍÆ�¾1¬Uœà·re>Ûaü¿Ñ˜¸3л֬(4µÊÝß�u$æ†Ï X„è§™S'?€µÝßÙãU� @UaR{ ¹…Бá5=. ¾«‘tñ­Ì†£<8j¨¢z-à¿Ñè&Þx«+·1òüˆX¨™å#*Ë»~3“�ï4^RaÜ„ÏÔÈî¸ø �‹`žÙ¨`ãV?±!3™K´é0³ÞmLþ ˜+ÝÖ,æuÿ­Ëñ–�±~MÖŸðfN=IÆä²Z,¨Ÿ¹’Äá×ÛÍE­Ð_s˜ž{ó ø²;OÈ*ú�߯㽋'ô?»U¡p ƒ$$³c+c«‘ú.E¥4]~ÝŒ¿\C:ñT¹¼üs^6gÊþ‚2<ñëV\t$V§¼‹QÝNHõ‘|ÜS‚!Ïœó|,F¿_§¼áæ &&˜µŸ”,‹ngëù3A!}-"fʧoر”Eê{-„ îü? o›ž¨"¡GÍmeÓ±ï Òñàw=(8É_†àÇœõ_70ŸÀÅè½[Ýúkî>ø*Îæ_ð‚µ“Ë”›N‚p–*8L‡Wã˜ù2l€å’C7šð1[MXqyº¸Ìÿýb õ°>~$ÿPêh’�Ù¼H`üˆOXüËp)÷"9dv[šÀˆp«=u‘<¶{¨a¶6Â1}š˜‡~9xPéõ —MÍ~ÿîÊ\æû€±ˆ0¸ÏõÜê7Yi@*�€C¦<ƒó”/y0ì×e%Ìoì|/‚£�øÞ¤ •Àõ9jERÊ°ÝØ3TÂoÒŠ` ve,ª)¹-EȈN�ËH:þÅ�u¯Y\ÒÛ¡-Ë&\x¤¯¾'ÂrªÉýk‚áÍ͸ݠ&°8ršS°UáŸã°‰±De<Û¹­ÈFÓâà=˜ ‚�œïQ ²ŠüØ0C•­wÊa>ËV.H-à ä âùzô[¤IY#’kD9”‡Ñ†ݹ{_HMyR¿+`¸2Ž>�tôü–ÔX–H3re6°×òÓ¬Ì$ã¬kB¡&1‹:EÎS‹ë@œ¤àÓa—²aÜ·+³•Äù/œ:ì–�2*S›sæ{†/ ß½… ¬Ó³ yþÞ_äÐN„}HAðÉw8ÛÒ<è…üw!zÛ³›/¤ÎàÑ‹)–¬‰‚»©/ôo¾ í`ô[ѧÔD‰mÖÑ�½Â‹õbs‹¥%ìÏvu#Ëõs¨ò_œÒû•& S1ÞÐÙ™?“Š¥ÕŠæl-à LJf˜“& ¸»T³rX+ŽÉQƒ¾:©`ƺ÷CŽŽJåh¾,žz"Ž9¬Ø°W1%…oÿÚ{píÂb!ÿ§~ïÇx@ éÆX£ÒÐ øWKoÌÿVó`WÀ=L•Ï—5EÇÀ•“"Þ\mUÔ�-eÖ-ëUÛ ý˜®ËY?c=An¨`7ÔÛfÖDåä•Çó>HÏÓÎîlCÆFÝJt«hclëm Rù`\.dP“$¨›bÇC$·•_n4®¨�ERÀn�f—!ØŸábÆlÜ›C8'Àê ™®€V•„Ĉ¶�pOPËyú#Ã{>5bfº¹¢’‘?䡾wÞš¡”É7NËöÃ1†"Çé郳¥p"õ cÐ N#FŸ¤ ]RZògÒþˆíêGÕ+’(ÐjD²¢kk z¼c¨ßÒ +�×¶­í(JZ›÷>DŒ¼øA‡çwŒ‹Án�á+§PЮ0¹ô~C%T�.NýÜ/˜ö¼³d ¥ÈV’ÎÉg’ ýÈ"Ãg–¾Haþ‘�òÃÞœþ¤0*HÒ™6‡ûΜ´Ü‹sôYð�kÈ"e1_ØÂ¹»�S°ƒº?|5¶±$� ^“—ãöù"r%ÿLï¤ô3tTDŸzR¥ £?e©Ä+VG.¬êt+%]4·9ý3ÿb~iÿ‡ö oº1Hê-sÐæ´[BW`Ë´iMø ·4¬4f\ ºÖðýدGýBð´ŠÊT-hhœeìL‡H»©r?0 í¤D³±ç÷x„k 3ÑÐÔ’¶w¸‘ó˜Ñ }êsùúQ=Ò;¬�<§`¸ÌS²­QúÒ…îÏ;’”^ñ° \(0x�Æ'ÓËvAùyzfø½Ñ¸y \–ª'œˆ³½Ò±ÃïOlÞEõ¦F¬5ûê0µ¥NýHÕKE¿n+Û\Ì\Dpºë)-p>Jñ¾¾v7>î¢%týïýå§AħMMƒ»\žn@à›²ß°FÏÑ‹›‹axùU’Âótвjw�ç‘bÛóQ”Ùr-ïÆ’¤åNTÕ»­{½ÄoÚFùËž´œ‘~) ¬ö3{UÔÙs¨Þè… ‡méѽôÝ ÛynXå$’Lë"Ì œüîŒéð`4Êã'"_ìÒf³tîy¿Ä~r]’Q(^|(•G…Xrk4Ÿ•ôÐú"W=˜a›ŸWåNN{aÙ‘­#Èœ”å ÿ¡¤Pó‚úòo¦F«Ä¶Ç×faÕ `®L"}†#ýn³[Š ûU•{6Ò9NÈÿYH÷ïþÎ&o\ {CuÉ çëu«ƒñWžš‰MÈ} (-C†Ä¬Æ1�Bañ±è…hïž·;Ì“²U›w�iýG7 ÂE·n1÷O`µ™‡†Ùsr°U ÇŠl|•OÀ‡‚£–)ë†m]“—6è¯T*âž_±>ÐÔ¬æ¤~KUÞiai#w£}%…ã KÀÜè EÐü¼¬ K®•6r ¡4Úl‰zo5Vtœ,¡^OŒ'VxŸd®Áž¼@šïø�‡.`#±p“Döו‚º4Ñ�—Å ÷:Ÿ"IEšú¾„ÊèM~x›1ëFBß.w@ÂÃObV4‰Sï„'{ò­2�OXb“éßÂñ¼zJdÃHÁÒeusÏŠ-"Ÿ^½Í’X^_M"P»J!¼;ß2èãy:’ÔùÙN�Æ¥DìÔL¸—B�,©5Œ8Jnzû{¾�štƒ&«ü%n¿`¯ W§g0Âî`:UeX%mŠi~"š£ÎÀC™ÚÛ 2à”èÚ¬rƒZò‰5²>�¿%rµœÿ8‡ÀU+‘J³±añ' ‚ÇC­yp¾\ðõæÄ¸Ec jÐ 8–ïF ºßT›9Ö~Xø²ˆ<[²hÚ_7‹CÞ¦”*ÏèÐìÏS(tÕ°Œrë³ÈOe!ªBü?Ó%d»â ³>€Šö£“€�CŒcG°ú´×y11»Î… *nO-5À*Q¸L¾Â9à]×ôàð©¦öˆæ` ±’.sö9ƒ“)HàèøAo‹g‰ì_Ûß‚��ô sU5•Üî/k@�vI äçÙÈ& õ´=0øÞ å$L$]hôÎæ_fh3#Úî\¹¢òS-Êzwtë n)+>œL„¿ƒ‡ëõ³VÈër Mæ¶¢T\AÃÑþ†Þš» R !é¡{;¢»6^µÉ&5ÈìÔ'_í^ñ²Þ¹Xøw@F²„l ­‰›ï±ž††‘á…¾ÏY6ÛvvÔîyÝó¸mlõ”’ÌÚc93)«¦Ïðæ<-Kpª[‹ªå3MHŠâv�zƒÃl2›u¬øJ[îô'ÚÖý�&ú±Xï²:¨º…Ù‹fÍ=+LšÖA‡Ï2Ð�x"¯Á“sL{Ä.hs7¢Ã“­ig|ÚÑ” ôÊšz¯[Qo|)5Èé(›që+®SÆr©[Ø× ì�þ’/û$nb™:öKòkÕó—/ºÓÔ~ô·Ÿ'æéᯣ„veؽB€kµE§¥M‰¾A³,öÝõ›­Ót…“dJ$/¼¾[‰­2£hG,$qm«¼”‹ª`Uæ6/ ’vÉ •ä`‚ó1öêš Ö�"qya¢Æý–ËXO%3·ç�y]þûR<(%µ<<ˆžùAô\ÝK© ›éE@¦ -àߨqLä¯oÖZà†Oª¡[>”sTjü6²�˜=låšö¢{½Â¨ý?%¸£ƒèÝ™œ2{‰• V¿žl$¨îT~yKE»šb?F›c¥ñ¹N[2@žÕ³î) ÇBîž8—ˆ;xÆ‘6ëË”X­úóF¬ŽÚ]’ò!nÁjÒÇl¯aì²ÛÉ„jÊÆ n÷þÅ4 #[.kÖž*¯\Êv¤¥Æ“©fe”?`ƺ¹´3±Úuy¾åŠ,,ý‡Å¯wTD.EIù›ošôJ':ÚàêJjª`×Îöj>PŠ÷x¨tä64Û½IBã�‚úè‚1y‘;J¤9FÓ[2ÛèÜ5ÿÚGçôô}ße®º©lÛÞêÂöƒµ„ñ·=8]#Ï �ÂÿËF±4[òwê$fM�,âO“=ÁÂ×vÎïø…(Fî? ˆ^5±æ7|ƒÖÖ5Qœ`òsjŒ_ŠÆÌ³X ü‹+·;Ïk¸L¤üD‘LáÌbê“Íbî‹Bü !=ÿ xB×¢� $äÀʰˆ Z&‘!a;mÌ Õl-*Ò¶7Q¦ƒvˆõºjõµEÊÊÞ3 7ÛØÄ3øtB�›š~:C1ãÌÙŠÊçî Ò¯¢bTŽã»ÊYX²øS!hº÷Ùã Æ ÄýýxÝŽ(´Ò�†g7I Šetذ`I Ì 6HÇeÅþSè{òô·?ÂS;Ð99¡S�”Íæ©4\„IïmR%At(ü‰y,ÿecÉh`í0²ª£ý¨Ý\µ!Ñb&ùÁÑpq ”H¦@ –E76g ÒÃuzrÜEƒ5U,6BéÌš~ćô…ì5MêšÞÝAº�# ÝØø6âþMi2€Ämcqο¤=WËÝß8Ÿô"½+ƉX{ÓÞˆ¹Ü`eéÉoä<ßzo2w "ôfùnÆñc‹Y�åÁ‚O€Y¤ûêõ¬óÒ÷Lwmœàî�8aN3šJáqKÆ7·.Y`e-ƱoN%Òbsõpbêf&~tR«Æ;怫'NYçãÓ¨6{l �LYjÙõM«c+j º»néy`f†LW\ËUBË„Uüwx{fFÙ�º9râ;ª“&’¯é(â$Zðíh¬7Kä(BÌhQ7½Ó�1Ù–¢‰ß²>ªž‰wXxDò¬ÑoâÇ111ú%íGƒYìvVM¼G‚R z3×l¾j¹Ä! Å CÖ…ÚøÏ1ËãRKz”Ó"wœ‡ñˆ¼L™ZOûï\0$Õ+Ô,¿¡“ºp±êxŠï(HèÚ;V˜¿u­¸b³6ë’W‘Ò?Q.¡kb™îîqNËbclÇ1ǽ€Ú}˜TZ»ÜÜyųDý¿Ê·ŠŸB鮺#É4É“\@TE[[lÝJmDäg¢DïÀ‚èÈI¯úÚAïsƒ¡(i3&t]ðOi3æDQª>Á¿ÞzQ(î˜>OG[Z°ðÑ‘OÅörˆ‰-!iw>WPcWÞ�jõPoáBêòÄ`uÂnÖ3^íF"J`AFuÙ—g¸×ûm³üšßqéhÛ’ó \1¸[eóâV±¢„ÄÆ6 Pg¤žãz»´‰çÙ0âÝÖ ºèñ:Û=ž"F Ÿ&îkã”=m8°;;GIÅžeþE¢þ†›¿ÃýmF¨i:%rŸ}2x’j5?óR,)óÐݪ0"%½ÀfAà:ùtÍŽ×=~41(üHN¬©ç#={ôbaеøO�ÅFkóýUçìý ,moúÝíÄ€J�^s"q<òÁí× ®Q¬©<Ï왳)dx+}ÕC»txÕíó f!åÄ ñ&ž²fØl—WŒ”Näyê^{섚 yöò”ö„YbJ‚ïI¹¿:ô8ÉY9ìñ2GtÇB„½#¦G³Œÿy�<³p–Ÿæ �æïF‚ËLÓ–�lx8S0üKP›2B†S§É¤uä„g!ã\p"ö`xÖ“¸¸Ê¡ƒÃ‘–ÀêÔ=1gf0¨æhÏ: ¬wˆ?woªÙT9~¼Ï÷çiI™²CF†+AùMÊÖŒG§‡væ/“ˆbY‹‚@¯Až¿¹âj'’3úætÕô¶ª×®;‰Ñ걉³�ÏÀO{ÞW* Žž ÄjFƒ‘cãÿ«ÎJ&HœÖ�ëFÝÇ©ƒB¶‡­ÎZÔþÖ_ ©äWªi©Rñµ³2(ð•á…;&g‘Ù¦û“kšÃ#Ñ]ó6ˆaû�SœÂJWÔm„c$ðrßÄ7/÷óDáGú*�´J½Mµ‹~îU5Ÿ —û¬� ²©q¢ÉzFUkf‰QðM¡¿£$溫M0tãÞHD>ú^e/hƒð7žœÚv̶mG”Õê óºJÞ:àfï-e}=×ííyªjï¦Û"îeÈ~؆šWåÎJä°4E Èò�ɰ|ÞH7µIÑŒÁÙ‰QG2_8ÀÇÍâÆûÏcçUtFBîÕÛÖ¡HÝ×ÿ%î Ãl&·Ú©nŸÇZõ»6òiLOÍàSÝÀýv­ÙÉ€wÑë\ã4×G�iªÛ¾Q+üêŒîÄxh€W�t8…iQä ©9gѳ·6qž[!¢†xµ{úQ;3®Ì¤&]!sÞ÷”ƒêÑf÷×fÔTÚ<ð±�ë²àá‘I™ÜAíÏ *Xú� nÿ¤Cz0ZˆëL73¸üv‚‡ 9ï�j�#œ ëCåVh÷–•é¹¥é8?¼ìÛk0¢xžÞ÷¸ÛV#ñªàÀÅÛ<¼§9·Øý³øÅž´6ïU!ê¥ı ³/€Ê|ï²·pÅë†Ø#œ"FœQºC±£|"´P°ŒÀB7'E�©4f»>34aÁTä�á1ÜxI¡‡f9ÿœvâőɺ k,gÖ2:øâd›7–¶ý'd½Yº&ÕÓ0ÇL�À¦”„ͳ†û�ƒËÚ-ŸíµÄùí+Ùw—9qÄ)Ð)…gÄë'¾Ò¢.v�e¥pqw­‰/¹BY ý9,´ümΓæU;wËígŒYª\)¼™¨³’£_‡CZüYC½ó==:�ݵÂ]~ƒ´[O]ä§üÿ¸€êÜI¹�³&ydvK‹ „Ü¡:[cÓ�@¥õ#®¹F{Ð.¨ÀµJAZ”Xé�̶LÀþ{#7&ì»ÇŠoöq]ó”;�)rŽâŒ Š[9,• ChçY²5ãŒò$O.:·R4Xˆôä*ä²äsÏÇý}w�øO ®M·D‹�Ý6¡v&ýj+>�ì\£$g!˜m(³ï#�0,¤@c:aËpZk�V|åR±Z`v­DáïíZg-+¨£Û¡¶J_ œê#VÝP½båMz;õj]Üû=©Ã-…ïTY ®ì7ÝVµ6 ôÁ¼ø˜ï7ï'ÖšŸ@ žÔIZ”$aаno]ÀWÙH{ ®|‰t ,z~ ãõÇ"Žû/’9¤"×86»µ>ÈÊc´¿4!ø—8¾. ÈEUH¬Ö÷H-¢Ó�ã!�—Á² |zh;zbN›|]–­A¯¹°×�§wN8å7Çáàû�5Iôv”嵤KÀlÙ’XÁ„WÎ¥ ËEØŽÆm’uæž�‡B"3 E�Å©¬jÙ«4UßÞœ*?…Ób])¤'DòQUÉHpÖÓþ±�S£wðU‰‰„–D»+ðÞÁ¯¾¹aîiJoë†Uñw::)ü*d¼ýÍ˦¾¯8öæ0Ä _·Ë„Q�Þ„€@]ÿ�ôÍ›r•ëÂÒf|�0‹O@V ÌM/çW°´ÆöùѧøØnÊ–‹Õþg”…ÂÑH´¾e¥tÁ'tŸ‹½©m:ëNµÈ†–WÒoo= DÏëÊ`-v ÏwíåþÂÜTŒ[ÿŽÀ׸f§Ekà.¸�Ñ ¿ 0[äô·“ß,†,=e ƒ=VÔD4ßôj¡ßñ¦!+¶êY“ëÕED'vuÏš l[õ QÑö+ƒ8G ¨ŠÅ²WÀ:ð£W×¢5¿M¯jÿ·Žû„Ü4D�w+Ý ŽÂZ1¿üÓÎÚæÌ˰è+ž¶å0GËÖ'8fìËþˆ"¥äidÜÃ¥�Kv‡ þ‚Smò9€qê¹´žBñl:Cìú}»?è Éݹâ€ë¸ÊKL_‚ªg_ôé*^]ìƒPy¯>ù\-h »=n(†'—d×JÑ„’«  ƒÇEC 阻¦…„*MO„ߺ?Ú€=¢º!JRVm©ÛÎTùþÆÉ8xú7$X̜Ԁ_"p\½ËŒŸÚ+�ìz|g?¬:µlŸ\¬sŸFc/y\qŸ# 'ö&f�°j­Z2cÑŽ"ª/bZÔãÞ³–ewÄñµ²œŒßªÐÂ{ôì’lAll0¼m¾&”8z¬èjòîÿƒÌìþÒßbi¤áùßFQXÅ�4Í(ȸÛôÔ<¾³ÀƬeÀ€ùËe}…Ò©(2 tBt¬¡ëiP.Íâ¹ÐkCQé!bMSKBÖ®ê"‹ù› \ÞûÞ¯¬5sšÇY¢º‡ÑÈžZ[ÊÉsã½*‰uQ"Ë Ñ*)K*¯¿^4°,Ôx>N;ïëqnô¶�ñ‡öÉœÔ kËã.Üž|Jf ¥ÎÖOÅôÍ©Ú4Açè¸GÅýå` Í<GÒždHô{pRH»í†DCãô«|úM§Z%¿·ÖB« ú쾫Ø4œö!ˆ3�ÃAç©Q檑¶ÎkžêÕYPq‘’ê„~«í<Â2%ÜOÔb©Ý_m~�²þÖ4.H ¢á}Ð#³@œÍއç(~a,–X5}MÊq×¹r7åGqJ²ÆÆB¡mìÁû&A-Q)VGF71p'¯Ìucë<ëGýÚ£#ò`B‚¤ÚAÑ/TõB¡Ðq�ì?–ºµgZÀ(¶BD«Šô‰E1È-¨¼CŸ«ÖPZ—ÌIS'0‰Ï�½+b“QÀ¯Œ.`0Bœ›{6‰ކ ·v&C1)7(}?ì˜i͓į¡ …\C Ü€�5'ÿö͇]ü!êÉGq­ÿ”y³õØX1FcópF¢ÉóÈ>~ wöä£#Cs˜”Eò:cö·¤tÃÈûâ¶é]3ÕHZy鉖[ËRK´³Íà„<̤MØØ§Ì‹ÅÃ{ ¸ zí‚õŸs‹\Ø”Œ“WÝ‹¯JìAéÜÔÖ«w/@Sî*·¡6)i Èœ†â}Ü4ÊO~�ˆ§úž3-'ʰò®œá’îò¼´¦»_��Îó5T‹|eN:L�{jL'GVGsæ³ü„ÑW3z6ˬ?õ»’¹. ´IC1û¥5ËC†�™ Wä©Å  ÃkDŒjb®Ë¹9k4�À¢NìÜG÷uêû3*Aû"„Dà÷ÑeÆz%¾Íª_ôEÁ¯õ´Ë™¯Û`™¹Cƒ˜÷áø. ¥“üÄÛ½}žiÖíp±\pi(6qx“éùÔ)í‚¢zsÚD#R˜›œ+™.º¶&=·NS,2^ó(j$ÊÙÀdaÝi:šI/Ðã ëH{óç?mæ§ØSÆrØò›¤¸Pìf|• Pcq3{þ~@�¦kŠ‘úÌ$n:­Ž²>} œ{hã¨!oø ín°ÙÍEFE£ zý$eàG Â&¦`¶Y壔 â¯k¸o'ˆ>A¡á^"/ØÙN8؉»ôj‰K ÒS3·=sàÐcZAq0m UðZ¸¸á²¡$fª®ñl5‚’)z h'öÓ-Ç'æXM/6þµ¸Ø=Nì‹­W)\_Ãé@ñþÔª‘9±38/žj‚é÷u¡ªdšC¬ìlv8’å�¥ÂïõŽ[ŽïíèpO 4úaé�î ?Â%|`2ŒÏùì¯q+ô :Øù|á'],¯X;[uâwâÒ ¾¯G.–!?ó�Í‚ݼQ]Ùê!>ŠƒKüþ­7ì|ø,¡˜)�6Nk©CsN—9蚞ɨÊ~ÖŠà× TˆužçG/ ÿDqW]8ïý7]=îëÞîV «Ã S*EÝq^±}“ìåå§¾~íwèEÚ¯õÏ]AŒ%7]·z…1ºéRJnu½ Q¼:8憢>;õ ÀfæMšÔ‘c«[¬~ŸÃeëÆ}Û§¡ÅÏJ·¶=�ɽ¢ؽ3’Ú�ÔéÖLä�÷v6·°i˨µÉz Ý!¤.ŒápbÒ¯›H‡›Òø¾°†ä¡,4ø-N#-ÀÖ¾±½Ò^tü"BXƒÖL ‰}¶J û‡Ï_5íÐjn(ô±Ú‡*ñ�¿:¾h‰Ý^×v\YÔãŠô&¢c¼‹BE«ƒ‡×%.ãØÛ!—¶¡Û1‡æ 'E\HBcìĬQ•JQ{ÙÁ Ž•„v†À Ú«Øâ— íf—r"ðåî2$…÷.ïч„¶Àô¡…Nß9=vjF¢GîàdœG«ñ‘8P¡¿�!6)š¾J±U"i2?™±üˆÁ!)t´&Ÿþø¼ð˜·XÙ“Æ?×pKeØþ„¤ü$ÔQ(n½p;m°Ý%÷_¥ñ`í©ZzE@�ñî¥Qñ}³m|†¬�˜›…eaZËwüPûÚÝr.ïÀ �FÖd‘Àöh€¤Ã×O¬³û ¥<%‡‚>3GÍZVAA뙞j¶‚!ä‰ü”åitU�¨:Ÿµ¢ØÓ¨…´µu,Ùü­Ðk“U8¸<Ú Á© üÓTwEsš+Éù]ËaX{¥rñÃ@•èzÇ ¤ˆ3c¡tìww�¡WÁ1;Ï É-PR^£–<|ÍnéZ÷Ü·}À[þ¦8Ü�ôc Þ� uñ~ä/‹5¶:B<†ê$:‡Osuž¶7�è#Þ9sÑÙJ· ð¸JÕA]Í9|Y\åÃÏsÏjJúD¾ÅÝÀí/¼ë"4rùØ"t˜+¬uF/ã'Ž…¢›%€rÖ@Èi±KPm´¶Æÿ:t©dœ•5q*Á‹9EaF!ä-ZV;Ò11ÎÏÈú­Þõx&C¯´¨X‡Ö62«"5wj„¼ÿ`E“h�9¾·Ë÷–hB:lEá§?èökç5¡ËO ûÎüÒdš:aþ0h"âR¤>áó×ÇDÍTE;½Æ… –â¥3žDt‰. ;váÉ®KÅËmÙεʒ|í D«):³�7Ì»Ü3΀ÊH>‘§~~]\:¬éqóHPä;’'׆äû¹“癋§0ÌY|sèòmk„îÆÅÔ�üY 4@åý2j<”²œÓ·¿œ_š5½:0r½©‰mÝj"Àµºì¯”+|™C=S¼ûE²B_3;Å1¼€µE^ôü -@—B'%Dï% ìúþò\A ”½v¬µ‚Ff1™,\[‘-Æ'v1�ä¿9=ÿëº1Ñ¡`©qÿ` “ó!i´ß¾"Q¤¾ô�©!"7zŒÃQ€Gæ1‡iÑ�¬ñ«œÆ%D_!0«À‘—•¤Z�$ío†fŠWtŽõªRüà€ó\W¼ Ì4 ‚øŠoúd=ôƒü%ÀôAJÏÚó6VÿMÁ�kËê}Íf% Ç2ƈZÊwù±{ÁLkÅ9®²Œ¾ê”f—úPéô¾+×íàF$îßz_m�l³¿aÑŠéuÀΘJ,¡Yï¼ñœ¬5$Óú‰ûÊÌÍ^„Ù«”({V[9ë¿ÇÄöE9If›(äxÔ8eÜÊh%%Æ0ZuÐÃhûúùPUÅ“í>á€`ìß1¢ ²5–• OßþP7^áÆ~í 3»'C:Wƒuñ»«TÈe0ö½'KlÖ®05ðnQkµ5y>#ÓþU|”6¼�‹¼¾×ÐUŸ[“#7Pý|ûA1»Ì^`ؘ³áØ5 ¶q£ {»Ÿ¬×«²äðí8-K¤sÝÅÓ¸ºö4r¥p"‚ ¨„¤àU®ø:*ÅQâ!î~v yðSk«½ô²™D©P¥§ì^z…ÜWú¼†›2°´RJyF‹ÍÎðp ªJ"™]CÊó¬àüÇF̶œU­Çz¶@¼mT2Da.VݶŒ-Df‰�£ÏÈŠÝﱋæì¢¥` ÆEVb1¦ <'–â̲¿~îR¬À‘eÆvWw¯ôlSc�zý ¹gžrõ]ë)nê!M:޾þXPà /çÀÅV6:`°KB˜²G Ñ€s;•ÍW…*Ì ÌèKºˆíñnŸ”Q×=®àÔ”]—»æq¬¼5«L|Ö ¥ÕÉ*£™ÚL‰uˆ·mI�EøôT£6³$d¢?Ý>ŽU‰SYÍêUÞ±'ÔÍWÊV {}íCAýÝX(ŠŽhvu™bVˆlD  ‰yŸVörý»Ï E„8˜ÝV§gRþ¹àñÇaL¦ŸRN\…g™tT'*I¿µlçB^JÊÑ´/žî‡_ ÷¢n‰Z§‰j IÄØ€}Õ¼R6�GÌM3Ýãÿe¹B/è¶Í¯º™\6¬+ sx`_•…ø©ΡÍãÄY¦®äg:�Ed��žSWŸ²…@h¼7ü°c$}>Ü~FS1®ŒÁ gê*xJ0F0¬^ †Ä éçêÈ«ÿyã_ÏÌ�þÄþ y¢éÿj'ú~Y' æŠì�z4÷žI¯C¼éÊ><­‹¥zdgG5­j<úߤû¢Ü»¦Eù ž>zÿ‰©¦‰¦å¸¥t�\>�š]dú¯ZòW—h#þÆmdD“Ñ‚Âc$ì¯üAó¬xVðÂáATP:R*Òv ¾­�?p ä»ÖÅó±J¨*L=ø[’/iyZµÉav¼O¹]�}‡{óÁå뢺õ8ínöŸf•ö·$·0)ke¾rQ—ùAħ`¹ù©=ü¯·ƒb‰Eû"ç°ùR½ÍÖ¼é[Q •š òž½ý³-P´b¾¦i·fM%àÓòGªÐoúo®/õß|È«¦ë£~ /mB¢�ué …$‰,â ³Ãðß„µlù^½r †qÄ(‹çÓË™uâ|¢>/sµä¬ öí~#Õ£ObqÜÅ�`¦Ó¶õO|þµ¾¸-×P~眶ôŸåABQ—4X®\!5À+Õ 7C¬Ìm¾Z;FÇA7}á™Ã»�ä¦þ;qô-‚B²·¡ù@¿C¼ÿ:(>�„¹cŒó�š]K�MIüÈ)�/f6KyÊöÆE5Nf×òO3‹ÛHLÁÁŒÓ?˜;„ä‘« Òj{hÕ°îk·7í¦}ÅÕ�£‰xq®¿¡'¼l¥¦fœ=ðx¡ L-Õtè�ÇÁ¬lô ðžXß4?}%Ú!_͸ Sð4?Q7ô¨|—*”8[‚}pjÕÒ��¢ Ê¿†ùèOæì"JOÄ=p{5ƒ¹Z›$ù¢Bác$µ<Þ¿ëꟑùZÀu?9�ý£áÖ`B#oªŠ¡ ؉>ãÁ äæTÖ»ôΧ›Ž°:½È²:ì7m yÙmDÓÈ?�S˜³CŒ(#ƒ7’Éo†Yäà­Ð¬6ð§Ëõ^Ĺf~E5[ƤZ‚h “Ê"1ñXíe}¦Ø�ÉI•ªmlÛU¤ÆÏf~5;«Ê?‚ºt¸;’äÆ˜ œÙyÌx,•Ë‚mƒà–+ “|®�FÃ�3£-4´ú~m‘µgI­Aù—LNY¥à�±#:· nV9^µ6‡^ü+Šj :ÿÔ¯!’àVI]ï+èå�óFõ’ç#\[A¸ŠN% 4\ŠEÇrÁÌ•'HÇêá¦Ï·PR)7Õ™]j]);ö„U…Àºdë”ýZ�FÀ9lª }=qúosT ¸njf×UºíoRU…%P¢ê›Ù�¬\Ç—IAx[)�!ÁâX—á)¾®ÏˆfºuÜR+‹–-+úƒlTMhü¼è°®¸>JIÓ#bݲm¡íú»¬w…—¤>AWW�À¿&‹Å•+p´>ð M�ö�s‰ÔC‘ê¶>]¸úÿ1ey�Ò¾"ËVwÍ”ÙM,¶ý Gf«L¼ ‚òlèa áUp ÐÒ²jBb[Së,Jbª“¿ß^´% ½ÕrkKm"=îØ� ¤%)Æ+�!~9`™—âàßþ${fQP:§ÏºìŠ÷…ë!ç2a†å¯{_õ„€„úèëR¹D†€U]øÐOp;Ê'Ò_þ§°�»L5uû M 0éÒH$û¿ˆRgãÈW‡r0³wÈZIm+·J‰Z΀èQŽ ÙE\è’WXÌWåà�úû¹Aùá0œ�‡¬$ÿMpVÑs§Îþâ•îÒ’ (yj{eD"“)E°Áõ3üŽTÅ’òÌ:ËCŽÖâá†2I@$Ýiºx ÀŸOviP$eõb»eãSjìü:•lQhuÆ&wÊŒ«ï;Ë&}zê ªÍéë€s1 ãÂ�?þ>ªÄЖX‡DtÑ_ˆ_kØþ^¯]§+Ž–þlOꋚFþŠg¯ræгј}Ÿ¦ÇÎJp¿  ÒB¶hòæIŸVý�°-¼hÂefWÒÛäa`·ùŒ—f›L±+Nè25 ¤�V2÷Oš4Ÿþ8(|ÿi,AmOo+?Œß‘Xƒ;p<œV2€å鉈美âõÔ=t^{èž¡P{0`¯.[é¥sƒ¥®f§Br©Øh8:vØÙ7ûæD3» n‰äÇ9ž$Ü‚ã9©%$Üp¤àø#·LÒ®õo–gzÌ3éŽg®s–Q#±z ›ïÿ±rUR½›DŠ z·YRq€Ù�ùõìwjtãL%•#×!_ZH£ ÚØ'›ø3~e¾gñ0¥™c€Õd¡> @'–¿±cõžWÚ†i¹O>êŒÿè;ñÒ>H÷hG¿°º?ìÀ1ƒuX£ðPC)ù@½ÄTz(åÑá¨h”")BÙFsv3;å ?L½[r€&¦s�îwŽ ‰GÆ| 'XÏ/2€a¡ìºÏå„1T °�,5óÜèÑ\¦ÀE‘ç­²¢§`K.Ïý�¦¯N3Ñ€C�º2yPÜu’´ëBU�$äóªc{ˆ\Ä›ÓTkÌg”-†©7DSX�)9´í}cÈ^éÔÚœn©ŸÛ…b§î"NXód+w˜ ‰y—äÿºþyEF8 BD�Øw޲êíˆì•Üÿ ,{v �«Ù ¡<3¡à/f2.8�%Þ�87x$@p—„»K� æòâœ~œšQQüg•*h`ݲBøkl¿Áj¡e3ËÄ—èY"¦ã`ÆÒ½«³–šjÁ8ªT3q•�›�5¢[‘Ÿ«ôBP±ÝýB˜O;¢ Èâ®G‘£_O¿[Ûâ¦ÒpåƒTRnĺò‰¹‘œÃ_¹"Am=Ũš[UQ¶8¸•K5ŒÝ“ú,†v,4Ø«î/Íʽ›0�Ói]7ØcÝ Ê-`º˜þ£Ó å›�çáw£`îQPuÔ¯ºµä:É å³/·KXä¡lËA©:Ë£¶˜IÁ·hô1Ä2ŒºÖsÙâÂB$ØMíлˆä(:AýDü D<” €P•¼ìv×)IŽ/dÁcœfóç ëvó±UˆýûL}Œ¸�>ú¿G‚c‚Gü»&é°"\¦“(ô7éQlþ_ÛMîAL–¶§p˜d„ÛÉ싯LoaëßlFV­º*©'nŸr�Ý€–9Hê;by„\ºÚ?ÌdU"(}—Ÿž�[lãâOæ+ +春¹=å2æ_bÄžˆTÓOë!çz×DÔßk}cÔ±åmò.—2ª2q*¬ªÐtÓ‹8ýÛ�–2Ô©‡*9�Açʈð1kœÿ#T¾›ö«%ûAb¤ßOePšP{YgòF�g–ŠKý†�ã3ìHr(L¯jé׋®£Z@b!Ü@–J°ÜËjAÍ _r˜É î5IZTtÇ”•‡Zlúëµ*‘üR2V6Š#= ÓÁËœ$ˆ UŸVi‹ £2q~†'°G÷Ä“‘�_r1‰”ˆ35-.£™ª ‰»ÔNI/$~ÉÅd:¬[œ | ¾YW¤äÁ�C3Q龪2b­ÉÌþF¥-O»Ó™íˆøŸ1dõê[íNà¦ý&ñóÅmÕêRÀ•´ªTÐ�¶/rAÎxj'åÛ /èÖƒÛueCqo$è<�7NðB�è�týý1ï.p¯–�bÅè¹Î¤qAWËŽ½ë0RÄP:ðË–è—�äõ!<`§'T^«Š ¹$ÝÛÇ/áú’¦ãêTú‚a‚ÇÈD�“ÁÃÒ½<‰RÈ3p…/ÏÚ2¹£ý§@n _.´ùPì߃¥ÿ¦®W)L�J'Oœyt“ßnëî:ôY¡˜Z†ô4ÂùË ^Wo¹Ÿ&m�ªÿÜ_‘š” ;f¯•I4�±Æcð:Tå«ÅÒ£ªh&EÞ–EªMj›r•®J˜.è˜94YU.1m%­>^\-¤3A³‘øîç€L!ÈŒE¶Íù' ª6²3@�9FȪeÿwu#Rû BUPkÂÉœçOQ¯Waæd]MyJ7YÙÙ¯µ6`ÆQ¡.•\»Žü3¼4¶D]!º8Ï¡“²{ µüžâQ9ÜyÐÛœm…òÖÓxÀ·¢&Œ«œÎ‘ÕÐêÌF£¹X=2wÂ27Sÿ@: Š>^Ѓ-ïñÜÁwÄÃ�˜4~ÆÕD,ÇéDoÜ–ŠÁXf&•Žã 9‘!Òõ~ü'ºý"‚ÉÙ&Ö~�ÉhôÔù®Ö_´|Ä„C4å¹h­Ž@½ râ Ð#ºÅ–'¶#ïçõ\í1‰ð l#DÌ—×M¦±G×1.W¬ë�;³t÷u4\ôå›q�åâü¢®�r”¥ *î{˜Nä"x;%Ù‚PúaÇ] #ÈŽ·Ï™Â±æîß´^ï-a¿( °¥ÍT2�û>¸û§ƒòp•öÕ¿õ 5¨…±œ[qbýêsøÕÄ6 ~jÌ@)*$J¾Æ¤Ñå4ÚïC$qއ(>À wJч˜ò¿‚Ì3+ñ¦ð\¸^ ót© :P‡-ñœ…B¢G‚Å�¶°Ì†5uɸXn¹vð ®?_|ܤCª¾þ‡�š6¨Ku…Rjûë#©ãáö&Hßp b0 µ`]ݹ¿Š]b¹U PÈêÖ¶GWü×àÖÖþ�pÞƒ ¾\ÚAËÝ‚”gÌôJþ³±®¢ @$Éî$éŒR�?§ò›øIn¡¥CÖG÷•òkã— …¤-ÆÊ1òú—y¶È­o°6‡ °�‡0½rǹϡnT³#<'ÇÂ�÷cÝÔx¨+°xV{²øf¹owßôûÜÏ’äÿu€h�Qúj`»0—»¿/“Ïk߇‘€ßzC¬Æ¼� gÑ–ÎŒÐÖ‡«7û‹õ2”üæ}*t.œ6¥¬¡¨ Ö§¢I?ç·zv\•ï_1ãB—-ÂD4‚r’½ßºœq�M¢‹ŒuÒPl¸¯¤ÍŠ‘à/×–ö»$ú.PØ*lO¡¯vͰ%@HE-õw*8Ê®”çÜÊjÐ1RPÌ2°ÇÜõe+ª»ÂJ𹎾��SÄ÷‡Èå:Çá'›êâàÖž�%yŠ®u`3«蔩ðàÈXÞToT½ÅfÛÿjª$GðíêD@/FBìïkXÌ vêü3aKã ²ýÐ1ÆyþwþÎj&atyÅÎOàf;ÙI $ìx¤b–éÚv§< é�Í[¾VY%Òµ–hBʯ¦=ÉQƒƒ/½¶Hj©*v+£HŠÐ"À^AäÃ5&Yr.¸`ô‡ÆIʹä öc†çXcËƒÒ VÁ-ªws޽ŧSv^ôÓ›YóWGkaÉÜF—÷y˯t‘ŽÖ,J7ø*®ØÉú6_Qš§7c’ Õ½±oœE 7$9öŽ•FjÏ0=Ø wAç�&aö0Y@u²ý'žab§+@Ê 2ÐRM` X7¾‡Š¬µªšŒ…Ek >–�¦–Ð-©€³®Z$†Uglé©Îoéÿ¿ÚÌŸŸ¶,°2$úõ‹3Ñ~»Ç!µç«€¸ÏÅË0BŒ?|‚ê}"?–) Ô ƒs¢3ï²_§Säø/8¼<|á·3œ×U5N]×í*%F�)ÞA¨¨¾Àé ˆþ\ŠóÿŒ)˜©o§ÊÞWBµGã…>ÛÁ“æÕó ý:Ä•÷¡_öu#(ù׋!±:浟õëô2ha¶�.Ó¤I«Ro7�WFïCC��s±åÇÌ›"þs·ö×ýo1àÙjD*ú;$w‘@qœÇýÜŸÍhF4þ!—c�ŽÇ;Ô{üˆ‰úq*ßð–B°Ëß(]ÝãŸÎØãp1«º¾à#¹G;­6Él¸ÙãðïÃ6A 3í7͉NÆ rr“ý Xô§˜x8d\€m“ÿ»ÓÜl¸¿ ×è)à [R"F�$f³¦Õ×ÇL»J(?H¾êðè �C Ì3½_3±3ȧ�09Ê÷Õå~H²@cI5„ C”äáàJ�a.ÜZ^¨ƒ0ŨáUÎ o‹zñ¹¸x‡ÛðFûÿ™„Ì‹Ô=E°“+�rFñ÷±A­wN®AÎJ­Ø.)±[�˜y±¿½=Ë�¢±ÏÑÂ,~fWÃkL%¸än?Aä͉K\$ú~a›¢ê?-½#“4¦û”_î�C!1ZU3Ç,|�Ï�#øXD±DÔáÂM–æ>E9Y ®–¶'YÍÄ bh„‰@@wfæw”Ô •YK†Ã³µônÝÝÏ^Í£7 Y’±5/ÙÈ�Ž5Ås¹n¢ãL8™ýa®”ê7ž‰q à€ÆòÏncT�‚�Û- SsÕ«0gCŒZ÷ú:¨Õ~üÙ?ÚC¥ABK²(× Ç9Ókë7æÍº–MÀJ¶¦7k9”ð$¯I‡Æzÿ$ARâšè›œìÔ׫r9Ìý%yô©¥¶Õl¤ü·ÈDô{/X6�o[¾ƒ^g‰ØX~í`á0³ØOo+�cÜ(ŸPlyÒiIŽœôÇ*÷ ,ûˆ–pN{¯ZØ)’¥² %ˆüI‡•Iœ’’;õ^¹ãÂâGÎ�±ðúbµþÜäT1¼žÄ¼²/ B:ŸWÛM�Çõ{T&Kz|® .E¡•Íñ¤ÁÇâJå/š§3.ÜC­-æ³À?Òöiƒ µÂ8vûÆTRî\‘¦EÍ·RbÂDÕÓ�á__t»³8ìåýJt™C<´"Œtiï±y°êͰ)Š MFd˛ΣÀý�"¥>Å�é©#Êëú|:YÕTq]Çݨ'2ý²ý;2dÑÕƒKžÝøžr1£ŸrCÖÄjèíÌ$N¦Z€¦n5ÿ0 ;â&ëdç(¶×g«}©ªýYŒô�Š‘fQ.) q]bÑì¹°Xc,!Lu(˜G$b@é×O!Šc"_nµÍB>hF9›�‘d&A§> AÑÓ'ÛÆªAp@gi`álSõ8ìO6T÷žæš'ºž(WÿñâIAßt4Ö{€#?¤ýƒþAÈðÞ¥Œ�gb&�4n•Ò.Ÿ• >Р Á]b.Ìg%}¿‹u#ä—äöm§î®3ÌóRr"¯ø�ÇÎD¹:kßЃOÒ™ÇÍ­ ²ÒŸç_O†q$Ϲý\¨µš.ù!p¸ü Ž—rǶý*ôÝ#¦&j„øeÉ­örõ`¹ØŽZÊQ”Ì`Ä9ˆie"2Ô]vÚ-¯äCÌð†À4ÕdP4ì Âqt“š ë¨ûGàvZ›¾›#,ÐBPJo“x6ýt§Êl#d1õþñûÖc^üÉO,Ó=l™Ü=i!g»9K}ëê böÔ«dRšU9ø„ÝÂí{!Ãéz€uÁ°_=†EÞ4XÌçë* %8ÜÚZ;Ü3¤*—1}Ô—4¤ 1¡£�¸U.ïaÐ’Çã¸kX€ ÂðëËTNlV©O¸@æ"Ñ )Oå „í=“´r˜Ýeà­œb"MgògrÑPj‰ 6Ä Æ¯ý"Ž JÊÓgû×Êóñ�Éȇ)Iï˜Ø£ß²+R…Bxð¬ùY9ÇNí!/uj©cpåMŠE·æÞÍ‚à1>OÛIÑúeÎ\@}Jl»w£@ÚþE¿†�%u›ke××6šD2qkíut Æ@Q´%*ËÇz‚q¢Ô‹¸T~!oBQࣣè0�%šØ+e�´nNÍŸÜ–µ·Ö^§•5’àÕZoØ¥7Þ µÆDÄøÎ„.äaÉûîÙŠÜ�Œ Z'm]¶:˜ìr÷Üa˜Æ2oNíB‘ðŽÜê_]Þœl]¨Ù „âhרâþaïÌ» ó d¬Ý߃±�ÀDUNu³fÄG„C´—^²n5F�°Í*/4 *›ỹ܈”²dí¸ËÓk)ÑÕâjæ³�—l<¹Ýs�&îã�¶ö‰¬Ù˜¡3ëÊ^Œž ø¶¿OÇÚ61â�¯k¤=Ü„2Ña{SCÂÎc„Îa¾¬mO•ȸÑ'¯Õó¥Ìe³4ã¥Ã´-�’a"¤-¦ï±Bf®+gíEÿÍ0þÀ÷Ûo¥lÈJz½†ŒÓ[˜ÚLóp³ÓÁ�6-g:˜<ŸÓÞÒçèšVÅ:´;#«ó�.¶'{;0°>×ÿé¼@Èsâž$¢2Q¦  0X8TÁcì\m´ñr„ š™˜³ Û×R„»Ÿ44–›ƒ£L‡4S aCƒ–Ì«ùÂGV¢Ã„a±½yïûŽ€TC33d€j~Æ«pH²CMmôЇÿYa嬽9ñx�® ÁÐì³:æý…JGl“µ ;·棾µ‡i� d¼Î%ëצ=Ëä æÞP‡Ò޹ 9+¿%ÑÉc¨ÛxÍ×­Œá˜þÛ’>Á9ãuŽ÷™ &Tà ÇP|ëç!9]ÙSÃáAA{s�æÇrzy zó èa ÷ð"[¤-�U_Ó}óAW"�t¬cá0µþD QxV~ �®ÌïÔ,yZéo^2´ÎÏ}SÎð’”’Z#Qù…×p’GaÒ³®L‚hÚjÄ›ï¶zUg¨nËN!i†×ùXYTÜS,âé ON UÎú")ÉŸÔèxú¸+¤ +W,ëÂçpÜJÊsš'¡#ÖZ S~—9•κN² l$¶¿²Åëqñ_:�´£‹:Ø•Œ3(ùðŸõ€™–¬×‹# Á ¯†«ÕCBôœÈƬ�'Ôî†é÷j/ÒA]ÂzÛ¡³&ð[ÁGx)@1nLb0-ò—=ÖÝ�'ò\ï’õ¹ÆÏY‡ƒ"Ó©É3k\ƒ�èÒóZ­ðç@ø #õ}«ÆnÛƒ÷䇮Ï>ÃÿµØS¿Dvl®_®¥�²!ú" 6üþèÿä³r{O›A˜¤í0t/ù›iõæ¦�o<+m·Ÿ‰’—^ï¡nTL¶€ª•N6¹AYßÒ¢úƒ6FÊ“ çBñ@{Òß¾ÞÐDáÍÀ?[ÀÒpÿÓ°³›YËØÂÌØš¥&Õ�¬jñUãê ß%g÷ÅM®÷ö•‚ž£ :ßé'•Ÿ¹áüØCZ¹j-T1Á’[£Ê›°ÆV‰,zD%;­®Öõ×ìnW}kè0Gzh�ƒø›õ&×qëûË K›E¢Q 1dwÇËÌÂgâBdS°ö¼‚IfˆRGâÍd¯ßt» ç�mº©PMÜYAšo6N™Â“1>ùNn¤õµKr®õ·Æçáf¥GZµöJoAÐzíùÖ0¹ŸH¬eEô •Š ˜ü³¹ÚÍ}QøóQ©ŒÌªpæ°Ùê‹RxÝ�aÚŒ9û@ÌOTPÑ}‘ ·I`Ð3Ðçfôu\ á\òþ#ãPB$2ƒp¯%/w0ìúBNBäK/Bñ¶=ßœF ,Ònpè !4 e�´JÁ­²IIwÒ!î‹—ÒŒE~ÉãÚ_V ?¦d×OÞR,ÑÍcBkšÙB;i¾Y ê‘¹Hî¡3„VØÇƦ"3¾Ø7PlY¡1™ €““×®ÞÛk¢¿æªÁa�Jìe„Ä«€5Jÿ>zÃü•bSµ«Ó�êê˜_3Q>AÚ�É­¼j¸Ž xH77ÑJÚ)ŠŽ.ççqü1´Êëµø­@Az}“ÃK@$PåÔdòäùòÆ= uœeA GtJ§]ẶÅé²Of•9|ì^°vx;Eèe‰}Îö}úË!1ðOf´ ˆšæ�±@GÏI¾ßÊå€�íîŒÅ€Ã©2 k^ÐÝÍ¢PÒ|ùêÏÔÙ5Ý›'Ù³FyšfY·ï˜�Oòüjȯ¾ñÉñµåÏ0M$ß1KÓTŠÐwA!…ªv®Á™6»q³I´àöXl­1¶ßv>�ÚÓ®5ûk2"s/‡§Eù.Ií�üB"¨îةʆ:iúO9, Zco#²ð×¹}Á{'�m ¹Ëè¾}?=¬{÷>‡¶z2tî>Ò=EHØ¡��å®gîtÁ‰ª®3aÒgíËÃ0xŽ¢ÚR?+U—{%2FÛT#9¬®sQ.øøí,‘›à|Õ1�0ÅÕ“²OÔ”�s‘e“I90±¿#ÓŽÃÉÌJzsB¿žIäå¼×TIÚ˜u‰N.ÏTQÂÈM–MFÎY»'F ßËv'#¨@y ¿ÀÃK¹:�WЗÿpŸ5?üUĪZ�ÉCñjJö›…ù¹ÛÒý¼ðRAÖ4ÁxB7\ƒ½l*wîfüÔ N宩ÈÁËû,QJÁK£UŸ¨Fó@m¬�_Ãõ"D\üªÿP#Q"ñ*�¾Œ«CÏm×lÞCÀ¶ÑZDèñ鬋‹ á4 ¦{dŽÿó÷ˆ²zœ?’·¨èqD¹fÓ¶—ê#/Q)–* Î“Žú5Œ$ û5ðf裊ÛF˪$ýr�Ý\š†ç—C{µB´Ô°þ ö4Øï+Šä�òƒMA±´³~ã}êCû쫟!yÕ”4*òò¢¾Á7CÓV5Ûzà�‹¦B;c…|Xaž™üVú(´ÙêÛ>r „Öi¨AS*N¢`KeÝœÞ"xÕ*©½·R|ä;t§Št+ø«8I©3drdô‹”?ÎМ*<· ŸbàE´2ÎÆ·Ï`S˜,Rg:pëBŒ´Ž�ó°ÝtF•"¬ìê§á� •SlÉîxyw�Òöüñ×ÝïþMÀªŽ¢WRí>+ÏÿÓMÅ$æèK|…P24�ú�ö‘"Ê�°x�µ(!ÇVçf­è¶=1º )ðJÇΞêsÎLNëc2b¢Nôpeú+?nJ\ ÿÊf�.Nÿ#½Þ˜Bƒöº%ó–ÔdÊ]Žã·0À’G†ý:P¡¦lDE•Ùƒh£ø€”“Ibs=¼ß¦z”§]Ü0u{çâ!\®ÉAügh¾žŠQ)NÉm2Yú6%Ýõe�²È/Ÿº+ÑÄ'�lf{ûvµ±òr ÞÎ1Fø¿õôΡ!sW,äÆðfÚ3y×ÛFíÔŒ•²]¸¶tÙŽu‡®/©’´û’·Æ*ki.Z(ø3t)®²á6N÷Èè:_/Wÿiœ˜ Ô¤½Cðaìåäˆ$§i[s=lè~˜ª*™*‡¡Îv¼E~„¬sLmä–θ‘CöAÌü6Sü‹ô¥˜æ@)ìš&;¥úVV'À.™Üœ—Ç‘vÒ¾d@êñèïë±€ÈyÕ‹ £l³¸8_,÷iœ®]ov­·âVÃa\Ûúd×§7ˆîöÿÑås-ã4·¤¾ô‘îj%Ú‰ý�ø{CZ);óÔ.Kl×I;ðjåî´d�Ì ¼É=¯†Å¦r{A°³awb# Ík`•Í•ï Sd­€[ùF¨Æó”—ÆüEŽnXëÄ�!Œ¹eEú ê Ó$úËöìj¸œH�vÒ¨¿Ì¢÷’BÌlÁ¬Óî ±qLRˆ.[˜ñúü°v³í¢ÒÛ±Þ›™“ÅÎ|žÔÌßúpL›”È—|7\›fGé2D�Ìïýò äÁß�äë²'&ËCÍÏÎ<›%ƒy5.›�[ CcDÆ#ß9ÀPmmö&@U ýv·Š® ½2KWÈ`l9ÑaŠV§�l²Þ¹qG ÷ÎP+Íþ(„åeµ�OÙ}é ¤ DËZîÝ¥ª±ÿy£[Áò–I·î7Ò,²“´%R4Õ±tY¡^Šû%vJmºçÀ31~tò€—j‚x‰ÙÈ(þm‰ÆÈÜëp‹,£kïÔÕ¯EçŒ/ûÕVŠÁ ¿";7ª+Ž�…ö}¯•ï�yܽ³ÆÊ3„ÀÒ äÛƒšyƒËx]á¦Ø‘ ùEQØ«âpÖCqÑï¹&Þlmeù• FË0Rö0ÀP *ãèl½=ºtìÝ"¯Á-±¸s ¬§)v¦cimÍ›Ô[f±“„õ˜u–Ôˆp]‰˜{tÑŠy•à=Xƒ�~ý9Ü8I#UН¢º2�¹i¿r*@>5VV?p‡‚/Ã÷õéÛO}–“ß�ïį³é^1a÷n3v ¡ÓG)Nú d$­¡Ê=ôÐ߀XL¯Ú̦Àz®èåLj~.kï~»Š]žkò-�ŒyÝn-*ÙP»Ô ÃZ£‰cr¬ƒ°;áWú�cï,ó­ñyuìS««-Ãê@°÷.?¾Æøãw†@¶~ï•ÄøÞ”éf´¬WK×&’êR© ¿-§-I¾S?Nž‚Î:ZN·<—†f}ìAOC3¿t÷Q‘!Â\\DηÜ*�U4®‰6I¾©­Ø÷§—Œfh.`1©éŸÛÞå[·ÓƒmB›ý|ÂÒ:3’Êâ ܤ<#ƒ%57·8Ë Â…Fs€˜¤¡{š>|xÅL,ô {ö÷Õú¶s3:~Tàøa‚9 (ÿ€”@©G.A,°Ô‹� Æšê‡8•¬ÕÔR�p_Ôm¦ ¬sS’討~ßÉ\â˜70[øÙ·‹ËûéÖtÁLדU Ø™Cå¡3éÌ£`ȽÀÛ½æÓK�ŽÇäöß9p=ŠövEµ(~¢ ò”|{ÃäéR.Õ3:èe“ÎΡù>´¤áGÃ?9\”çØýh×WÉi¥´±Ábv{È�gm[o;'q€Ó\_,E^g“•çäbZÜñð¨×zCL €+:)Üç2§;S²6ü憵aÉ*»WÿB„5ýüˆ�é1@­fm&un!³+*¼ÄŒ2ê¹é-ÓoÇÚOµiï< MÐbÈã(¢Î¥Ã3GK�T'˜»¼úRz € ãê ÚÕè5¸bô·¼°`@b’ѹ&íÿìñáŠZ9¼¼Ú´ž}ÎÌ hšPóûàžŠ_’¾•<’õ`)åKR’®W` Q‡Â>3+Òö�,^PÁPT†îæÅ±;æâ=ñŠ—�ݦ*·ŸŸ@_¯>u@Ä»�^�D�y.Ná±ò9�w3Ù½aj…Jz¶,¿`Шá‡F§�ñÝ«/:êt˜¼¸5¬ÄN@Äu˹[?MŠ�Úa¤’!š*=*Qv@PbMíL1.ïÕ¬½Ã½t╦Ÿç¤¼Ì~ž±6Sø¿[r¶~']¡y9 —1F¾Ã­%çX¸Ã´2?zÎÇ µ‚9Ünzû\ €“«áM¬‚Z œï°ýJœç]äÝ䮨0˜'÷¨œõþC.ë•fi–�þ*šý=1Öø¦Ê,t§_¿ú%ê³2Ž­7ÿ�¡‡nç^&á!fW¿~=šŒL˜tP\ëZSQSÓ}`.ïo |ÕÉå§•4.܉¡õS³~‡!7p²Þ9WÌßi¦ÿȈvލmáÈ,_Ìðk^fp3xª$°sÿ¦è*z˜=ç±>ÌÉø¹n õp«{ÁH‹Wýújù±�û¾mꛩZÐÏ�¨ž±‡Øï5“Ö=¨E™,¹•ª¼ºfÔa²™&Xƒg\ ì°•ôÑÙb$ P©• .‚CÈ€…6žëE{d+#‘óö èµ1á‡}⺵»¥[qœÉ:U:j þ½\Œ^QòóÑ\ì*ñoÕ,–ŸBÕtCÏe[ë&,Õ¿@¶=¢f�ýì›!åz�çM;ÙwfŠ>­Ef›ÑA½P÷Ú⸺Ú8›Fvk¯BeŽ ý6[øÒJ,õŸŠyY1RÖ32€ÒÃcŠÙ‰€ŒPiü�f-æhâêàP@ž–»$~dù§Ië¿¶2<à'`W¸Ä„[ýŠ´-ÒZ¨Î]¡`…Z>lìûÚ4¡ rÐâ‡[@ gbùRuƒe> Û\k[SYâX¿ Õ�“1ò›J+:º�&2ÿ’¿q1 ¯ß<à³À3?Eq§1 ��kâM,²–·m“‹¦Ôr_Ú·-?åbiÂŒ™‚õ ,<’ÌHAwc륣šäd­ÚÆ{kºªŸÔñã‰nÊ7Ÿ�ïUT¾f³” aGìÞM=¥é.ËU¿¡/ص™„BZC‚¦6 ”WBZºÅaûø �;Ã%½Ò|ù•J±ž>#½,Áµ¨_ÙžµžbŒ¸nFfã^¶çP Ð&±hÌœ:K0Îbe)ŠLt=A.¬CZÄÑ!ù1• kqBpü`:‰7üu—¶ Á¹sÍ·—$f=òç~lž|”¿àÑ`ËGX¸ßËÐho!€²›�<ˆ”ºCÕù£ Ù�_ı¿Ê^}¸YN—¦åù”¾Þz=ñ…_~¹û”n¡LÍPÚ`½ÎDOÏ›^ÉýV<Š&ßxVís 4ìÌðƒ}QòßÏ®Äèh'i�Ÿ·šÐñÁ!ÏÚÞa±Æ›Q¶;× dóŸ)Z5â±2H2±éA¾Åçs©›:,P•0±¾7àïµã�7WTNUq#(‚O6K¯:åŠY$ÄŠBgÖžh*Jº˜½Š÷œÕž� Ÿ÷‹UÍ9Ù]8œÝfø+¤¹n˜Æg=¢Ï«ÄÀòÐÀ´5¡Ö˜XgYfv€�n[˜ïŒ<]qOu¦­9˜´­ûÕ3•Ùfr° éXC`'ѧ_Sy»pO±ºìèцÇà»Må [ù›úZ"å»EÂÅhÙ ½”CcV胒Ç|VÏIx&¡Ôx-ç&2î`QÉ}û‡;&NbØ å÷å³n{cŸsø6Œ�sš•û¦°Šï=ÇÆ—¥ñ=ößlFöŒÝø†�é}]¸?È>qœá埣5µQ‘aÝ/%ñ~ôªyýÞ…×ßUÕ³W:U¬y©<¥_‹Ó¾4 h 8ü¹.šèäÉHÎÇ:n Èé¿Îaq÷xÿ‰Ð>¹cáÓ ½8ƒ ¸Ó”£òÔ«çÜf�É@áÂ]^}²Ãùý-æñ!“V½÷ |ñª/a;¯_«ÄGgUË—ÁZ³ðtÓ?[0iÜî´×Uà÷M¶B9òN 1K‘(êg §º¼ZHUÄŽL@âÃÁû )ÓQ×–õøÍ@Ü Êì5i†èÕvLךøá&ûÐÕl¸˜ÔÌÛ¢!¯Ï}Cë=]¬;û©¤òƒ-]M ¼ï§äËÀE‹9&´0ïýÉu¢¾Šk|ÿ9~ÈkH<³“ ;BüqV>BÜH»oK‘KÄ)“ãØB^tÇZy`Wk¡eSî7Mƒ`:æ,~Zkõp%ЦLÁ""ÍÑòèX"#Ë v˜Yê†Ñ�ã[œˆóy"”]šæémvºægn#¥'))p±0† ¹¸AX£%â>œ¼ç ý3Ïâ�¦rÇú`š¡9îñ(b[—qÝžï+‡„w= 3í¯/ÄâÞ×'£|I ¿‚‚ìØÕô¹©¶!HÁ•‘Ûøˆ(ɿͳØuÑ àLkí·Ûƒ+ÞìÁ7|õŠ÷ Ì .™îÔ¢ZÈgYRPÝ™v™˜™Phù›°‹¯s’À½ }‰'O ›L Ò1صíßÁ"f@�C1‡ªg¡g×d®R4R«Y¾"™ˆª ê|“d{4é,åäôd–ˆŒ!x–<$„Ó¡º…¦5©CƒÁ =@²œSP=åâRž;Œ[ÓœÌ�6¼)ļ'Z¢p„§��„Mî`óðcnÒ;½É÷šï‰ÀçÆc*)e¾ä¾t·R-©®c7\XAG@˜""Ê·ôîàAÜ×áÃû‰®ò#%òž‘-ƒuâ©’CÐ;œ›L•îí÷Ü=¡Ñ—n?x.ÔÏŸ°„K‹Ô6®›-¯³îCíL¤"«KõWpܹ‘Åj]_J7­k§¬Ô¥Z¦B HLóöJß›êhmnë6[ó ðœcEW%EÒø‰uœÍë·;b¾‘ê~÷ Ó1\|O*œ8O‰"@Ÿ PÛëUÅcj·‘‰7ŒZ»Ÿ D›V+FñdbbÜ2f“¦{ûäÇàL¸—á.±œñÌ“˜VO<;oì:¥BLxMy~¶g îå«§ì¤?u ÷�\Áüuï©° ÑF³ Ö””šCe]Ú…`—°%ÓÏ™šÔ_Wk¹ë)b£Ð6Pw•À(ÓÂ8X{jeö/èY¾gÎ=¢±¨Ä Õ*U‰,ŸQ;é¦+Ñ ,r0ë,âØ[¸å~SkzM²¡kî9¤ÝÁªÆ8 öF`ĸ¬0+Kac×™�wwƾýâ—zÕ.Ý^-à*ÙSMªølßÁÒ;CÂCޒǃT!‡Üò¢w¢´}ç o•” PF8—}4ÄØL„®ÇÙa%©„€øºC×Ó�»X­5n�Xì¡�Yéma>ŸtvøànƒAyìYˆ›pÞÁ›ÁfÚ Â%ˆà~||ߨùd^zk»b•\Ì×›£«(WÿÅŸT äí9;¨Ð�á ¥vidv³Â(8üÜ»8¥x;ž¦=7sÝðÎâo~ÍÛ‰O-¯u+韂”R�~óÆF[¸•ë�i‡\Ç�`Y1‰è˜°Å–ÖØGÕúþeìÑÒ•ˆhaû&™�›¥à&›N«àl�N�ÒS‰§oùf¶Kˆà·†-]:Ÿê°`µ„Á&‚Mžnó!þAÇýƒxÜ—G’qîè{.c˜B¥vD²O¤\;PgºIÜwå>õÕT}°A\ˆ5)*!¶’ñì°‘©¥ªþ�†²ƒôø!?t„0oþfÜÂ@Œ¿êŠZÇ@‡Œt ¨ÂrJƒý§bS„ó¢�€—®|©¢à,6‰Jd.†¾Ç}Ü}3W¦×ü_#[I Q"��–9gªãoAðhl¬ï1¶ÐÙiÖŠOöDî 4»b×—ñ;Ùuð�ü¦¿<�]™ Ý ª¡s¥Â_ v4ª®ÅPY}CŒCm·é?ʾYh$xÒ¶œ�ÍŠïÀeåæä§IñtEÖßJ•ª¹kx;zë+,%ÉÔû#«ç„$,ã^:`l§vÇ…­Þø¸Î\3މÁçÃæIé")¿¢y�Fw¶4 ­aÖƒ_v»I™ÜGᜬ}APÞ耾à 4®aÁÀßQ-Ê¢‡”á�ÝðsQç. RåhUŒèН’_¼I|*‰{«)¾sûí—œð’ )¡Œƒ³¢ƒhaL=¾µ>¤üÃ&^!Ù0ÕH¢¯e–>ôÜÓX´!—­‡‡¹éÒ§JØÓç\Mn>¬JôÝ Îwe^¯Ï*swxk“‡þN”˜‹Òn2÷©�Ç{5‡á�o¤„KqÕƒìð|oÎW=ÆœŠ�g£´ÔJ/¤4ÍÂ?Zfþ}¦_ƒ@“j •©¦§Þ©Û?ÃðËî÷œÃÿ;ý.öêÁ©5j]öR»ØzyH£¡;5óú’�"Át(&›5Èð’ºy†F:Ƚ/åmYì3éMN˜TO˹7†›Õ5öJÄ‘°lŸf�zi$¶PTJâéëï?üŸ0HÄVMFCû¦Êð¤aë� Ð)þ{á¢~4ˆÄtq„S½ ]ädd9ëSFiŒI”½G•‹]Š…wŸKŸ²·”ÛOÙ³ÃÙ—¢Zkëƒ&ß+æ!®Òâ‹ý­¶=ûíl³Ù]`¡ª@²ØÂz§€ e.›ë !ýÈ×Ñvét#2FáZi^$¡±ßº´SeÕ+ÖC*Ï׈›oU^÷å+%hN­ÙòÏ'­¸“ â@ÜVð`µøêö7’…ætõ›R9®rWŸ>.ãD¹Õ¨îÇ.㘥@�ËBmñµT{;XÜ‚êb®›ŒÉ.íõ˜õA¬ 1šß°¶òáEH‹`fÞv¶Ãu—p­ïÀ?«¹þlÖ¢½GêÚ@ÝKÖúpyS‡Ž¿çÉFüžÈµ$y:«DµÁ©ÅíkÔ M%Š…ÚÂA… ¿#·q¿­G$5ÂZà±Î :Þ‚!è•"¿Gì³û]š¬$ ¿¯D[€OÓ_+b}ÄnÒñém'K%U�¡x�îŽe㮇·Æ»ˆÂ]¼¯I°cÇVV9ç¦2IíyÄèf©†{ç{h�3Áˆß"b�r²Nø´¹6WÚµþ)9/u“¬—Ô¨}\¥òÃ!’<;^¾·ÒæpÓÌ‹ í•‚šªIçˆJÙWÞm&Æç©F7”èÿóÙ~*+™ôbk–ð{‹SŒ·‘šNVu@º�LY«@¶ù–Z"ÞØôI™õ¦g(gçBèYˆ½b141Û&Ñ¥å¾;tKÌÚÞ†.¯±Šõuï>vH¶¦ø6“E{õü"µÆaë®´H]é‰âD¶—jOB ¾\Ï}Nˆ�s®ccë Xiö�ˆÄÞjÓ�]�™7–þÐ6š)Îû‘u4gõœ•ç!þMÖÿ&û΀*ʳf€ƒßú$Êp}#:w¾üBôä0Ya…ÎÊÔéþê„/'÷êØ™*„U.Ü.@Bg íýpgw´âå1e×C»3"?O:ôU/²w¶¸Hk£6ÎAú�r$ƒ“ÓÝT¹±Ovu¡ª k‡d—�1ëP“úZmµ&÷ŠüqoÈþ,~,*ת4Äç©l\CRYÄ<…»‚‰%ñ7Ù´q÷®ŒRþû Ìf˜æ�`k–@£~Jú‰·µP‡1M³�$¬Ê¼’w(š-3j½�…´…Øa<šbúç»òÓŽœ×M•¨–¶Œ„NÏÏlíÖ»q&=¢ß�q²¥âÇÔ€Ô1ÏU/›s1¥8¾Æ#L$š®†_i0ù£ g~ñm% ›L^ƒ‚è+Ù;·xTu¤–ìØ’lÃÄ™txæ§cHÿ wÕhlsN{}Þ›q¼.¹d˜Þ\ç(z–IÓ½9ÄÞœU‹bšt�»×Ü Rû÷˜å…(´ŠDnÀë®ûzžœ5½ÖO�D^À¿²P›�H`Vð}?¾—¬Yu�ñ»!WôÚKÙ²MæØ‹²§vq/»@\ÆÏØ%� q4„Ÿ‚t´ÿÝwÐWB\ù8D â:¾Æ®v°¬Uš5B̼7ÀÁ Ðû¨ƒÃ·?»³ã¸ÇŽ…Ï¯æu€æÒ@çY)æÒU¾lÉøÁâÔ\OÛØ¸ÝåàÉà 5ðÃ/ÊðU“PÖ·E·Ûÿ'LÚ;Ô”‚À€—xqêÃîœqG ¢Xì&ý…Ü<¯§õ”é•Ö„ç–<1¹gÿ/-[çé´ñçåW™Ò¯ C£Ìt·3¯Ø™À5–2;GÓÄ×$'Î~'þ>F ¡“™MëÓÍ¥ZX;®SWà¶)$�â’¢êZð46o†Em²p ±¬gBDeE�íBfÅYô/;œÙ!Ì[‘�mia…`Ü*ÇR˜²Ìrµ˜’¿,2¶h” òF|ÆbFF1Ó!óÑao³(a\Sï\�´iÕëò?ʺ'ñ£�t»ƒ…Ö�ï°³ªÚ¯úeÕ<ß_Âó(uºød¶:§[î|÷‚üÊã ÛÕ¹B{�&ÉÕØE…KçÞF„”¬YÑBX³žTûy…µ“®l‰bÌÊáNª!b€d8àˆGÈÝÅ’zü‡LÛ\'Œ·8vxÜœ3ßtöxMlݲ0,¡šn¿ý�kìê~¿$šX“H [\¨ a7¶Å ˜Áè¾ñ€¦{)É£MÔj­ÅÊçGÝ«§æ®V)ŒMŒ_‡–ïunv×–­(9j#¾ ÏA:z¿p’{ºâ>Sq P;kÄC˜¤Œ†•aÉ'ÍR'Ÿˆÿi”ßÙ �.peút†¡à3.À$ YæW3¥Öwz@}gøÞnq«Ï²¼‡Küqäxf{®{,»oئPϽ†D 8djµhÎ…ŒÜãõØ’RúÀ¼dSFt&&Ïh+Á•±oŽé!¥F[0ÄÛ—ð6¾…Ÿ~¢c +Œ½Y.i\ÉEUìËF±…¼žìôÒ 7H¶“¼Ÿƒâ”Z ¤³¬›œ\¤ã¥¸/Yuó(% Çõ´ ‘˜`šhCYö*×ÝYÒT õ:Š5€®®�ÉWëçYëÑn(Üv‚¥UÍ ÀC8Õé-yï�µµY“àtîÞ@ö•ííÊs;¿¥¨ÝÉ=Øiïš}á‹D(6¸&L¡Qpô$¹ùÕ�JZ5§\‡ô®0šœO’MŸ™¯ƒ¹cÊÿ™«· ­*�xÇmRKGšá´µ-Ò™iüÐç˜ñ|´Uîbt…”32CÚ0 ä—ÃJ7Éñ¤Æ�pàÁðò¯Í€»`6¾dÑ<‹ˆ2ŽˆÀÝl*þÁí Ð �bT³üLCAqÝÍ®æzAÖLªòßd3Éñó�Á{À+äDÔtáÇ®ŽæGÔÄŠ°-A>·ïA–ƒ¢Œî�Àņr윰øoØì |¶¡*³Ë¡ù࣭Ûe>šrÆzDüO©=„Û±,]7…gêÇÛubԪܱðÑ'‡Ã·öÐÇ-Ìlˆß~÷»ü<Îkžƒ6�Í«˜Bù£bRC+NÛ¡·êéI Ò²<¦ß5ˆ7mµ™\¤|µú4½Ë”n>¼R"¤ã@ˆ�¼‡ã’î{d1 æ�_'d(*âi‹¤ì°‰<Ý…K�?2£¶ë¤e;a¬"l#—w™V?›÷ Œ¡…%¤ZûÜEô�À=—_á�`—^ö¹øK 7×v¤‹k)t…9[‚bJ® „WËÉ©>Ø}ÌŠ’Ô÷w&º0× K©¢2qsíZÇ´Ž’Ê™Ñõ¸Â"9Ÿ9^‡%/»S‰s$Ë&Ó†%hD6Yˆ�h#lB¨F·`\‚�QP]_¼Æa�²*œàò;h´-¿j¼Þád(õvŠx·UŽ7tñ¼¬ÉQitäfáú q¯ê5¸°^¼±¬Zz=Êùa§<‡àB«Œ¢Kòª ž±¿ÜÁŽ[• jju‡ôARg “²ÒVÌX?P…UßÚ{]4�›‹üÔ„�`õغ/¡ÞëœfϰÝŽ p{jå|˜¼ßãí2ê4^EÔâÎÔ˜hÙ GkêÊɾÁ ßù8�(Ia (&¯®Á® dÞçäR õ•ÐhxEî2‘"êâTŒ{Äjâp²æ��$H>\}D"˜Jt¹9ô©šcUÅ^fS} åJÓÖ(ö�ìí¸ô K^¼-Ø,Å E}S¸X馯ÒÀ|×J�W˜…Dy[ÐǬ%qÅ"K0hËÈl ÂŒòñ²'[AXm˜R?myzÚ¡G˜(âIóé%­Å#§y8›»½²I¿¿ŶÂuí7{í÷@‘ÕñFÉGË €ÆÍÏB -†•ôþ@àG­×LØ~RV®•êÙ8ë ‰s{y&UT¸ïóFe �ƒ�¡,GN©zJ{‰ø§­ %Êë<·‹9›1+X«I›¹è ,þ#~îÖpÒêÊšNdþ�:‚¤Èåp�´«!Ûä÷ñe`È;£»f�_÷’ï*™tÜ„P`0.n’*eнËX�2Êß#bl8µ2â�­ïÙÑ8O…äÝ$gç’8“dZ„ ’>`ÉæCì”þ[Œ_�ÖÜNxPIÇL|ݳÕ` ¹N7WèœHÞm¼šŠ'šVÝ�(&?°ôèuŽOßÄP… ]:ŸrŒ‰•3ˆ]è㼮ŸB¯ Þáû{™ÈÛŽ8J†¹-ì1©=AÊú‘»ü™iØ(¹øÉRPäX¡:„Û#Îyx7oöC ¸uF�ð.`»—”hxQPª]ÙºYŽ3SàˆZi�È�"’¸9g’¸•;F<ŸÙ[؃» NµÞ¦j¿ˆtÉ@ðnþ³£nޤÂ%Üt,Ð{ìêZŒóÔJJÍ¥~´x"šï X²œ¢;hD´Á,p(ö�4öìSY “e>>q„Ò{öûéf§a‹þž9%2ŽðÏ…^“Rëç¡g¨µÌ•˜ÇÈBÙi6N'—të¶Œ‹N:6ªù�’Ú§N8fÌ‹ÿν‹ÿ⌠kvÜÇSÀ4›¢s÷@Wx\6y—Ä øŒc¬ÔC Þ¦ÀIÔÈ%ûfD¼ÜüœGê ÍY+.¯&7¢ ¸ÚW…;8=�I9éá:¦ËMþ3n�~A_³àË31¶ˆ6šYU`æ0‰Üœº7'Ëmü•XNwP ›Ö¶gêMð‹¨N»½»;ˆË)¶3ĵ¼:]ShHóÌX£�…”GG ‘\õN¾Šá¯I1¸dô¶0sAµöO¸¸[;L¾�ƒúN׿ñfHÌ·þ?³G·KbüP¦1ê<üôìa23@òËî“[…t(Q1 ÀKÊÇð>we˜»ç§Iº“Œ"¸žXâã3¯~½'§/Oqu ï4.Á{ÌWù¯€ç�ð» €Û$å9°~AÔ¹pŒÐ5k*NÞç—´gò|·%�ZZR¿�8†7‡Ñèn¥öC´k!ÓàïµH� Ããë¢ÇÒh^ñeOƒùD hÇÚ*Î@¹Oj²ÂÁ'!?¹xLo ÐZp#¡„én)bÊßê_Øï ¹ä†ÞѱoöãÝBš”½òú½(ɹÉm].Ϲ˜‹+/3Ç´·‚û´ÔÔ(âc»©AÀ;?p`»Ð…co&&âSÇ{ÒÚ«âE ;QAN+Ö¸¯Û!\sÐÓ�m›Ž^»-×ÔóÅ5¯Eõ8"m£«“A÷ÀvÙ'�]¹)ŸôP ŪjuIE¯Ã°Ô9t\ÌN˜‚Ÿï?—ž)J%x²€ì<÷ÝKãIM˜Èû•Ò;?„·‚ùÁWÞ^ _Hü=Vë"·úÖc ¯ÂÇÅȆVJñqg”@˜5ï9ýe2Q€&ŽS’–jlIHDZã¯h^Ì:ÍI•›&²ß©~úfüƒÉ‹D 0ÃZJjz¹¤bh7ijªõü9[ÆBVa÷L�â Q ”?áí÷UÀþ6½j+>�Qõz�½'œÅpH׵Ñ’…‚Mz®ð£qT¹8,ÏsÜ‘PNÿ««9ITÂf’ýeL5ÎnL˜kÆñŒl.É·ÛpÔiFæ¢�™™ÏfÀ;@\©G^ ürê,>cOªÜÉçF¸~óeÏd=òÁ¬s¤Õ÷ÁÌÔ³¬ÇÁNáœøü±I\ÈÁMVCäÎbQ�Zqnš¿(ÈM 6�€8û&/q뜥µ¥p¼’Œ¯E®²c¥0LCel ×ïÓó¨õNÞþ»`ðdå—jJ£MŒA׌x³ÞÒo~ÔLI¦„Wƒ‡ç¸¹ÍÝè›%|MO8SŸ~¬Y,9o8`—`Åt»¾¾¦~²p¤+î1þgÖÀ¼B ñü°æáÞ$¸“ÛösÍÏÀ Ê6qƒIÝ%ˆ©áŽè‚S5©Ã4²/(ã4bèXÛ)Šv)trCÆËmLÛtÒsÑ%‚‹•¯`È9Êõ1È.•xÀuÚ>zsíú!š¥®Õhk%6h&µ–9¾ZýÑ7Tk`ÎôŠˆ¹©Ä÷+Õ™gÎ…%÷y­Å`õ?C¢s#bx⬷ü l¶†ÿ˜?š„¹˜‹ÑiASH"þ³ù8v?B¶Ÿ\#ì•;P¯¸TÞ8·‰ïAnu[u'ÁjÄ\-~À ššÛjìøL`z'TØ´ßøG5Ë ´\Âé¬6«Ç÷¼ê£}Ô^¹Ãpm9ðRžË÷îO¬Ôd'ÊwvWá¿£T5ÏÚ?NHÀ{ðØi¿å`œyv_›±%qNKäË”žø û(õàíù‡žñqŽË©ó›³ìk2©$&Vx(C��WÉ`݇˜“¹I±å¦êŸ§ã9žŽÅ)Á¹î]P Cb†@QHdÒ‰èÓ)™;äÁ£hñ˱oö†ÓÖÞr”²ÙÙŽ(zá¯Zý,b#j*‡qõÚ¡›PF�4ÝJCºpbäÞ4ýõ“£ûU•صÃ`à-ÿ*3rÏÍN�Ç{°€Š&Ç"¥>”/¥B¢±„ƒÑÃ\©Ñ¯ÙnªÜ÷VÎõ|Úr¼ø•r‹RÇ^ÅNûѸº�;E…ǺBzhÕc0n ³·.ǯÌÃbÔÆº%6G�’r*qb²IUE¥„ã̲€ÜyA\8òÔõ7þòYð<ô•óµ8ùaºõØ3îâLuä³gÑûõPÐ|×[õ¢†®Óvva‡ ͽ“œ3CW�î‘åR’…°î�ü•C¯Û££mK~,¸…±ÔÙÎF½F+´Ñ·€U4€6‹¡px±“.?^#Ð&‘£@ w÷ѰÆ65±Ãè‚éÀ}z)~úÄíA\ÑŠÇÖ–ÐBð‡€.°}Ž}Ù® ®©LÞ µÈ©kÖ^%–µO÷i†\jýMÀ›‘ª/«j¶í}rGѶpb¿EñŠVo‚­r$G~Ch=ѦöwŠŽýfYZ¥Eñ­›‡xd¢æ ¾¯Zª,DÒt”%�ø¬UÅ>­gYÊ‚õÛ…Ý&S‘9¡cº_üçý"ñ‘(¡Kqõºí @s™ÁR‘B“>–݃ç´"-`ìÐÍŸÊ‘3,–Ëu‡ÇUDøuØ>ݧY½n0­êH»çZÇNJ]‘ÑPœ¯U×Y`ÞÉUGwË2¬�XïP�$=59æ Ë$…/µ±èµcù¯™ ±$‚Ç‘6Fƒ“HMìåo{s̃­¹Dö�*t:´²ç2ƒ¸NÁᕃrüÂÜÿmU ½ú7ÎŒá9IÂâçü˜ž ©à˜ O›Z™V«¶ýñ a6ÂXÓ$§£Äc‡¼ÞÙ¥ìëDÃ|“B‚Š4аùXà¥!Ê�ÉTš452ð‚ÌÅ|ðM+Á)Þø/ÍŠ–€ÿf=ôB»' ‚>æë…`-€á?&-�DòO¹¢c5ÓSå– ™µ££f©îFÆ/g>û3õ°ó ìý¸§À:¼c±ÔM4Üõo|eÒ?œù‹›Ip¶N2�üYõÌjo´r&%[4­{LA*(çÆ£-�#Ét]�¸ùÆ´ýÑÃàR!Cç”Iÿ�I®+i,¡Wg۾ݷ9Þ!¡?ö-�УWNÛšïOTeêöZgÞi³qõ‹óÿÒº¡[�ìQÿ×Éì9»°­p.—îÌßX-Æ©uýš@ O6ѶO”ÎüV«¸á²£6ðitVƒ”Ê2{%$�ܰ¦@¦÷] °cÑ÷ÚIÞ�àÆ/¯rIæ?(|¾ ¾l]Ï Ã±'õ¼¯ýgøaÁßZ;§ÿK4‘”�{v›9㙞èÞÆARe7xkŠ•qþ”¿��#p\þÎÌ� _G >�’!?X�GÕß\Gö’[§ÿ“av,G–Q¾{æÀÐ~pÊXoÅŽ’ž<˜´úŠhô\ðè§³iТyï²Þæå¸wð=\œÿûÚKþ~´Ã0áoaµŒÏ„�”)£Nl³SâÔô©š²¼–j¿™²ßD3Û½d’µð@{X¤:uÍ~cp fïîž4 Ð†åæ­„‡Wa§çG%[Æœ\4ÍtF,”h@Q†_Ï5¸&­/µ‘dc†'ã-V¥$Õ%œ êQ…`^õ<5†�öàá?é­î…|uî/o‚m!òJý*µªBÔØíÈ�õZ%){ Ã4¼þÙâéQ ÿ‹ÓÈꇃIl×½à ë;ƒw4dî •ý\͸-,Ñ%M'kÒ–C¨�/ézmâY ãÝçz<}òRÑú³‡]|!§ˆoäK0MwB¿Gqbk�ݼè0åæÊÕ(câ3‚ÝT<æ Ø#Ÿé‡fèuiITKhϱ¢ˆß�°5ïNoOxô9™N c·xjÙÁÏ É:µ¸ÃÉ{¥ÒgQt‚Á‘Ý"vúÈ‚ˆÈ zª˜‚¥æÂè«Ð©QRGÎu·H#•‹ ·IïJ ¤Ä˜t˜ÄiUÂÓS,§’vÌqé�W+-âÔM''ZWÅãþÜ:$b<Ãj[Ì?‡îM[0ØlþÕ´{�Hˆm%‚7v¾¤—9ZHt‡¤å½aR‘Ô¡Z>’½–Tp÷K÷<¦–�Ó6Èëï Põ•œ—x§<‘–Ѩy½(&-2žÚöÛ©PÆ>¹>Ï/}‡lÑ›¦ É[©6�¦úö¤¹½ažx� r>]‘¬ø°Å�L§×cÕ1ÌA1¼ü«¶Y°w_«vŠ3KF§Íಜq”šVÿóî�²‚Uý^T?A Î]ÓØæöýmµ7´,‰–¸•Á†¯–Ž*.¸ïê*–�ÐÖMètãÉw:ÑlŠ)9n\VÖë+RŠZznuÜ^�8kï;à×tò±0 ·‚öœ?à8$é”ó“vG ADOM9–,T•óÑ(Ÿ»ù9I|n^D¦ãtÿø^Ò ?CÄÒ¨Pã8POv8ÏÔ¹%©?`ÅØ»%'Bßà 1¯¥Ü7þ@£¢÷³C¹Ø„þ�)Ï–meòÌ*¾cuÞ ÿ²†}DB½Rˆë8½Ó2Æ÷«.yêïW�a·²Ä®‰ÔLÁsm…kXßn`³‚ß�£´®w`ÅQ‚:…r¢”­\ø ½”aþë…i÷eÐËš«Äº¿a»˜¥›ÏãKÉhæá�ŽÌ{sò`ìȦË,ïjSO“/ú.b‘”Ç¢Ø2í-YwœzÜj“=Ì)0Òä�´Œdšð–/#d|¾è53 ù¨§«$rsœö_¨!á¶v0§¸Ò.ÌVUM‡‡ë`©æ;@Ytu�&r<ºh…±¶ÿÐß@#›7ŽŸvŸÅëÃãYü¼¬Ë¼® ­ã»ûïéÕå~∰ôA5Y›ä�:©Ï(Õ®yýûÄ ¾Ü²‹:f=/iËÑ6çç}y™/Å-à(¦µõ‡5L “ô#…tƒ¥… N7üü,.,A!û÷lÌݤYF Èd§û`{TÿÁâñÄŽÊ9¢í¡Â>žÊГ¯tùÜ@ú¤¦4( Ÿì ŸŠîüç’ØW»lto±dئ¨´ý`ÏáÂ*?ßë•9Ç’¿ e¾€Õ¢F,ÄÌ oÀ³Á�'Zç]Yक़ "Ò„íx¶KU©Ò¿Me¯°p±õó0¡ypÙ¸C˜Š3óI¦u¢�{õ_îºóRà“|§Güÿ~0c?î6e’ÕÕöè)&0"à˜"ðDÈãÞÂdú¦è°ÏTŽõw¾rÊ­ÇùáÏæo›l˜ý!I4a�…Ì£1YzàÒ~K=ð©";·7Qj’uêrY7p{HåsyRÌ—GòAÝ:ÖÚïo‹gÙ�ã·¨u C �'þ:È”S[µ¨)Bß×<àZ×pÛt~‹%· +]‚s§8¯Øß~àX‹¶Ð¸¼÷©<ÌOoZþ~*¢içù ½ÃI€¥ÕFX,òmHCý“‚:PÂõFe1Ì"$ù&­(•O‹ëú!Îö –ׂJ‘˜ªí©¾ˆ4AÀYCå�Q!ÎoÃ÷º®`QñÓ\„C͸úÎZΛH8x×ïázy2Ù@Y �Ræ®Ãïj³XN  WB-·Öå×,†¢ÑÒn”´vïˆwÉD�±òóTÕ#•uv(«U—š8stÿduõTúÛ8sø3Vyo'ôÚ/ƒðoæT«~à},}ª|5;s# ^òmËëÐÇDZqOeV¯'+1½>Òñ‚GÍŒsnßÛ×Å·ê5_Þ�ÚþB¿|KT… M¡Ò•Ò½Ãyùß*ËfLŸ3¯iÔÐZûdãçÜ ~qRÏ,]ž¾’Ë·øsß›6ÌéL3 •ßnŒczlJG»nØìÁè$6l÷Ôc‚ZA÷ªn1ÛRk>‰“ù‚ òØiš÷ cÕsM«ÏÀï)ªCnéØ& .Ï3ß� œj6 PàúqwkVyï^-€»÷¡š¤<…#X"!¹ ±d±£Í ÄÁ:±xY3ñåò‹艜P`K³‡üÕË—]=à �V‹EËý¸Ozh�Û•d×^üݹUlU”Ñ)û½9 T6­°¨›¼t:Œ{¡¥£`d[¸0Æç6qÉ:k9÷²Ža©¶;¼ÈóJÂÆp÷œÄÑ23\½4ÑoØ7ŸÁ΃ð/ÑTéA;(?Ñ ðœ7�rz²³‘¼ôírˆñ•ü#|ÈüxðR0>ÿ¯ûÞ¶îKÅô�ͽ÷ûeÿÁ“1ÓK@|qÜkÝ–ÞÚröÆÆ­ë„€+ ”‚gZ“*ÝÚß*î#h*…Ëõø‹ïhµ­f³ì&!ké}!j>–̹›[èubén­ ôÁMŠ � i ðèëëa´»qŒZíyö"K°Ì9¬Á©’]›þ7e0�J GS’Ëó£Wq¨@ ÁáÅé+’TŽ%á¯Õ€˜•Ñ:¹û’>†ï¦xC�­3 ¦ ÎÖ¸¢í“Y6?*o–�]§¾%ªT•jQ›×ç×~xIñ+IghÜ�½*síAð=�µ%=馄8ƒ‰ò6öªD´€Ï€ó¶Q®œH¢H3åBêSÒ\íŽ(¤QíLÝÙ¿WÎêÐ6Wœôe¼yG c>ŒhϬ3¡gô˜@C¢ê§úž‹»ïÌæ–&D 6“^æí2k}»üÐOß&g%?(sˆœXöc5/˜$ A83©n’eÿs~â4ŒI¨’ø,Èl:GÔ«n,iç�Ó":6€”×ÒÔ¦¥c¬61x£Ó¸äÒ :¢Ûö4€ÓåKצx³™ˆu*Èã=Rw|†%�ïdÞ¢G)2w|ˆúü�PH˜�žgyHÝ4�h âžS ¼˜Öý¹ ëÿÓ“ÊÀ ‰Ûc‚­L2ýw<+ÝU6–JÖ¾è!zäùwáùá'\‚´l€(»݉s@†ýÔ™6W–ž”¹2î¨ìÕôY ~‰$ œPÊ%”¸.�Á–‹é^6gp%7È@ªU/Ùì„ëé‘×1ØÆ‰¹LôHÕÏ0Ê{�ÂÄðÖîàòH uµana,›}�ã,-!fÁLÑ8Ó¤®…ùš¥\µdv1&�~îͪ‹›å£Èÿ¡’I9F0öÂF63›âÖßÔ†•r–?7a3f VéM-©®œ™CŒ˜JiFÇFjû:Ü1*WC..v£©MØÅåü˜xû|¸˜‡_»j‹{c“OŽô^ac¢¦|¯‡hcßz=wxài.àgÎ�î´Q¡í[GRÜéÝët»P%¹δ$ËãxRÒ]³Â’Ùl!gîAð³+6Aaï¯l¼‹¢¹•!â¨½Š·† ~ÜÏ#9z%(å^­!ØC£ ¹Ir æ® ";-[êó/][‘Î} üì¶9TêyÖFÆPªá¡ø~æûå“^û£©’pV›GóâÏYŒgÁ”=³ƒE¥àÉDî?°¨~Y.[ì ½¬~Uìžæ@+øÃC\×­J(#5œ‰ 2hhïà/O°GÎj5ç¯:¦™HFX†?�ÇÖ{©Ù`ûè”�ìÑpéB«°LÛÌkØïûµº¶Üù«!²çˈLÆÞQÖ `ç’Íývv­¹dôþ|,]æ ûsfã}Ë�;�–» [kðSÎβQ¬´¯^ÿ$…Õçέ –lC ˆ P©)“ØÄ$>©ã™+}ãç«•Ôª0|�Ër9ÈGÁÄ÷Q‹îéÑÅð¸"Îs§RN#Ö4™eµeg‚9„scìïó·˜ Ne6y× ,C ¯vG_#ËÙúðo÷à•¼2÷/ËÕÆ’ŒMc±‡¨}.Û|UŽˆÍuˆÿÆ P zÖ�~ß?C™_Q ãÜ#vϪ#jèדÖfÂ¥ÂÖ·ÕÐYNmóIÜý|ÎàG4Ï™ ?ºFþ™ôÑAtnEHƒ,¾dVRž}kžÚíÂD¸°Õ[&~ÏÇ w”|!Hw4+È�Ùç'eÇ‚AÙ‘]�ÅqW¼±ÞŒ€,^ë{Š­óñc_ÕdNJׇ)Þ×dï¦'ï…KÊ(/á¢2€0Z¦_öžŸk›üñHËÖá›Ãðë7ô-5PZúl¬z‚Å“ Þ<7 Õb4G¬tÃ{Z χæ¾L‹es䆑Øå[Ékª¼~¡» èÉŒh3dÈpîén×:74FÄáMoºò¬«q®¾Æ3}eÄÍÆ[‹=m]!¡„”ú6]V)½jò]|NÀÏ›k�k«ñµ0½«o7ñÓÂ{Ԁʸ¥ j—”<ùB9 0æ–á‹ tHO&\‚iµ`Q‘n³~‘Áj‹³7M¨>?á.…¿zÚΛåÙ°ÝD=’<Ô\��Þ“ •Ži§®'t£%ýø³Á œdF=™œ8k½ôŸÏùõ ¨‘áÔÉðç¸Ç…Êd(Ì?òKÓ”¥£€ÕÅÁÉÃÆÝÒ�9ÍŠcí!ËgN<½SkÂõ¡Œà±z»‹LN=Ï6mcá½â¯5¾"·fZþÄry!s�ÃTèYœÔѵÂo—C½7wQ§I{;CzØ~(M~ÑݲlŸhEî³XšdxË¥Eï˜¹Ž®Ú `r^ë�ôqX¸Fç×kx›wsxð¯w(÷s”(Štj†ö¶Fµµ‰Ÿ†]û=Ø¡E F*³гý�¼ÂÆ}´Oä‡�bزÑéid»ëÙš%?¾VBXùÜEfèA7—î°«¹åNɺŸhFWÀP899H“&\°1ó¥K­§¨´ÇA‘½¥"RÛÀcxbüMÅggRRMx¸õ’`�½ùvÏçG,ÛvóÆJÂà�cüA<·«“ �hûáÃR•³�«l/°d÷̯*bù¼çd­�Ü6 ÕYc% <ÍY7¼4›·Øi--¦p Ì–$ÉrƦÁ¥qO؆gÜM×MÉy°™îM@xÛIýÆÉ�¸Uyk†KÏwPO nט"ºk‘—lMΔ¦ÆÁïNl:?‘®�H�W ÙŠÔ½Ƀ‡÷…hÒ�ºÌßË©žQOq»#ž‹©^7¹Ä&ÓÆñ–T�+—)*üí�뀸Š3 É«¥•âTß\н3ÒMªÇc½Š96rÔæŠw|uöÉb.AÇ�{G*¢˜êÛÛ6_»ÃxÐXmšáÇwÒ².8”Í7µä#rÞˆ„íR㉻O>é£J×]ZÜ�§ˆ™µ(UÅpœ¸î7&Æ€ã"aTöáÑÂ… š+pâÛË�µòæ0#ø¤þ9Ñ-]åÂgê¶3Ýþqgý¹ßË õGvÓÌî’9µ£ .Ù»€"ÚA¦Ž1¶6©‡0E( ÝÏ*"™Ý®E]<~mA%n’…HÐ� «„>p¬rã/ÀþÆjKúåüœ ¢¡E=ËN«|x‰‰É·¨¿AOm:*c¥*w‚<”i¯R‚ïtnE»ç%¼m´ÏкÑÁqMÿÌÈ×ù=6l“úZ;Ü#ß×Gsbݧ< Z`¹Æå±Üÿf–|:—Ÿà\®× ÓºÊiøÑ™ªÜ×OL7Öe~7µþöð¼e5î¹›<6áú�û–•’Sl 4 rÛªÀ¸Á�÷7;T>XqÑ$ã°óÏŸ™çKv•@35û6)°[@uñ2@Ø^ØëzP#J�v—É ÎÚøuêDih«tê°áˆ#NüYšRA¬|˜ hž[<ƒÁÄ�R.²«¿®ó ¬ -¬~{§F˜csþ ä§î™û³óÐ-Ÿ¢\Œð¦�ZìjåpNúz!|î>)É«z66ðe2¦¶ðÇ(Tu:U'@Ï�M]G|˜lrðÁjõôн*ooçÌdhó$‘?ê`JûÉ >”{•�fJÔäÝà´©1×W'øÓ›i®EgQ¨4¯©×`+ß³bã!­Ë¹ÛŠÑ ©¼XòÕ„%oe“úÝ!Eî#a[ ¨ôu�Q(¾"—îß=B[eFk ®¸Q™íeÁ[èjÐY;`¼q:7¨Ô„òµÊ(£ûHRÀ&sPdYøbV2†Ýð;ˆ‰Ñ¹}r)Z–¿ò¸vøŒJ),Ñ2X�Žé.䟻׶ç+[ÛS[ì`\2¿é!D®¸RlÂO H�mÃçä¡J)pT’ކ*õ||0^í-î¥ÀñBÅÜóÓ˜U‘$úÓÓ猱‡F +ÛSßÐ ¼; ã”cXB•üèûÍ3óDSˆ®ñ§‹å>5Ó全ζÚû«{-~¡Å×'®C8Îd9ò€FNÉóŠÜíÞÆ«éI]:é1ž´ >;Ùôíz¶ô#fyŠÛ¤ßŠåžL|iµùЯ~öњĄ¡´»´bœhZ)”Ì�‰Ð&ؾïâTU¿o6}ÚýB±qF#†iPRר÷ˆËíÈD¶®b*Œ]Ø™ItŠùA’%@†íR·ü”Ä+l�“"ì1R¯ìÊÙöÓå%Dß9•mßâÉ~P£m|ñ�Cß9¶PLò£ìß*kô˜ˆæ‡hS2Dª¯ó-¯‘²æ‘Ê�¯ ÷Uà –ÜH :m¦aMø²ð x _^¥ÒHg¡ Òer !ƒ—©‘N—ù|›Ž–på˦„ĦŸîŸvÙ›©Ý:|tÍF¨‹Ç‹­Ó×ŇI†8})]p¦×µ«ËÇL!k¾mPÀ¸dæ”%aÿ˜îædm§©hÏ*ûáÚúü‚ï8¹°z¹¶v«º G(ªLˆéP¬Õ#2¾¿  4_ µ�Ù9J?ÿ�7'OAé5‡ÞpB´Ù©\ÀÁ<»“Xë<Ê×Dâ7‹ì@G\ …V´P�1¦7füŠþú†8xõ,Ê2U¦¤]<Íã)ï3 ´ã¯)AÛ�lƒ,�ð”9ÁÎä-#†-�Ç)ª[kNE.–³”šÊË ò÷‚{µ”‰âõ$ìóêrÄsSG‘º�?»¶‹©åÎæ0b|êCä‚ѼŸbK¦KooxÝݘ0IïÆj¥Ø‚µÌ#Ä£ âf©D˜YRm {d‡ô ˜Œ¶æÊ7I[ŸÎªÐ¦•pþ(5jmsà¿Ë=.<¾¶•åòdÚÿ#á¿vÍИ¢¡Ä�ºõ ío«è‘hë{9¦›NæC‹ySª¡mîßË[|å›È¬sxÈx¼ˆÃªó¹‡‚,åìÅ\¨™ª�”bL¬øx°®¢iQyE»qgQ”ÁQ•?¶«[€:Ä1 À‚Ùc££„&ž‚’¦TVõÖDRªô[g~¶'trdA 2–„H(>½É}Ý‘D°ãï9sñà|èI>Åc0J¥žæ·_üi�ÒíÝ›L"²ÐK¶ÆÉŒ©µ#ÜV"¾{ï“Ô Â ùúaŸö†ŠÑ„±s==º)£»µ*àOÿÛÙ„‘)zhl¯âÿž�0:/4ë [hÍ·žÞ…hIƒMÁ³ QÍök39Þ=[ ¨¾ÄO¢ðÞ›¯2åRy’íÞb{ðe„-•g§çÍP¿ÌÅ6Ñ1ôN=ùè A¡ÄR¥oâ`;O$ÇG)ˆ+ÞÑ*gñØÐ=z Q �¦Œ/ Ëd0›£…µÏdiŽË–ff‘«’þˆf»[> «Û�]™æúk�¯i]pý–VáXw\sßê!Ãр̬ ³g`3VR(L¾šj5àÈ�½˜œœÌEe2ˆ„C�Ë�ìæ+Ž¿R¸ ²`gËf-D||ïN£ è<FÀ(Â'˜2˜)‰�ƒä8±W€ú¤ÔØÌÄ`-YH—^êWˆòY$\­�4 ‡#Í1úš¾ #a7›I�Ú"Ý_ó`TZÎÃzA ‡¸h`ݧoIFð¤¤_²< ßJr×ʬýûýWy{ÑÞ\ƒ¾Mïáõšs›w¹ÎïÙÈûcU, F�‘@ÒàÝE5Tý©a5?#`ñö���(øð†ô£ ^LË…Ýt¬ÉÇÛ[Ïé•ÊJK(suCs+$`åk¢ŸÕãœ=yÞf4ü¯i…H£CŽFpId~ò "*T]‚6Uäéܧ)�ºI˜³I}n;$vt„„ExWY²ì½�Ïr¥AØ¥ò¨0(Ê—‹ÚŒ2׋ì~!7wÚøeË €b]­, µ ¾ÐbòKlìXµ63Pø=t©´t¡Õ¨þbÕ“R7$¾$ï ªÞ-ËìGQâèÍ'G4£îÉ ¸Ó{–û=(¨´@po§a{Í ª–ƒýýB¼Û}0Ë}¼öÌÕµó9oh¢ga=OÌ™³5•o:ü7Ç)à9‹äf¯.½LY›�ƒ—_ª\θþÆs—"m|£Ì5”*&Ô Ë ,²6 ¤Í°¼Å/oÇp2ZfŠ)¤ÆN¸pݬÀü›{ÎîLë†â]ÔhÄòëŸ-¨ÙÁEÍ0tVx¥þjλ[ý+=ñ.ØVl˜Ñxº?!³(/Ü•‹®œ\ A\¾Ô'³ÑÕÚ‹¹ Ò/ç\2ã—0d˜ø§Ù&–šá&ÐÛ×ÉüÔÚ:ƒPa§JI[ñièôÂÐŒÌí.6]µ&íÿÈÉ•’Eð(¼¥x[jÎXÞ­T~�6ÚßåÚv‡£A4òtÙ>­ÙÞ}@s ’¿Êœ½…ƽÈ<¯"�7즾¼ø>Kj›9ž-/­á`{âµßKd†YÝß÷bϬKêã 5e”иYdÎNúª£·Uõ2öú%âúäàò�ÀPBO$ÊL×Çä}(LÛ”‰‰ÃŒw†ÅÒ¬¼>2:0¶%|cMÌL숺‹½Ë>jH|yUs¢i"¢ëXí²3:Ñ7�S /g³‰åñbŸõäY鋤֟/Ñ ´A_elœ:Ö¡f^eOS¦Vä �›Æ©¿‚ˆÂ»ÝС`'Šž Ôõ2\#¬¤—ÊmÈx¿`�lãÊ]x,=˜™Í7ó8e‚9}Ûú25±‹®ÔV*4ÆYžå|Tx¹ƒì•kl>þáû%-lŒ‡¤y¹€w;èäæ³)Õ‚·OÜi­i«0E¹3¹Ò1ÅÝa匵-ßæ‘AdóÌTàlñ–¼¡3Sk—>µŸP§Ð_ éB/ Þ�}Ô{ĉ…,pÚ5>ä?†hVzUŽ%rT«u-_–kÄ–™²Å�ú¾ÿ›Ûäé1ZâP¤Q×ÈjÚp—t,úú4Á^—ßöTݽìÜÒ〨rù‰cw4Ü ˆ·�c‰ÏrP.—S 9âþùJåhií)D1¼™™U®oЛ‹36kwÚc$ޱÿ˜«@OUaG´¿'·2QQ@Ǥѹw^ÃEõO„Ï&bœ“,ú e¤é5SvÉ»ÇDGt_ßjñ¡"5 ¤_ìÚÖ¦a´úëÈkùƒ5ÛåwtM³gÃâòLœa)fO,MÇOao«6­�t�R,Õ‰U·®„žL&Ð+¢rÆ7à¿”í¥¹µä-WÉøZ ÿH�ÿY>þ‘utä�>ù�¾ÉJ¾DBÂÝÕH¨+ÀNÌüêm‚y¨Ã¼ÝØÅßñEð6e±˜¯*JVz²�—ò‚Fhë‰uf¤‰ë×€5.º÷1ÊùBþÙ?Å{ÿãjy#ÍÝ‚wã�kn…<‹kæé÷Ú4ßgŠ$šo]ä®ì·¿«·Ü>ª3ˆ¨]&æIðF4‡~¡•ãý†k[¸„É,ÆçIÕçîyÁ�î(µ�u�N¶+ãæ>ˆZƒCr‡5}F£Ô²žê�™µÍæ˜ßk± H®â‚pÐë‚ñ‡&þê”ìA|)‚낦�­f´àMÕ"i^8¿TCyäí7²¨PêRÇ‹ŒéæT€—¹85[�·š1«÷" 3Î4,è‚Yws¸€9:ä d"+�N0ø2�/{#ŒDÛB?]àž¯Ájk6ï?°mÛž÷9å[êrÍ~0ÌÚC¡ï¦ž‘ˆcüÂÿúj|·ñc• ÙÑYI MÛ¨Õ’p›Ü1*“©PÜÀ”§7�o¹-ìhÏœò:dÏù ‘Ê�¿{/¯ÉJ”¨å­ð<“nj¤æ´þ§ÊøÕtá,x3³G—‚ò#–\$lfeÂⲿOa Ò\“M7@oû~;©ÙТÚÍæº3*þS½-‘>›0þˆ8ß4`€øSŒåyºé(÷&ϵ^ÿ—š 4ªU˜{b¸K/GØ&=oýF®ÑcÛ~ŸB¢¡ >o J³§™‘�W¨C»¢7�#Æpb7˜C?Ù߉Œ1ݼHq§±�nº e¢I­›Ù†�M ËlQ= ·¸‰0zæšïM·>@ñ q*xn´@ݼgëÖdÅλDì°%€ÃhÒ,vöM®ˆš$‚óö!~Ïò J¥:�w#,œ>~çäzÒˆ( ’/÷n°ÂF ôê�¸%è.+[’€HhÚ$©Ón‡Û\è_Óý:k:}3ý6(êK^Œ?®káâ ,,1Ò dò]õ©nªR¢Îx}Ç*„ÔB:ß,ìUÝŸpa\ˆ›X˶ðeðž•7ìTõ`Ü#GƒÜ<‰�˜&3­ÚÄà…,Áùl>‰Tš\\>ÿq>êä×_ïL¤™Þ‡ï½ètÃÑ.ö\&ý�ˆéô‰'T¸_ðPäe£g“M‡±^0O½êˆLø¡$O¿ õÅ]ÈãX�¡c¤ 4ÅR½¦×Ê'äTê“ö»°2�\êQÕªAü@¼Ø?�QÄeôë²Ýo,—7È=»•rJ*ÓœI^ž+eÙc…ø'wž”Íå%i$…·q±Ás� ÍíÕ—ªA)�Òƒ[Fù2Å„ŠJ‹Åj^wïY´yy:l ›CØzV/ümÐM[´2†l>vù„ž«�ªÓ[:ºÁªC0 Jà_ 5•v Œuªç_�|¥|Sv¢ƒ ºíÁ ¡]VêRMÐzãD7ýœæÜÀÁ)Ò£î‘hL¼LÓ4¶Beÿ[Ea.]<«*ÑÞ””­Y)³FQÖWœz_EÌY3�ñåóa!–ûJ‹çm~æ[ð –ÆwAûÎ/¢Òeê9¾îþ›ŠsdYJvi€Qå^Ͷ�q_ô.G+ðÈþ6iŸý¸¯êˆ‡S¦N«3‹ë!? µ*Oкßú±(C7ÿÂ6.îâæ^^7­qk¾Ú™€�¼"ø–ôï̱DX‚÷6Ê…‹ïiBÛ%*söx�ÿ¹6 b®ˆ@< û·@³Æ^"Èl�Îm]í¿>+éôxÃ*;ß±-ôuÆ‘ÐpÆ16и�¤‘VáavêöªÆEî}‹¼"oW<ßíl:…PÃ$Ö´‹Y§¬ñÝ aÊ9ýþ”’ÎÍXYjP@Â*Š·Ói^uKþþÿÅå�×žÈ J¿‰\`.rŽ=‚S~K†ò(¥Æ®nº#ý|Õf‡[Ý)‹”þ¬êX½FeƒŠ®9ñÐ?¤`…,&§¶Ðig…R¿Š…)Ä>™°V ®ic ¨@‡Ø±@ lå|ÓJ©ÉÓݰN:f •^}hR²é’^⊟a^ºô;ÀUÛK?¢í�£úFMGðc63Yû§œÄ¨ ¡)Ã2Yk§c)$™ F?xMÜ@Ó)’Ÿ�ä‹C€>-JçøL‚´ÝØäÎËPÏË_ôίý%Jh¯�Ä„®g‚äcn›4$9؇)¦RZ-"ÕоÒJϳF�EQª·‰a\=©s4Cõ‘Ô©¶/øÿ¹.bY‡" �è‰"Ï4 ݳªUxûÔV¨í=¤ð gûþ´aÜ-79(†gh4g_ÆžùOP¸äã/Š{nJ+Q‹5ÁÈ‚Át˶ܻt—Q‚¤®èy@`jµ§R�ñ¶ Ž©3ÒY¡™µ£«÷ŽÔÍuYxÜSÅ—°ªù»H$ЋæoAv¨Ç&ê¿»÷Á.5*Ø9Þ‹ cÊËKè¬%\Þ:kà¯Õ“¤Û+_¥àNjS£š¥Z‚iä�úæÛdgòæC^:&}ÐÚ'‰g6΀ì†Ù·XýÖþà|sh7Ò•ÝBÈrN�YäÙ¶úX ˆ @2xíaž=¸¦5€•Û†vTÄDtèÒëmW)í´Ñ¨++:°øt)ƪô�M‰3<'réØe„˜:fL£]Pú"…|Ðr×=H)0•&xXÀADâmyaìÜMpâ Ž=ñiÝ:!ð /·y3*Îý!Åèþ%,|£$Ñ�4tE='ªŽdõÔE€Ì ‰�q ¯çW |uìsi»pLÚò‘\ú±ì‘ÄÄ+ºowth·o‡ôàÊgÑlG€g›•3Â;©˜Í_]ƒÒßQö'&æÝš¸ÃèàÆww)øÆ{­€¯!aà ç6õˆPíxÈk&íûé¶D¾Ï[ ½Ü»BÛ©ÙmÖ×iœ}}�b» ©Ñ E€3E‘•*ƒ$�‰¥[Ý p*¿ƒüÓ,�Šr>2KâMJ)®Ó8'Ë.d2QYé£v³#Eü.ÅÆ:áÒPDtCÿfU°Â†1è–nÞ\Ÿã�e.ü@ãÈÞÍ•çXü‹¹"Њ-äßBØÁù6Xœ‘Ÿ¥U#¢ƒXÅ» 'O÷KGU †U˜b×Õk\�$eÛrÏ!aŠîí©¯ÜDkº1¡I ß° {ƒfñ³¾LƒÛG�ã©7(sÍ9¯m>Û‰%p8å»°®±3“FÞu¥&1ðDÉ^¢r/�ö¦z¸Ý_YacÒ/ F«'¬’Ò�‰üuAL ú»˜H­ÒƒJ“Ðä]¢ÚšC-Ëóéš` ^™òÁI½So$¬aO@«±”dú¹X'?²ôò-ÏYõd¨Å ‰ü FFé{ú™ank¢5LzÙ¾2ÈÝåV,ß.?æã¨²¹œ®5k’KeJ ©”1¥Lše–}Kþ.ª~|J¿HBêÐñˆÙø ¿ˆR„E)f¤=âÅ?rIÞ93;äµ${pé}kbLv��±¾¦J$Nö€ÔAzlóf, ßwØ8UƒC£>°¼åôš­hÎÏFØË”T+åEºƒªicÄÂËþõ²]Uˆb‡¼0βžgc¦“Oƒák§ mmØ7ˆ †Wv4bœ." îã4'GÓdÖT§3ÆEžqÜ6 ]ùf {‚µåÃTbg 2ħ9KÚTd,éaq7ÀÀ€ÕD6AäG=þƒùœËqüQó�C¨bÕ€}ŠÝ�L`/¾õ°}èóYׯÒÌû×ôÐ1mm7pƒ°»»,S3$X»ü¼¡Z:8½¼~ «›Ñ�ϵ(%Ú‚ ívÚlkÙ§‚žò¼2öË÷­IÃÈ“»žyNeÀ‰Á`+Ìø¤r£ ³çа^!Áá³à¦{_ÖenÆj‘(wð®)ø¬ªÓOÈ.–H.›ü *@!nÔ§ò¶íš3)ÍS¬Ž ï{á�RWŽô‘‚|í}܉ý¾AÏæ�=23Ýk¶²x ÐÞ ¾�ý`‰¡¨û‰ÿŸJïcí{_ÈE“Ã(è}båœ:Iϵ¦Y˜BÈÄ<>ÁÑFƒ<—焆lå÷SòÖm¸ßHšˆ—Déz„×#>·b>ÖaÛ½ x¿OšïÿrªÔ\Šg¿LéõØ÷Ï¡’G�<Ê ÛUÛº©«³Óä‰SÃñ5Ö\Œ P6û ekýUÈtrš±ð¢¦×cddç, ÿ�¾DP:)ʡܞn>n4¶ QÇàâêààŠ³Ð ÍÔ§he\%5f-iÓ×èãð¼Ùã6™N–®—� *Ó9�" eìBM Œ·9® ‡EB[üj÷VºµÃ[a¥—Ü”‚A7]TZ7bœ[; tm°Œl¹!¥bC¿.®s•FËŒ¨S°.¾X\°é~KÏZsióâÚ½^0Á×g?»§‘‰¡8%èç•Á(Ç ˜Ö÷�{ Ÿù±ø]×Xþ‚ËùGãµÓšS͹™=_lì± ÚÇ <ZÃdb½˜½^!¤y&,]¡“Š›¥õMcþpˆ‚7U‘Uôf“ÂKÚójá GÏb¸ž«ªëKÎ$ä@>0VÈl+!Úäií©š"V,çƒäN¸@¸÷ïwlvñH°°ÚstgA%ºP›²?�õSMúßòÜJ-¤Ë•G”çfœðÜ‹“óÄЦôB)Ô´ ñ1æÝÛ¿©©,íÙßd¼»ž¦#›9@«šZd¹$1wûUrÃÝzxlD}6�%.|®Esœƒ:æçö&Ôç<•ÙP¡–�š'¹¢Ü=”'R°R‚:b\?—hÆW­IÝÌ�U‚óBÊj‰MBuôcÊïìïë*—©ísÇúñÆÇ€lÍr˜wZÝæ©µãv ã‹G³åcëiýŸ™óZ4uª¤*ÊTÉe aÍõs�pµ×yÂa™.¿8ߘœ÷W�·a·]!RÈ€ª›Š¸îÂ3‘xjó&²—b;LÕÖºPeÂIþp]¨„*bøS©–—ÞáPùúÔ3ý2SÛY:"¹@ËýÔ—ü«ÈHjAô7¨?ù*�ëË&úV·õç¬�tZ'ÑrºsÄJM_Ó);zcR(Öï>§([¶j`Ù’<ÛQös�ƪ.Sþ�ð¾ã# wpòXFžð˜ït¡´™†Ê†Le/øðVžWá^ ûº±¸@<*}x´`“è+˜ÑÚ| E™[ê ŒÖ_ùtœÁ}jqúeàÁ(ÒfA-PJM3•&.8;¦2»OjÐ5®ÎqW�GmJ7½ÿ}[ãjÙ' ›ÇáE=Ë]MññXá¢éˆ(�ÊÝ9½¾.V©W›ÿ1v̢퇷ŽãŽØøg©! ó�Ÿ¯™µæð!¸p^æ¯Âħ÷€¯›]4íÖU7¬7oÉI†{�óp¸’Ô&b{�HNKtúv¯¦á»á lÃrW‡¶/(جt‡&Øý‹A/Z@S9Û_ªÓ½™f©'ýY&õcQöF†P>ªNÑF B à.€–vÞnI˜êÅ#ªìEùx]Œ‚Ôv+e¾×Ó—¦+œÐd•G©mÖ9 G ‚rJ ƒ[—æNΞŒˆ› ¿]=tHÃmë´çuÐmX—ĉ›çéUtÑ­ Juð�> ·^#ަ<ôáf5Å.»�S…Œ"H/Û”ë£=õãºTö=C›Ù”Õ ‚ÒïK#£kþ�èW:kAQ~uç%fŽ1`“{¦qQ…]($˜Ji Г<ŽaM%rz2 §Cl›…”æÆ>åŠ�r‰ÚîÖèÙ! Y§'4–Õ‹òÎ;Ä|Ñiæ8‹l ûC$ÈU¬w@XЈ½«(uŠÜJ¾;2v°ëÑ Ê¸ø§ÑOiš]Æ„òP&¡qüª³ÇG‚ZËŽ�Іâ êö 3¿˜)Pa໇7¢›ú�è¯øÔçìL”Ž%»õ3=©2ñ?ÈQVX%…+öÞÔ&L™Óà§)¢Á°ì;øÔbĆ|s;Y‹ÿ¬àFKšV­8�ÈòøÂ,D’ÄFÅÅì÷ØZ´¹Où‘E4ÖFÉ?Ö«òhNe¢�®Õ»Ž R-¡Eî)˜›VaË7NàZpbe4èn«Þ/Zߣ�$àF(—øÐY.; kw%˜6®¾E'ÃÝhI*Œ/D–+¥|ù\ž09Òét�‹ãÑ´ ¶šM§÷8ÿt>¹\X¦(âMÅà õàks%`LޑȹƒÉ½¢’&ÝÊ“Hd¹çí<øˆ“È¢Ž9 ",fåLÇKluÆáCPTÌžÝ8éÙùÞ ×ogð9Ž\¨þ„á¡Pw²!Ö<Ÿ’× š[ye2Ø`¨‰yðR¯�Â¥3I‡^K›È÷"De*}»Â¡ÆÒ™;€LKÒ'Ê�/-©^Ô³\‰?PZùƒ/ö¢KuYoS÷à¿!Á)–ôÇÍñ…ÛqÖ$Õ7½us÷çS8¼7ëeºvx¶”G[¬0Äj=B¬ì‚fÎÖ͇±5`9#@�Ã5II�Ù§{Í¥?r%éý}áƒ-Ë|íÄ9P§‡‘™áÈÛÀEa)FÒúÿZN`-yk«X²½'ø‹�¦\¯)7µ}åËv)(ŒÊZ©©·ÌwÂ劂ñ´–ß+jxžªƒSÜë‘­y+,ýn£ªß-ö‚ÎIáÃ$ íh•géÙ¾Ú*Añ$"í%5)ÅHdwaےĺ8�Äǽ¢Ð‰?jèšX^�š¥¦òPËvù*ûˆL�¹ï¹Šô|–ÃHÍt�xè'_«…ˆ‘=½Ìó^ïðLZlκ·Í$@:i ÷8xœÓñÍ­ 3÷E3öš4½ SkYu´RzÚlyëVøÑúÍï9‘ÎÈÎW§Þæ®’WÝÏú%ΞßÈÜúŒúC³÷�Ô'•4��1¡yþv¥ ÇD¥¦ù0e]Ù¼Á7¦V„è­�~há÷‰54”aETÞm»)Šº÷Ð)ê Ó<%¦�,XI¿Îðɵ2Ã@êûvG÷õ¤9ä„xð¶ff1ºg^š»šþ% o ~pšŽŽ4¦Ù5~ß{îs·Å{­hìsþÄÏ.ÑÓWalvÔ^Ú$8bäÁ^$õ×ì¹²åãUÕ+T oŒ¸±1:ò¥ç¢ô G|�ØÁ¾ˆßzï8~Ê�œN‚ÕÍ1‹YçhÒüjóüÇ 3–5í¿3‹$BÛ DJc攄· @çÀSÝÉv…k‚G2´¸œV ©˜ÿ€èhö�tž (âgÕÉ©­Æ"”çpÖ[ÿeû(È¡Zn„jú$³6n¤–dÉxÌwç®�,à­Ý›‰ÎÇ4D/‚aƒ'ŠR)‡fRaƒSÉØ4¬‡#ìÔf x¾ ¸|}„·‚š+n$3³íPØÓ¼>�2›d¶¬‚ü9@Ùäú Þ”ìÆ,ÀW7¥ú�žR3Žœ×T(LN¾·: +Æ#í‹NœåõâåÊ¥ã“,ÊjÒûÕ´4€ß?ûóçõ»áÜdƒØýõa4ÆEéò“؇R4A%ö2Xe¹#3ízi0«%Ó€‘׆ezp„’ž±CÕGFûs¹q—Y°wí?'}j#–iÉØ3Úº$ÿ/(kÇ®å%‘YDœCÈb{Š ï`£õÇä(ï[$½Ð�ëÊ8ì¶ÅÿeZúĬ9ç„ifOÃ)ýÉŸhFÖ´Sé Mj?ä¹)›v�ûšAvÜ5v‘_ev&vÞN°±åøIMÛ½¯å!5,Ç ÿ˜°¹Ù“> v˜¢Ë2‘IãÍXßùˆŠç±Æ¿Ñ'y|¹Ã!F–n�F»b‚BK ,^]m­ )Ôß� 3¾3d¿‡N³Á….ÿ Y}É<,õy‚ÎHÎT/x5ÅlãÈl(}ÍÁ®Ø0­¥ùþã%qèU�p�Væ4×yóáÅe¸^ o<{ßcÐ`‡·ï7¿˜#Í�å#Œ¢—"tãUõØ?ñé�U‚îr=µ·(çÉùU¯˜ÙbSƒ kΫ>0x3SÜMËyrÿѨW®ÀAÔ!œUðÇ® ïGÿ§û1ø²"x™±+ ‹¿{š.„’,)<ö"•ÛS FöV< c Ÿhß-’ÊÓ¨9ƒ{ì†j®µá#“|ÓTëÊêLÄ‘ôÅŠfÎÉî Šóñ׸O%J€RGò(‡—ƒÄo¶†m‚årªá5¯©¥tšÉ‡tïìîÒ4û™S‰8‚ê…-jýÖþ~D³�ê ï£ùšu«åPãë7Rļ81qÊ@äåN.¢4´iÂÝ1qøСì±ó¥ô)i×TT™úÅêú:¢¸fë“WM¾í *,%­¨e�mø1n}¨0žV£KN]âS–?ás˜n}½i ¨€"7‰µ°�ÝAxðXð_DôÓ¤¼LÎÿ˜Ï »�9µ�ãi°ÒøÎ´þCC/—ÊU�r˜!8ê{9Úú7Q¬©Qý©2úÜ„ÎE˜^ñö‡Q'cn–îT$›Ì.ˆî°Rª¬À¾úõOqi®ô ß;YNÙ �)ƒëŸ(I_µíÓî‡80î¦ÙdN,2Ñþ�þ—\!⸠Ð º@—´Ã帋 U ý™Ç× šs¬P›BJþvmÆ`gƒþ’\xÍhèÁ�áA²\×]«þY]+ë8ÑèÖcØ~P/CÅV޼ŒDÄÈ3t¿’ýdpá�ÂãÞ:Y®)uÆ[½Ø~¡Í Ôoó‰‡5'Öüâó.Žéšz÷0aJýN_ÏçRq¬ÈôsJ¿Çbàü†ŽÈ…L·’×ÉÔ¢oûeë.§Ìƒå ª9$@_M33àˆPÉðu›�õ(8éÆ×hªªá�äVýz”„»èïÝдTmÞ;7nXrП¡wÅñ¸�M,¤0a5�ˆsÙ8ø²°(óÄ܇w$.°y+4Ä]õ�ïk@­ý•s €bÉçOpÉ$å´¦I«ÊiŸm$É®õ˜9•d QàP ÓÖÑ”bQŠës—ZÁ#”¸".®CÆ*HZŠÛ’¾øÖiܯ.6´=á„ ä4x}8þœË…5×ÎgõÍŽœû=źº†¿ðœZ¤R¹fj&É•øD§ õ«×D9Á—qå’&÷ÍÇ[ÌÕ’å߉[¥f�J ¾$pÈá(¨¬U—µF¤pÙä¶P3çÌsð$ m ÕEžy#Øäªæä4<­‡~OìÒl\©â%»¹µ÷ÖÀi+êi0�<Ž9éNà9(•$ñúêÙšNcBÓiIåI Æ-V›þ-!ø­nJøµ‘�3“H²þ t á7†rà>"(kKrNŒK ¾{Æ6T@ú³¬½€¼0¥{ÚFny;_ÇJKÑ…ÓÓ\~¶F\¼Ðb? .ß’��tÑAj,pÿª(ºç”©K3<¦0b}}¡²õšS¢ ޝ‘„ùÔ|¶YÚ(Ûó)êhumv1êx£Ðý] ‚ KÛÔ™;Á’á,Þ–/-³zÑr>©CB7„u#• ` :<*M4�Ÿ·È9Ö2�-kç~#WG)ø+ÝÓú@²Ã¥lÁªÀYN6/Ï0MCó>=÷`§òàú &�mPY \EMÙÝ~i݆¬˜ZŽýik^�áæ pÈDm2ò ’jÆ�„*Ö3ˆS˜Ì�z¯ûwwóý0ŽÑмÏж,iCQÇÒžI&hU Rý»†ÁcÃýôk”ÎàÔwxšCÂf Q{'�(Ÿs’^E¶ÙQƒõ¶ñÁÜÎ\”¿ÃÔ²­R¤Äƒâ°; _ø“mØ@-¬9)Q½È¤Þø´P¬Êåbkð¾Jßd!Ìg�ºk½VǃÇ\¾¹�Fm-‘žÕ¤beî[-äfa±Ä>Ï HI,ÿ.yîéL^‘VCÄ‚YË=Y%ÛŒfÅ.”wcÁ£ZçÑ&Q ¸6àQêŸ(Û€¾×cõæî`Ý“€š7�|FkH±P†˜Z[è8Û]ñþU$ŽNõÖƒ|ƒh”¶¡€{b ø ¨óÃ#È:§øœxn«áÿã*teâ±°RF9í¨MÅÄ"}å"Ø‚5ŸÙÛç4À|˜N…C˜Ð‰…õ�’¡¹�[·?›ˆ¶û-“w²bÚûŸ¤vžœÒB¥Ž¡à öhï¢t@RÁ¦ À„Ã’(ÒYùWìP �D‚¦Á|K×éýÞÚ Iu×ô%�cŽˆU>÷RQ-¼(—$(•»�k[=Ë�oò�É]cØXPñ‚ãuËÖÞ³ÎÒK·u[ër®¸:ì°¯wÝL¦ʲ«'�fW)ö-XRÙ¤6ðm÷{ ;xÝè XÆØ(Ñà»<ˆÑéÂ_¾¿íS7ûØ: 0K�E3ÍõÊ¢ìkЉéWI._F„Ü Ùユª¢Š­ ®ÿôÑ3‡ dË�®4/ê–BŠ(·T¦¾JN K†×ûãÜö‚™ËŒ^1Ðú�fÀÌÄbÊÿÍU•sQŠóŒn) ÌOgh6+ŒÃ¶®ÒàŒÃ‹|xÒŒQåX=gd!�Ç·+R Aò-â°Z!,�Ö˹af&c‚/{S‹èÀ‘’4Ê‚ne]÷iñ�…6êX6 5´¤i&ÁÒ`Ûxf�V ›JÑ™z…¢>1=±G†Jç±�4­võª!EÄŒ�ÉÄéêñõ~éb ŒX=ÎüĽä:D—£Îm'›Þ4Aí‘j a¯Ä®ÌÈ¢'Z‰A㊎”×ÑB0hÕ8P$ÌüßὌ´sÙÖŒ¡”ÝÏ$èÏ*jo…SOÁù\ G TÓ­ûKŠfˆÕ§+'g’wgt}‡Ê“žÈB×n°údO B¢õ‘ Ÿ…-šÝ �|ðš`G8[¿þÝ)¯Â¸k`Ѥ‚÷èã÷7‹_ÍzÆ|B„p‘ÒËdàž /š¬?ÂõÑíðƒ¹z` X¢º’¦+ŽE�ÓàËBôgÉK-hÏJ€¤îM–Š «dÍ£AåpE:õõôÐß‚µd¯Á’ìÆFôuÒD#’6HptHûÛ�87ÂJP—ŒM­œ@áQ8”Ÿ-›ìªý¹‡.{b5ŒÒ„= æý`IäTÈF©ÝxÏ#·‹žî}üÓ‰’‹á º0¹¸dR”ÝߥfØÖ4înAîΚù§¡·í�¾ØHîÃQ¯¸™ –9N]›\8!•÷v–Ö¼Å�'f9“àaP£îˆ{’¼1冥ȟä€GêÍ¥ª³ZX¸+9n"ˆh�O<_Ε#‘Tz… T>²H …r)+¤enîUÃýÊÓG§o¯F_H›)RKÏXT#� yM0 ;÷/Y€Jµh&�§R H�|ºÐÙþö|\U�B,;é1ä¢5o”`€XÇøÅ©*�ùYLªÞòs´G}yø#ŽP°räM–kœ\=|zPJ, ùXø7cÍ‚aŸãJªOÐ òq¬wÉ­ˆáëÎyÕ÷‹Ë ­ByÁåЋé�O¬�K+—zá¬ç;�¥&æçÀp˦`ð–¢(\©RSúã`O¯±ŠÖ*ëLSÀuŠ4±kc]ˆu`ôRøNœ®öÄÙ’›‘+ŽÏ%zˆúÐ+0Ý%¯:<º Å»ï—{(– %´¥‹W4òZñ¬!¶‘ש-ÆOõ­8 íèJ%Òa䀖FœÆžÖ´+pHÖ‰†`â½G½èÈÄÜ¥gB,Æ8RZÐF®+€¢~JÈŽ-ïRM^ÚT¦ ´(ìèÀóF “5lî!Œ‰ÝSÚÏ­YÉ{«Ò_à¯q ^u÷ëKñÃü_fs†br<–v‚T¸�¨h·WCy’�³J4ŒIâ�Ð\Jš@$öæÅéqöÏ­Ð?0Àž­Bxz˼"È/ÉŸ¢7®ÿÝç•(³ÝñuÇq‹Râ>ð½xÐr2€�`xSW…âÑ"ŽZ"˜¨EŽvyñ¬oƒÞ- ¥-§Ž l€ g 4âÜè`Ö8§†¾Á8\H®ù\¿X8 uÒ† Z�§€BÚ°·_ x'�ÝßÅ<<àŒ�¦3=_4ïcÒéaŠÜƒà¤;b{ë]rL†Gøëj@¾&3�yÚzp°ö‰GmØ,ÒáÓ‘Yw(x¢\Ý÷#8s:`}”0¾Ãú:ÂnÛl=ÅI«ãa#lHÜä‘ïŒPÃänoª3W@á=¸$ú�„âÝ«éð«7mØõåý=lU e¹¶`Óæï@<ÀÏñlU^)‹P¹0Ž;ËŒ³EõLg?-Œödµ3"’LÓì™æVq±Ñô4*ñcú*d´}^ ,.}y˜nÂÀ{4 Ò©yÝwX„Fª¨«Åÿ/ü Z;M×^–=½…N¨XLp¢Ž]â¼®}µ�ÒZ:£zšú`8æ+Š9L¦¸æÃ3S¡³êíykˆÒ™”•…ðññ"!‡šÜšè-X|Õ¦£ÎÈ _Ë &@ñæÁª×Z’�'ÄÜ·±[ÿäÀQÐCš…�“[‚gí�Î{ÅùÐ}K+6Õ�LÀ¨ˆ£Ä@„B˦ž'Œ´O\ä1!ZÏ�2DÃÒì숒ô[Df> p[Å;Å&é„WüŠ©SáË€[ZH:~0³ŸÔBö¨±ÿJ6z�Ì>¸O;zü�†&ý.�n�>dž“™‰EØUz@ÈǃӕCëFâž]‰…T “üê'Ú½àc¼Ãy£c@ RÛÆ›]ö�ÔþÚ;JqÞnÿ„õíZ.í€$KP„äV·ÆS¨ƒ7`¡cÕÃå$öŸýÔ>Í3ow\£WÜ¥\ jÔºòF~8úÍBfr¹�‰42¤EŽÊ‰!À'v'ti›Ê7Ö#\:šlæéšù†þ ÚFaLè¤ÂD¬1¾¢úZLŠÏ¡M%üI$Ÿ‰^\â¸|#”t0¦”§þ…æÈírg{MÖ8Çž,ÇuÀÞ9è D¨¬M"ŸÊ˜³xãù~¥½ýRaÑúÆ‹Å݇ ¯…Ví[ºZTòk½Ê™.¬ű„yío6av(¬Lrš ïVì yFß“ÿ¶ÆTb”=çûw‚¢ q૽é"f_‹žÈMIã¬&¥ni/ä@i#m­òl>eÑT’¥‚t1—¾¨–†l³øVëá<© bµ}ifH¯ƒã8 »€• âaÖ€áG?X7ûZžÍx�mÀuÞ¤÷+çeÙ8\^·b·/KÃ2¼åÍ'ÌW æLï‰Ã›32e_Ic¦E ÃŽÛM`…\œÚOGûí2­oÕ9õKwbôœ ÈOÜÚ§!êú²þ»v¤¯·à?Üùrõ神2’ú©4*ùn§•O”ª ¢Â�6Ô¡(²»|yr׌›×¨?U. ì¡ü²mQ-¬ú¦"�£ûcÃqŸG Ÿëg¥Š<öS:ᕼ­×ííö6®ø| W:½ž»Ù"ÎÈ”šÞg}‚o³/rÑHH ñ‚3cQ†bfµiÕUx(ÐK  4××7P»¹¬)è ¤W1ü ŒÂ”„1QÁqìº÷'æû?/ Ø5\Rô7…¥"OEÉ£~°Ú)ÅèÞ=þXY¼ gÞÖØ/‡/c{µÁOFa{S©¿;9i5Š÷U ÅŠ°yî G–ªÝìÿmÁ¢®�Ål ,˺•#,\QwW�äK„çéDŠ�¡J:µ “ØÑÈæz½´ù²#Rä&aÜu™úñû�Ãf¶x*ù,Õ ¿=ñ¨3c‰Û^«œ®{�+ÿ�1[dÐ3)>Õð¤²!#±ò9šWÕs;ˆŠ%ãÂmr܉Á[6jJ†Úb']„Æù è¢/öl=cƒ¯Ì}žê둚—Å5QùOc‹ð´ï™ ñ–!ÐÓ™ž”«½0°jÙ‰ùâ ¶Ðä]Sº›ÿú‚IÜM—$’øþ‹c‘¥°«ý ê\3šÒ@©á î+$CC€`ÀœÐv¾Љ€\i²æ¿F…fÄkn¤Ìt¹L6�û-,RÏ”ß#4Ð&sg³½èË€¸ÕÐ^Qm·Zå6s(¢êƒ�ÓÅR¢·G˜-§))Ã!”ð@7Ý}ûkì®aâý ¬N_D#¨WÿÇÉéU\Ÿµ©÷£ŒqÞº?A­·ÔÏ£¡‹ŸA,2©EþÑ£=Š ­\3ºï-ÊŠÚµLÅpٌؘ!õKTòûx4mî@ò±œJô²Ä”Ç^Ñ—ÜÛá%ÒbM펞þvÒíèÜ.Ð(¬Âˆ• eXÀL\”h`×m.h:Ô�± ŠLP‘à ?gÖr±ÑÅï"ôt–Ò/»ã_ßlB`«‰îGçX§¿í¼Ðž¶¯± —LŠ‚etjƒx!§ 岸G-`ßÊ‹Àñ™k—‡h¢àÍNöp¤ ëÂÍå £òO´…ÇÃLý.Q9)]t;áo‘Ø}éœf«ñí¡Æ0cs5Ô*íZ�£Å`6ÔÉ·nÑ⌙?”©�ŠeTâï´;Ø"yÖ«éi» ƒ*È ÷÷ä�’£€ŠpŠ5ÛféeÔ«œíZ¿ëŠë0š7…Ï(Óy û}ê©Òrž(û\ 2økS\÷‡… ä;"ÆìFÀín?õtšb¯  >sÅ}‡Eò-Õà[yÝ€§eSUzf"—i�cO¿D-VÌâ.…¾Lñþ_œÉæh«Çk¿ Û.I?òÇê§9Sü„µž˜¹[.‡(»Ó 9êA—d~ky§P'Ã(ÏšB4ÉJ)�ÿi8Aºvà^²FáQ§Ð|{>ùg7Ó{ïX9 õœ�!UÜAwcmu‹~Á:šã¡køÜ¥D�­›�ˆpÃ+&Ä/‘rY`ÇïoȘi-.t£…ÒaXˆ…Ý iÿÞ�ƆÕÝE ÓeWÀkÇC½YV+U¦ ü�ªñ§oåœbàï’¸|ÊeÔkÓ|B¹[yµ¸Ü¸õcÒa “0[˜ÄJ‡«¿°k%_pÙ”KZÎôˆÑÏUñ�D3x�b–:¤Lbþ÷ºo\\úÞ�ƒÏ€?m•„Šþì$QuÞUK1[ë[$×›†Ld ô"N-µ�Î$ü"¦ŒÀeº3X–“hºQ;ÓŽÏþÊ©*:-/s]iÌ ’òÐAPç^–CÙâh&FÀ{¡O±]êÝúBÙf¡GXàcm„ѽ‰ýÂ3ÇFÿʰ̔xΦƒ»ñgò¸ƃ ÃòzTG9‡¼ñP#J¸xÒ­m[ä–:î‚2ÅæMÒŠ •`í'_&‘“îÚö”Ô,Ù�o:Wï@ÌÁB?>ãJ€%úͧ=º§o¦~¸P öú&àë"°¬í?6 ,,ˆb#G%5p¨8šI¸Ý‰´øåOÓå;ë ï[»MX¨Õå2æÍÌýqw:#j7«é(-b6�ßìíÊÁÕnÇ’)þJq:öÐÇd/IL…†¢ås€�9DÍlC¨i GõY(¦ òÝ«�xI…ôÂÛJ&£ùãèF‘[TiUïaêï@º>§j¢.õ.ôPÜ6j®�:� zõ]¨/ù=`’;|PÜPóДW e±TñVîdñÛ#¬gb)lx-¯¬Þ“/Aø�É fÚ¦Ê'™°‰bê4ÕòŽ_³ÓžÄÆõŠÔk ÈëX<ó1Q;�ºÅü�¢F�ðjk ÿK©¬ú=Ëõ¾ý 6Z³à#z½NÄxå^:_¤÷xìš #Œ•:jüýÖÐl˜›×֩œc¼`{9í†�6 ª§ì©-ĉž�·ë,$E÷Ï6J’NìÚ8†hD¼€nRÐåL.(E?7QVônËZÿ_77g"aìE!¡–Ζ¿Ö·Õ©FÑc¥7nI¢=tåG¹ˆä»Nkm™�d* KÏQrõ:Û Ã&ç[|0ÿ_…uxq? ü¹¶ãWdÜVÏ ·^(úç)3ü»èÆÐZDÑð‰v¹œJˆ#f"˜×I¡ó�€�éÐú¦$[Fºf,TÀtm-}ý9Iä…‘*2ßMÞ×ZdeÏnÀÛBmS³Š¸u0çpš¿ó¦ü/„òíüÓ•ëRߣޯ««8gòŠè@2,itÌÇ …ù1]ü_…nYR¸=ªœÄš,Ã¥0l†fLVÏ£e‚‹Aâ×[˜™ÓDæ2á\†¶À ~IĦT\‹n7 ‘ꉿñ e â_aÓn�Ôs¢ì#×ãþ”j¤¬8ZFá¸j'Z§ªaý0;4Sþ2H� ø7«\¹R)7Ë”ÐL4œÞ1‘ÕÔ|+Ä{\/®üåt­æ É'Æþíü%”Yð0iH¬Õ3E3q‰ °ìt%áçÒÛBM“ò÷ Œ*Xû$ <+å6Z?h0h$÷ï&[\§ïw•)'†�Ë0§nXkOH«Y­Áêa›MÄ›Ýk–G®tTa»xá.NqW‡WjÈÕ`‰¡¿Ô”¡ ܧÙn3¡g—°Ô�€ßttw: ‘Q¬e߸Ϋº%(§Rð"ó�j¦ñ=Æ%û¢ïµûÀ„·«:+ É £³ÊG~ZúM,aÝs ·–{Î|AjÞÃÀ�6ì×p3_ͺ\8:x5p¤Véél¹B·ý.‘¯•žä@ÂÍ+¼çÒ)V»ö7öY6ÃÄOÅ|“.Aã/}Ý=Ύʃ)Ö’a»¿ßTã53.`ÕÜÚ2“'ÚÑ! Hy+ ’ô§d‚N£¬=öÒ›Æ "sEŽ}æUG±g8N¼”ÿy†. Ý(𯰱$ÃdÔ”³f+¶K2–K$cb³>·š*D¥yý„+d27ûnÛ'Z�›44%â4YƯ•<&h<| m‡í�/˜dH?†bœùA>I©vpIýtJ›ôÛª‚™l_mš¹m "–Èøaí¨ŠÛR›Ç7¥n½"£ô,Ÿ–»fˆ:Älâfiiµ9ºrŽ”‚¯©dòÙG -\[Ïý– ‹ÄüPµeºôR§§ó„W°åžQµ¶g³%ƒàw­Ãî=Ít š¹HiÙ >$ ãNôkó„{qeiË’‰›)Z�zt„Ûvá}²{PIöšk 1¥B ÏL1<6:êgù @@06ÝJ?µ‚ö10>YU¥‚#™ém4•·Å§¡hÁ²¬iê $D äÁ½ò/xÖ°ˆJïÆÁaÅÕ@ãÖ7Qleß„²[š Ì”¥Ð0ŒFšY§G¶”�ù™ÔœçË(�Pzâ‰O7ë/åÒÁ)R$|þÖéxd oeo0HÙ¯�6åéõ@èmÓä¾Êrq2øâò±Ø÷Ët'kÀ²W´§©#zej`KüL<á3þíFØ;ûÙg©$ÜDûν½�֋ćžXYsff¯Õ^Œñ?†S|…bf^Ñîƒ5žÇËF¸–f:¹Qj -b|=1\“ã°½®ŽÔXè4QºÎƒ’!ä='‘¢õkˆÊ›º­A�™#p›»k¾ üØu8Ö|a qâECªC¢Bг$ˆˆhÚz‡ZìFz€b™Iè»mµ¤à»tVJø5×–Mѵ|Ð\é:0 ÑÀ®×Ý-¹ðPOc*=Ðÿ©ÜŸ|z1õ\K²h™Ä� V )ò6Y—?÷&Åpû$aû~  ä,Ûƒ·°¥Ú׃,Å£ÓÊÀ·Áh½“ð/5ë/SÐ*¾Cs~á¾Õ#ö.�ëÑ;v.®”¿t§‰–�ýW¬sv’­Oþ ý›É™x£… ?:lz1G�û×¼4ÚÊc ä½nû¿è.V]$쩼•éšq^¥HDãºxÞRŸ–_CŸƒ¬Û wÇš'ËPV™”¡6_×Po:¨L":WÊ.KÁÕµLeU+™K›¾«µ£Ùãš>XsúæºÅ)žÅEŽØY™<¸æD—<\gW)ó h;o¡‰+tDŒÕÒ9,ˬÜY'ÂÌá">­@-Hàà»°R�G¼…VÁ[‚~'µÇr`ãnúePo¤Øó>³@Õj• Q UÆ-I…2ƒ×Lj þƒ,ìÎ+ã[¶†´5%¦Ê‘:ÿÈ m„½D&ìÓ»]˜ÕÚZCò†áŠž|ÿ�ý‚ã/+÷)HU²MÓ˜ %1wŠï/ ~0,Í�V-ì &bLŸaX>î§M»jYJ‚T Ýr‹™h¿ Œã­´ßÐÖ8TîO ;˜—=iOÈSß ©Pæp¾�ü( ƒÙ¹‡ëË‘G{FŠ álEcÈO Uõ™Ã0d6GÈÞ·k ×¶¡ënò8³¡”¢Ôã²—rí‰äì€xtWiQãØ=E;Ð{]xû"©dô©PfžëUÒÿ¯h€[¥îóyÏùL¿»c¯ý™$O²ú‡ÍBþãù†:Ì&•rk¤vmø”AZÇîã‹eUlnù(,X„}$¡Æê^•å†ÎcØ›îÏÀ;¯ê,Ö=ʾ:´®ïá£{º žñŸ»€ Qí”âÏo%Œæ£ÇÊ•ã �è�ëØ]c=7±Öª» šJú HŸV¾Ê(Xœû,Þ„3NΦâF�ÀR9¹IV_º[¡:ÑÙ3Z?‰Õ°ì'5V°ëEœ•[„êŽÛ¢V ÎñFJIv7D¦væÂKHIÖT怇Àñd”°BV«ÄÅW'}/µäKèoÖ×¢ÍÙŸB¯R§ãtF—‰M\, ºt†â"%—lXükš SÂã½³ ª_£rd�'T¼ lêßäÿêLùÉé¨=WåÆjOo�5Äë¬ð �X§¹#ÿê;(X¯f�ëMþÒÎPű¾K<Â#�€â;È ˜kÿîò%:(jn\ø-=K7³o,èÇ×:Ïúõ3(Û6~=àÑKÙðáÖüà&�çÉ¡ÏÌ;‡ ²wZùcä!ÉЂ``ÂýÄo­ ´e¿œd>µ0Î碷qòqL­­–©Çý°Ù%,á�IóÚ‚ºÖƒIÐUûíf-�M,‡æî,o\eÔ¾è—ÂØm R$ÈvÌíT2ÈÝ!Ï…Nu.ä‡ìº³›‹ÑÆëïV>¦`šir!=‹Ùmâ‚3Oçbëö*ÓSÎ.»„wŒ˜,ßw€\ø´l9N[º¡™9•r_¥4�úDfÄNR$¼‰=2ÀÛkÏGmÑ´„tü¾¦ ‚Àðjz TµÞºEbÕZ¡à¿€Gg7­\Q¶0&B÷;jJønL (ˆNý¥ŠF_æ8�# QQÄ…Ѧ·6çH»¨¿� ;’�=á)}yä Y«ÂœaB»CØí i�„=8ÚŽX³¸L†bDºÉ4àŽK‘3 KõG>e7éP�äMôO‡7ášîöñ$Á{ól 1Þjüø ·œ�ìñ#]ŸÇ÷”gÏ–$V†M…‘ð"«¬l舘A�¢Ñ›ÜyU¬<î<8joAÎStM)Œ6@;‡¨ ªonKÃÖwœ—‰s,0x2ôÏ›’�z€»¥á]ÎÙì.á_°cPÇI¢†+Î=ŠMP–�QÍ7ÿ÷—  äé`KAt«EŠú,Sãfç¢Ðc%”:u•�¢ç˽àwRªuƒ›Ãí©é¡„y‘—¦ðj4ŸÙÀ kÊ^¼<·ôR\`½¿.ÁU`öiZ{Mÿ¨â'àšÌ/¢Lêö iO�«½GŒœJ¼ð‚@U |k®O,¡÷Ê Ê+ÍŠ)J­<ŸpR}.Ó‰UT'“£–7rbÚŠVo€Hp/÷ð?’c`Õ ç¿ð2øÈÞpa8p®få²yÏoÇ¿P0Þ~lUÞ0\[P(×>‘D†Ò�_¨l˺; ²>êóÏOcé£u×5‹p­84„îh¡]*”áÓ»_mg‘ܹŸŒw,Œƒõöm­ô M²ÖDZdtƒ6 6ò¦ÁF Áe@½ö€Q²\¬]†Îá)ˆ®–S\¸e×&¡6>Ê–Wý�ùkÝ‘¶ø5ÿA’Z¹è ÝŒñW0jº¶ÂŽE]x®¿í†ø-ÏC„­ÿ n…�ÝÇ7)t©›W§0j�ž;±ïˆLJûÖÙT@ Ó(%й!]D>?—ÈQGš¤z‚Ó·õúXŠÝÜD 9kþx‚i °�Á¿>ÑS—9×kIÏçÃÿœ6oGÄÃjçÉC”¾Š çs2(}‚¨â@¢5Þ¾ÿFº¨éJ%¦D*MÙœK¥òÓ O×ð:2&�ÐŃ\ùŒKýÒf"( ùŒâÓ:ˆ•õ2<¬«Ð¯ù¡5ød (»mŠ]T娒ÍAßÁMEm*"´2’9ŽZ1p.vIP݇ÝîàúåÏãGCHKØUž®Ö`I—�˜|£RÊÅ[›á Õ{ öÓ‰°]�’]£ >v­|£S´È>sËò=Yskz²ÜQ­çøKpx›+�7>ó‚QÁ‹92`ÖY»Éi¯»E½Ÿ@ ø‘¿…»ÓÄt�t ˆ;™/uá¸D~õ¦"„ÖËÿŽ‹$¤òaÔ]!�ÖJ`ÝÓÓ§L@Ï[ï’ÇûÎ-0‚~Õlú¦(¦žUXû" ßkËfü¿üKÖ'*d¾¦ÇœÜ7€L8âU{ºÓPQrcôk�šÄ÷'VwÎÙSß(]- tgÖ¯F†^ ls[LÙ�£'è|ô�H$ûZÒ¢,ÁI‰ãÛ/ë/É‘Î×lˆµÞŸ@xj…:à,í™â–Ï«�B›š²ù˜š¹fÜŠ�® ü—ÜMZòñöf ™Œ² ¿»ò1Ê‘r„±w§¤|µéE$Ìc÷Eý 8PcJg�5�W7�’E¸y­LË «jc°ƒÔÒªE)p´Þ ÍC[ ¡7b1f[ôNá L‡.éú-תoZëu#-Ê—àkŸEî„í§ù›’´¼$±ŒPu 2Þ>þ~•oìN4túBÙ{ �:¨ ÿ}DVÎÏļ»!œ4î8 ¿fWàâyDRT£|“½í+£0”¤È†4HbÎu Xª¼:ˆŸ¿�>ÕòkƒŠñ« !U½Ñ3ŠA¹äâUëAÜwOWNÄ$C%f@óHi ªB&çW¤MõkŽõDz×A <%3xÑ&’ ^|7ìp ,nµ$>ìÏÝÔƒßE–ˆR÷ÄÖÅëÍ@=+ Zz»ŸÍ1s¦ñ»goÿ@Ÿ2YƒDbu{~òx0ÚÊMá§ûì½�V�–�¦.[†^uÌö4ƒ>ü àðAËØÝ0Åý1Õ5äÍ–­Ü¿jþõ‚ºïú)ÞÞ“_ 8x$›NÕÆ¨$÷ѤpMÏqÈÒóe',5à}E`–ô¡=Ôl¥JiÞXßô–ø§ÞŒÂûΗ)ñ÷ËöŠ­ä üW~éµFä_™Y÷tG%ǧÌzN4�^Ó¥I¸r¶uƒ&†ˆ®¸¬ìгþ/ZñHà'•?ÔòfC2FE<¡¯m/QŠ•áü˨�ÎD!—”Òáx%rþ:M˜éý)FU'Žý“on—°úVT‚ £©H•w —Œ+¯bKÎÒ¨ A ‚@Qµm>žð|kÔ!xLq†‹kËÿÉÒ8óZÓø‰�ÁV´Æ—Æ^ç×À8z:è"!?¥…z–´>–J™C·¹`!\ªXB‹öP e°ñðçyêæg~™©ÏsY³´Vb^NR ^ö`ê?ñ-é[Ò «Ö“Ä–©Ô^£zP›¹™a@w—>7ÁÝÔX>øÚJŠªŽÐYEÊüEm˜—sOº,¯Ê¡·¹4e¥7Ö–Ål©nRü›àg5g“ �r•|Ö�µí¼•‚Ý2´•Òš¡å›zÍwù´iýð�HS‘ ô&‹ƒÝÐa‚Úø<ß {ýý?"Î;ò!É‹Ün�¾„§ä÷÷dåš*Í@ú…¯{ÝÃ…Š®8¤6Q¬-¦ #}ö”¼êYž–˜È/M"6¢áx/-Meª„W'E «Ãÿ)aIGz=°dÒL¹ïЧôä?yÙ8Žë=KJÐ?¼W\ L‡n­¨6º@ü<0Þ}@/‚úú`֣ķk�¤ÿ´¾qS¡ÍY4w1ë“G±-àˆU³°ÓçV~£Å4©XÒñ ¨|‹µ”,…Ï#%(2Ï|’¨ý;ÂÊ>:Ã"Ô±]œÆg_Ή.��ôlEmyõt:ÿð»ÝSj½7Æeï8�îö C,ÍY9Ùk˜û‡M!±:À6¡Œ»À�Ç™þe…OÑqŒ“†oÃ;óš�•™$¾‰KÜ;¦¸íVP¹ÇpØÙwT>l·ô@†+ Õ•¡hîGË6‚?ì¥áz¤Xmà�7$ÞC=ÈÅî�(§†b±ªsèˆÜˆC\�M#£ÀhŽšÐ5ô"3%�ò-lV“ª*$NôÚô4#ð+gü¬ûÊÌ"…˜H:Ä®xè rJ�‰.$n\Þ#3¬5îÇÎ’L ÿÐõ`äc5Èö¯yàèáöYz´‰|Ô¶Ê^`ø_ŠÒP­X€å °”»À‘8¯HÉN=u�ú1òÇ­EŠDƒ~È=%fOàGƒr«Â!3Õí6¤õÁò�sׯ„KYjD·X’ ²šbYª=ÿ/›ºõknË´‹i9?1°UöSÌKºÖ¡ (2˜¯¼Y&Á.ê\º�a7ß•»,é�kü‘KêÙŠ&¤õ�}=²û2¤?*#&%“èz…©¯³&ÉXÓ4hPÉÜV>-^ûÏ…Z7Ør×+‹>§ÿu —¤h–�º™xub›öß¿O$5Tó˃¡Ñj…¦pVHئ‚xÑ_Âç­™é–{–ž�W¶÷¨ç`ËÍe- ¸’Q�ì¨^èÜù‹î?s 24ò 6²Ê惹`ÓTxÌÞ~4ªâ‹6›¡èþwˤ·Ëo±ý/\q{DpBÊX‹”�Ÿ•’ì7/”«Ù¾ìŸê$£dq‹UÌYŸè˜ßšpŒ8 Ü}>>=+_}ú8×Góß·G‹É0í{ç6Æ]e߇(XL"¦o ­ˆ}ßjÀ¶?¨.’kNtAPMF¡òÂ�­Ò{iç–(É^²Ýì2ÖÿÜ‚ Û—é2Ù“ )ë^;„?Ü#œ+%ýjàt ¯ÝŠB¡�ÖñH‚¨°Æ£Ÿá¹ü�ýô Á#Ây6<)磨2]-§^þ­=^Sn£FÕ¨ôýÔñe“´Hj»2JYj ô§†ÏQ,Âú¥?vdüa@ÄÝÕ$„�H moÐ>5`¾™ußf·á6³:Q ´UÉ}Yð'*E+�P·ÿük=TÚÍ£[œè‹„¨?#ØL÷ïZAAa3¿oçöz°Ú³g@!l‹�Φ7ÑWó3§j÷f’†Íw•ç¢ýxíq²“-Òv"È >ó»2¬ ߣïeGg`¥$ˆ®4¶ºìèÑ_MÌ=½°\,¿59˜¢]kñißt…wûè Û=/sÅÔPmGèøîÅ4ùÑþÔÐò&õ!ìCeú ñÄ]Yþ¶ORËB=7¥{8º¾2L@€–{Elé"‚Ü\ÑxÌtT”îo�3Í%§…ÒeÞøºÌÊ“͸–"? ~-yòή«g§TJ¯œ�ÂÔueýb} )N†ægõ·Üd=‘àS_*ˆ+F›`ló“¸LUFÃÿ�g( úï�óªa¦0^gc˜Nß}†}ªt-ÚO…�Ô.Ïpõ ×äëÁÄ 4z]žDjýcÕ_ÁŽŒß-‹GÁûÿ\82зYè…"ÐaË…ýRŸÌÁËÙd4É‘âˆz¾Ü«Pl/Ž6õëás­pÌl0”ñì‘eW×Qê°Az³¡Ðä™ÝȚĎ}òóù�d B“*¡ôÌz}DÓGƒÏ*ólW˜à÷W]ŠUªŽüà«B�ªJªñÃ~;^_°ïÏUSN࢒µna€‡:õü5'Ê�?3Š q«›áÑÔg×T»Ü×.‹ü|\Gôòc¬!kè›/¼ñ¡/^Çg½`þ8ìqR“Šqf€ýÐÄ"`õŽÿ‘ŠœŸUÝÕ`¬hWMiœQ@3âSèc”ÖŒ7¼’ý/+ès:Ricµ´;¬ü –Õ—UŒÀy„ ]·ö­yL'2+«.”jE3Ê´¥4äH>é¦ É)`ÈÒè™îõ¯~xJQ2g.0…\ج֛¯7e$`ë‰ÉSªÖ´±(Œ7ª. ‚â¿�q€9>l&°ÝëzãÎ8±½ÝŸ¢8â | —0ç ©‚‰¡å0ÖM¶L×Y‹5`Ýæw`E ÜMXÀ`µÇ¤ñKÙTV«3|*6ƒŸÒ˜†E0…¾–:�,çRÙÔ¨h"yk¹{ HeC×{ƒYÏYì¦ ÔR8>³'Œüp×IÛÀ7Õ‘¹Âš¾ó»¹{ ÕQM¹°al­}˜ühÎWA�jhÐÖ`_ÖàÚÙ¡ Í ›I$G“`"µ»!¾ÅñËØy60ï²�…�sÄqïjÅmp½n%Ç/ Ò”"¹nðh¯-ÂYöø(ºÍoÉÇ~5IškZ½;¼8¸ïfy £ ˜ïŸçpø“BðžäkB1UŒ¢ðê zŸH‚Ëé¹åSÃpˆÍ\–r/á„BõÔ¡™ž…açÊ]»Œ…;Š˜È@ÒºÌumW&7¡4¥#j½Q\WI"èmnjåÇXG ,ÐL¯jF¥œ½žìàÈØÀy�ƒ\Õ0&ï•ÉZ[ m[ ÷à�ÇV`”ý�,fÌÒ3˜WÆJEýêOIk8?z F‘Hœ²ÌbÚ›Ȱ›yqš9¥*48 )¿Œ®íbæ¿Ó™x=Úùº8f ä=Ä.£Ö¥ uß ¤M+“?‚6wq–ÖZNË»ð—s·‘ðx<„WÃÃןºq¿ëÛê}%¤‹Õ²ñ©äu¬G#Åcwʇç½ä[Yw@.Q»€V=%ˆzU»�Ó=*þ`Þ5°(¦³±RoiôS�\™fÉÀç9-‚ Ì«¦ <’OŠŠC 6ÑœàG§Áãp¹?‹úYeRR#©8õ©)Ç;ÚO�±òÉ� {sŒªÅÑŽÌ,ÇÈ&cI6Û¨'€‡xá-O$[!àö o±î2诡pŒ`î7¨ƒð(>kYÚÓ´-Yî°Ûch+‘`T1Ã%›xô؆‡î,D+…ãÈ¡X@EJ<•ir-8›Xo†H¸w„Ù±ÓÜ8ñìŸfEà �ç0'¸Qî·ÎÆ�c”màbi>Áµ%6@‚ôÖ8`ë’Ž¨ƒBØR8�q€¡©ˆa/äÆÂü¤œ[éò€ûù¨‡¢t?C¡£÷“{ðßdº•:ý�ß €HDx;Ð�ȵê§»SÚ«“p#k�5´Ï§(Ç�ënÈZ¬;Irò˦•+j,`ÌÃП 3¹vMbqbϽ.w™ÛäVºWÌ×:óCMYt;¯LSgÒmèfUü;›ŒxV³˜;û0`Ó+ñM¨AËâ„×½ùÇw\u¬ßë‹°~Ã�kÌZK(@ýÆÜ)f_KC�{É·ø‡|h=ßè¯ Æ"û|ËwÕ6¼äŸáÅPÑ ’µØŒ‡6¹ì%ª hõq¸þÍWd¨1¼ P²“�RF'CJÂbêk0ŵw=Í·SöTÌß·ïg=NF|M+Uò yÒxó}ƒÄìáZÏ÷X&}›ë:ü1Å:T÷ í|Ó€p¬²‡I§hê·,¶£éü†T—˜íe²"`NExmyv¢‹Çyá˜Ðù[Õ†Ž+5ã+:^¿tR¥MY±õü†XùÕHâê>JÛ HáÆ¡Ä”óoAmaó)³èÕä�Í>^^„p£>fÍQI¦–˜ÕßX±ç ËpÄ÷“¨«øc"ê™G{p‡*áÄ'„“‘<þ$úwÖâ�¶³ŠÓ©è”`ònT{¨BqœMXç––¬{%y,ƒ­Â<%‹'—ݵ6ÿ‡ÿô9BX` l;]›òMì]™ÔT',•›—usʰÛ`4àõ óæ4èË?œÀÇÙ�kÅäʸ«³JíêØNoM=zþD�h=çãùhýòM‹æ³™Œ¼äMÍË*¤0†Qspù²½Šò8ñRóèCFñ”$¥gT­ÿÄß*& ðsTG-z;9Æw”¼šM€ðñë…ú>-™ùÊRzK?ë>§ Ü,öWÊDAÎÉP9Ô¢&¾‘_ p×Â?}>hNt…¬±õƒ¥:�®s ñÂ\ß5�€‡0ÜccG»Å,§ÇÅQ*Ÿ;Ñ.©©;„¾ ‘e»�tû-|Ÿr½P¶:ŽÜÛ‹kŽÉ6hêÖL—CÀiZïXmbg/T+Ð¥\žWká}½ÓAëÃPÖ¤…½!�­_Ó¼±;ZèCEð??ü 2mÈ!T8(ó_ñ*¾W‘dÇ0mo£?÷hZñ;ý]ýowÄ+yR>ewÎNˆ'U¦…EôSÏ›Í[Ik¢r¢ƒuÀ=w ,z1ìß@Œëò›ÐÊÃcÏ ù\ºº…ðÙ�pl2èaíÇŸ|o§æ=K‘¶j(H‰ph|1”ÛBõ¦GûQ?•}¿Ì‹’>y›×ÌE4ƒj7ˬ‡'óÏH¶W.¼Í¦¨Ø‹f¸Ê-�<ñ›º#òñÁzú§ßDx—±mŒŽ®Ü¿é“Gh˜Ó~C�ï“{ꮀ<ÝØñá9»J4²[bw5f+/£º eß#²øPÞ|í.dM/œ:wñï]&™ÕÜe0•ΪJX^'nü8²æ)SF„×6|½œžÚ‰×&/;r©QbvQHBޏEºøÝÑÖGºé 9ßåУŒáÏÇÚ€"|RbÔ ÕÐÑ<ºøTS`6¸˜«ë"‰¸]ÒŒzøCæ ðñ^Í�=(‹¾b›~Û0è:ÂÖ� 8óñÊHþPè¶q �ç–c„Hùkå5$žr¬3¶�äºf\*‹cÿÖ«!ÿ*Ìü}“!¿ Ðï&2ÏÓ¬@°Ï©±þ�˜ãs[4f…~•oÙUÖJ+ˆ*àlû¼®Ñi ú•}Ù[@‚ºOyËë‚3A™(Í¢ô#Åí'¶�ûn»缫ø-b`#7+¼¦¸ø6fë[e@aF�š¿†/ìNgŒUOBM&þ£œÔsž„ZºÞõ|]ò�qSeš…³ñê«kd'…ƒà¸øbȹcàȰ”á6¯hG>q¦v’GÞ¼Ý$lùî¶dIû(f”ٸ׬æ¾åI%Šü7oÕ©~dÇåáGBîɤŠé³/®³X±¥Âx¹\蛾~]j$�عO¸ÍŸ·BŽË&O(ù›:ô›ËãòiNF3;ÈX j|<$=4�'[bàx=ª…“´rGľé¡»Ëª8 £U²?P‡ÿµ^víªYÿ[ÔÉvE#¼²YÎëÚø'$y<^ϽiQ–@Ü•ÇØùH»À0`.Rì’¦¢Ï^[�ƾ¼ø]nGïÚM›¿@8Ø[ï#;jG"ãEž"zƒ+{bãoí{fð>dž§ýÍZ24Ÿ|’4³~'•‡»RzNˆò´ØE‘œ÷,!+ój`Š-(«DmÔ¹Ï v2í*ø§E6.åL¢$’· *˜ÏîÒus2Šþ£IWñ]‚»ù^s»+ê㛸ÊE�„sÆz�dW®á.;ò÷b:]úÁœ‰,ˆèŒßû"w:=ãÓB±fÔ´Á.x#ÚÉP`ˆ=.¢óh_Bª—{öBÒ D·ÔŽý�)VB}Jr�@4Ž;÷«ŽbþèÀÂ_ 4ÒÇz™íA©` œ8žë5•h ƒâè«°æ¯=+{TV¤·ÅCä�QQ)í@“ë¤Ý7*÷ÁÕ5°¤éA�žOñY!i÷Ê�š«gŠ•uCÍax©y «Xب$ôRJÑLë¡ÜòóB®*5 ’ tÊÕgÛ[üz›ë´i'*jTwÉ>r g8ÂãdA9Eˆæñeµ»òÖ„]ÑÐe‘¢,`¼3¼”´KäBkà1IE,¢YSÀR p꥟!ò¬!ßÈtCzºø0 ÿ´!ˆO!h�`(´p_ˆa36± 8èn¶û�¥ë;H_Д0ý·éÉùñ@’³Íå•8³T‡of¼'–=9dÃÉqÏg˜RýÈÒÛ33‘ã*P"t«ƒN¾‰²‰Ò£5 Fú6[`óŽõUX×þ�çö$!…ꆷ_r÷ºËãÈN{ÜJi²Ø­‰ l×Ègt�BXn;pB¼Ø(¹K1žPÌ0ÊGeR¸ýÜÕQ°'ìAò~0³;x†ã·[""îÅêé÷M»øt†+ä½H£–Çoœ–E^X�F:74@ùUÖ?÷Ù'ñh‹ gó�¡%W Ö o;‘¯?|Ì‹êG®‚Š£^›RÔ.‚+L¥5îåb/­`ìðJ§ ¤f Óµà%úW{­Çi†t¸M!K%án‡Hºµf¥òß·ÑvЧäö®ìïD¬ ~p´TÎÒ8};%èÒ,dz¥Êk}\± ¤éÐH±#ÜÏþAÏèq�ÚÚ#1\ƒpo*Y�tÇÄŒìèMTá�‚~Q�×Ja‹•'jÉBúÆ–6Ëdþ= ¾ŠP��?¢ Å"îÇ„,|_\ôL.X¾>|´uï:NmäY¿@ÔÕ-"b»Ë³j%Á&jkIKà¸qàÜÂâ;üKÆÂhÖip±GÓñ�˜³£ö£·ª�딵ïïÎ×YÍ\ˆûÀ�µs]<Žu·uÿü*ŸYx…¨Â§Æï¾Â�}Äö(B(yjOÒ·¡ª•)'RË“÷ÔO«!ºøñµ§HN¼t_˜ÿ9Åÿd­MfÅ#¦Ù²ÄxÝ`¶;Ç«pëÙ~ÊìäqáA毫2§GKQ;±Ü8ݲeÓÉuð(Ì®+s­Ûþü^уX²õ)5�¢èÚÄ€÷T}ÒŽñ"óAŽÉI<ø§\ hF�·óè ïÐQ1]‚¥ÒŠkÑXsÀYµlñåH)&ÞX}ëñ?¹¦Â¢|~b/ fÇøÞMRd.µñãбàc  H}ÿ`�uC‚£Ñ† çBï9hè¿„Hïá/]]Ë‡Ê ø5‚eJŽWã ©Ó­y….Í«‹½Õ›N5C<à ‘­÷šˆ¤Í7%DNç[ AS™\H6Ù+F×ÛÍ+Jîö(®^‰UÝä(jïc¤É#ë7’±Sy ÄV±Á‹$l”ðJq3i°ì씫?k?Ž, Tu€8ÝǃÎ!¹¢fÕ{Œú3pD'clƒ¨ª[mW�ã<ÿI6É4Å ñ¡îØ@-úkA·5êÞ¢j Qm?·±®dÚ 4À5Ù7°Ñ~¶“ÑžÇ`a³­â@6^_é…ŒQZÿ\Æ- 4c�nOL<Áî@ø„f-ÉÛÄ&’Р@=Ot•Ï»ó°«#¢Ó‡1Ð�"Ä—£gã°çæh€ >­EDÅaŽYkRÏþ¶ )Ð�b’}§R™¢uʧ ¬“1žøŒ%ܤÅj‡ãIçWˆ«ƒr*´e.£3ƒ ò7róBqMN¹7ÃÁ ½ �­¬›"+Å"Ìe¦ÅÝÄWÜó«ýŽÔQI>n�¹RºsÄ3“ª¹RŽ[œ@‚†úx¾c–Q{/ Rå·t�”¢æY8pmŽØŒ{¸®«Ã`ô×HÜÄ¡OI½ñ­€Þ¸\$�r0?Ç;x“ŒÊ�Eóm'h–¿üA¦…ö7rÄL¿Àèܲ×4'Ò)¬À§Öù/_)Gn} ­‚õ` ÇRu6y§â?l)öŠÑ{‹²¤èÅ‚ÆïU3įÜ÷Ë"Ô¥Y5M´ i�Ó7DÕ»ä馈 "¤�åša ¹JKU×s†�˜æÕ"ö&lŒÅ®�5X–^§àh¸aŒžVC=GF\`¬V÷œ\¿ KáœÇ>Ö„AW´( [ŸÁ�:D¬``¿çM–H*³lJz¨Ýù>ÍCû n ’+ä36ÏN!J;¾?k!%Rüv�’¸a,ëO¹ü×®²3K~Ç'­ø«É^Ž•ißÞi²ú^a"'nj¬8ûõžú£ zË/‘íS>|èñ¦HÝ1 ‰GŠZñT½Á½ñ¤wt—ÈÒV2ʇ¢[*¸î×Ã…$¶•K§WHħÖDï_.E=Ûñë‹yDOÇ1hãÂÊÚEBÏךP¹º¼„+‹CSLÒ¤Úî2S.dÔK�;B·uFtжmv¬€Ä�4ç#©-à0¯eóßçÜØzlQ4W-»·).G»Oæ°§»õÖ­Ï´¹ù›�5½®€2Ž7óÔœKÚ¹AŠEÕÒÞ¦R¿ ¬úqcmNÛ©úù~‹<gâ‹'�ˆig±âªþ±Fl›;�åX�0Ñõ7õ…šÅr޽<¯‘$c*ü·�ëù¦Òb!fT°a‚�J6Rö�ÀƒžÔxFür¡[á�W_\< ½hr^˃Àìû<ß_S–哞ÔzøìØàǸZú{gž·³„–‡P5wÃp2 k:5óÈPmpÆ~—ÍÓXÉ£"øaÄ/è©÷ˆ@146ÈŸ7eún¾åô1HDYt¡ì .[j NO“µ.Z-`|¸v2�?®ŒxtÀ¿N<ïÁ¼Ó*N — ìn““Ì3εvž n4O{šî–y½ˆ´M<ß¿†´0e/ž |ýIîkŸÚ}`–¶rá ñj^Ë”}Ô%�?ÏâQù3'SFÞžðå¹)»õnlṄJâó$~5å5«øŠïl̬駂v;.¸�ŽúL õNÝxÎduвUówK5D´Vã.[ÓÍ�´?,ùZ²èbÄ&ùöÁWVg=»K±CXòþß]¸ád´D°ÕÍØÌ “å µçØîÜ4ÆHóÁ#˜«$È ´ëÏ-²ç ¹ŠQc\`Îsܹ%ò¯¶J í<: ·¹i®ïbèCaÿ’þê ±»'k[»Ê$®¹{ËÒqQPz'F\¼þ§ƒXa'½E¢ JþU—´•,ÑOš+%Ä^4s~àüä$¢ÇEê Ûs¤L"e}�à8Q›„,¾’`äßá˜çãʦÀ¼ †!†aiɈÿ>>3 LpS»ö0 \:Æ‹rùe=üŨ[Äšvuºæ‹ÀQåSÎ�ï×´-³ù$u½ UP´a,NÆå¼ÿ×ÐȨ́¹ÿw×FªŠ(‰zÐ¶æ Ø‡p¦å8{ê:&…Ç^ŠVa#˜ýZÄ*[µ î#{�£™O4„¨yrG²ã퀸X»â’·YÜHØ�öìrs1€òp"øÊµ‚/)z;â{J µ80¡ÒÞ0‡ë+Þ[528KˆKXlx˜_»§}ß…]P;Ξšz‡¹Î½þ µ�U4I4¹C� j‘yï�…Ã7iøÍP$-£>[ç¿>< L¤Z±{ÆÃáso¿ç<+hPlG¾-÷�K,Ìe¿ãæ†xÂ�› éköÄ2`ªOŽx*蓨¥CÈ0�6mG³vïPE”75@è¡þ�ötPÁ‘«lœÞ%e«¯I#Ú@„Èõï]¿+>ðt!Ð|õ€ïHã÷È÷$ƽújåÅ×ÃH=Áõý/ &ÔæÔb§¦—UòHü�yMLÎ�%‡v‰›¥/÷û[øò õ�©<“ˆâºàŽ:V´$tÏ[¸õ(w9U�¥4ÜRÙ{p ŠÊÀYLÐCüK˲ç½C×�–zô�ê4–Ç3b8|;÷è0J´_ñË�F’)K•™W㹘¾¹Ñ›<èË–ÏfØJË›‹=9ë[õ1B|Ê�¦Û¤¶'‘ð7sáÜ/1Ç´s¹rÖ7Ê€lÃABòV�ô5¸1ŸËP/.WT¦ëÁ ,¹w¬âšãÖàP!0Ýl'.~·‡KÜøÕNÐŽÃ[¹Y]Y³E o¯¾úp‘ ´f‰®Ï­U�VG exe_Ûz¹£LÝÍ]Š…Kœôëé›W#(ILZ„oÛÂ:ð?‹žfüT'9,.Y2¿Žå Š4N¸O!Ô°g~Ÿ“ÌØµRØIø[ÙþŠ�º�|–ßh©ßñlï~Ïí³2Òø¤”胀ªû#j¬±øór¡mÖi¶ó™75 ð™˜I�C¿‚½¸I\åCµpp»Œ¢\rúò ÃwN(&Έ‘Þù•§íL.ãk®¨¹‹è…�Ý?/f5 Ãõ; áU²gd�• «ûÙ ¸·Ù2Gžª ÷Ö¤åþiãÀÙo©òR×> òŒ¬¯â›KðK°É/Îef»ò¦.*öä50¸0;CçsGî]²*׈yn30.�µÚ®&.^C:ÕϸñéS}ÖŒïNÚ,îèP™ìp˜�af˯—ÊÑ[Y ¼¸?æô•)ìþ‚\üíiß{ü`v@©_BAöÀ“Â9NêAó¼ã™�º‹´"pûHªÇÓxŬ‚Š‹ÛB9X¹�pYž(ØçÊ„¨ÿôÞšz¶'Š´!ï­¯«oÙü8Ë0ö—F±¦{¸±Ù.^ÍÿÏ:¼ÙÒžò~tÒ�†’f35"„ÁèѼ­©lÅÌ[í¾Á>�É' �þ/�0Jcï¬íõÓŽ*Ñ-}€é� ¬LkFÊYß Ìô¼ô°¶W  [sò`iæÇUCèŸØVPKt$‚ß¾Î__œ‰³aÿú×èÂñ%Ì�ß4<öÿž!¥~é=ëjl6vDó1AùŒÍÒMK2¼/ï%¸¼ugIé0©Œ…9‰qá8Î�†>�È@÷¹ÆÝ[¥�‘TM²gއ­ÕÙ’pAƒªŸ™ÓÍê“5¢ Á›”øg9!}6å让�0dþYñNRyXᲂíÎÔ]ôuœ±8KeÊK%G~†/H+ãvü‘dæþ‹aÝ’:qz¿å¥ß ¬†N©v´u˜¡?«Íy� ݬǺÇv l ¨7dñÒRô$Èû•võpØð•ÀûM�[-ŒrƒÐå²û×p¬·0ðêƒ62U2îi ŠiW7MƒmΧD2—®ÛˆhczT™<‹g ~|K´ù°¡ƒtçê´Ý…ŠêÙsÄä0¦Wà[�mÒÖôCt]YØ$·.@^g«"Ætò¡{=.çŽsTKYî:˜²Å%�^Ç®â‡0o0+6b åxå\~¨NÉ]òoaFe:ÒÝM“Ô�ëfQm©#:DØø ìÕT­R;Ÿä!‡»Š‹ñqåT­¨ß¸�^QgJ[œý›‘¢É .·gt�D£�ã· á ï*­è«bÙzãö‹_¿ñÕŒ*ëÉXáF ^“>sPø�§±ZŸ„Øz!ʤBºu@77¿êpÁ):øà>ᇠŸ*pƒ5Ù—Øx§ñ DÛ$¥rÖ4Ê’­¤¨ü™YO…k— *kG/¦Ì®"HsG ¸³·´H«ŠÂârk®˜�ê¥V$ñ.þqפª‡Ó¼Ô'YËì¤hb0`µmn±ù°v œ˜ëó¼*“Û…ÿÂoØšÜEÄI©�[ù?ÇÔ¶½è¨LºÝ8 ¥Ž€Ô`Å@S¦‡Uèo„g»i í��ÉêuëN(C0‹½Ó2Ù*wtRŒ[ó0þmy|ñÒ–I®8ÿ‡“;Uàl %6M9.ÜgÕ�:Fö¢,%<^1cÍé}>ï<ʉä‘&†-¥& µ2-°0.Æk¨—ómµ{Ò ç#åR®Cí™ÿà6ðû ¥Í˜bDÚÂÑèu{xâgËò�&"|ù¿ ˜´;uÂþÁPŸþyeÄÔDþ¸ÊË™o2óV2 išR7Î^¯&n^é~Àv{]W”Ðýú­%É¢ŒÎïÎ-ûÇÛ•i’í¨ô�¹›¨ç£•¢OcTFÙ€âå¦fjÑ�l*[ö0‚‘pž“¦€‡¯©‚BÜrÛ±~:˜= Õžòïc³�©°"”r*¡]m¨N¯óy,OyôP•eÈ‚§pXØÝEœ¹®_f#2ÞùR¯ÍŸŽíù:·xÛŽìá6~ñœZ$@·€­C­¶5£{昺zUÅšGÞJ`ÔÎ\™ý,E$óäèâŽýxvS¸uA ­íýíáz1S“�­ç  /ãˆvá0Wsü¶ Û©9¶B) 2?^š ›ÏU—i‚¦«ðûHÿýe¯•g;Ñ騙RTß>7á¸Ï8ºW©þ;Ü]¹IÁïî¡CÁ)OÈV‰ U¿Ê Þ <¨m«÷ãp;p9°‘ü[褽Ê£– ÏŒ¢uu’j†,F¥"s°”1xIMk|¡ïSß'ª•° ¦ÆðiIQ z T¥ Ýê炿ª ä¬Ò%‚¨°à‡Šaß<.; 6©A '—…±¦ZNsb SQ±QÛÙ3Üj@Ø#B3ãó7¿š�áÐD,ANt–r{žRù>û„Ô“�á…’Ò/ýDzô†õ–Š ×¾k!�cí†Vr€Ÿª5ÚŒäJÇZæ’¸±DLžÉ! À�XT§5T’žçf}·Rè�ˆvñ0R}•Í^–æ:¦ý¼ÝH0~p7pdþJ„ÅD2Ž œ*6±O‡Yò²öé‘+õ�‰Ý€yeÌPà¸gÑÒfx1 � ‹ÕÓ•‹¥Pƒ6¦R"=j�Ѐ´ÔûÈÜä­7!àÓW)p¶þ’‡\LY^N+ò$¶#6åßR—´/Âny\á{cìþz¥Þ¬ Zëîç8+—:Ÿ+ÆŠJ\Q†Üi7Ÿ¹H÷òß}2}€eéEuŸC($hCµÜtÁ¶Ð14Ú)^c€1è;š;>Z¥ø¤8Øù‡rË…4nq"u¯.†È§ñö(‘Å¡k�§òDqub�ƒEÝP�iVƒób:É*=Òá-«Ñ/V�?c4Z S›ø“6[vñÁ«g]Æu¾µZ@Púÿ‰ƒùOÚônzÁ(¹”ÿZp8|@¯«•Æ`“˜Á²—´V}†f½rA  i¶×Çõá+×H¤%Zq¨ÕP¯GbœWpÿ}ÎÐŒ O~vy>¥­]m!¶ù¥¹ûjqŒ×^3S9, åÃPŸ••Ý%\aÓ‹Ëy™Îe¶(NÌvÑŸ�Ar|9ÏrgA‹#,¹ñØ£ÏÔtÔÿ%k6tÆ»‘z0cÂZ]Z5ŸÄtKß³|œÒJƒ°øtlk»<=%Œãp0筳垖šûŽœú¥)Ú;$©sIþQÿH «²È¼_ùöKUKçÅEõŠAøRÛ;Ô©<_6D … ”îáúAšä¨Ñy´áà"žw?Ýüz zŠ+»@J€¥EB�MÞ3UÔÈdZ>vg•vcrþГö÷^Ôàã«Oˆ |ǃ×ö[m¸æsÛCu CL_å_LÒvò/bÏøÜ‰ÊïžÏ¡ºÖ©‚EŸ×ì¶€º ´÷{J5 *·‘s¸\�Zî[@‹ÇØ•yß�ÖÍ`¸FÅ$è`—Cü¤Å`ò†!R÷öi**›}Ù@-Uø�\qZè ˸ Ìv”IÌ> Ú°²†9ãäiVÄý�pî^—èéÓŒˆ™„&V)òªMXK«>¹AkF÷bL:ôÄoòU—ó†uYzæ ‰6B÷æ[ü,ˆ³ø×l§«��¹+Â?J˘Põ $- ©½͆.&»P°ÙÄhÅÐÉÔ +L'O�°þ EuGÌ嗫ɲTNÍŒ¹V™6â°ÄÊZ¢’V¾D�<è’p«NŠ7›fy}Í÷"²uSÂy†2K@ª[|ëÍ$„¡éïNY ÛÅ)²f¥x„¢‰ñÞêóѸÿ�XúÀ®ó`j‹Lú8”*Ä«àŸO¹/Séà£qDþ¬qK÷wì°é-ºW|LëQÏçhg¡Œ>Í;º36’ f@ñ à�O>…½*±gôg�ÚH9�€ç\Fsš_û=´Àx¶j9ðc0=BNÛÇ+<±,&®ºŸfÓôu ½-U�€>$SᆠùˆÁñ@f\¿‰T׃ÌVʨt¬7ñkR\óÞ�H%o¯‘òá vŸ¸Ž»©È+àÊñõòJÁãÛ[ØRXBMF®üÝü•Ö:À� ä”þ'‰"A½§Ä¦ok¢G~Oìé†A»€÷‘ræA81AŽ#h¶fÕçEkå\8;âã: €Ã-j�¢ñMwn3œ «j12© gÃÏ)rÎ’Ž— õÛBÌÔUê*+Õa{Òî.†’رÅN½)¥º™ÁÐQ[*'òÃ6Ú–|•œTÏý²=�\Y3ä˜]I®Þ犥ˆªuÜÏ„~Ù^6¸»À—•,]•³ó• h/§\Íã{Wnôû‚ŽmW†ö…%fğ' >àUš7!Ìü+ðw¥1‰0yX1Ü'ši^KŠF N=„)eÆq] ¶:òëÂiH²{M�,XM‘´Ê ÿ"õ¡† äiÀˆ¯Ö÷H’jì¬s÷ÝÝI¾xùÌ£íX8Šÿ]:Nhv+#ZËñb1xk(`󡤭¤ÁJ¶·•#‚'÷P¿¥þúÀ”‰©á£ž ©¬ÈŒ7Ûòê¸C'ÜýÜL3qÉ.ìøÂÝGÂ2(œöã4ÙÇ-ÿwñFUŠWÔ9’�Ì¡Jm¤~_ìpÀo�Ï›«Ý¿#oË#=E ´^u)ÄoaïäÎ� ?§È_JD˜f{qE¸-A4AÅŒ7u1C¾r8øR'Ú΂Ô[ÿt,@ïƒd\uÍÐ/3w\ȵ’—fy~„Ë2¿­=ë¼³eð¶³û¤!®­Þ¹¥å|z—r­2Xi¥hÏ4WR[vn÷вa¨¬ÑÞ{ငÃ½ÓØ�¯/Gp.}ýú'pFz.&iØIû�`~U„áw:‘L UªýǶmí?½:áqW{­¼ë¶/4ý…ÚãYÓ+‰ùîj�ÖI5MJǧAu6Þã«7 ŽÁNàh»š�FDÆ|¾Ì…Æ¿¿ à?_Â×s¥ó­vÚå[Ç$†ˆGY¹ŠÀÇ–ˆˆÉ‡‡ôšú�ß\/¼�ãXfìŸC »8‹Ðq¡l·in Zî4„ h¢Pjú×ìÊÅ(y"œã�†yĸ*î’æ`d DY2Wëâg3²3ØF~Ôø¼ø¸øùàÖËæZm½sáN~GP b›ou<Ï]£ï �SjöÈMn/4>,º°&ê°  �ÿÙ,qNh6׊ €žôTêuú¬J3;&ô)«2[$Mhi²ÄPÔëœ~ÒF@%d|¾ô³Zðžo#’K°âõ¥×¸K†Éú¢I ¹Ü4íM»àÝŽzW³Ì7ÞZïˆko©…d6}ïøXiŒ�+ƒNq{¹5‘Á—è“ûÂS?Aï†Å‰/Öäô Y]­._7(¿ëºUÆ^$9 I§¼ƒö—/aVz�¢FPÛÉêÇs’XT‰ÐÎ ¨u¯Z~(¾â©J%PêJ¥dø²ÓÔ.­›b(dö•nKpqOJ÷˜øØc»ùE³´;z™"dÅ®ÒAÀ6*Lˆ¾S¬d¤^dÈsb~6ÞsÜz2 …o!è$bª#'^tËÍC‰^í+ueÁ™oÉÛ¨Š±ˆ¾²}'S@Yû)E|ÖÄT¨ß‘´ ôNÀÀÛ¦ª•7]Cç÷ãÅôu¯\µ¨Ò²ùzmÕ1øšª�JeC9àöKÄÏØ`1§­Bõ‚…¼ °¨Þɤ›¦*KîSŒÄÂÛ<|X]¼oÏÓQ*i.š .b*˜0ÿëll@JË€ÙžËnˆNÒ|cá4¢€t Ù^Ëb¹§ÜÏù�eªëõD~V ‰$í7-ár Žèêå>m.SÄ×ÄD_hó¡eð ,óWZ÷tª³ÂáO”;ôãXwì‚VA\º˜dLÉ‚±jÛþÒBæ·+�ͧË.÷x èÿMÒ¥…G›�ŸeTfÞvI“Cs·]ÇŽàÁVz\øåS%¢WS“5V2æ4›{:Ìšž7TâkIáŠérj%‡ÕÔìˆàyµG¯îެ™“Ö]ByÅ7φ"³ò š‘¯u¥ê]ûÝd½5uÄæÈ8 ¯º“×_£#!û¢[kÂ>Ãp¥Z–¿1Ó- ¿©ºß‹À)Aù¸$ò¬}þ±�¿ç8Y­l5Ê�â×(Þ-/‡�´á_Z\rŠ /x°éEçsÎW�;Å$�FÉ4`ŽÚõä.”o•ÚŠo¾áþÍ8 ÒM:aÉÉaÏ” þÐ �÷œ 14V=æ_ö�¾ˆ`�/ujË2aÝ4:«Ëÿ1äÕ Õ!„€¿J²­SÒâü64üO‰i|‹r›:é‚-)á*{üÎÄc{wt§þ�fP;ÃŽfªjˆ³6ú×hHe5”üfeÕŽ~ÜÍï¥Ë*T£�/í°´u¤�ú¸|Aº1€©ký3é_?6ü�¿z£Ù?ZÀßvb–Kƒ{¦S;xDZ`Ã�„áG†˜è}M§GÍ!½î–œ‚UyrZVÜâpâÝ l‡Ò¨üÔª´Gþ·è©Í"@šövñ@=@`53ÊuÀR[ü·yE¿kæˆv‰!,·ÑÞÝô€>„áøÕ|âPlÉ*“Â’˜(4‚˜�‹‚ßåÛx=ù[¦Ä%}¬ŠV'WhÀüÔˆì?ú«[à Ùuê |Å›©:Ñ~µxJ8¢%haÊ^;qMøšê]ÃãâýÅŸ8àôÃR_†:6° â�1ä~Î#Ýt�oé3cmçR1ÉýºL ¡�Š"Õ�û(õš‚btS¹ù7¹5Ä×=9ßÎRSâÐ;=„z´íC0ò}Ñaö¯ÛƒGI·ø• >–ðÁÖ?MÐ5Žž¸(¬½dB$_wl}>g ' ðÎ×Ñú4×åÔˆ¤ ä§(JFH[š€fòYG°Hj°¢£n%»08rJp ,ãUâþZßÝþJÛS4N‹áJqÒ”¡B7å)‚Ý. y +Іö-øp«òêòkØÜ�œžÁäV�ÇÜP¿€ˆ¾ïöÈ©6ôyBz�ïüŸ4ô¨Þ:1—™ˆ˜ò;±Þ9>.åø}A¯�Ë![¿ô˜Ü‘úÀù?#¬ñ¥€i_xãTÊÍRµx�¿iO0ó¾Ôk¦ ÂL¹”T¢cÈÃO¼`� "¸–¹®±Xô‚+ã'$Í�VÇ݃ld ŸÌIrÝñ¸±ÓÝø±i¢-ˆÉ%V¸A«”ùü°UüØv<®§†„ª> @ƒõ2¹)μ¶FJ…¹þ�°à—mmr*¬¶b#¢\÷@zp?öbî€bu¼NKÒê,ó Áá?�W�ØB3k;U6Ïô6ûdÒ!áî'Çh2[¥Q±°aë 1­Dƒrø4*jç u3ÂÊ~"À¯··©¦ÒíèïI±ßÍV~g=Lÿür´iÐ#_t袺âÃî‚ wCòáAˆÝïj\Dè1!'^à)vÁgÞ³#¶&ô÷6ŒS}�9ŽúˆülXÄÞÙfàÕ5ØýnÒ×Gm} èÏ(oD°_�“�².z€^‰Œ&%ÕŽÝžn|ÄŽþ֫!b¾ÒAœñ£L¦ÿÕÏå6 •¤<ßµªD,�‚*O:€’é™ý¤:[üZÆÚéÐQ%ž‘w×^‹Y k�ï±´wPù‚Ìï@ ¶}49àA!¨‡ rµ×ÂeaSMÔhò<)äâ¥\n(Jw)¾qi—Œ¹ái?óy =šL·+ӯبÒmÉèÒöW�9„]úîCÓÇ@Ñ3D¶¬ø‰ƒMúfñ• -wMi?Øõ:Áón{¨À§ÒbOfIhšw`šøÒÿ}­× kÂ~P¹q•ݨ=ºB‚LãÝ ¼<7GèfÕ#ÉÜC1ODS”oúß¼<ªû¿›~¶eV.y=ÊÉÓÆ A➀O“{¬Ü{óÙ{ùô. Ô�ú¤Ð*â<™n9rûqÖ^Pë�¶¦ñCð…@Ú—�æa§% Ð�!dº/U»dôÍSwbî[²š»K9UvúÌîd!ñ¾’,3.yê,µ1 Ñ=öħXæÏ±w(¶¯׫%º}+½Ü¶ÓõúT µi°=£p` 瘻©ÅK¹·Â94ÆH Eè�2µ±ö^ÖiMeö8€¶™�NäíkÏØ7"º$�Ž(UfŠä¦ÂiÜü£Ëví_úœ2Á$Ul·ÍFà6âr•#Æ Ý0pgI±èæ©:ÿ‰°Ž4ºÿ#tK¬�a$e(]Š6UÙZ­J*ñÙUxrΉ=mÖ©.\),�é<Ëö�l”¡QN4£Ý�Ë bô¦òš¿Î¹¬QÏý³î×´µAyœ‰þªcæ®xßäÈ(¼°Mþ¨Êém/ðûY9|¯ïãä {XŒ,9 n5zŸ½/nJùf²â}­l9œÏö¡0rpß²¬.J¥ÇÚ²„¯ààÎò2Ø< °Þè=‹!ù7Ìßt¶|�Ûç(. eä©›¿‰S,•ïœ=˜½†œ¬`æ4<ï{_P $[Ó¥²kE^›©»£êöŘc)KሂA©î }…ù"Œ9ˆ¡Æ€}NUžlÐF¦Ï1*&ýn i´ü$¢!Ã�œŸu©(,EîsŠl|5UÌ œ“êŽ:üMŸÆ§àNïr;;M¤š±dvvÜ�EI‹˜1ØÛœs%R�ïgG€6 =ª9 J¸ÄCØÞ›ýŠ^ì;È…UVã&™D’éG¾ÒÐÍ$qÝúè³ ¿Xð@Ð t/bÖCà¡"´^¡á­ã›BÙû<±ø@— ™ ,ݪò¡½ùQµŠåëŸu˜ðŸ Ò +§ +•~`èŒÔ?5ºq1bõm°¾lPÐÜËÒÊý°#º ÞPÊGÚÃ>®ÙÕù°4S—²×óÌ×!Ô1ÛŠ§E~Œ=Jv�¢gb£ £!eIkQgŒÃ&ÍZØè:ÞÏWý÷ MˆÄ°ä�ÛÆs!Ë_z{‡¥+�‘*φP@Yw`¯v\ÿeC¿½ÆiÜ cñkЂa5�tß_U5[[Æ—ALò#E”™Í”-ÐÊ®ø]NJKÿkGÂõG™pEe¤#oÔš¬äèDå–éÏÇ~$Cï&-±_i×"®»¬4ˆ`¶fþ=Ñw@¶2”w‘ÆÆ-2É’À¸¥´Ùüœ]�X¾V!| ê…½�ìRpÞ«ØÌpÁbÄ,õ|¥ÙFVÜòK<¹ Qõ¢Ky!+ Rô{aÅ‹Îøª2Tzƶ&h Úoaµv `Ïl0Üð¿,åA ½I;@ M-2/Fÿ¶•Ù7ò*9ä«©�çÞku³Q íT¨ 0(š°YBfÉY¹µß»ì[I—Q'ÆÒ _¸}ûR¸îzþïš‘¼ýÏ$ªh}ù…85ŠuªêèÝá=À¾¢ÇGq·\ä<´KI‚V¡™à~Ôõ×bA÷_$ÈDøë‹›ïƒà„Ä�Õeo³À$ôJ�w�ñdà׈pHhÕD’õ•»‰¿©‹!y(-ÌYKÑ;‹Ü€ïœ»�B^´¿©`ªò VGqÒI× t^½œ÷´*&Rk÷êÑJú~2Êšµ³ÅœJÊ«ø”?ÒÜìüªIÙ•á¿f&±ƒ˜éÞ$Úƒ5â¾�1¾É+¶�Ge|®§Aá5Y(|½¨Íš~£µ¹* ê—DgD|»f9¬(õ2�î­àOƒ«žÒ‹bºîÕ O’©\t¤xº3¥Žsö ²z"é,_«Ÿ�UZg~Ƽž»Š­ÿÛ®_Xj /w#1¯PfNJÄn,ÈEJÓŒìÇå/6À“D–ÃôØ™îqIO8gbÍÔ»¯f-×ѫܱx›ý‚¦óôÁ¶Oûv�A \ÿÁü’RaYsüsg(©ð:ÈÜ£7u»5¾gv8s¸Þ¢$ÛMY"]ì�9`Nô/["ÿlrRfGh/ûÖ|ìC’öh4q-.(|sÕkºÝ•#c/W�,û+øâ°(K̃Øx—¾ï×EÄ TÒÈ®ÿŒ û�y™?º“ôðãfkÞŒ‹KPG(Púù¸0A”ɵ}€ò½Ùtg²¿Vó (y22Þ6K‹rVt]¿7d|$î�äýT<ú!UêažY/óTsű”¼vûÐ9¥§‰Ò!žW|a1 óð2Ko3Þß——ÈÁQæÿzbIY¾g0©C—útŸˆ=J˜…�ÌQïˆkí|0ƼSîöÓœ¹°¶=À×H‹0`‚æaòÃB¨}}‰ê }®»�%è…�øÞ8oecï�ÄÄÛÜ¥lÖ‹ƒ¾£c>Ï€*ŠÂKÄÛ›¡ï±§ †yXVeUŽ2,…sú\ÎòâƒsÞ_Ù•êØiKà´ÍK®‡ã%Ç”ÿ¶·?ÊtK§àcÏz‹p(’2µTr†há2ÜÝTh1N´!rÀ7ð �¾�7*6ý ¼bÝÈì–u"µã\O³",6ð‘mÁª±‹ÑÚ§ }c¡Þ2ÏC÷l—´"\QÍjÂÜÀ ¡:ô×ÍRPmöFò¤3ѬZÞ:m ¤Äv””š6ÙP÷0†ú_ù×s‹‹8”XTÍä °Ú)¥kö¼'3ŽÀ4›"KJ4K[ßï‚5 ¬ ƒœ°ÌFbg»Ò)^Ä«®®5+ª†B¼dᦖ\SÏ>uÇsÁOzÙþÞ§©€fHóÇžæ.㬦bÃ>Õ#n¿¨=ðZø¦‹)qÕl]b7*¯j±ï=áòyÑ[mˆ’\~†­"eœ]¢â!VÕ3›c§ƒ,Gp»ReÕQa©ŸØ¯O->ÂfcÐ\kÛßúƒ¸(¢´ÿ à®M=à—a8ÁP ¨Í*33º5´é;•ÜÖJ÷¾Y¶Gm�q HY^WF½®«Úl—–÷Óäš0<`;wÄâ‹·äeæ™Í-z+Û­ÅË|Ÿ!QÁ�d�3®là n^yÕ;muõ“ýÌýRã%­[‚ Ná^nmP7ãÏ}òÝ3!Û�nl°‡°q`7O뾈‚ž4äáo9˜ø^Û?œµ[«=î“ôþ¶pÓÝn¨=]~(´ˆ%£êR\ œÜãgSP«�ø;v!hW‹1Ü…¿lœýå|1ìÔ°§ïu,–UK-ØÅ­»ÔY´´± �C-\Ý>-îZi�®¸U/‹úÙO!Ñ`mÛ"�×/YÄcÏP[šTÞ¬c»Éý(Ñ"¬�±­v2ZV…”EÛ"*¿SÛFî®Õ±lMÉŸ#[Dö¸/úÝÅ>J XU³÷l~�/Å­q‹·a³½k�E¦PaŒEñaèæ­ä#Ns�Š‹ß@ºbY\#zÐ%¤ï3e¡0~ÙöW‹“PñU{FþC6Ho_ ²Ë"ê)ŠÙÆŸcd‘¡Ü)Æ.UÀþE,‹{UJ�’¬ñÕ.lb$�vR牷§§ø\|Ó“$£·@\Yé‚nHÜ ÙEˆ�†À‘X2(XOÃÁ]òŒMB&ÒCÁ~ ±=�Ÿ©-ÔÔ†Û�s®áXWuïdon=¤ ¸©§ôáãæMÊØƒq6ËS4Æ,�ìÂ^z9A–¥¡>ØÁ¦î€f»§Q¼è‹  vÖ~x™Ü�æO±ÐnFŸöÌßù«�n Äç‰Ï™Ý©UàV½ÎÞÔî Úæ¿µMÄÄ ØëXòü°¼ °•Î×°IÄ Tfº£:$Ò  ¹Lâ·.gW©oHŽÔ8Âúêü&c¢~q !<=¡÷áj}6ᾞ…¡ÌÀfÛ ý‹RÞ…¸ó•#¸ù„8rø�…ÛÏ¥ d]y„¦ÝüÓ=`R÷<ÿÒ™IŸÑŸWÐBL(ÔÆÌÓæ,ðÑ%ã©aâ´Âbïâ@OÛLÅûaðž�í:üdÍêÐyôšj€Â�ý¨ƒðË'¯ ®'…|•ˆ¸]@>z-‰ù䯋”ÌQ®m£ÝqÜ�_-²ªÜ™û=ôɹ>°fQ\"ï8[KŸ¼¯7_%XQ4¡=ýÍ©wÉú} ÞY߸ÕÙõp™~oYbË(!�Y£—°ü,CFlvöA2»–kÊ r|ç.#ç.�ç—‹†º@C±‰�rÙAp˜-GŠv¢`ºL ³ÔO�9ÔÆÊ# iŽÓ‰ï8‰ëW5›f�Àn#÷Ф•X~¦•X»©FWƒppç…39»(y¬¯¥6ëËRk¦IòÞ‘²ŠÃœ»]Æ»Ö8ú¤—Íhir©ðîݳ]öP‹Ö~D�“åÏ¡/‹7"ÙòÛŽ5ÇUkÖŠÊíOvÄÓ÷94�6.¸‚]UG´ZÜV£†—IvL.̺ÉEá¡…õ©l"p¥ 'YSàÖÒu¤$‰×¦·Ý„ÓжdžïÈù¥ê½PÃACÕg¼~顯 ¾ÓÐ,:ǃvʘ147ˆ£¿Ío8šÈoêý3Nª§ ½Éȶ¥of•ÝDUÂE#"ŸTˆˆäªÆåÝ0CFVVd“©•÷ ªBÔöЩ(0Õ‰¸›½ çl(³~ú2V¹:¾„Â0+NSúæçx³æÀ¥¼žçµ$Ú¬A‹�Z¡f°¯ p–Ý�¨b¡Š>`åÕ®ôDH°X{¹ù¶ˆs7âf“M5YÀåéç5éF¨õ¹H€fËD³¼ƒÐåë±ð˜ ŸñHýå\ä§ÌWÝÞa@H�}zÕ¹Fe“=j›„ó/Ú'L‘[õRó”²õË£¸gñ^æ[,1m¸[°WU±%F?èŽÉ}T„/¦@²ì|’n —ÍšIRK÷áN•E³ 'Ѷ@HÁ”ÄxFcª̓Q]Ø8I@¬ Ыśµ°â€6û½øw´A36в]»Ómà{›2D¸™p]ä«™y‰yyƶÇó¹¯25ÌÐü(Z-ºô‡«Z~é¼Ê�hô'ÂßDgƒ6H¹áé_•ϤrQ¨�œ=†O þñÍZæž™äÈ4wT(åúœgÑæcJÂ0œ¢nT][ä4tÞ*\éßÁ‚¦ÝUÙ]ñHxÕK)£Ì“?ý�µ¯A|:ïþ'ó”®×™µs¾è£vš u¸õ¿pÞïö={`wîÍâ ‹–;VßM¯4WÒÉA>.Í…cáž2Ò^oqnˆ ¸èƒQ{´]g–h–Êsn’®ìIdZ"Ô@¡êÛëwuŽßZhVfíø{0�åX.ŒÜJ¦¡zã°¸#–Ÿ¼Ñfþ�ðÈÜ€þôù¼ ݲZ¼çeRZ­6uÆ䨃è#éÔ2·!3Ñ pá¤Ú¼3kü;OÒ$U±þvIµ=8t<ÛVˆP>ži·u¾–bTC­ÿÇ%(^§‚÷MŸ÷KSoùé6­xêJ�-8>¡Ñ±àÀ82Šÿ›È—«õúÐØbCD©AÝQ7Ü*ú±2¥›îÔ âäq§�q…ºð×ë:¿8áîä�-¬ÛN¬à™j`yÄÏ/¼u‰’ËšŽj=©Û —f‘ gXuÏâùÃúý¢í »z¦(ÌŸh×~QŸr ½´çz˜Ûýg1÷Ÿ?�¡@%ÐùU8oŸ¿r;Ê‹6Q ÀÒ½¡¿¥¥ëhµ[¹ÚKð,ƺxo§%>¡˜™ãÈ?lê7.Õ‚[¾l.t‚öaØA‚(îšÏœ¼ß9ÓŸ½‡Äµ"Bêâ¿×·�ZóàN-x¿’쭧۞LN�G¸Á°œÒ[Ûž=O o¦¬"U‹Ï™Ei\²«f|^ Ø” AÎôgX©<ÏšÁÐýC`þ¾Vi'œÁj™ÖÁ¦C�æâPì1w¸s „Ùn¥¦$Gžµ<ˆÉ¸áÀú—zÿ¶?_‘çÌi‡Êª37ü¡èý4àªâð·¦+>UW>M±Âù±qa"Îs8°÷Øʆ×Ua`¡rï¾%ýý$"e˜“­oÈòö/ž�,ðôVõ!˜í×'¢OÙÔˆä�ܓ͸»]š«q6.IO¡t60âÞuCu'*Ž¥ÌÇDRgšó¬ô'ïh–ŠØžoÔÜíYäD:j¿yðn˜Êp¡â÷Ãé ˜ÂâQD¯‹ŽäK>Íheó0m MüÒndú'kôélÚ3�ºÐºÝ‡F"µF¢U‰G4\xÉ"¢V%uí-”nàýÖÈZä–õÒƒ`>‰ŸŽÞ1bÑ©wjô�×w"9q²uV,ð–EÌoLZºIŸ}ß¹œ§¥9 ˜N–M^çKêùÕ õ$dq‹:ȹƒ©Þôó¾Ãb¡ÍèÄæ¸É§ž6è=Rà³·Mò¡¢€Ð�d½.š@ã�v;°ÏäÈKY�ÁhçY YHpÝpoÝd7¯x‚k÷ǘ4›yt¦eÛvœíáö—]ºOn�¡c/<õp£8l`q…›FfÖdêhê òv½ˆú�ñ½‘Ø °ñµrÔ–ÝÉXS§¥–ƒP†ƒ>o'{ÆÑ¾±Üáîb„¿ŽshVp³ÑŒQ�“u'¾Ya¡}Mr‘Ø„Ã!ùo�k I˜Áw�VÚŠûtÚùT-y߀œœ¥�!Üß ëQ‘݇rÕ7ttæbUGw¤®œæ@öOÌ�Ǫ¸Lò¬òˆOR¨ªSƒ´©t×±÷ΜƵÖo÷®ïVXuã*©Te†O¼]¬ak‚+€Å}µ,‹%Þn›…o0AJQŠ*@ü0�—fIéw„½Èy°¥ ÌèÁÍÒz®ã1—a©5ÂkÔ÷rSb’U'ãߎQºø‰ÿ£NY¦qÜÙñÒwåN›¢SߎÄB·Gg!"]îŸî½‹·c¤ýŠñÝ.mþß-�¾ÏøUvÔN[\¹ìZ¼ôÃàBQ˜IÖMäm¼˜j’•BÙlÅV ž¡tï\éLÚáTÊ$ú¯~�ÖSÓ~†6ljwPHo'õar¢@€ÐH#Û�€åoØF¤þ¸�ñ´Úg“$ª™ÌÆO~º—J—Çf¯ÝSc¸V(>³£^à÷$¬ßÝ”^§~±¸ð˜Ò�ÕC™Bò†ò²7>ºÖnc�"ï\-ƒïcÿ�ábã4)Ï¥²üU1Ð8~„¯tØÉžÍSiØø†Á¸á‰ý¨ë¶á5ì&cÁ¹É@ñÉÏxd4ØÖŽ«dèÍGnï6Ç÷`Kœ¾·±ñ肤؆í’T‚û}”:êC#.âm˜û°Ð`õ¾�*U€“.pjÄ#lyËnާ–~*[ä »�.ue+_•l¿všËJW¸pÎä:.ŽMÚ?a–¼Úr ±ì”ýÊ †Íf¾2A­8†}|œÍ�„+ifÞ÷Ë»câ›—êeÔ„3.Qj¹*µ#~Jü^Œ|–Ýo(•�×#31ÓNÎÃ�3�D´ â•ËÞDÏ�RN ­QðÿÂ%¸g‘Ž\l{„ 1ñÙ𘆠ãn!OK oÇxá®Nx©àizœÏ¦ ûP�}ÕεF“¼"TÄ‘cŠý½¤‹3,EU¤­e >‚œÖaç„<ò¢j�ì›A3¶AFÚV’œþ�“Š¥}¾…Œ�tÜüR+J/µ-Ò>ÌUö$V´ëX[3pg.JÔÇ+¿±qȇ|Ÿ$EïÇsß7&稥e, @Þ$x¿ÚaÒñrÕ¨h6´RÛ¨m©ÜmG"�$ò�¬Í2)Ô'þ(r²+¹7Sqp‘:·,0vkÙ⪨]�µÀyçЬs‹’.4Iˆ@õä"K¸Ñ` ¾‚Ô!ëjΜ;éêüë_çP$®¶/€CcØL”²Uï ÕJ¤…ÆÌ›äyH`V¨“ßø·{¯¼ö\!/Ô-3FêXôüÇdŸÀ@»IèoñgÒ {ÿ¼Œ§Ž�oo?'Là;þ©Ú³JMÂ>ë Ÿà\^eíûìaM¬Ìx kA9uðrÔw–žÈqäôß�4HŠÝ[É0JîîóäŠéGBö¥ø•Ñ>® ‹{A´rp²Û%6x|V2e4Ãìªf~·ªV¢&`šºŽê€ª¯+“ÅãeÃìþ‚hb6"cÔ$ý40Ɇ·U$øÑc4)A~®«¢LÉž6›šE¶»­i·¦ÃA©"‹ÙÞ‹o@N}ãuñ¹rñ-y %�Àe_pîwÚ7zgó£wèÊ1“Ž$ÜÝ‚»ÃoÇIÁSg U°ÚXd':¡<�biC©òâƒñŸ`¹«$ŽÈiYx=·£¼ª3n’)¥í{¸ â倊Sm³»~†ƒˆ‹,á¨w,›²îy•Ó× °&ÖdÏâ¤ö°»!j Ôšl‡Dã|�¸D{od­BiõuóØpÊWû­àó�GÑ¡jTõb»�ÜRú*Ó¦Âìâõò«„NNá Õ4µ~SB8ßq[ûä E6ý«2SmÑÜ&ÅÀª{ �§ÈÐ7˵#Ükà|s¯YZψøÃpDN&Èw¢G|ª{£úÖ”^£eðÀY]Ô�–o> ‹\×/±ŠñP¸‘ÑhÇ$GÍ’°€ìÓñuÙ%$g537ƒ dc¾�¥?£¦4¸NÇ×mR•ø|€óõ7[’4ôbAvû¢#ãi?ÿ.6‚D™#›URðo ˜£[jÌœ†ÜÞÂ�tðÃØÅò&ׄÂéZn)9ávk UœÑÄhã^k<ñ2ÐR|rÌ=™í#g�ŠD­q‰°i4›îÀpw[i<ùßÿ¤r—P æÑú˜`ƒœ4A+ºíð¢ÔÆÈµ�@KˆÞsù…†;€ü:7¿uÛÕ/âÄÇÐ%ÇÌèf„@èݱ8¥öTqЛr×…H«‰V²`©UbÅ¢¸¯¼¦†{ÆÜ8}ÒOy‚€ªº†–¨ÊÇÜ\`^„5™�–GN±—›‹ØÕrŠ>@›z•�†¸Øâu"Ç‚X….FzyÀ‰Ë†+r?GÙ࣠«9μ§Mö ®#U®ó“Wo9ª¬æ UÆ;ªšhlvå �A6Jˆ�-ãD'sÊ´E5¨^«ŒH5,ãHŠX^f(!’Œ£7‚RZHQŒŠ àéL?Í`–sOw‰¬N„]ÔÛçùÔkÕK,ö*Õ¨¼ytl Ìî çK.gŸAÁî¤Í4ô›(hš;Å{!o ²–K7{ƒ•3¾ëöx ªàL;3pÐ:[ÈX¥7ÃÇK^ç«r� SY°¹ }(qÇåoŠŽ—á8âvyáÁkÀ9™FŒóz-Ê]#ñÖ0gZFã DòO¨m�TûP¿eË,,OÝÆ"{Ô_ùb×».KÌ‚8ÏÿY~ ˜äi<)ze] �ƒK‘µ3BŽ©÷žÍðö£Êúf¶A”¯g—PÛ®6Äå^lÔüK£ýa#-Ÿ×¿D#ÏÃGùKÚ.I c€IÛ!»-7¿ŒÅšVŠ%û¨f᪹­no÷T!-g‘ô¯B§5^q¿³~¨¡±Û¦»µÀxØÂy„ò¢—_ž|1\¾Ÿ8¸ˆ‹jÅgè4á.2Ù™kîÌW§¡âÉ¥Þ@M8[�žÍ¹\_1–ir¯ºVÑŠÊ´ €�r0$âZ‹'=ƒ™8½7¾Úø{ý3ů퇑¾!u©IQØÀ »~íÿ oÀŽVijß">ZÝòŸy:î…ã9™¯˜{Fw§Ê$KŠ‚1Ë�I¨‡¬¸þ!Ïn^�ÓÊž VFíJW¢q;œ2€÷V`¼TóP«¹®ñ1³l”w£1x³³(¯ª/«pÕaR óÕ½ËÃ[ÔóâÉïªÆ±é1¼ô»À"$W¦~Ü[ŠVA…MzXâ4Øï³¬ixmˆM/8%Êhfù]ºÔ�À¤û�E}7†É‰[¾Úl_qùŠéÖ8Åd .úp~‹)òÀ åy¥,Íéf“œFãÝû—RÀ*m…‘TšKÀÝÇ'¥án‰ ˜a–†åM—ÉŠuö=¨ç‰áÛÑW¹Bvb�*B×Д¹w_>c) × Z®†™–1{×§Û¾O—/�<ö�èF¹ Ú:Ä™Ñò�¶7îö˜jùp;¯º=2]ä´ÒðKÕТøÞÇ~3­¯Œ7Ý$µë¼Ý•«ÖhÌéF]°RÅÔ‘¤^½8”uH){gÍç2Tɼ':ât8�`= Bi†CÙ^í�9àE§Vdûr´:Á'âd¼[ðüo$�êÕ8“Û.Ï)ÿÓºédÂîŽíôJ«£cš(„XÙ�$0þ´ˆP�ÆQ<6\§ÀûîD%q—Úo{Þá$–ê¿ðÚøÀüíÚÝëéP=|SÕo3ÀM ò¤…æ�ƒ²Ž©0š¢œY$!hµC®í`ƒy_Í — p B½g¢³»7ñ|äMjÜʾ»¥+¡ŽGmYÑoãj=²²_Õ?Ìì·þ¯z'-f8ª ­Ç—)¬LÚm­ì”µZ™ÛS©Ïçœâ"+(–KûJžµ¨ÊE8túâOCë(÷nû„¾lTz“e½¿P7W««�½¥dºÁ)òﯫvbÔ Å ãL¨0†ES¤g¢+¥ÌeZø³Ô ¦Äo·ÙÍÆ= AG½AñÄGj}X°Þ]rɉ]4©NM%Aʧœ)‹�q" )bÅŸifɨ|´MݳÞÝys¶÷A2Ù^Æeí¸Ô³µ&Ð?‘ú-†¯˜½ÅÔ­¼ šL `àö¼x’k.´Ÿ¡Î §`¶¿Ïé²ý™š)Lj{ï·rV5Õ‘Q> ©~Õ)ÚÆõhLUIâx´ÕfÕ:µéê¿O�7Ê`Os Þp™¾D“ÄÙ ð êi�lÌœ¦£N£x3Ê΢õuÏ.‡ùüf¬Xý‚öSMt¨‚HÞj©�ˆYOHŠ×æèF¬ËE‘"F†/]�άQl…[üPÅ�U»(f‘(žNòmÝØ¸å‚ºò‡�I4 üÂ4û°>{¸‰?Æ TѦe’+]%fëѤl‚ö¾‘¹Î�v�ȈaДL¿B†½>X>Ê¥¼~¬ñ¨}:®È¯©±´ž_‡H²?Ú¡Ô@R‚r‡ÅdQ{ê§ 2òà>1¾‡ür¬q²DËèçXa‚/>ðiÞî«)û †Z¢DU¬‡ÊªùA“Ò¶¢rŽ>™@ôœ smŒþʆåJôKKá±® ™qRh“Ýâ ·ó^:XêÛ·„@‰Ucê’ÄH—VPtRÜÄæ.Ð^v>�€ä–úÇNÏRÆY$]_w»Û¬¸›?­‡¸¸¿OµŒ‚PÇ~‚ª¦Š&oæPVoBîñL;™<{ Ž\#Y9ÀÜ"ÔHâO “ƒð¯í“ŠöÔM�W�ANþÎlÃOØjm�e‚Ê*­SûJúrLS‚.¦f•Àø’M¥-ZjÏî?$…Ñ yãÑ.ã?_G¤Ÿ2À¨(®%]·Ç"iJÜ„Ö1$¸>ÎdÌ þôÛU×+.‘ª·\Hk—zç|ÑÄΡ8-µd‰Ôû!šÊÌ ZJ¨Å LâDÎðyV¡Ý]L þBVYñ¹;sÒ_´%Lx_“-ÃBÙ¥ ºZ›  lÇ6'çúW'^åN Ø=|Hg RÑçeâ釿 m!F2×&Ù˜j –kÇ ï2»¥ú R›–æÍ~Ô>V”‡Í){Š•T+Ó;åœ2¦)–¢¡äí(Ökþ™VÊ#`Ë¿a4×5/ÐÚÐìÿ˜þ«áT¥í$œÄ?½ŸMueŒç!s%ˆ7h2ö˵¤þ¨ñÍóJ–ñdBœÐVæ€IeSèšade� ‘¤¢ˆÎ|•…æÂ[ßèHMÊÚˆ£�M.^E8ª„úO©Æ~!í5À‹­¸buB†6ÿ[*|ä)AO‡§Ï Bèã3`ä×µÝûh¸‡ð¤Ü&”T 1–$¤eOûÐ/49O¼Öj±tÊ£»ðû|¶÷2¡ ­ÞÎ"Èï^d û5¸©e Y[ õìu.&\m°��qÞΘœ�(N@·>Jtm“™,Bè1Ò4§÷? ÚX¦z–Ÿ´k‡nµ‘_Íþ äÇN·™)g(î1ûŠãS Š ° Q5Hé«ÞhâÄÄ1@ßv»¨$T‰”qà«’¼ h)ùJux8¢é\ªEŸQZ-ßq2¦ñÇU|쩜Õd\péB4tò+åH§ ¢°õ„ªÚµŒ(ÒìzÉ©Ññxá½1YU¸x0`2"J·Wèú¦0"Á�jVìh‡¡/šy�»–œ@¶íÿ))dþ¢ùjlÆTEÁÊa»Ëz�ʶMQEûGjзt;Ž6ÝØÖÊÍҼі¤oLoÃ(’½85}+l/.0M2U•Yã¬ôÈÙ·U}x�ÕóîèÞX:8ÃŽîo:žyá,ô:ôòٳˈ—Fožg�~dªÝeþqœ•ý2hIý»·W£€! õ:™NXgøûA9ñÞx¶›-_3á+=`êÿ WCˆV 5Âg§“9 >Ý ÿ,¤¿¡;žÄyÓñŸ•¦.É3Çl¼ßVv$ä|·Ì)+ç‰-ÉšÛ @¦x�.÷„·å36êÊ96±¾ "ÝGKEϽ�â¶*³ukIHÉR_W»'wZ¸¶™¿ýX³@�å0ŸS©<’'úluejGʈ‹­òJ•ùý_£O.Ô?„ŠÆ_¥é±æ8‚R‰{§Î:pÆí‡�w¿¬í1{ªóÞ1½lúA/ú:³ÐT¶} ¨ÙDR&bñ’þKÛP›rèÕèOÕ,^~ËIqA%ð([2–JûÈ¡{,ƒÊü•%¢wéº!JÛ.È–Hi˜»º‚KzØHˆ.oQü��\Ž˜ ¿{âZ™"iÐßÓ¯²ÐâY¿Sͱ5iê´PWý7ÓõyùÁßoÍËNv{ÛÅìrl…fýÉFb}ê›Äay£ëØÝÜžë1ÞÎÂtÑ#4éCdâ‡ßÃ$wô”@H_£oÝâ,W…Iz.£¡Q“htØŒÔ>�–5m]µ²'ÍFô„Ξ|ô¼¹ÜêИ0ãù…îQèrî+£uªQcÓ’N”cÄ-€(…A̧Â%Ó¼#—ƒ ý}¶-ŸæîT‘7'ʀ íåJëÆuV¹hL“á6+xøUx™þ^äy¯÷Ì> Šì£´†¬æ�6¸nX“S2o¶×kó¾Ï‚æ0¦¡¹VÔ8|_0™·Ö^ÈÑM¹ µ1à*4½y»nج«t�zæfÛ'A¦"!¥6xÇuÃÝ—�6EQ0‹bÔÁ"Ù5]Ê=C*Š\O²­ó¸] •@Ë�ó) \‚Q´uoÅc?m.‡1x¢ Ù‰RùIñ¬ÌnÁÎR‹ÑÈL¼ŠjE ÁÞ€,I=—-Ód�ßLk±˜nYŽ[Šÿ†„xøo\nm`}�ÙÝ€tS„1‰|A7+Ïh5Ñq]”ņîÄb¾UGÿk2€9Z`Ú™Ãe#òæ¹0šCèòм“}àÿ•Y}˜ôâß³³‘5¬B£ne‚CšO¤ä½ 8]C 7B3«Àº{j`ê÷í~Îàau’gÕðÒ8µÙZØIÁó^R�Y™õËíÉÑËÀ+Õø¸�c|ð%SÁÄUËyŽ[½¬�Z$˜^éæ«Yf…1>ûÎϲÒõbüyêÇ¥Ý~úÝñÊ s¸ÂGˆÃè¼ÅijœMƧ¿òælµÕFþp—Á–ˆö…ˆ«ÑHÕdéÆ} þÿôlßv>¢B€xžålbÿµ©¦•¦Ïùè;să)•³‚T?¼onÄË,.‘èò�Êù }�H–ËÅngßñ]ºÍøÿƒ�{¨Ó:ÿZ�íÕXR‰�ýó…Þ�} «µvöFhA�ñ-�ã`' Æå¥X5™-zI¸Ú­/Á(\;–KPÈ¥ô½|Û C,zv!Ænº±ìw¯®€^C+µÜû󻆠?{Û÷§¶0þÖêĹ?ض;Âwˆ¦�!we§mWl4uÁÈ©-Þ»�£G2ç"Àèá$¾J±@C’ÁøùƒR/*gÇÔøezc\WDˆ�+žcp=ÄOò´Éd.lÕbµ�S,ƒj½µ,z¨ÊJÞØ¿b›:a9âØÏ Áj`Ó·QtlYîñõÅ0´h·ÃŠnþ+V3:ÉKÜü~h¦y¹�éê Â¢wtmwð\h¼Ð¯¡‡ Ãí†ô±c·ÝbëQ9‚³Ø–ÖÆìÜò:/¢ (€œÔˆGaÏ€v2Ÿ„ vDªo*–ÚB-‘{úZ§9IÏÁpKl†(n"ÅSVï'Ên(—’òè¤Õ.¾îU«YS�»hœ¸8îߢq�cšSÇò÷B ï�óæ#™DLä÷ ľ‘X»€Sj0�ül�­_¨ÿ8ÿå—ÐáVÖ—ˆ°}J”Â]ïÑÇ6=Ùë‡R)€Û‹týøHb£qð&`Û;a·Ï]B“�î�ùÙïÕÙF uÉyb»¿ô‰‡2ý&À4=†TVØ�lœà"#;Ž¿‰*¶,•¥ ÄQ ¢MµØþó?ת„óÓ \¯ Fèq\¿�¬ï¹µ”ì\^‡ºÈ(çó{ìo¸^L#è¯o°ù°³Oë«£öÍ{’ü±“s™bš\ì™ /ó1~BÌ©ˆ‘³B@hûT�p ÜZÓ¿ùåb^¶æóÔO"óÞ*0½ù ™êr½«Ó¤G‘XH<µîÉhX¡NP‡ìïžÅ¨>ÔP~‚–=EËÊ©º˜»€TŽ�C•ëãMö€3,·Õ¾Iݘ'`û·§�ŸÜmr™ðóÆÏAéa^M:N�îÌ'@VüH±_üз¿›/]苹n vDAhöÈ8I[:É,ïÞOü oó( s�ü$®L)‹Æ2¾ÛÆÓU 2$üݨÒÈÜÍ&¢ÅܵiäJÊ»¯ÛÌàéþºCÐcðÕ}Ècù]j…çz¸•[)ì�PŸ �Tb}zà‚?ˤ{$v)frÂ3DfyT65Œê9~V3ö­ûLS€fh¸4ج§†B¢WÒ£ØÂmØø�<0 Õ´+)0SO“Oên<ÖÎÌ€ ®m†lý}Ãün%�Ú|”eü¼È…ÝçГÙÂæj0ª:Ëàb•Àmá×e uz¿F4*r´ENi"4ÉõäµÍ¸=x&å8=!’OƳ÷ýî{ºá0»ëWÇñK{B ÃȾ€!G¼=ÄhÞ úìš~Œ|>RøÝ.,ö\¢êP“—ßÛ“Ð>...…7—-’"éåç¥?’Ö0ä¨SŸr]Àf£ñK“.eÏ»:ÖíLW�5®-©ÐäÅ!Õàtøi„=¤�'µÙöéT–¦! íÃÄÚz³Hþ,º7';$R{`ŸÕiÔOG:(\1x µ]±ôŽôÉ›ØOnçîî�ƒðÌ^ϼò ´e€² øpõ¸+ÇÏ”!*6÷h€‚døétœ‘¤’~§‰<ÏðÔRD˽ÈÅ O{R}kæ“x‰èÙ9rÜ#ÍYYæ£lSç* Ù}ÊEî«å=¯^¤²%MD- -£�øÜ‹</íÜ%4kúß Èr` Ïå®l!àì“lï ±J›5y}è[0€Ž¼¿«ç�»‡ ®×™…®k¤b(7A½ûÁ&Š@û¿žÉ6¡Ã‹dÅ™ÙhMøyrñíÉó,ó¯Xà›Ú±ƒJá!�ã)Ñ�lèêrNÆÜÞùé®ÉCbW®òlv Wà�Û?Ùv}-ïÖ±�úÃH"ý.7²Ø�Kl"jceAÙsåÕ7°|ĨZ1+Ä`Òï¦|LZõ¡ã+Y¬Ÿñ"®/yó0)mBDÝ„�“[,õÆ&I]±ðž.W¬¥ò?«¿«o¯ï�Rgs¡LU£Æ#U~¤Žr(ôVe…oó'Èd�?UK~�š99ÎÕ§ê½LÌkË; UçÛôAñÃà‡Œ+8?_N˜2ðû-L<÷‰?*»iaŠÁêº&'il1(�Þ×§‰pf{É®Š` ï¶ýž3yÌW³fº9´+~®c`}k‚4@ÙÝi¯ÏkLU9&�i:‚‰ZI"3°Vˆ­“–B.B:OŸ@Gö+êÃíôñyùïÛ·Ó�‰b?þ&wi1k³ìWî꽿Iþ��óÌ!ËÑ¿`œMt+{��Ÿ8Çt¡ Ÿ,‚s.[ƒŒ2NXѪчJ“–œÜ•ñ(¿ó§Â¾¹ ''—;x?@ÜE$�Öè�Å»²žTzua©ñ—ÖÞ,î´l5éï��íÌ�Éïn}0ËÓ%©Ð§½q„�Ä� {:Ý—Ò>Fêa@Póí›|׃cU•Pïï–�g=»òí0­BzP.€qøn�ší¤ŽŽšZ¼9ÞaØ¢†÷3”P±#Ö¾•`^Ý—mŠ#ê•q-�lè+èÂIEf=îö— iôöFÀLšÑ´­YÏ#-sº‡ÚA ­˜-WlÞ¾Pñîs©asíOÛ+ú'›Ûág¿T ÀC½ù#t~7†Õæ¾9âÏúŠ…#d¸ Çmèuí>·73“e„‡ž:Ž=è‚©ñ0B÷Ò�Ý�ÏÜ�snÚdTôi[åK†¢E#K)2‚Æ€Ÿ³=.k#&C|=\U±óZÝÚóó²¥–NG\˜I¼˜ÎÀ!�F:@ɬشÑ j�=#';7ø\ü•1Ò £.g-"+p—h[éÿîÅXüéßÓ.é…¿±®å¿§·–Иo1.AK”²íƒ XÁü¥sëòœ®‰î/1 ¬}d¾�Òǃ†WèÙÛè±E–ßöyÎëÊ€¦[7Ÿ]wîžÐãõ ¬,ƒC%Ȥ6dØÅ®%Ÿ($NqÖÄ‹ðb0)F‹SÇh")ÔFÒr¬íؼôà:ÏØ%v:ÚÞöƒ�ŽGÕÞ¶¾®«é‚ÇdnâueÛr YRVͬpÒ꾂�%zÅ©/ji-ioºPa^…¹™_2­E°/OXukxÀêÇÆÝl÷ªEoÏ×îÖ%ÿ‰�(?„�tIæËNô ê⃨/ÐÏ~"Éyf5ö î”?«æ0D-ÏaÃn?•"g—G“ºB’¡#Ù·Cn×QÑhFH¤̰Ê4(iÇe*öP)L’éä¯3U-¢òƒ2›í¶P �VnySIo…‹Î«-�u8GŸ‚ç%¦ÓÁÎWþ x°•´™}�ÇkÃt:™Us¶U‚­›Çêm´º‚àMíû%1¡p؇=˜nÅôÔ–º€hóxΑY48Ke[8WéƒË6fÄr F̦B§whð�x´!nãOµN ÄÐII9é÷l vηÄýl5 QÕš¼r`z�jo¢L$¤Ðý fœ¬Ýx�� Ü��7h-zw‡"L"ÂAüÌ R2dÝW¦â¿Ñ ±ªœ²ÄNF˜qJCe•Ê!†¡‘É›a« 2+�".׳[,5¯ƒrltåjð×Tq[‰oÝõx˜/ã<Ë’ûO¦^Ì\¦ u⹩@É(ñZ}Rô6­ ä`Bu9£=? €h|ïæC¡CÆ\±m¹¿ð  QLÖÙñ¹Z²?¥z2{Á6a²oÕ¯§â0]5¶CGUK%Ë••¬uo²WI�É©¾ëÑ/<µ7›LÅ2Lh=›íÄ=à(¤Z^ÚR„~™ýAŸ'zÁ=ð±iú‹ü¸ù49*¸È á†Z/BšM;ÿã _:X�÷›ýZ/™¢†]¶Ãâ£xy¶ŠÃ¬¬– ^Ø‚À®eNOÌ b<Š#¢Û¶ðí»ÕÆÝu¡¿pTï9qã:Âo  ¤¤^Íöú(ä Ï£;0‹?»ï¥ù`d`(ª6Ï�Û ~úzHÇщ tÁ;åßóë;kÅßQ]¤0Àý~ùÒ"…¸b÷g3çÆ$PòüK®*¨l±ÑŠØuM8¤qkÏ�S B…Mì}ñ@@(" HN€yýü¨`xb:è X¨6Áž�¯‰'s�ä.âUsù&rY�GŽ­Èd“ø„r+…J™Œ‚„›HÊàs¡ŽTõaSP+ùnVrä ‡š;<JȆ›—…Þ>xçÈéÀC¦†lê�<4´ìZ¼¹<òŹ1&Oï™sFÄm¥òÇÿÑ~†«a¤ JkO?V>²ïÜÚ€�´6¤.l7·u€¬öö#W*ië’LôÁ Ë�Ûìît8¶ ÄÆ¼b �ç¹5Zw7ƒü{`jÇBa¾ibŒC+…%ùìÛHw.ÝØ˜jc�Ö¨äë7„M{à ßÔŠ²÷¯�-^qoðи<îpÛ-Í0ç:gX Š FÚÍ"Šª')rCVXÕ;ãÙlÇߌžˆh�•Çù5 a“îýC(œ�ÙÖÐÈ5@îyEÚÕ¹æf~�kðúƒVïçM1Õ>záOXÇv% ²{ ê¾s÷¢ž�ï?‘ÿ§Õ'HéglyÑ&ÂéYÍ¿û.^,lð®!ª`<ÌNÑ/Ò­_Çc&¥�oE±�"d‚ìjXŸû“X£ƒ£’S ¶¼ƒ"²®_å·`¥ýv…ŠK£N4?˯gÓ‡´ æö;s‡Æâÿ¥ÛzAgÊ}Êö$@ÅN’À Èò/n!†×Ö­Ã1™{Ê�QäÀih9¯î»í ùO¦ëXƒÓR.Eºoˆ(s(C³@f× ŸK.¬b%c¥ûì³ã˜Éi[l£ã­xÂi9Ë�…™Dî’ 7…ë�ì:>j§C퇺�û‚˜Ç�† ™G˜:Fhhe×-A3Bi›ÝóÓ~s7RX-‚SC�¼Â#oUµó´•ð˜Ú³E[-�Ïݿۚ'é5“OE“æÿårCýnérdú¬’Lѧ‡ô1PõwaI88Å9V7b6G±çû²çæ4jº£qŒ0%x‡°€3ô¡"õ– ¯eU¿cajm:Œ¡™ÓÅÂüBÉW" t&‰7ÑûÛVi­_¿ ÕP,Ó h”Bˆ»“'èÂL¶í´ Ú Z@®ô‡vŠX®œî¾•8ñշ�MnïÜÛ ‰dkì^±_UÊŒÂXõ-‰F‚}j¤ÏÛºÑw°"u„ΰC>´/|§ƒÿ®»SYÝôO¾Œ:ÂçŠ`•ë‘ZŸŽè¶ý�Z%3’ˆ‡™ø‹4YATó'‹3!òÑR™ê÷„ôÝ­æý‚®z\TCäurÓéNJ, N%¹”toï,²ºVV¸‘—¨©Z…œÖ¯añÆà'4k£#öBD@áã! D.z£‹ÔgŽ™MO.ÉUÑövHܬ�Ÿ¡Tt– ³R«Ÿ~›K„È$ÎÎèoù¢g ãçò¼ê™RàoŒ¸8ò«ùS™™æ(”G=󇿨nœê\ ” ¹ósú ¨³;×°©‡[ˆŒ†.ÕÔr´ò]Ju”1¾+¬\ùEH¸§êÓ\üúG YUQ±sËLÔü³øIXuetÿû\û�¾Ûµ”¿Ý”÷]WÁ.ɺm%gd Ÿ)»éÕ¦ˆDû()'Î=� *á;¦ªÛ�6>‰ü'­!—³uWâ~ˆûYÿ\fä´]­*µ‹—ƒÏ?•÷Ô™ÌùŸW%÷ë!�»�«jÊ@—�'A™æ÷¯qaW_Ë¥œ‡É­±t=Ö«KSìFéû-ù½tÓ|3U"*͵¶Ù-õÜ~LrŽGO8"Iê�ôYV*RvEü?X[U~è}o‚–Xí§…/ùÅžŒÀ3iKÞÓ±~°yUƒ¡‰Þ›¦ÁM5é1=§É@Û�¥�¶qL ÒôüL¨¨ú£ú®@7ÿk‘è“¢ÁíøgÉß½|®Ð3¾v[5dogŠl,‡ƒZˆ[Mwp‚&¡´ô¯’†àtȶŸ¤åZÁäúü�»nÎa&t�¾ölH¢*Ї8|·‡Œßµg«‹5Ɔ´àA«Ãò;jïÖߺJCCÃWŒSɘ¢Þa•ÆjmÞ›Ž›-Ò2Z>Ý[Š�»Ïž‘’U³_6“PN›?ðÊb-%köžX v.ˬ7Â(ÖüŸjV1Ç 0€qéøFܧ=Ûy¯ß±»!ÇêW[£½„Xõy1ܤ9?XVŽ0=aLî,‹%çF 6vâêÈ÷\íÂ-²½»±ÛÞ¸BBÞPQNvK‰ˆrRÄ9 râ­[>Wòy£ô�Ã}W“ÿö�Ò�p`ýpѶ$Tp@ùsé¦c wÐ2àµ-øl<Ä¡_±Î²EI-ŬæIÏÛÜî"ÊØ¶€yé ;«„�ÞøCï¾L‰†ÙÕGÙiŠ$ŒÐöº—lúkNÛ2°-Ë£»Åx>¦qH�z_"çId¼µ�úê4¨÷+Ή E”b6ƒZšÑÆcùëçyÈ{ cÂæým€‚åÜ@ŬÉ&Á2Ùá�^Rãz@P�øW×@v�h7Ñ.±N.Ú2=~ÿ²•9" àe¾Rqu ÅÃf³þ‘¾ç§�Äï’žä™#¢)¤(õR9ðçßý œ�`ÈjÒª€U˹‡"‡Ùþ;Ç/Á%—­jR‘c“:¾æˆÇèü�ÝÉLYBç÷X½£%”îР™0wÒ|Ä•€J¦'Ÿ²?�ÂãXu@¸º¤ ©í¦Îk„ùæµðH¿ŽT+õšà º6 åÑðÉ"J4?ý_E‡Ù7JüAÎy÷O*±“�æ O‚ü{hÇ:ª™ã}—s,=Ûs¨µ0‚„´ëm?:Ë PÁE¹?™VŽÍÐÉÖNÄØ¤ GÁã¤lRñt¦:ô|1½«!Ûñ�‚¾Õ|hÃèßQ„€"6ÖŸlšü««=ÝÄ p§Fö¿FRçᥟœôÿ˜Æ“§‹E XªõìWâá£�\‹Ì Î�úÔdÔ+ ¾'ÅÒìídzÛYUÐ xi#ÚÛD¨h¸GºRðy'šƒbÄþäQß$DˆL‹µPè9—7sH�&‡ž¬® ÜýËT¢RS>Ô´@hY}Q]¦Vñ£@cì³ÅhW9‚"’bOZ놢Ãê¨S^Ô`${ŽÜ¹g�9•Î.�&œÔ=ÿi=%è\ê3r|q=~•ÂY%üfãp«Áå&Ìd°Ìr=í} C@ú©ùy:A©½ÄQ‚ð 6ëÃ2É3ãŠ+°q}ÊZ }ÝÑ—nv|hÄyù|4ò9&7«ó»�˜ï«Ðgëþj öuɇ~=¿+#+ï§be�]t7á/ÌÛ�ƒ&OMc±.®N襶ãeòöt6®…NnN’઄1"ú%jÊ´›†@ï8o‚„µRØÜ‡{k�™9ŠŸ;Ê¿k=ðÓi,ø•,A�'·‚ÀøË„ 9ª@Þ åL:¸6Rò5ì_K+g;¥Úƒ©Ý—ÅØÓxÖ<uÞq§T’Ö0‘,Çt§+)+“,#âVcgú�b;V{s(7Ô¨çv�ˆcAxÿ?~Õã¯DÞYŸl€×{ Ä·„rÞÄ|´ˆÉ²Œ]ý»JÈ<† ½¬ê9{ÚIÀóM�Œ`%©®²L¦ Q‚À"ZZÖSç÷mÚ¥—Ù-vBï=ûò�ç…èEîíA¹K‘Gñku8Š.c±Æ.åW cifAŠz¼CÔ&»p0È[˜uØ6ð¹™÷Ú"m5³tÞá‰Id‡™þè~Bóâ`ÎlšºHx꟣w”„š//nðߺ½OBÌ�RZ‹@�·ãù0p¾å{�bÄ1³Ò©…y—åo+“Ìb‘Æe†ï¢÷‹ÅežM°¢ræ�¬XTªMOF§Š|–#›êF(›å¶Xnß>:*ÏÎL¨ñ.ø…Hò]ÚêCrñÆY�Ó¬~ƒ%O4K—ôN¶Ekä×2gKmÅ4wW�)ã�ŽQŽ@+fÔô�©¶ˆŠKÞq8Þ³„Ñ6AªZ–!CY Qº"±ÞÞŸ<@ø·ÌÊ+4Я…Rh&�‰*ü€ß…Y¨º ã“âpZ†½«í ù”å(õ€0|•ªÊ^:ëäéðV¡@kÉ|Øfx4�@híw PÄh…Êmݤ3/ @|ã–wÜÕí+eo~X|cï�ô®e×N–„}Ï-^Ý/Z#Ã}[n*Ù*Å$­Xzó¢;Lh/úý )`>¯ížS.úÔàü‰ €\òÓ®©xz´KDÆN6ÆŽÁRXMt…,íc0딃?/–x=„™ýq[åTéŽo² ÅÔzS[„ 3]˜óD¦Ùx¼L꺭ŒR^'bb¾¦ÙsšñŽŸÐZmX¦e�Ò쫈&FªLÐæ(ê9ðÕ�•¯ÁP¯þg”ÉyBý-þ åÿ_zÃZ-¯�jÅ'‰&–Wþ’“ F~)Þ ¬¶F2rnÙ8)²ß*¤ô¥.Ï NÁ`² ê˜d�VýBöî™.³Àõà¿¿ù³5q%X…LZœpG7­·MmX#$®Cq@Ù½–yÍú+Ìmù*²�±~„̳!:’ã@i¶ï£Q°�†€‹”«xÀ›}·¦Ž·Ž?bžûø8¦¦?µ9VÕLôºg‡Dš7̯V�¾©¾UH½!svÖÞä%±‡‘É님†�‰W׫½–ÖŠ$œÃc ¢þIŸzž÷%ra ¢H|=¸R]Cæ˜Ýáñ€ ô•{Ó |r-Ë�Œ¢D¥wF‡+#‚ÚšèüË«W²È­ªµü°„%µÍêa÷¢á®šôà¬â*Üœ>4ô[†âªì73þMµ#„ÿäNk•<}²ð¸ÿ5ù4‡žy§¿_0xÆb…£°uIv¦ÔÆá Ö¶3{{#tÆ+ü/M4oWÞ¦=–[Bì÷¤æZXøŒ‚*c6‡“èc_xï}ìã$á#ú߬G?¡ÈPË´FUMðQ©L£o£•l¯¢7ör‚V‡|“ÃáQæöh8ðÔÿ*DÏÆ ÇæÕ'Í^‰ìè �¹ØÇÄÀRïçEÓXÚó~Ö3{&�vúm"ÛD;•9™‡9?ÂF-µêêo·™Œ{K§‡i�eZ_èÿ£ý(ËÂ5ò ÁV‚tN].µ°EÌŽÜALf2×ìi8ÚŠ%ȱq"~žqµÉøíÞ@Bšä²¶Š¢.¨ö®³S}þE^ò�hâúF®¥@ãÙù‹³Ej„µ™qàµÎÊ„�<ê>ïA[Ľæ-;ÈiŽ®ö°<¨4'µõüæ\мŽB¤@\Ý^ %uÞv[ÖÛî@¦ ¤ËÕ'`X¿ ÐÆ>ÎB5–Î1ÄÜÆ SK¥i}38EÁ³˜«ÍŒ f­Sœó¶Q›¬ ÐY÷œ9Œ#ÔÏ]N ŠôBëÿÄH€oæ�UÈvÄ7 PŤ—çðظq¡"-{ ;,&ô;¥‰S=~óȤ{ ¬×šÔÑë¨=ž,î#ˆ7Áq”™¢aƒÍwJm/ðLk· >¥ PëCnöD39³=«È�M” ÏäñÙ'fâ€7€Ã6äø”oÙ­¥r Ç�,Ÿ \ë’ÐÉ©Œ"âÓü gÒ²fk´è�t�¾ARÔô.¦…¿ôóê!ª°Ï a‰$•Þ¥aùâ²°àÊç%7µºCwÌ(=o‚ 2Ï€F¼�yò±š3Õ¥øW›‹mY“!ÇH&!Ó%çâö÷ ;ÜÇ ›ÖуD!ý2oEÕ@L¢¸™:%CþõŠÆW£ý¦ÐÏâí ¿p:f+dZïì¤� íHJZÁï¿Óüh»¾ˆÕ-eªÝ°a‰íõdÊáK_ÁÛÒmØÀ?ÿ=G»ÅqúqͶ1E\<Áë„'÷�W7ŽIHèmº˜“Ô]Ùé¢à)þ2¬Mlý¶U=oÚ!ŠŽ‚–CbœåC†H`ù$öøš{÷2\'9ânD HRøà´FG²ª¥“+»Ú}¾irúË2vkÇØ%S6?yIÒÏÛ¹‘ÇФ=yFÚñ>NÀ Úâ¨Íè•,ã>ì6S¶ô© N“cÊæ3Vë±0?lv<ôLWóq†xíBä{»­oèr63dýÂe• Íg—£^½‹ÄQJ× š…4qRó° ƒõdÔþwƒm':§)@~áv úå¶•¶`îH*°À¨zóýM¯õäìE¿ÎÈúZÝïÝ‚`—£KŒÕw}ôÑÑT=«?2%�ÕF ÂÆÐÕþmäi3×&+‹ñí#ãz�-s¤„6þd€ø=GšjA3: xæ»úæËc,Ê»>ýÝ5ãi¯>ÝmGä¤jìF¼t¸»W¹˜nYE7)¦cÕJçÞß#Ñ|è”®µEÃåC8�8 ®¥¸^B dªe’›ŽA àêó-P©Uq_^qOzJ¢Q|NOØ”\Ù‚ÊÕ35aé©J!µÿ«²¢cùÌfbÊ$;F˜!?óü[#a'QÆùÏ‚/†fZ+ä[tG•¨v”Uù´ôUòÃ{Æ íÕg)懓 ô©rV@Xzn@ïj!YÀè@ûIYù 2º©ÝE`9W± ä(( äcñþæË¸�^/‘q,á< íKi� x¦C|2‹]} e`úQ{(`G)Ñ´ö†<»¹bFƒïŒß�ûH´9­È" 4ql�â!eéù™@ý)!±µÓɉ“ ‹Z}= ^�æÊŒÿ¨QÔjŒQŒÏ~âIrøçÍß[xSzw½Ö×A¶½4“ýáÍüº°�&Hó=·KK·Òfiu,L;”“ïš‘EUòñ«ˆ-nøÛ&2ÅÕÿ_‹úXšœS XÝ·Y Ž™BÝr!¢–uÂý0øÄÀÉY|­Èí€yU†m7ƒ+?‰¡ÄUˆ\Ui“ä?îá½wvOšÖŽŒŸt6½VÙS æ‡ÏY1¤6(¹LõK i÷]•U>of^ŒHÞ³Œ¬%¼R—«Û±¦A6­ŸUž]—š�N?_%Í·lnêÆ? J\ÔSRö\u‰ò?'Jù‰ùÀ ê˜òf02‚Ê´v¢ø›^4I��äSt×;]QbßùËfÿÒh©ìñÊÆç/•Õ 5`6«,™ó®âŠÊ‘kR'¬Ï�æ¼È>•2б±èÿYKÖ¬�ornÇË‚i¿•¤¼e}ÁCømBå}¤`‰ ¼'ì+–šDÐ3z.‹+Yš um&¥4ýz…8YŽ1/·$§4šÃVå¡ýít°iep^íµÝ·EopÏ�rwêQè;Xdž·÷Üõ^�gp¡°+¦kìùzÛŠ`‹ÿ7Æþ¬&êâ2¹ºæ§s¨Þ¶ÓFx |»ºïIß,?J�½ç|:>–_µ“ßÞ«÷iNùYª¯ñ2z¤«ÿ==4b?)ñ|ø­ùØm¯nrºàÐfä1ÖgFíÚxØQË#Ïï‘#»R|hŽGÈYŠ94$™0·<&áKd2á<Åc\í�Zà<Û1C½Â²¦�©à:™ï=€R*ž»˜eêúž;lðÕÞ3î²ÜóÞ·qriö+¡#ðU…kÏ•‡HeR”ô��¯À]¿Oâ](ÑŽýÊvÖ†Nc7üD{/“ưÑß³ñú³÷ùîâ†þÇ1«†T£‰~Ú*^ j�í)÷–ë'¡°îQÑ¡aÎFh)TNÿ±*-éñ×ûQA{ó[€*2†q fL ËÖú™tõ·+Ö~¨lT›pAô*¯‡+=á›Úà S¼4Ècîܶ ëxq¡|”­Ð…g†è-Ê@N­iÆG>„ÕfK�< …fEJíhÅX$mH,¡ ²PÓƒ ï0¬¯OÌnquÚ'd$­ôo?7ÿ�~¨6däPÄ{+²t±wuBV<üÆ[ç/ áDïë±­‰c7÷¶D€É&ý5%ßÛ ·“Q ,Òæ¥ oêa±ÊmUxõ-“¸… L•KïEßã4Ô÷G‹sãUJ\Ûa:*aZE#©V¢-h=ï‚oà¿bбËÓí1[󥙪nI)µƒ{+Ôáªð±zåZ÷+çwàn´¼ zœ ² L¿5SDâíóÐ ÒRÙi‚~cYLÍÔÏÞƒÿË�ÃÍ Y§ˆ4ôñìfã‘•óÇ'ç,ØÅd¬V+°aü|~ÉŸÒ‘S7³ý‚Þ˜÷˜Lþ¿LG·é!ÐFÆ‘³äö{M›€þMÍ�Hxc6©³ÕCÍ"AÀeqÎ}‹,‹d”—ÞZíí§W²•°oÝØ;j7ærrkc¼8u¨‰d—då³(�bbðÅdqB±^ziŸaL¾˜¶óèá =®@²znÍ©oâüT0{{\añÞóêzŸÁ¶E´€ø�‹LÚĆèï>ß|»úPˆÞ‹7Ñ?²¥�Ô¢<¬‹¹„f6S}~>¦Z4…#äû=Rñ®½ëcHÚšÉ&U½°Ý§±Fâ¸áûÈk]ÿ¾¡{btJºû8´NA�mÕÍo®HWuãþQˆL|[½“õðïï¦)cñË;ʃn³Ïœ†ôêªï“xíAU©F鈔0™5„3¸“ ÑdÂTÏnˆŸ;ŒÑ�4ŒóFå|·áøò´m”¹»rEÏwíãÇó.Ä_“#�š,BWã‹·íº‰teÆåµ•1@^‡™r£¾Å¬Ê ¼+à”œkšZ`…²,˜7_)mÓt^­¼sൗ¼Ì¼ªNvf1á6ËoÈ®2É}4Ý£�RµŒèø¤©*w�¹;²È– çãü—i7zâ^Yˆ˜HsšdjW͹Ÿ\x/Lº?Ⲛ ‰¤›©j) âÀ·è«&ÆÑÄ6‘‚Ú‹ÁzS9n´Æ}1)€ÕªKs†ÂÛ+ä‘—-„‰Žgø+æ'X¨ò/MÎöØ^Ê,BYó¸Êj«c™Ðu͸ ªêhø1¡{1ï"ZõçÊ}–ã¾Fx–Æubϧ²‚ãµÖÎËòSò÷c¨Òo3m\ï›Ø5™áAýd©.[J§nÝæ×)蕟˜»ñ…AË]È8(“ýÞ²Ž�YrÇ_À‚ÙKø@”RŸÓ>Aca› n}BniÖì2ÔDÌ:#e\»€æšg®iX†LÑWôÌ•ƒ,�Ö¾5Bæ×8 ÓÜý6ïÓ:®P•‡BQ+ò[<”¬-t³¿ïâÏlÈ3}Ðã s ;wãç¿‹øRçnpièe6É»rU£ ÔW³÷¹Ròù˜Ml—·ÝAÀ$Yš¿=$ûø‚´i5�ÿ<àP0*wbÂþ¿jÀý7ë:c¸j4²Ì[ÌšaKèÐpaU7¾�g{8¶ï‰J÷¿i› ¢KŒ‡nN€�>¾o&ûH"—)ð:…–:¸ áF|"tS8bSæ€(ÃWƒ}ÌeÓ¾ÂgÊH°[ÄÃxá’9οéQÀ8Ea)Ö�âĤ±SòÉs1ÊûæÇˆfCX�8¹ë[õ•êˆ�¶â3æ9.̱¡¨‡uKæÕ­û'Y娑½åOž«„7d+ižÕ8„°_á¾v¤ˆàñò„·s‰€‡Ô´{oà‚ö3ѱ—„ÍBÊØ5V©Ux�Ån%”)HÜg°õ1/2¾™¿ÉÖ—ús �š4vV9ì'i⺯§èðæŸ«KÅã¾|v%y$#gûgøÖf2ÙÒtNýgG{Cq»0—GŠ»°Ô˺J¢Zü`PëO±C5h5 *ŽŠ*¹Ac{&†›ÂÄÿp_,c…/£~S\#pO6ð3!Tæ4Ì·Æ„ýziKV°PßþÈÛVÚù^Œº„¬Éa{ÓìýfH§Ò²ôÇ}h¿ å*ìe¡ÔÃ$37¸¨$ÿfìdKkÙâpe#BAcµ}>®A7y1T9Ø4#¯ z]Aš7‡Ñ#‡êÿú½ Å¹Z^Äx;ô¶�ίîxð2¾{%_ð'Fu3ù®ìƒ|X±VfÁs?Ô›âæºŽøZŠ÷VŸKZpøökûAÑý¬!Ñý2œüÙèGa‚̥ᥳòmPkg ¾'Lu/4,ënîæ‘u»<òÛ}¸J¬%Òc†ý3 ñDÒ'°_-¼Â¤1v? tvá{ÖcO°Í”ijI¼eð[nb¼2ÕØãÃp`I pW¹£O–à~�Z¿þéÛ=5¡¦lvùs¨Ä�ÎÙ�0k/zå™…Þ§ÓÇ‚³’èN¡Éªåãs!# jRHaœ±tÞÛ63#�zZ •pÅê‘ jØ‚CÓ¾Da‰u"1`V}LeU5R+ݲC®`9Ñ7 ›žÎ¡¤2aŠvee=!|Nj•€Ó.¢-à%cú 5´á›¥ßü/vs­\ô#|¢iËÏ a*�‘ópr”§§ ™%qA¸ÿ(Šý¥¢ÓÙh'ï®F…x‹¯[ßXQX2ÍäFâX�µÃ¨˜•÷X…RÝéã9&w9S ¡SCö¢E™ô£’Ðî£ÅJ òQW©–ZÌLÛÀ¦¥œÑ;�ƒÊ4oÛâ%¦ÆçËk¡7‚0¡É(�hÖmƨÔÛHÀEw¤Ý,íæIXQˆ'bÈÌ»Èû�èp؈ù~Œ”¾FNßöCóq:¯J=’\Äþ™~å4Èx[Õ†¸09GÅ‹yå×ϱ \´›„zƒÅ™R8¿xÐä= ÆaѦ~ÝJŒU4i‘uèÅO¶=©l †÷7²*¾ôKÐ >ñÎRm´ÆúN颜ö§l{¬vÈ*¦Fu»M³|0¥rª÷TÃD5†ÍÓß¼˜ä癦ŽßˆôýÈ¿@~+yeÈ ±µÙI‚îà ‰¿ÉŒè¹/Z8 +ñõæOЪ‹¤,‰[úÉ�§IÅ/ì¼þ^³g›G¸Ãü=ÑO�ñÀpÞ½•“ƒ¢cü°¢Òž¬ýîg/‚5ØÚû ºa)ÚØ“I£e­ôék”óðÉ 5}|$m[tf°›Çð¢Ç×lmE¦©Òz>É�“åÔŸ_*E6ÄÎ]¬Â¿ó4˜ú]P Ê4¾ó1R{)Íí¨Kç�Ú‹m¼ò Ç0ü<»ÈçŒ?Ë“ªÿØQ QEÿA¿˜€J ÔnIãÁñаõÕI £.ƒuV"é°BbÌ&ˆ î %’¬öÕ˜îÕìW+P&Æ>•I~º4peúòã¾1 ìùT+<º"0Î7´QbÉV,øÜiÞ~�ÑúøæÚc‹q1Ô43È¡êyÂH¿bϾÄ�‹×{ì5—øœœ†œê~[ð¨Ë\rÌMÜ—'«ìH†ÎXEåôF¹•�É79¤ *­›7œ’0e�'í%ÌïÕzç3T´Çöf}Anç›�|’äáæ.u£î¿aa!É-P÷§þ6¦“Fé�…¾Î�I;9åê<½ª»…±` ¨+ôÔ£k™1Ÿ,¢„ÑuO–Ïb©�ÊVk¹NÊjaö6@u,÷‡SsÆUx/Ò"kÈžg+C–ÑUPá„塉jó™Ø÷x).¢T¼+«l’”–�}Ò7áQbøÌl#‰Ós ÝT‹òh2˜VP�'�F+¹Üú>„¾êÜ'¬SŒî�‹Ò#‘!"½Úä¦Lif3B6eÚW#fÿ–ð¾>V¥‚4¤¦6ɽ‹¦'IŠ�zcáž e fߨsÆ¿9¶ì’i=÷5fà5| Â$s±_z”]{2À^Ê™:ŸQ‚kò:,òsÐ;ÀQ÷ÚŽMÓߣˆÞ€†ìLÜ)ƒi|)N8Ûö±çŒ9Q¥,߯¤ü=¬6Žæf'O,�˜Á·5|ÖNƒƒW°%…ļ¨N1s‘”5‚RF˜D¿¾]wí‰ÍŠªcåPmë…^²´ ®tÐå²þ°’4Ú¼¯�Ãv.“¨"Fb̹©"¾;€|˱w†Ï /ñ{G§ÐeD;'.—² -u­ò¶¯K³óKmÛíMGU± Î�ÿn]¿ÁHUyŒ…¹:˜�T¬¢,(ìvqnñh�Áþ°E›O^ÀµlSÀçšY«­ø™íV^ ††ó¤r±ðÅ lðûæ±�æž~ gÅw´î÷µ£>Äüqm�>pž çÜÆô½_Èyÿðdà¨7Pò+’‘DQºFú>#( EP¨µ.�¹D2Ç…¤Që ²FPF€áäänú_Nʼn·�*±�¢Z†‰4 áýC‚}òÚ’2³¼Â~È’ö0~§1dD.üBô(÷ ÛÝ;Äýûe~š[ZXÆ‹Z‚“Å W@ÿP³)Óoµj4pÑÝ…ë¬Cí<—5�fÔ î{�8LUG©©¥4†IJ4dE‡¡µ¿ø>ÇÖ¯ú½Çÿïˆ7BbÙ 3ek ^+ÕS?š.á[Ûâ“å=!ˆ½Ö®lvÑä™'™ÃëÃõ^¤¿#®ÉƒÉ>œH_�tPü�­žƒwâ’Í´{ø—ÕÕXN'3¿Kz 2ß½âÖT9H¼¶tªGx¦\¬8Û’Et‘mÃÚ¡ÎSЬETè)Ç)åñZ“Û«éÇU;+œ�圖¹âeµãÍkaۜľÂÅõçÆ,t†˜ÆuE† -¤Ô�"übr^‡ñgÈžP~Óî§Hé2x}žàS¦×gÜi'¥ÿl£ÞKÒø¬êäVçÁ_$7sÙê«Ç¯ÆYŸ;÷¥ø]ÐË› ‘Åj¶›É]Œ�yÎfÎB9„â~‡i°ðɾ&¾H&Œè%‡N™sòÎ4ÞsÚjVlèLÇ�DâÇ« ïA1>•dèÄ;sq¸î«äõ9•FE¤nÎTº�gÂÑ4”qÜ”öUÅ` ±á@A*Ç…(L4QÁlEùF°�"¾|9ܦƒu&~¬¼¢œ–Ÿ¹† 'GVr›yÇ`o¿²ÞCR<–Ögç7+Ìé¢�B%Â�Þa¿5©íÑb¢Ø‘¹Oé§_„e”,ј¥›¢ŠŽk$©õÀK„(Ûtøè ðª’0v–¸ž­r:‹¸~룪Mã¤f7Š’³Õ?k SŠÞïñ;Ã[*>QæëìõZD4¬� A¶“'®€‚U×fÙÃͰ÷t®¸›*a5P¾ö�™“¦ NàÛ˜e�©³c©ŠßR£�ÉNÃ;ăE‡6S11 q“7/0[S${a‰ˆ{\¤PGÎm­Z¼ }½s¸¥<¦šX€ÍK×wÖiЮŸù­µÑw5d âeAaÐÙ ZÖGoþfÞYÛ Ñ+?ä‚�¶g¹ÆÐ7çÛI`­ÂÒN,T²‡-óøÜ7,z»Ì>éë² .4ð—C :&8e/‘õ1øß{Jx‰q¹÷U²®¡ÉcT¶ÿPÎ}È…ÝW¸•×ùãÔ(þd°ýæÝ­¦6Ò9JÉ_ZÃë.51¤~n†¤~ý(w£pMÐ\ µ Š~Ær‰l3Ð,Ý”>¯^ ñ,˜PÏoS“RÝsjnõXŸ„.ïP•Ý™¥z›­ùöy½L¢òOåèR ѧ9ÛzµÁÈþtUýí ߘîÛu¾d6ýÇø,`=óyô8Ùd¼mtDþ˜‘A•„ÊIšTâ �çCÀ")d.?TŒlÐÐaÁ«;æ°Î¡éá› 4X P¬ûbóÄôà@}·rîñzŒ’±ÈIW$CG#pà wJÑuVŽC$€Êþh™Yk;} ©·¤#·Jà‚€îû9y¨ã]FO@�d»€Tÿ£88°¢”‘É[°‰ê’˜YÁÆŠ¾Ë!ã<àÚ"‘œã´² ôsˆt¥ŠÁv;ßr=µ/~M°¢)­í:³JÉÎäºç>Kȃ®4Žu›\Ô9p h@¨Ñ=h†Ñ$^Ê«›�$0Y)fÔÞð:œŸÖ1ÇÛ?v…߉üÞÅÎ(yT^yÎ «BÛWý²;¢Pƒ4Žf®g·HâUG¸­ƒùLÀ"Ÿ”yX)´]ÓœÏßÜ\£©ˆÊŸ[�šZü§ôU’f.?e‚yZi��ü�žµÞßù rû‘gÝJ–šµî÷4i,8nÄ.f_Uoʘó!ÕJv'Í>Ã7΂Î`ÍuuaéWlÉ@š‘}VÕ²¬;ßFAå£ÍÔ+÷Ç%š)áÀâL¿œÍ7èA)Χoñm‰ÚdiœS«!ë2†¶Ùpäé×ϺaÞZ;Ì'K5XShÑtÎÚ½†Ê¾2Œ'ä¹Dœ×W—ÖôÇI¹º@ÛÈQJ§]�1D»Ë“$O[ÌCÃh‰qžŸ#AûÙ 0êÛ}Õ´OÏ�Û?ö\X—tqÅ–¤RS§�·Ì°Ø"¨¿7$áǵA•3•§¦fd¢QR‚á­·‚ÏàÑnχÓ$Ck¬³:ØpMCxÂk*}fIÂ&å$ÚSMÀü@w³Ç�Õ-1ÜôþÒÔëÖà™‡WµÓiž3¯U‹›•»\BRÔ:PŽ“^~‰?�%pG¶¬Òñ(UÔ>²×�—©¬èlÕ#jÊâæI÷�êB€yÀëI¬à'í;V¨jÙÿ«éáÎqïòÏ¢—gœÓÁRPZ:È5/ÄÖõ:*ÕM¨2NÌŒ¤,ŠÂêby¡ûgš•,µ‚srW{XÛOES(µ­%ãJ;´ÉÂÞZµ�`¿"Ú«6 E̲áðÌÉxT¬÷¬¨tѦ¤HGú$Žën\O¯™åõ9÷”"Úåt‹ç÷GáàŠÏ´¥ ˜ÎE±Äp Ê$ ñ@¥é"4Ø�$€%çMn:ëD ùÂÂØߨ$§PK8Ü‘6…bˆ}¨rÓAkÌlåˆ!CCÅÆÔìµgkài*I k÷ÝôdËuˆÔÇQa<›ï�ç¹më—LkˆqxmÕ=9cËôžºÄîv‚ñ]¢æú ½¾H J£ß™ïîôÕ¾&DZ±œ@)­µÍTRÁ¬óè¼Ý|¼¬Hu‹ñÍÂÙT�ÛôŸÅ?³è¦”óÐÁIïK&¥ QEßÒ Æ.=õZø_YqëJ$ëÂÖ…\)ž‘Q}ý¿Ïèrû´‹8~Í([6ôÊÔxé—5½³Kœ£MØ ·¢Y‘÷ÿ¸Ï)Žò‘‡Ëcý¥Çs�a?a‡µ¯ï/>qtMéoH²sŒ÷Nœ¸aßèƒgƒ7Þ"“@y62„]!UF©£DÒùÉ_4Õk ¼ÊÁ±UÝß=òs¸¹—v„»'̺Fí8û$³&bÖý¾ãPÅÔf�ý‘ì üw™Ðhz“ÛC™-òg;¸ð'¼@·N÷�LCË!~‘‡~Z4ìŒCïÅ×±Yb_ýhåpûQj>aáÃ!´ñä9ó¡þÒ%˜åE’ª %žÇ¢Ö(p¹G œî£ÉÀ�‚¥¸+sBöO•EÃ1&&«ËïΚj̬gwi“¨‰µùÈÂpöÑï …ÿ4£Âd®±9Ø9ê×5�M¤ê¥ôKM?¼w½™¼XpE*K£VA§C¿”PJIÝY¨#ê~—6 äSN3¸YØC¥ìÃ�ÿŽ�§¢Mša`bÑ÷‰"ãjØÆùÕ±^‡ÇÒÚ­r~ò¬GøX<Ú~xcó×ï6ú†`’›¾fx/Œúœ“Ò“‘&àçkES vâ­ÐÒw-ð=…t/ÌuÔ–Šá:dƒ(é$ܺ"‰û'ÐÁËDTŠ5'{˜á‘²Ì­„ì©•×S’"Ju}ÞVýÊœ{"·Tæˆ óX¤jˆ. É;l¸8ŽŠÂ˜ëz½ þ¥_ÌÙÀ�ô|ʉÓG:“’¦I†d]a�‡mH�³aÎHŽûlü _ÿ˜Ñwý©)1H4óÅpNõ/´ÈAКQ5ƒ!ª7kä¼ÎÞv/X}úõR´b$ói>£<ÄNxº¬ojm«E¹à=좷0í§Bµ&‚ ¼v,ñ0éº�„¾J¼…›Pµzy¬ëúÚbeZP­õ“~Ö#á-*ÞóDÕQrß^?9 m¥ñ¹LÔ»å­�Ö©ú`¥\KmDKÒWÔ;âË·�únZ™])Ð9ÝSÑúwñpªà?2ó#þ=.Pa„hE�¹do½Ynþa¸!ë¾™ý4v`v\(Ñ×Ö‰T Õ›ZbyΖkõ<­œïéÌ3‚fOU`·Q3 Õ‹ à`.dÛ© ~ á“†u9ä”;ÏN)[z>¹Ä¬š8'Î ¹n›rtþ˜æ4ñðeïOÖ{¬Ù7㙟áµùƒ£Ný'€Œkœun“çúv‚M…ua[bQ�à† Ÿýï+”=S×Y¶Ò{ /È+ðY.‚k.ŠOžÙ-ñØ2©ƒù |Ún,³_{­&BÈö5Es 9pà^Ý ¬ïçNøðÒ€ïÍë#�§:ª1DÍÕSŸ~È�ú¼‹³Þ ÁÒùú—§5xŒUäÖA;ðÄÜùT0ÅyÒ_ÐÔ–ÅÐ2ë{p—®ÓíVqÀj¬ˆk¼ã2¾k߀,´(gwZ);»Ï†�¯)ñ2[!\™Æ€Ú>«Rj†ôGÐd&;FwP»¢ø¾_ OÝLÞ©°T*þµF±­t`¡"*<­ç"¢gTv7ïcjÔf1Ö9`ÎåèÏÚþ`'²•ó°´Qpñ�¹¦¾AP Ne ó5]íº$ ï4f؉Œ§”Í…Õ¿ŸæŠ+"ï4s©HÆïEåC1®ˆ¹&g\aÝmÚá�|ð¤MÖl6RúÍä-Žþª_Eµ™¥¦µd¿¦ƹìñ¤'-!Ž•¨ˆ¹7¶3‘ d’"?H€rrt=ý£=ñø~déÁó°W7prŸ8PzUçOÁ¸FC ­×½÷›�»`¸<4ÜÓkFÌøÝã¶$ÿ±Úðâæ08ýaî“Ða¨ôœ:v¹/•­ ³JžFƒë¢9{É5õÉ$�åøAµRÜá6Þì«2¶}ÄtR5\B6ë ™tÔ!7ˆFìóÂ_ã« ù¢�}ȇÐo®S;=fP*zùÖÍ&§¨Ø M)ž ^‰˜žø )+ª†ÌݪeôeFpÜ.­ÆÉHŸd‡ËöCïzÙ)4�Ÿ�ªŸŽ†„x_ïæ¥DQ†óÇ@¹cœà"ì‹ç(ä¬Å’;¢4¡B¹dv6Æp4Ç»s—“ö/ ¯Z*­ç5Í_h]ûû}G[¾Ó±bmƆè"¥|÷hÚtÀPÇŸpDeî«{¶«¡ðR>yTll†¦öÃòû°µ½ ¥”ÔêYYù%æ:Ú䵂Ü{:x–ò߃Q>ÖDd¾¾Á±)* ïá€#ê¸XZ¯»6ʯB¨ŒóÝ#~Îk´éÃí�€šÖU€Ñï×Ôé�å°=<ØdÿRòÀ©ùSqñÙô/Lƒý\ž4LúÓ4§ßša»Zbå¯{£Mú–ZyaŒ…ŸÂ:³RË¡¹B¶éFÌ¢™<*±f 7ˆòømK@B€±Ü8Ä[Ÿ>W1  FA9òÓ03LD?ÓÊšn¡"~©Ï“«Ç>ƒ[’²ô¡�4Ï&7˜+5 e�N¹ç¥x»ÜÛ_×Bð¸A@qÓøýM³™Zõzœ8ñŸÔŽ�²gRÓ<sSFêRäÖôÓ’3ÏS­Ý ”R‡ çÐdF'øJ©"ARƒ£a‹vÎØ_ŸJ]Öû×½¯ (ÝÕ‹ùa^VÎyLX cëÌÝSõeiKç;.,ãLÅøóÚWֈƾ¾/ôデ*ßoèÖ›ëàN#%Ô-mÓ =õ ë®\Ä(Ìó‡ïËlE�<õ¾ð±õ?ïÑÙiÉÀ±ÇqBŒJÂv‡Ž)¢DÒ.ZDUŸ&‘�s>ÌšÙœhßoîAÏw<ºóh ptxŒžeVŸq¬XÊ ŽQ´«"7Y-Àž·$·†�é{X7ú‘9˜Ð!œ~ÓÖ͹uF÷zÆ/ß7éÏIáaøü›†5ÕsG%ë Cjη!�2Y'›™gsïced5c5��NfÝ'é »Ð¸YC­èðOƒÛãn+7®’¨¹:~~ÕèºM"z6'=öë• Û{Ü~âóÌÄõñb¶´öÈŠEØ‘œƒ³%Ä‹ù®‡µˆ0Ù¯jª¿ÅvØ·/ÍÉ®p ¤üqd@9B6ƒöÒ꿟p¤ö`‰'¨ö4?Gè“NeI‡ò–Àj•Bê v ß¼tô.=‘çé 2˜´w¤Ï g£m ¯?»¨ßŠp¥rv,ý³¢¡ÇÚ!„äîy0º¡÷LÆÛɺ] ?ñ #&æ`A‹ÞkôÉb¡ò?¢›ÝrC[Uʺ(Ð(÷ÏëN§W’ÞV!�N_)òî¦ìô ‡ÊWħ¼Èꉩ‘ÖÂ�‰]¿v6“Í3 Jë˜: •1¨Û[MxÕ4ÑýÀé�tÜà=Gì~H]ÓtázíE Ò(ì"Å’ú¯£§®lbHÑ]Z¿êVWyS*üùïµÍ~„»^B´òéù¤»I×Iè#ªXü-MýþbL3‹�•Ýlr¹x4i›&+@9ÇêN¡ÞfnÃ,UÏæqÁÕòT9Û»Õ¯–‚õœ®z…w†Aï%·‡O ЂF°\7göF:ÔG›¦¼˜ÆÃ§�b—àg‚¾%W™ç½LÓ¥X¿0Rg¬�!Z&ù<û×ÖÆ]†-hHåGù¬²Ž!£pÙ‹w&<„B ßÚA ^ç‰?öûÍe`~äêÕ5ýÙ÷•yÏ õ©·äÝîÊ~ �ü¢4.áRùQe®%¶‘Ü�¾�àlgÆ 9fk>l£ÛU~˜�÷[ö©Ö|•¶¾)8tpàŠoÄÃTm9…C†ïõö u™ KÛ/âÓ¾K^èþòÒÐT ýÉÜtä=Ïçâÿ)Q˜Ÿžj¹¤<@jKZzP—ÿ�R=xS¥÷ówåç)ÀìxJ˜ÊO“Æ6`~J¨QòK† ‘·8öu›FQ·‰ôbÈãÔ+VN—³t’�>Lcߟ@ÔÑ ›`fë_Ï‘ù¬¨—4ÎÉèÐJÅŽd¹‡i;TþóØm€¬y©ðxFʯ â› ‘d”cQ¹b=-tÖx;Ù¯P;¥ü~Á–ÚæIÈEÀ'ÙU)™Aò å- Á¯åq &ÝtöC3WLщsnn3fž^‡× �r®f»BªJ8*G‡ùÕÒôêÆ´$—èÀô–¢ß�FEur>äbh7²jã> ŽØ¸ú‚?‰`C‰®ÓU6]ãÿjöcǸ0¥hÝy›·Áæ–ǸbGèñÊÉ˃¢äa�Y†GÑtHWÏ–9yÙM*VfÕ ]ù%ß–¡£Ÿ„H5¾°G|îÉĆ,úÿ‰m¦©)”Rؗ׋¬xp.›Vµ.Ldp?Ìë_£;¨�ˆ°Ÿ�}�Ô>wÓˆ$­= Ïïy#ÔwC/T|剜 ™^—[º Ñ;3 "ÿÄ,¯ –txæc…z¾ê\ÚWÂfPÇðùãºfýøxy⯠�èhÀä±bˆ.zû¯"¦ð?wËã1èAB¿X@Áöú´º„góŠ.Š%Säkû�ÕµåGõX9)e¢ej .wyèòôG�†m�.Û©+\7«?°;˜Öqa¿œÔñ! }i¯ë%+rÓÊŸ”õªœþ³>ÔdÉ¡ó1•íùGÖâ΄ �Óz~a#,Þ ”=i›Ù£ä`¼Æ�&•¡fLS„ú¼pôžM.¬8p/tørÕ�µ”>ž±jÿ$$ß‹Dkš&6ð uÞ~(¾,Û/œ²X³ÒZªžöÀ½ßͯëµÌ�âW�0áçÝ È¶í�©/ µZfÚýKç5�vXF)ÎÄpfø¦ø“Å •#ôHÊb]%ÊÒW¤—ë÷’[lL½ÌÞ~·‰/ŽQ*í:Ÿ§®ë½Qœ.>÷ØoR Z%ÿ…à¥yaÕWø õ£×ÐŽËnxèöŸc4÷ ¯*«´lã—êÈ«’þÞ&+Hšfr…^TZ 8{XëãMž£Æå>ö³Ä;sE2„„åLüæêi€OS-ýÛ´PsÉ2GÕPüH%~8“¾)"Î$¯œÏ›—¹•f»®€î©¯#G’ ­ÄzÕG&ÅH™î“‡lKŽd.¶Ø¾6÷“„¯\¨‰Ò@—3ß:(#¯ê\vMé¨? R'“¯š«ƒêdk�è’=Er=ÿoÒ0ïVz†çØd¸�ub%Ó#‡4Ú �Ç�:7Ò›vSªÚ½AW|Áò�·a-¢Ll}±þåbuø®)ý‰éêQÕm>;…o¶B‘Ù�¼·`®Âý£ú…›dX­_™CÁ_êZ¤êD2t~“»«7ŽSµ¥¶ÄT¹ô� ¤¶’(ý^BùÃãž6»x°–òÛ¤òÓxh�tyZ…Ãg¶ÖÇ”Á€hãÁ{_ði@Ý”®™˜N¨º@¿V»¯†²‘J„¿¡zxº–LàÁžlI_AV¿´ª¯§+FÒ§�,蟞vÒ lAu’ø—N"½´É;m|´LeªZ' 9G1«E-Ÿç‘$çMBZ|µŸo~æKãÅœ!æ nè–¾hª“d›ÞbP-m˜%p†ù‹0ר—ò‘Lo(˜¡ûhØ©Ù"MBÚ�Vý�Õ«�“¿•Tµhš.§eÜónTöæ_F3Ÿá¤AÀS¬ý1•޲;ºÊÄ9`"ù7` …ìb*¶Çi/ê4KÖaÔ%mC©Ö‡—¥yèªÖÆ$¾ïµy‘Ô¹ýj~߃* æ¸boBþò%åö�ôÚ#ÚÔ8¶ñwuØØÉôÛ’™kÝ&#<…‚–2Ðè—ß­"�țĢŒDaRJ$Œ©¢‘�Tªêà�Bìnˆ‘tèц¡bÇlL–T´M�ºþY.ã_š8ÏbZˆ,„V"»÷áŠÔ=ƒOr±lNÛX¥°Ì·‡¼xr}ØNÏ?ò{¿���;‘  !‡KûðCûÆí�À€žÄ³Á‰‘Šáìz~Y'ùƒÂÁ:�´yä-å³…¸ÑgÙxêâlZÛ ÀÓZã Øb]ÚocæK�~â˜z ãEÂám(Nå¯: �ò•Œ8Q´ ²v‚Ö&UƒÉ•FŒ¸ž“ÇÓ'#ɳ"·f9wB…Ú÷³ÉH~{ûáSY‚²Khý §`Ê?g?íó™‹œ¸-RAéèzTl2€Z¯(?’«€nZkšEó è+rqkQÌÿuÕœÆ:N—ë6"øèù´>PI�9²J¬2JØ"úüÕUößȕó•GzÉþí}£°Ñ¤•}BÒud*º”Їm¹líWªË„Ã"Ëÿ€nƒq>·Šmîæ3â^ô�<†1{åkȶ¹ç¹¤êCë9+Ùì8~±ü¤¤]vš?{mÖûû?æ)ÖW„Z¬÷Íå46[•¥ÊÍ]ä�á(l O¿®CF\dæ”—À2¤­7.—ò#¥¬I¼ ±~ŸÓìoä ¶?*ýÙIÊ€„Ljž² v²2:v�Ãö8�J9)k´ÕÄ'À|�²Ç·ÄRZ³ìSRìhQìòq|™€uC9Ý&žŽzͳ+»²8¥÷†Y3;1 ÜÀy=)n¼NGĦլ®–ŠO{ó!=4)„ô1 R´{G¬ÞæîXu?Õr~ß$7†¹ú>sp¸£ƒ™8ßðDÀí6å c<ÅèÚc� î-9™¿¦]–»Ù˜õþ¸]> Õ¢gΜ"ÝŒ,D!Ì×�F?“<ë§ÔäWöLs¤yŠ•NPkÀa€3�.bbÍ�£Ž?Üò¿§�TOjh‘^(‘}ÚG½ˆËU1üÈß7è˜{mß$™£ª6êã%¡³64K Ns¨ÖÍ�ôrôöç'Po3¼Šó1° D—<�rØgyµÙôV�¿÷f;8°¶íMÆù"Þ$úí"®žßƒÜ™·cÕâÂú|Ì¿Æßµ—²Bæ‚V$Q#,…‘�¨¦›uÁï×ÜÌqz7÷±~¨E+G¥šÃ&œÔ§µ²ÕöïY!’gGÐ*=Ž“‰³—Œ(ÅO³mؘ“¨î³ØgÊ)ÍÌÝMðN±“šƒÑ~Ù¦¾›ÝcPPÛEËY’Am³®þ�NIO¾‚ýwtõÞ�]®Si¾Ú’�è¢H‰ô9R–6î9ؿق%LvÜ\PÏOH™“ö?ÕãxÔÌw_¬âêÿ¿®�”¾†�MF Á’¶xæ8G/¥En«ê¨Oñÿc!C¹e80+‰¼˜4™1Ò-H$Z%V€w†½“êõìÄwWú%�¹V©rkÏä!bì6­:K±9iš ¡ÿS½˜½S2 ÁȵÛ.".ã ,9¤œ-q^õ{ñu­Fü¥9:M)ñ*õ¹Æ_e c›ù4²iD{H£YRª¡ív“äë¥ÕiÌ ô‚0ü-”èôL¥?tŒe7Tw­¥ø Ð!ƒVÎúX×zA-˜±¢¾Ù´[SÅ3< yqåüf¿ ÂŽ 8ä¤Híä^õ U’ÂFì¥sIÐ5ÆøñF¡ë|?oU|ÁfŒ¼­}ÆÚtHÂÏ_X%Æú½ÿ꣙˜ˆ°@ž‡hA¡,ÒÔQàØñ]'ìw½YþÞ0Y2¤½`"P¤&™ý`|›Æ�°ó\ OÈëÉúÝ“–zH/ég•‡Íöã 4Λ¤T{!AÏo~™è)ðqLÊwèü¶«%e$˜@ ‰+…û;¹«É9U�Šr>­½Ô‰›`�kéÂTCD í~fû0=ÚuvÀ&ºz™@5` –Ö`¤D5f‰Š7åÕ-xט½X¼I÷’N©ÀÊ&^yº³nÖ÷�Àp¯á%’ùLÈÌ:˜Ô“Qêx3 ‹,V�?P¶DUK=Ÿýݦ˜ä*@‡"1 §¾ �‹ÐFrÿL \&B¤¿ù£½…Oóçð퇚|¢.J©�ÏaÆPA)ônéÒ:_èU§¬S Íô¼6¹ök�ûò8làâfÚùþŒÚ‚!ß+’Z|Ù*tóH&*“Ïž ï/ñ¥!öµ�ÍÒjŠ•�s©’Û#]kÜÆàò¤Í³?¼~¥ ÕÁ S~9íJþõØÚ—}qû,eýÞ·ÄýM¤¯P1%ÙK!õ‹i´k\yMÜ]1eN¥ð¾IËÍ4 ™\bî7'5�GO R¬Ã©oµjÔEHƒ]üÇÏ·læAõ­\1µ=Î È\ãY”!C!SãŽÊrO)ÎÔÉ)Æ Ï;Œ£üÅýFO˜9©?Zp€¼Üm#.º˜ëFŽ SZûŒÓà_ÓÒx¿»ƒbÿ¬Ì×A©_Ž—¢|4ùàÅë¾hÃz2ƒj ½©’CïwøN»ÂLnóAÓ §òºøHlã»ÏÊ0�©ÔeˆyJ|ñk–IEÁñÞê¶ù@äüCÉr™3 F³>TµvlÊÞî„%ÍKé…`›_­#)Äõ+f‹¬s'³*ÀÔÛõ¸/+sÝ“”õu¾BôH«Ú'½í2ËØ OσpÏ–¢\ Õ‡‰JBÍíiøï\ ¾4µË½ƒ²Åއ§NÞSÏKÍÏ9ÀXÃÍæuɳ µ¯ —~uà=½oÊ5•Zr�àÓ..ÃS™QK¨‰ÑV µXoÏ`w2¶AÆfé4¡/W—E–4h«…žÝ�Iä°�ü�H6ú98ôâ6›š´à AbM ¬ÞYòNã¾ãÖ$”#Ø^„LàÆÜü/¨<9Å´ø ŸÁêRiÃ0·G+(Çü ‹XÙž_•l «ëuóõ‘ÆôÜ!™¢ â³§@v7¨IªNÈu?*è]®Q�S7·Kºí½j0ƒü£ÎzÔ*®ÌY¡úuƒzV'ógŸM'xbg CC|ÐùÌš…�ÑZ›³7ŽMüŠ@0uÌÞ‘¯Ü½¬\e/AÁ ÄWeè¼÷ â”Sô(=»›x: ºÃ8+Fq¼¡M ˆ ß3J…3 \‚ûv³hþ2çiò?^¶tœðkÏ^>ôkTfà;gd,Ü D.ÿÅ¡ÒThv1ÐÖgÁ |Ò&ÂÃ{N5)Ä&”Ü'‰ÝԵ»epq’ûz‰:Ìþ{lKmo‡'ÕA¾³Á©öí¤Ãq ¸Ôyc‹ òý5y­T�«'‹l¹qˆ®ÝÞ¢XÀ؉~äÝ��±ΙÆ[ï!°;›åWmÊè‘¶ÔæB9§Á!Fý ´ÆïqØýÙA¸Œ.[OXïzºQ§Æ¢‰E}:¤&UÔ͕Ʈiûa5wt¾ó“JÄRŠ6£T&zmß&w¾Üäm0_j§ 8ц“aLaŽåÖ)Èîp*Ïñx³Ò{M[R¯ÞFàŒÏ–híºð¯M¦çãkCxjâ £³2Ëi¿½´{Ô>ë¾bq“äꎈž²»\›ê'·ˆh B{ W)ÕR__)p²ó<Ö õÇíÂõæеÉ阤|Ít¿¤L¤þ·5ÿ×`ºµ½§êðªRɰúR c*1ÒŽÔõ¼Rb‡_? §É°­s`{).tò) 9¨&)3má°êþëê¡ý‘·eËúaUK`4Ï �{‰?dß½U&ÂàŒE€'Ô�ú©Iº?³…@´)´çM~.ç ":vÙpv߆Ûó|1 ס’™øéh_L�Ä=îŸå7!V€£9´ÿ�kƒŸÿµøÓ”ö×¹IñŠÙI½žšH{*invå± ªÀõ³¢™{w¹ ÍvĈ‡žbÅ D}Ñ�iÛŸ!9Ñø{©Ñù:<éwEØôuªôv"´Àಌleñ~rC´8¤�*aø<Ž‘©ƒr¼Ò lÏ÷¼ÅvÑ—Æœß#Í‘Ä À%Q²c´¡!^P� 2™þëô¦Rçi‘¯R—Pžtžå<¿dÁÆrdFâïØ%ìú9œ{7KЩ�Ök瘵3„sØ,å3°ˆh�½j8‹´.Oª‡˜PL‰•®g F Û¦ZW ô~.[™ü{öKO!é¼ã“áü~"®ÉЩ½%a]Ö(„Xxz*KI�60ô øòð;FÊ$tœr!éiÚ5ÞI?ãñ[Ï'–}5ÿÞV^ ×Q†|,F�k4ê¢@‘rxÝntc fè·� yIãŠ]Kî¸YÒ²ì@'þÑ]¨c=”jÃKDa¶¼ÊÇ—€I"ížù¸€î¶ýL+f^Py‰‰?ô ¶*öâûT;wTÒÔØýGuéÞãOê:²ÎBß§Çõ;Œøú¢ÐÄ3±‚=ÀèÙZSwcà˜Ì ” ÿ‘=ŒtøuªnH¿í÷îN»bÔ¼v F=·¥Êr›‰±¯Èú~î³°§ª +H“ý¬þMd-ÑqÔRùÄÿšá Æ{Œ,ØŽ×F©¬vqdÙ‚w¶þc¥ÜRĨhì0�úBìR¾¶íÝìTR-‡ýKÝq¦§2Œ�{´f½�²}Ú i<7”›A³ÛàĦ³™ª‘f@¤ÞÎì›ÌëTI»ï¿ag›ÖàÜ‘Ø<ë‰}v¥S!Œ¦mT�Ý�¨‘’ Tøÿ<®ÜyãÒÿÄó‹…{ŸüS¨Ê®«†ìä;a{ó$Kœ½¤w$“7eÇnÈtÙy}CœG#Ek šl®=­ß,%Jo?ò`}¥Å§…Õ´jLNT„<=vAXUjã‹uÆ™nã!¬özA±te¬ãyõŒ><�dø X»âAŠm0§Pœ÷å2E|æéŠ˜Há`Ñœ&Þ3ÿ4ÂÖc^­u6ëJwÙA%ÐÊ÷†wi‡^]Jw¬xøèÎXi¡Ç:ÊVăB¨Gá#J–ÖA³èH9ÜÞàÓ'õä'Ñ"‚xLÀì¦5e)ÞA®ºÀÛç\ªûHoÑ4íÈNŠò¬<âR‡^6ZCc?á‚*Ftû2O�#rûâ‹X¦'Ë‹Ö�÷Z3kc‘ºBa]LÔ!¥yМ¼‹"Æ�î|—�YÚ/Z=åï`ÏP‹zJ žg¢ÚÏÚ� Ï;•¥»†y僿è#¬…>"=ún$“âjïîœþ53Ó¸cV_¯xÑa¶£*psU˜~ªcí?NÏCt "QFÒ47,!>9,%¦¬´ƒzÒgô¿ õ/øî›˜´ož—YØ-ê³v-_«¶Ëõ§ª)‰Bh»þk5¾z(Y µ15ÁåUKl™½JÍHf>�´á±Ñ½ŠMûÓdn vA}Õïƒ ��4›Ò9¡µß´¸½hè…h'£¼F BVű ;SÉøýïÕ…å¿,Èx¤‚î¯!ŽØ€úö‹EO£äÿ¡JY¼¤©q’3�Ž.uìX#¼g/�j’8þõ?Òp«1±B[È<µSðS£¥=z¹®÷1ÜþÔ~ÊÇåª3ü ‘_p³äYè÷P»YÉ©ï¾HqÆpÃå²ïC…ôb^z ð ‰}Õ¢b‰ª’àˆÁGµ/Ä3÷å;ÐOù·EcÉ’IIâ&õd)=7BŠÛè-¸™uþ37jÄ8 ¤¾RtÜŒœŽç!ÈóÏ*’µ»¹ íU?¢£;_yÅKÉñò€ožºàòŒ. —‘P±NOlþ¯÷á^>1`/—@xŒö o9e(mדÒŸ¨Æ�Ü¢¨•xã¦qpÝ’°†¬ÇÜw];cÚ»åÇú¹@ß×ëK©Yl—qkW‰\™¢íÊ:§So�’wÉÄÔº”ޢȥÉxœðÜ^«°È!f5ó‡M_[1”™kqà˜³kÚÕ›û[œ q�†Æ�aÒìëœrÅ!XÄ¿7õq8@y»o„ xÜ{Ά Øb[\ûÔÊä›6?„“$¾Ékt[’Fµ#¹D¿ çh‹VÖeüém7©˜}xâb˜ˆ¢Ì"ˆ :” €à ¼åö4da$ų‰þ}CFûîÞxØñ ˜Š[—Çý±q˜P'UgM=F6&ë…Še ™åÃgé;ŽÉivx�Å)̉¨È� MT²Û`½¶ßA"Lr ¢JžV¥D˜NnüÈÝ�©²Ju #p†g–‹B2Ÿ«‚Ïó3Û½ïG§¸¨,#ò¡ˆ5‘6ÖõªȬ-$ʱSñÖ,ߧ߀L¼Í—kûUT«Œ+ «.»7™ßÜ­<¡~qqñ˜Ïò×ÛNÖºåÿŽ�ìP~«~J ¼ÉÊ¢»îÌí¸bm´Šy"¿¨‡¹¥Ÿe·CXê© ð�ÀÔZjŸT‡¶ÌO„zbdþ×»?¾eÃÖØÛËvC¢ÊÇÌÀ–E�à À!§ëêtœ.»ã Kji7þÐÙ €˜b»Ê€3d 5íØ¢}M¬¼ºò‹kpÙVŸ”¾¾yHFÅDñνÓ< DÂŒàÙƒM½·¼×”ux…U@Ñj#A�¼¿'µ·´^;±UfT±ëm�™Ëu6^bªý¿ B3ʼnC½Lpšc›£ž$3h¬�òÿì LÒ¦úOU‚±r"úËQÓ:•òy†‘2 0Œå2�vÐÓ|õë7£é‡¦×¶$ήÅÅîd¨TTéš:Þý)€ ü‘-öí §*޵ˆÖNʧm-М¡®»*&Å;°êÿCm¢FÁ–b$¡& •C›vu¯U€Ø¿Ÿô^Ì�¡‡dŒœïä´¸.Å[s0Áh/dÁãYÖ ®ñ¡ß-Ïì N¿Q¯ˆg… o Áu2ò9îˆ5^N¥‚[Iô�ñ+æ[¸d¬ù ‡=)<ËB¾0«*ŠŽùƒ¹ÿ%Øip‰X°MZ$ÒÑDœ-ÇÁGPß”„6êÙ�ÑÖÉdÎßBÛ›I%àø© Ê‚J�çd%ô'‚•J©!èݙٿú‹`‰Ôú�ab¾ê…3 á}á4ùÕ™•�½Èɾrbƒc QAmð–0 œ!dŒ°‹%PjŠÍïW* Õ(°×dÈS}5ål9ÞËXô³BÂí‹�bŒ9 §º;åSÁý´R¨WCz]ät­_¢Šã3ÿ²ÿð°‹³7Œ\BÝÓG3³¾I¤[O/¥öמA•ì×X¥… áëLþOte5« ™áÏ#ì.sLù³¯QxOÆ;kB¿^x@ÎW;Fè}è Ññ=žVÖ€Ö.ý�NžÇóVâ«_‡ýî=f_Úzš¼í¨+õš£²ŠiiðsÏCPíë8+ɵ»JWI»D÷su @!RßúµÑ³ð;sÚG|èéf5ÔV]sŸÙš‰4¯K«œ6YÖB}^¡»Ä»ƒXûMSšCÚA?:=˜îï„Ö?�¸ψ«xZð«Uÿ%2Pö +"§ ÞÉp:ÑV’¨�¬yÏ;y%#ê4´cù½qªD,[þCjXhÑ2ÔÜAx×GiÎáæQöÑ G¶ÉT:o»D<]¦ÿ4œ UBàG±*¾÷i ‡Ÿßf-åå�Ê›´®áÍ3P<º‚Ð~Z·ãxK¶»Z÷ب­RCR!ɾज2‚ ™ Io™#i€”O>T)Á ¥I¾Y®»¿ÁŠ·0ìi†ÊõP×àÜ]¿ã™ç٦Тþœ‡ ïáSÞ…Ë4þ¾¢Åœ]|¥wá%غ'J/ØÒe1ÖšYÒ.LԦǤ6’7ð¢ÒÞÁÀKsÅHâ(Ý/ó{cN %jy�ߤäB\SèèÿÏ=u×Q ÒÏæNÒÒ*B¤9â_½ðÁíÔ@·N‹Ôø†Õ:´©mÖÎV½ƒzW `­†)›ñK&uÏîÁ£ÿ«F‡I×’çÍULµæ­ŸØëÕò~-à«Dõ®û²/É�ØPæoè.…g½ ñ Ë«/»n_`ŒV,+âž@SãL™À·p‡47¡ÚŸ‡Ÿ.jab‘òÓ%®è8ÑXó¤jx|P3¶½Ò­ç²@_ƒ±îgßy?¦ÂôÛíÚsD`‰‡RíRíåùiY vƒ¾»”ÃSÖŸ§Wnüoβ�z韕ÙnyåWÔÎæà¨ÖEò(òY8ð%f}tc�aÃ6œÁ=o·7Kâ1gO1‚¡«R˜ÈP-í°S/e­Yä¤`§SŽ:Ñ4Lg1 Qäæ~ýŸö‰ÎÝXcy1tÄÍÐÉPÈ[YHˆ ×(Œ û0.FmO€ <àãË ¦·U¹ºJú™ád<¨ïì ×-Ê=Z¾äÏÿ`›€ê9¯©TGÜ.ÿÖ/q¸ °WMO¥kçý‘é�ÉIÊÜòß=R‚Àñ»œÔ<`�«ìè~úXÂÅËÑWø‘ !R^Ö×s•,s,D—oL ác+Ü—€ÎV”!Ú–›O‡˜)žt’‹éöÇ2ùN´�镇�Œ Û6Zùü¶n(M'—wüÀê¶m¨¤àˆ–®‡ð¹¥Í�œ¢\nÖOÃ;Š èrüc„VAË]àßÄÙŒ­dX>·ô :ð'w³ZRw¾ZrI±Àˆ¨=)äeíð€#´d!|ž\=q!¶ 2¶åä ~?–xé!ýtËçµàT=׺³p¼2.E—kc ¿YÒyЭ>_]¥¶‘»gG‘Ë]{ªî~Ê$£2õîÊDÓù¦žyKŠÖü¡â6n–÷%Pݽ dþãYå‡&™OÉ™m¨Ý¸wÑôpO¬±ù¾^š‚xŸ _;úmJŽnƈªEõwšƒ¶õÓ°ðRçLL qòRÕe:鹩ÿ_ô<|غV«±j¸úÛx£¹BÍÍ «oçÈ//8–�[’Ž–Ò<ãA89²ÔÅ8"ý¨n…ŵ ’ÃËÅþ)Öüí•ÙHÏ0LÈ)Ó «„ÆÝŸ~ŽÖëPš‚Í: áÇy&h�ÍÒAûŽ�ÆøŒ�ÔrqåAy±û¡µLÙ�[]ýX`u;• SKeß ©§& ôüŒ8m!0ÛRÀ %Ø5°QU!b#¤è†áE©¦‘=ñx÷v¼ú½¤>ÆÑÐ$búµöÎdíMÂí~ÃBðHø'æ¶ŠlÀ­{íÊ;Tä³ •L_.ÀÙm_!¿«HMcœG¢ÅŒ¯€ƒŒœ?‚c6äÞ n‰�S¦¯»ÌV«©åiü<æ¯.Òì}EÕ™…/¶ÀÔÁ¼�÷ôʶnLÃ'¡7kÒ¿ú’d?|f%z;®( ʸ½ŒMÝG§,±ö*± Ö}]pÿ¨ ÿø*蜈'e¿Æ7¶,:¯2‚òÚJÍKãðhºÞÈ^™NëR�•°¥u½‚­ÓlÂu^B‰­@7ZrÜO‡2ètó²×ÿ„RÇ˦V`êš\Ÿõ+3ž ¼á3A 5ì™; Áýü| W_‹£CÙÑÕJ08ü×�_"¸ðÿѼ˜±ð“=�jÍÊÅão3VdÒÇj�ÁùsÃÛ›™�¨C‚UŠ)gÑc>rgøØ×ÿ¹{зH_þ‚ª¦�Òf· ïTî*äv™›ôàw�yN'ØY,k›çmz©Ý�'¾ö«ÙÒrÇ/C,Ö('P©[?§k’ñ«ŸK'ç·Ð´·yÅ“í¥^ºæ¹ùžúpIohã¡o@jHKìé�­Gg^l‘�»vQUgD4êà+Üê¨Á,Ç쥅Vp5á�qîàæÜä�ù�ዯø f>­?ѩڧ퇰&4GyØR*FsÕgÊ$*]€C "øgTR6pŠnÒïEÿ{.xÿn³×Ô—tC~úÍrÄЮ©Ó›}'Y@ÒÕu’Ë�9öc¬˜õ�¨É˜)sÖ²$»h3W�L�$é ˜ð¶ë&,k˜ |Ãu‘nÃ<4Xø©ÈÃe$ à²Ìùœ¼×³A V5«¡t8¡aVàú\Îü£üý£�л֎Ï)÷Hµ±>ºôÝrž”2h+ÎeÁFŸ¬r<˜ÇZ½òâ W™©xUi�¿ÀŠ‹G¹�_Ђ>pÿíßQâ,,�»¢å¢RÐÕÛÁö>…ó`“ªYŸqºµ÷?%˜mêglB±d)ø}ì5"Cªì~¦ Øö�T¡›YÂ<=˜¥<‚Mô=�:W+¬PŠ,§�‹€ Ïõ¶ jzãØ³2[zû�ô㡜'=\[ðáËk bìôÞݸÞ,Žïg;ÍÈË–UËK–çÀȘ<™í¯V1ʤTt«+ÞAŽŽõ\ªª[”3–¥õMí3¿ÁYÑz:N‡®P0nÇG9sD±üë$Ê0‹y½f¨*E-¬œÅ 7¨XEºè½óv©-�Ø.ù–´,*¶ÁEÆoP­­ƒ#5„2&Qy„0ôh!\t�Ê/¸+4ÒÌÎ(:GU#ˆÇ~ Ä-n\úëSŽß&à7w)$gBPÿ÷ç&åˆZ‘Í}_$ªÒ‘£„GUâ$Æ‘O{)˜Ã ‚ø^oHçš.[úå¡Û©ß€L-YÙå,gL£Í«”¼mËš��³à»gа¤IÎ/HšÝ�ºIìXQ;¾ìÌYóuB¼$�ÉòÑ6˜ÃaD!µˆÛ‰nWÎî_‚Ü´¨,ùwä:ˆŸ"ô>nAÜ"ÃÉ �úX„ÌÑ utììð"éõW�ºoTäÔÁ}%…e̺‘. TjÈбáÆYÓò<ôMGjÉ:5Õ´¿5Ûý—fýpK¤ŒÚI͸Ø|BdiD*GS5ô†Ë…#î ˆ3Ý2´�±¡ ‹uôîK ±Õ͜ӌ9tÙëÑ‘|�{)† ácdJ^b戞_@^™‘;Ô ­i1½üa �°îkiô(•D7¢eqÕ %Ž ®.”TÊþ*eNá‡Lez‘k§‘–¬ÞÐ Mê•Ciž·Ò­¼@:ûväÿ8Ž^Þhî„KaŸÂ¦²š/þ\NZØ\�¢äKP´˜�>-„](nuõy¢raÇQªœqC„ÇׇˆÕ¦Õ­g`”ù�èA¡}Exr§x2‹Ãæ0E¢B Ë‹?y,hxPùÞ÷µ ¦ül>VŒÚ´ ŸËüÃæá´-ñ¬÷æ”GZ Çy‰#„°²žJ Zzhè™b;é }.ÓŠK’¾&„¾¶D�…ÍÎ�™ì J³>3ædm€ØÍP>åD�ú^l¦Ï¶uW›åN%¾‰êõ7V%â“øË DrÕ=äÔo½"¸­K”ÎW‡W«ú³­å ²©~.ò­\ÝÙ U„ý1u1AG‘¹uö,Õ&1Bq³ÐÇ’½³› næ“#¤$Q>´Aãô«¡Ö=k¡ž�J½èàK{j«4T”^ÓNYÈ<ý/�š''>à 9·ñ¦'ê@à ªÉ&lÿFó+Ž_wQñÈ ]ð¬IËi:În[G7²# ?m{q{�ÛVY+äÒ+yUgGV˪JÇ߉¶= �) •D·ËZK±[\86“SVåçÚ•gÜ-Éä®X8—Áx9�,*ô¥cÎw0êý•]#›Q–"‹…ŒƒY´†ºõè¡i”ާ¯¥K>г„6£™˜LCkš±xê«„LÇ*YúñsUŸ‘§‡Ð�·ö©oã\ü¶'ö)÷>$.ð9Y��oÄ&Wï=?›îΡ7Î2�h ņƒÀdÂ�5=�šsŽ %Å÷­*3bdë/›Xm* ]Ë…å�yÑÐÆÉDm<8v½7,ÿ]?âTH5MæšÌ|P4þUëHY[pe£`3wŽl0(É}Ú‹VAh~ß÷Ùz¥µon û$)Q]mýg’ÂFæ­³¤í Ü•¶îvŸåšEÕè#:xZ†¿[Ç>óóÈsI|t½DQ[ŸKuLÝtyò6! á)Es³Ú|³ÀñÁ!�Ùñ¢dÚ˸·¸òdÂù@*úR™¤1 ýPkªk„*Ø­lGï ËÅZxmh‘™[¯w:t.¸ºZšýu’©n-<3Ø&{*$¸øÆB®¶W^º—|VøÎq1cÊÈ“™€äûÁ\*ÙKkWZ™ÅÞü(—D.RbôØ…îŠ|¿B—,o1B¢h LôCöõnÕŠÞqònÂiøY¦%²ŸjÃn[e¥c„%¦i�WÔ!.‡ < \k ÜÄȪDCјFG�¼5gϾú‹!²÷]Cò¹Ê>ÂÐd†ÁnÞk„Ð)¥Ý¨çb›3‘ŒLƒÍlt·#Ëå¾¥¯dGŸ‘%HEBJ‰Ã)@5]’]1BÐü1&Éç„Ë^ŽUw&Ķ Ò ºxI#1Zx£|¬ÜANVî¢Îüfcã3†LaÏF›ŸnH2Ñ!ãÐe.¯^/õ“þ)Íx.£?Èa!�*ÏÝuRpÛ£�‡}|LÈ;eÜEfÌwÔµ\27+I¼ í#sOO-`�© ;ÏI¤(¿€ð> ð¢í™ëýÁÉ?€³wŒPQÍ’70ým$¿³U²�rg“ ÄR‡d ÄéÌÆ x&¨±þ»¨8?ì–BŠ‚“àà!³­®ï°(´èÑç·j KmÞƒ üd1öº�vý£Åp¶$H¼mˆóbÏ”žfLc@®e j„p,rbйFêX”UÏ̉Úìf®Ï÷4L]¥ïiöˆàl á‰Hד'ùôø®c?:z~ô�ІLµ'.Œá÷gSDüU•©qsœPûH�R÷ìÏ<ꎼŸÈ¯´-ßÝ·ºŽfÛ)BjÈIþÂͰŒ^¦áÿì›öubÒ&q„�îq¨êúŠl°qvÖ„ˆâ2ÌöÞäcš÷RþFõ̧¡­:Ž�²x<·h <ÛlÍ¡®¸©ЦH^¶ãò#2X¡r7°3ñM$¿V½­vûÖMii¼~`Ñò¡8Ù‚ê½õ¤A*’KæìZ+CMÞl�¶£ÚÕU«ÈúÑ£³âŒ{Þm`1«èËÕûw™Í¥PÕÅËŸ¯ð¼SAØö“y¼s“h;/®°Ùiü¶üE‰‡Ì3;}„Aˆ.jß‚õ˜9ÕœW9kz›åÖº?ÍÂúf)�ÀÆeK ¥2¡tµGdÁìu¨Áµq­äZDÏ;‘¸õüjd3§ËyÑŒ0–Rw÷çÊËB›öÌ¿¡[é£F©ÂÔx~ŤuŒÌ79AýBwÖêß=‹°Ø^÷\>(Þ¬¾ÁAðò+þœ¢8íÿ\ŒKθ‚n<§iÿH��'Èú1sIà4Hõ¨Da@h`´Ð²Â ´ïè _Š+é¡y"ñ7fá> ?=¾ò“lû{Ùsí ÑXh·³±caÓÁs½´#ÎJ•-åÛõÖkƒ?Î�r¢¦Õ@S?7;L;î\mÔ ûa± U†_2I²±›„%Ãý�šW9Ï~£úÀâ8?HüÕÍÚ‰0³ímž”r|0¿ý3sâørðzN¦:÷Ì¡'ˈaµ½CÁ¬æ4é… uû{ÏvF#Þ«eÓO$86a LÈ탤(Ę3[è'±6œ­ü�dÒ õ#Ð<×®Inz›-§”í3ЬÉýbKUŸ–¾OsïZ»#™ZrÄÇð.eÃ�Üp*Uï3×ÿR'Ýæ–D®þ¦×†æ]&åhª;Lñ$òÍŸí·Ä6Ñ`vuéT™Åsj¹Å:þ�• g@c!"º;Ù@ÅT‡«Vñ`ã†VÛÿN›á…�FżÈ·ß�Õ’W"ÌšçVÙ>µ/A�®‘ÑjX«ãÞwÎÉìÞE*Pú‡¯Xã“uH�±¯˜�–‚^ÄÆ‡kzmÁ¬ˆa#~ïN‰Š#–±>û±¬¹…ÁnôÿNÎ EGûê¿^×µ¸}²\½lÔÌNÌáÑ)U Øž72#_xjQQNÞó—ÀY�ùÊôå³ÜÏ´k~Î;œºC÷œ'� C2Õÿþuˆ?ù.AkÖ×Ö–Ö-¶ú;äaûË«âÁ µÊ‹Œ�Ä¥Jí&¸¯Bh«¢�ÿ¨ˆöì¥ýº†îÒȘнbXlS6ÊV`Ý®Õ*pL×Þ¤É{$[[yŽÏÍê¬:uðæ>Ž"–ÆãBt¯<'õþS©Z§‹™¶â¨eíZHåÕ©�}c+.ØBAú^»õeìJ˜o¦ÙV܇^½ÙJ#{@°ßeX>B-G$íïvK íË ìEÔb¸ffÅõ»ì-Ù¤Ùã†#±éKå` ÚÑ—k=»S"×g=š˜,$ÆÅÆÑü}OÈ�˜ðX¹ÒÀ®Ïu~ÙÃ2Åzv)ªkÍV †Úx®öØ#éݺ™bŠ}~óz.‰ùþ{�¦ë$gì˹ Hö�t±Wßyî.õö;kÌNãcºï�œ‰$JìP«fþ™€}F~,na¥ÂZ͵Ý@YA³æ•W 1“æ ÿtgãÎvÿtóìÐ6Àç|ÈèBþ¹€ÆicþÅå×jQ A%ÀO°Æ¿¢xüºñw F)ÌÖy™©xü~Á¡†ÈáÝ¥B·l„&?B_C7X¢àr|¤÷Åq¸DžÆ{¼Úl“&þ»†ékQeF1z¿Âˆ¡ôƒ _,ž›5%WóèòZ%=3H$Ș<"{ø#ÒóôZ¬ð`­Ùäx_A?F}x¨¬ê¶-c@3 ÓQÓ=Íùwððäq§ 5G¾M⯕ ]º†ƒƒ‡k'²"“EÑ$ê[ ˆôLÜÙ±ù\wº ú…›ãªóIØInä�D檪Þ^§)ÕÅ_ t¯¹÷­^8¿Ã1�ò›e6u¨vÿs™Œ`ÓnˆKÌx³Bö�öj>�ØÎþuø\“2Û’‰qI}ÃÐ *‡€ÖÌ �âµ4Ìø½0ò؉r¶¥øAö] ‹{¯ûæàÄJ6xT°tšL¦ðEƒO dzåêŽ'¬~š¢z P2ýúyV#üf_Xj¨�A—I{æÿÑd–:èÚÙZéaDÞú{}ï…ólÕ¼ÄÅQØ…8¦ä ™áˆ}ý|§è'ß°¼|�Õ÷ÓåÅÑûwï¹7æÿNûY Ih÷ ŸQ4,«rEòF’LmŽ‘–ù·*@,ëþêKRã#¡Xå!ï^¡ouجx�{Þ^�PàgŽY"4<‹œèÎbB„Gòv^D¼£ ÿ……Sª¸?L‹Î";9:ägjþ†D¤àÔŸˆ<"›$¥“­eZ.ÃÖ$.>OzLضMs ЛœT´`9ç 4Ø×>_ËÞRêÈõ ·…ìXp%d´p‚ÿåiàèzhN®æÃš0×�­…D;ã×ÚÚé²¥V62Á q~%j�â½øz<ßÝLz8«+pÌ m]�î51á4Ú•Hû˜´I‹rc·ˆd¡úy 3ºÅŸä º/ÝÈâõ×òVeX_Û…Ù� Wpßî;®˜mì£õ “�+úÄCæ›âà˜»ÏX ®1ªKÃÛÈQB›�±·¥Uò·Á­6·º•êLã®|ͯ@³×œ}bÀ§ô=· gþãG�Óz®Q%.@ ¨:ÚÔ×B®{s˜¾W&Ë-É‘À/áE<7TÛrº]×ßNyˆ#P%ôb ˜×Y9\çþÅÑbt×l–¢oé•,~x%9�–<ú!–ÉÏâÈBV=óèûyOµ6èŽkÛšLIÊ[Oã©aªÊ5÷Y04W —ÑÞŽ€Á /O±ì Êzi¥;‡‰ ”“Þ‡úÇë`èC]Æ@n#†LÕ•“áûìvÜQø!º0SEBRé/X<“ú¨tÖ${¾ˆ °î5, ìAë-ø8¶A—Ç~ô.Ñ6¿º#9-T͉~<«9ìˆ)÷rb†¬ã˜Ù/Þ¯Ô™š ^ù›*IfuQÎLÅà°ù¦¯)¤‡FFÕß8aÅÈ¢ þvšÐ[be§¶ iÔkñ+. â 5{̯H¡X•‹0·ÅŸ³ðó !”‰‹6Ï4nºs{½Ùü®;QåìŠÞþ%Ƴökg‡�Þ:³‘ÞàÁ"¹(ã´¾ÍNiŠŒpóºAuÃXFVµkB(‹6aF?Â!Á¨e×þêUn‚*�Ðâ>¸CådÑ‚†ïQµ× LNW¥3Éf ‘‘7ÔCB‰ÚÉ�ÎÁÛ>:ʔܷ ¢B„­d2ºcëBø ¶ˆüH¦â4°_9»28'‚Ó:–0ˆ‰[)Ž€êï’�@¿ù(&ã9™ŽF€LRáµ ®ŸNä2ÖqÚ= j޶k@0%Øþ6йÿÐc÷v¸Ã“f¥oËEÒAÉòwdk Jß»#3u¢°†v�·ÊÀt�ëw�¨¶H)bÎEx¿&»Lz7ê{“_MçáÑ –Í’å<ÿd²âFî‚Ôºü?ù©|;òà- ÛqX�sÒ,è™,¥Ø,>òw‡l‚¢v´Ë8­Òôß3å:ôÄã÷ {9XÅùOµñå™°–±+È9áb›ö”}À¸�û¾:½6•Ût©Ÿ¯÷òÚn*Õ¯¦¢«Èu¼hc[XÀ¹^YkX¾ôð» 3E%¸åÎÑ>‚�m–òZñ�³ö¯›ÜmoÖEµº@Ð7õòú…, ëÅöOo‘÷1˜4Ÿ#ÎM¡ qѶMIÞô^êfŠ ™ë*/ßG»(»ë€w¦]! áR~Aˆ!ÖÁ‰£T¨ éÁ�%g¿$�Ó£€˜“ÍGVŠ@^<³GæÎDÜ®cáŸvôwK®!ÿÞ:ìÜ� èeÓß1ãn¤\™^z*4,cˆh†ƒ£�“ô'Ū bƽB©Èr3|z5^Îþˆ%·OŒÀJ\úØ:«<»~ÛpP­Ðó$ñoÊ3æ�Ùï·%^¯@~ü¥1©Â"n°uø¢"†'äë¼Ð¬—Zd¦’D@%étÛßË9e©úšjÌ‚¢íHóÇó¬øü †Ð1l·ùƒx}­Á$åó!­tgª™ë¨ü!�\zš"®z†QèItå’§½²~2íý4h3M¤«»çÝKb¿¾¶ïgûÛcì-(V� )Ñ›Åï½Ô­c¿. ¢êí1ò¬AÝ{œ¢×\eí¤ÍðâØ pl륞\0�“®×Ëû@O“„'ÃJÀ´üµ!'ÿže‹‚ÐÀûM?¸= ì¼0[Ö$¹†áu c«y®×›u§}bXÓ Ù6e¾×åª@…(ûÔÕ¤{¾ÌýíiL6Ê ùêfÂX{Ä} )ä'“•ÉGÆ(�:.~³xO ˆXëq(Ôºöásü`9¹r2Ú¾-tsÞI=ž½3m8rÈv_6ñp6ljür¡ë.­ÿ�À>÷=6ùpÈ5àQ³ÒÒÎûæ�*df -�!– �ErîþÊ¿v<–9 Ù­9Šh0*Ê·¾r¾;h³0r\r+œë½¥lÐ#\Ð?¯8›å”¯CqÑÐÏÐU�@±Ö(¸·æ�.ª6‚–¥å3…¡Øt¹aÍ:á㣻ø3Õ¸¡g ðK©oïC†W�b¦/¹×áÞ¥a®&Â!g¼±i[×K¢ �õ˜½Tˆ  )§»"ç¨(Ì|!Ÿÿ‘ÑEž%+¼Õ]KR™÷°À‘j®k­§‚’)©ÅMh¶êuVÔ(5GiÈ[7&á5Ûi_·)5¯”Z {¹£KX¢jko±¼mSGæ™ò}ø>{80”Ø'{¼µ˜_Þ¢¶'¶¢uÝä!+wN‹S2ĈŽÃõ;óôÙlHU#T¯X¨Z¡Àû‘°ZjðZÍ7³)—Ú¨É(„Ý‚j|p\pÙĽ—Vømšhb"*PáÒ€2€ÓAôÿ³ ×9*/½Tc× Œ±$"Ѩ}ô”ùk_rÌ\§£;_¦vUòõ¦HX…*�%¬­ŽÆý$ìX €ˆÿëx ZHFiÚCÒ@í„s]sSbå%w/Ã?²Ð¹çÉ�öÞ ÿ€Ò1~agRrk|ÁWa«u†ÈâÚ!®ªß&}ÊÈ(˘Îq3 â:ìž .3m\ÙX†{Œè7œC.%½iØJd-El…ç„6Tƒ½eñÙme™_FW£” %öÄdlF�ø]ˆç˜°ý �n'€�G>‰ÁqIàZiã ¦ ª8KÍqD¿Ùºò|_°Õcv�9EX é(¢1Óyka…ýCLÆoÕFÉÄR²ðëXSTº’8RûÁÀ¹àqù߉5Ûl¨ÔSRB'�®šPz¦¢cE�¹�¼ä#C½Õ¶Ë¤&®¯eÝÂ&ìl¡ÒVã“y¿–ûUõ½o¬»ï…íÊäêýe ‘yÒï¸j¤'tfß'ÉÏÐOîÕ<å�q.¥d¯×ü%,&3 üMü[ÍëSœ7ÇÝ›A˯|/¦ÃŒâ*㎚ء]*Ôè‡M¯ÜƒH‹$´í‚U׎cnN»?Ôºý#Z€+•¹�ôpq®xÚÙ¯Àý ï„Û¿4<°BÄÁ[ô"+'`¸5|Ù¤Añ9šŸ -ÏŠÑ;ðÞ&Ð}»ëÒÍØ· Y}©8feSÏä8-ѸÂe„ò֦ޗëp`¦]øŠ-ÚŒ(·ÞtDA"õ¨ñÖ”Íd=.OˆÜ*žµºråg¤½â%Šê¸¿áÖ¢°Ûµ¶2m—¼ÌbOI~P›À‘Àã[å_ÎÀÔJ?%�Q' 5ß>‰ãš•QÁ~üz»˜Nç’m4-ðKme`‡… r_"µÂUô"E08©Ív�Yy^î)³Æ1‹‹Éï9Ô@m æ3Âk'|˜¬Þ¢ü È .1U+Yc(ˆ‘& &�{?É¿¤Âð×€¤óÆJöÎîW‚ÀÕ‹9³3_›Î®‰j¥IÞ“ Ë¾”fCŽCÿl¬�Çbdßqºô|àÒäµA})œhÀs†05ûÀ›:i5Û.�¿â±2›Ò€á5æ†6®€_˜ÔôŸváŠ�v²ivoÔýÉû±Ì }ÑÈ�NCc———=Ù¡à¶4×åb&þŒ4Æìíä?>¤�DZ\3ûÎàõ÷c'[î,·<2fU RÒ8!®¬Q‡E#a&C;$›hfzF‡%+û„VøMÐÙEK8#¤`~\=ÐèáÕuÐù�À[åã§ßVÖgZQEÓ f‰Ì°Ö´Îj÷¾Œäþ×9Œ™%}ÝnHôŠêãñ%/*ž”ų…>̪�Í Þtý…‚ºß÷ð‹2«^®M÷v)A®ñù;àšh´Má&èØÖü…ð¯;@ÉS0€ÂC›€Y DšÕÝøü”¢+Z_1r|1îÁf�x(9Íúž0þ�ÎŽ?ž?�‰Nö?Ú(DUx†Ø‹bÞ‡C�› �—WMŽ~X’æGK$6ÉjvõG(LÉYAEªº•ƒž¾5YúHœ•]Ê.©Æ1ºv«?TPï*2fŠQÛ:°±í¾�„ÕÓcI¹Dç»ÿÈLÝ7f0œÍ}œtx¯«;é½1�uJ�W"Ó_ô�ÑòЙ{ä×¼¸ì`°ºwäý¦—ù» °Ðt²¿úHIÞ—œ†�s @ÆS}C„ìãßÇ1£ÊwF v$gFí.$�ÃÞ‹XÇXÒ=É·lÓë�à£k+ƒ€[¸’"—Ïü²6&ÄV?:`�ô@å™{¢˜èÙFµœRäÂúû’¶Êub«Žé‘$ú˜M<™½[Z±º= ²^õë®�æ§×È‚éÐ<,®T èÀÜ~Ï‚‘Š�Zæ¨ã¦âÏÚ®z‚Ô«Mºýó\ºŒø7“…SŽçÍ‘%Λ®×E5µ8-ƤT!ÓŸQI Ófga«æ¨ç¹H¥Æ} ŽÂÃy8Vœ`X"^f·ø—Q_¥bÕPë‹+x±éžT�5k¶ÆÜgÏ>BÌDðú þÅCNÍý�?ž 3$ðmC†Îò;¹nH IqÅx©&MkÆ;97ˆÃΕ‚¤Ï+/ØflmÓÛ ‹'áYÙTl,…f²Å‹1ÊNŽ ¨ì´0ŠÅSÚ2“#¡W@©Õ0nt3PCRzÓ™þ¸Ív¦Õ¤ìk•5ÃQxm¬××ÒwsÄOÁ5s�Ëâ9 +;AÄ ¼(“G 7:”pîQ8åØÂt&¢Ñep½s�–X©ã óÀ1Üï“­*íIhÞzñÚ%�rjzÙkM,M²CÝ;è»]Nˆ:%Œ·�tLK^ÆPŸCϳ½ÍI·³>Ùv€³’:s˜Ó#,/­ÿ àGTA²^¦É1AèwTI뢃ô«ÿ8îCR¨E]†j“Ù³�X³¤µ¯ž(¶# nÚq}‚=ƒÖ*Rˆ(³™ŽFCÀPdR÷«if6ÒÞ*Dž;N\ÇëÌ€Ò¿,î%ÑþC¯7Ì€vØ×àiA±è¹>º-Lælâ‘U»%ÃzÇÉ{µ5ë�Ng‚ã°Ÿž¸5µZ’\·H¡*€Ö˜¿W/m®nrHè¯xË€�[ËYÔørüzwsŠa‘<ŒrØË�$b *Nß,‹‘IõÑ­²¢gªC“¥“õ-?âÙ’ó»&’ÂMËÊÒš[¦ÑiaÃæñVõÅÔ]”/Ɔ¹ ³¿"±bäÚ´ö}2�*yZo®ºœ‹SK/0Ș|ÚûÚ‚=š-øœòùFÄÙåïÙ±ê?ƒ^­ VÑv|$ D”¼¾0ù%\^±£Þr1ìoa¬ùΗ�EZø�TØ<$+ѺO~X¸ävužgr—Iú &8¡;[ZÖÞ¥§„I[\?˜Æ�­�R2jͦ²Ž§Ã�»G@„w¶ù@‚1ªn+1Ì‘8¥Â¦ýã bBœz„ü;EJö”vêà ƒ£È蘢„¶Ÿî]=÷RßnºLàŽj€°C‚@Ó‰œ$tþ>¶ÑìÑé²_ÍÖN)þç�[ikAdA ²F…Ù=BòNwÊÖBM¨P!xaH=tx6ŠS’|6ÐQë¯'��™›o nöñ'Âl*.,{š�ç9ÍÁZÁX^ñL+¿dwú+C�?ÌçàN<-!Èaê˜kõZ@òÃ2ÿ[ ª2Ž<"£“à ‡%ÃÞ¯ª˜Éõ$Lªøš‡.ºã™Hc¡žu7IßúXq‰oÏeðú¤€öÈ_S —6À›‰¸ú?,Êt¶ìDð# /õŽË�Û†¯ÁS¤>ù~‰¥ZNS¨þÄfW-Ñ�n®qwë?'¹–‹}Ó2RiÈ‹-•“TÕ׎¾ÇzÔ49ø ¶ø¢Zøå¨©q¯®ìÿôé¢*Ñ%ÚèÏ|õ°;Nø,½^$*=,OÇép;n¡ÅŠL�,ŠNèøl•`ðÇpçf{&eÓÃ!©XS¦âM‹º˜H‡qº‚A�áQIÝ7k\É¢"WÀi¨}„ZôC£ÐZFŸeðLÔuùåEÝ}_»Õ9ÃwÂÈ"®tÝ¿ ð¬ŠÓ˜)ÚUŠinæw$hêþ 6£� Aë4 غ¶×hæŠý8&=çm¹¨É¦¦Š·I?ÙÓ˜¢núúð-¶ Ë¡l7cÂ`�†ô€‹«üV‚7"„ýß y{}†…bò·Ô/ŠÜ±œª_ž›o2Õ@„Œi§J˜+ÄN™lB—NÁ·üÔ´‹@l¾£¸b<ŒÄ®üK>¦„77Kb“1]/¹Û5¿„¹Ç|¿C¢ÛáíW˜T=ëÆù¶û2£ølëÿ¢ñ—ûB¡ÌS/ …IV@,¦·Ò(x¸êM8ÙûŸ-[UqlZÊst9~«küºdÄÎ%�rM§ƒ då9 t0ª›»“#Ë~‘„º¿ÕMž 0¿¿¼lÝ<*�‘d(µÙ4ÓAçŒñE4¢U®PR"Ørc`F6Py㨯H“ÃP9.'„ÅÁ`¦ã'øÄÍÛËÛtλF9¨¼rNy€fEô½i{ê/€¡3’ßl�<žÿÏd•¨îòÙlDN¥”Si›Ìš– G�bÈ+ÈÀÿ rÚš�ï�>­JÔsT·Û½Cú\ßðKx©k ³R)ŽKÎa©™Ç¾üˆ§ãŠÊášÌ´}ìLD¡VÓ·a`”)Õ[yÖ^³ûUMV"#ÓáKÈO>ÔNÇ0Ýfïίð•êâdï"½bëðš|ì©êË•AåÆßÌñSïó´ S¼)7[@Ü ÈJmóšR ‰"Oc0Ó!iæF•xnB—NudN?~Ùë)¡€0-¾ÐÏ-úƲê‹Öã×kù7ú[ÎñØýjP8¼ÉáËŽ BC‹Ü¸FëùðÒjßäŸ*JLƒùÒò‰0ˆÓÈ"´fÒËí²<­Ç{¼0•}9lSg[Õy“ têKw¶\�•ò« æÛÑ™í•OXîÿ65tsºßÙ ¹5ÝÒT£f‚âå¿D¥Qf ©± ZêÈ&€ švy~ý˜1eñ 1x£{¤×ú¨’µ\(·�§˜¿«Úšä°²½ÓG¿UFpÜ'©(«Œú�{� ø‰¸p;úÕ³¬—Š•Bö§¨Ü. ãû^G“Æ Íïÿz,‚ÌLL¥·àéð7µÈ�ŽSjŸ^Â�æ÷4�›�¾Ê¦i_ n°4ý/êõ…@C!]ëèVÒ™â4(ïc )jÝ×c”^%e‚nç]§Øú“)ùÕõ>þ†�ͪ¯¬}XšrðS"®he ¿!e®Á§ÊJÚe‰£Œ“0íüeº ø0Îôbwòå�—è@Õ8"ЯvˆA W0àcÂqoÉ‘�(¬‘q‹Ì»�3G¬�zÀ+¯ýú<'úù#þç»IIœü|‰¢^>mnnÇçMLoܧÖÍËiÈØAÄŒ×?í›JÖýÑdìä·"ù¶Bˆ\ù[Å;xiosb}†jA¦ÝùÚø›?e'#(_u·ÎAê ‡73«/b]o�=[’ €¡ö £{¨l ŠAZÀGyâä Ä* ìê§#ô­ òYÞ‘4` pW¥ 0Q"»[} ë:KÕäZ`-3üà­E;ð=Èå¦ývÏdðž�µí^ù°n®‚ßd®D!ÅÇÜGvN�Ù‹|ì¼¹ÁÒ4OSŒ×tc¡žqt2Ge\ì�ñcMÎy3Ü ·¹ê‚e©r) fv®™ûrí&ÄÁμÿëOèI4žD]çy?ÝñÛü½J&@¼ î¡�/å:Ï=E˜~ËH²DÊÍü.Ù˽A¿B°n4e̵•ÖùGj”‰D:ék®ÕàhŰÃG¼2—û�]¸¨‹w…Ÿ ×nÁszŒ‘Ödtiób¦$å_ü¿þ˜C•:µZ $•ÆÎ]@³²˜p>:4z° CåàbØŸïÄŠCßEë—}ì˜ ½ Nƒ¢ePP,缫‰¢ÑÕÙ*ü7€oF¨é ñ±ØÉT³co;8o·ì Sj–X\Sž†2\\ïN·=~m{…¡TyHùTH²„:1.fùw·à̼ü,¯þ*ˉÊޤ$nÆÓì÷1t%BM4¶‡¾¹™3§38©XûðÜ€Øü’„å‡&£áÐŒV”¥>„E²”J4þ Ä®`ªhUˆ5s©Žî5ñ¥Ë Ò©„Òr±9»c0B"hTP¸?>ðÜÃĨ¦y*ø“V¢ “�ù”#1@Li7(¬^>8ôIYº~¤@Àùùr: lÀùU�Ö £„�vÇ1ÿföS%’¤Iœuq²‰`=¡+mQñMK©€¥Å¯Ágc3«}oãmj?ñ�éʶ Ä…ÀýžÑ×;Tvû�ȇ7Ô´tu‚còßìãîy ñL*_£ÂEU7[ÈeÏ—Vþ÷²×opgX6qS—/ðBÝÚÜœ·wÎú§ç±Y�LDî ¥yîS)Ñ‹’&3Éx1íƒÅ÷’snߌ|ô<¥‰ÞEî@)c¯ï9”0ë-ÁL;„ÁʯŽÝî�¼G}Ä3áÆkòãšæQ@­¬=ñ@«UÍåIèX^w¥‚ùòYæùã¶Ãž9Ø„èKÜ:;}É}™xmêàçYâ±$�<Â4G!`÷@mùĈ³úe› 9‡˜1´)‹ –îMAë€$ÿE¶ØDö‡ë÷mYW{Ôq€¥ûEéP%•[ˆ¹€}±vli:�:/Ô¿_‘ú”\tÐ'ΧiqÄ‚y–0õÎSO¼å½îö}óš ìhÉ�¦oõ)ƒ/ÑluÜMˆDómî²A¬‹~EñÙöSyu5†9\Ã#Á†C'ƒ‘æšwîâ’˜Yñ{Põư8³ñÿõO[5m.”Ž`ZÃ?ê)eÛy;±ãÅóÃkbÉÑ ‚þ /“R|ÌíkÓÇtB-Ýa‡Œ[Ê[½%æjªvCÆnÁàóÌqÁßÞÞî1køüŸˆÿìâù„%µ ¹Gk1™ž<ÌÌ BíÆpu ÒGR)cAŽ6>ÁOÌ(ÑÑ`N³¼‰TjàtoøTRX�lLqÖߪ)Ÿ+GÉÄáÓ×'ƒºE×ð>Ø‚‘ +}£çˆÔ~¯³e¹_œš°”?#vMLÌBÞ¾dº{PÓô-G1ĪñHÃݬ·œ†Ý‘€·Iî±Y^{�Š&õ$(YȆ;#äF½ò¡ Ø…0ób;˜¢Ïqbÿ/�TT«V¥+üå.^ ™ÙÙ€‘@YUÙÅ�3YÂ1Šcø¸\^)§ªð=ìÐRÖ_ºîb`•nʇQqyt_ào¾ø{ÎG%¨€4b¨U¼SË#º´Äâ Up3<šô7ºš¦ÃˆŒÎ– ð—3 š÷¢Ï夊Ûÿݾ‘ÃâW¬Ñ~×, Ìôn_ºË²X‘>5SSÊ|Ÿ'pêYYžQ!ú :c5»‡º 02à†”ù¼ÞzÜâÀ`=ºãP8ET ® &�ñµÍ‹¼ Qœè]…5�CÃÚo‘ˆå xr7ñðÿ igrŒ´åŸ°°í²~�®ŠÌÔâî»qhFE² ïì†P¦èñf‡¾Êd|iH¡ÀڛŴ…*žP°íݳ†ñKƒ£ÃøÉÛ­â‡Y·Ù?z­åÔ>jY{žÙÁD¿z­¡dŒ'ì¡!¿6ú2ÜÄöðú�wz¿†¤zñ²ŸàeHxå!ŸZJ!6.~:TBH ]ðf)“Vî¶7 ô#*C$ÅIÓ£¡�i3›{C­õ_¥c‚&ø2¨ƒ.>€À€Ÿ<¸á`-»ÂQLIx+_þª�ž§nÓøãüM<[_¥Àéaaã&Î<‡Î7TY3#ö7Õo<ç¼4$ó.×ÅyןGï%øæXÜÕ©Î ð]o¢HâÅtr¬=É­¤Ô#Ð|ûC“»ó­!íÀ%Qu«¿ãðœTgÃß8_Ï<ð¢4[½#ó¬.Ö›ŸúâNB¿ïE"ä�‚Ð ([™gÄ'+Ce0:‚@|£Å•£KáØÖ¹Ò¨O[£~¿�y–EHgqäÞé½ÕqDs¶+G®U&uër�Ú4)”€ ÑÐ5=µ´ý=”ÖÌX® :B#‚†AC®#%Ë…ð?ž‹RáºöOØ †GŠÙÁq> ÷ŸŠíàZ’‡¶|®-Ä*¥M¢æU‹>U Ë©3�ž@ÿ ¬IöË E&÷�E¸]�ñÉ5+ÑÈùs0:O75È�Ð /ø‚÷P ‘S¢•�e\ÛÕ]`»ocÃwä•<ûÅÔs1¿å<ÌÂ~s2³úå½ð·õðÛµX�.¬-?”©;¸vèí/% 8Açà~ Ëòª‰¼ðÅîÛc¨tÄÕ²å%T� ^2yq�+xox!@»¢h?á%¡MG™ D(¹S¸¿ßáIÜ3Z·©©~ž³nº X e¨‰8cŒç\Äß5—€ås>$AˆL ÛP¸–kµk±p ’ÀK“Ž£>ÞÌa[“ñù”ôð­^ˆf ;DÝÍ»9;�•¤såÃØW#iª.DO H?›FS ¹œAÏ¡¿ˆ!çv&×ÃhoìÈn�3ØáCf–o•—"·ü‡bˆžÀ=#ÕÛR1÷!zi2”MNd1C¶‚‘3ðëÂ=fz:y¸Úù)1ÜÏ—hÝÚ„Y7Ìɵ÷�Xhé íxWf£Ú‹Ž1j?Yk6�£›ÁErŠG¿•MÌTœ6Ä\óðÅ+NàÒ¡ø¾,l¿™DîqégËàG̘Nw}öH »ºÕìó9Ÿæk:BõÚEZ†bÒ†”ôTÏ;‚;¿++ IQˆ‰PÇzÚñgc@�ªÔ<‘}ÛOÙ¢þv»q¼ðÿžì.‡»\£þ¼\s.C…äFpŸ6pø‘‹<~ü'æ%h€{И®6ü¬ ë’ÂÅi8& j ˜îŠlB½¿H÷GC\µbV)R'N#F»mÂÓÚ7Ųh£ÃšÐ ï0všµ8”Ê"ÔÅ£@K¼Û¶,‘-7…«þM~¿³]»0î0g¬ðÐi9î¬x*aö;�³ï¦Ç:î8Œ€¾=ìÊ_Ýu$î™JC/eøû»èfëD©FUKÎÛ‹˜Úz–ˆp,·’ºªÔÎÊR©Ó1û«Ü]äõhò&±ÙK·,Xì™ù60$ýƒãÄÆeóΙãN¨fæ^½`Ö]³Ì:L9pƒË•æ²€¿rMR´P[Ñ:³ºnP¦L ⼆ä¶ ̹¸�eyoü„oF Ü~þñ¤öù›ìÆÂ¼mEëB>lÁœCb»|­µäì¸ÖrÕãGµÁ-3½3×+vR\(Ëò�•«‘æµù¹­þêß+f,õ]%¶½)dfŽ4¤•0G5¸Œ‰IŒ›Ž‚Ñ[¨6jê‡+l]¨ 7á#¸Bì°^rÚƒÄ?—ÛP0l ©¨�›!n)�'�²f'éõî|¹HBÙ˜ÚdìL ©3¨¨Ü+ÝtÕߣ‰c»_¯¦‰ö¤¢ (�þáwöô#–Jô˜üH¯§…®[ÇÉ,˜ûȦ(‡,ŒÙ]»Sé®7ë”Õ:K*å-%F'ŒÀ>„±hÕS>çñûŽ7ÉÑ®bÛµR�90$£�N÷ó0hªá¼t?·Q.(¾À�áA,Ñ"}�!ð:^=½]�ýø>éŸÖ&ìqVÖ›­ûäV €À�f8�ëÇð¬ý-H R~Ý~ñµŒå«Ó†Í®t7NkÝèÑ(ËZ8–ñß�y-DÊßÛʇÍÀ~Ú/šÑ´ÝIiš± Æ8&°ªÌàÍø1�ƒ‚Ò eoeoñ¾®SÙ¢'¬l¥Ý‹`…“D» Ak&Ã|ÞË…j+‚ý¶5+.k¤¬_ðöáb�ØSE¹:È8‘[{¼•†¡eT†“‰#¢<ø¹eÙo¾pÛ¹¾Œé§(ëÚÅ>+¾²ÃF¬Ñ1¬1mÞZÞǶË×_4�²hAøö]W®¶±‰àÁ>OÌ/ý2ÏC}Ø‡Ô *ß›×À J4\kT%%CªünVìOɸÊñ ]Qõ7�Cì©«‰N"!Áz×™UUrµþc)%�ŠºùåX`}h„m¯¬j—~ÇžÒxíæ‰7™ñ¦‡âYµ�àMq4¢Y'#²˜w–©±ýVЯä?ÆU·ÈÈ9}�.9 òµk­ÿ6î ïwÓÚ®®þ2;Zð\ù¼Œ#U!“as»ö¤3Oà'Ã~û¢•ë¢D³”ÛOB">ãÑç#ú&^_)fpí¥{Z–ilÇÐñA˜½œOnØåÃÇíØ£›ó#óg¸ŸÞ²‡×ºÔÕãPÚmõ÷ÅW�/~Ðú(ö6Ø€�ÅˆŽ£á0²É|Âɰâ² Ça2üsÄGßQe‡L'‹€¦±¨þÂñOñ$‡)| ËPÌðæ¤V¶K×—ÐË››dQÉ”pØy”À„Íl8|Ȍ̷‚Q[MØ�±ܽ…|�G‚Hi×}¶ YÀƘ•àYÈïð; ×WtF"¾™ÅÝ–÷=nI@üÔ/I»¹eÐËÓ 9öy�ñB¬’¢ ¸½¤ÔDZóÊp$Ñ ç»*£CZé¼ZÈ䛺¯®ód¢ÚDPr�éÎq=ë,ÒÉT ©éÀnÇØí�ç|Z°uØÆ-g%ß•n2àS³¦”L0_iç*ïÌÂtAƒ@ndÓ}áS7l#rrXÇáV­þG'FD”¥!¨‡²FãlØ“êã™áo+ùIÿSbþe&ÉÔñQ-Ðv’v;�¥RІc§$ò PÐÊï—B”>K�y§b�FÕÙ ¿V´FrXX]›ÿz]ò¦~¸­ŽÁO§«p`Æj€L>ܡӡé½*�zräâÇU·¬A‘JÏ®lçñqòé.à"Z`˜*?Ú!øâíwÀ¸¢²%VÏøÚ¨ÊÉQÿÓîR?¾vÓú“å\LØ™'y‹\8KmÞ'¸ŒäÚ|]3^é$¯,÷6ã² ŒÝÐâ€ˆŽ˜e²ê>1»è�!5 òç«tZ.TOÙß‘³ö/С1K8–ŇYDNåÃ3õ5æ PØEÍm@óÖ8±/Ü|Rlo�òÞCôx8uU¼má¨Õ¬ì?‚6[K¶ÇÖyX‡H³ÕC}ß·FÕ*õÄý¢Ô­±LìRYøü}DëÒ~Àr +3+õÅà æî] ðƒq�ðr²ì"ÎÿÿHGçÐhNbé»§ì�‰dòü\°õ3Ñà~,�¬áp¸§zZ°´@ô™,6¸ôð{³6k‡ ±ÜU€é÷,hä8a½&;$Õ¥döƒŸ©/¾ù›kß ¡r«áKÍ·ßþqéàTj‚Ôv$z%q-oéoøÄ#Ź*`«k ݯ'¶mà€¼Ðë«]Fâ?ƒxn°kH[SƒÝÇ Ge† °R?x:͹´…¿–ƒº¸Ðkgˆoný§ö#ë_r‰Ö+“éøáyì�8GV:Büé,WÁr棩s^Z¥Ù¡¶Ñ \š¦8–ûСLÀÚ³¤zpßä0È´ñŠGaÙƒ»Hõ;uc´ÞY8E䥸m~"bGs³~hí.‘;13ær‘:ü�1Sp÷ÃìFŒ: QÀÐ*�‰Ÿ,émWí¸EP¥éì¶Rªz,ЗUì‹5pc6° ‚ïSÀ¿Bš¾ò1ªêÞ7,r÷\yÛzÒæ¨©-,›âÍs«2}˨6;ýby²^-¬õqmä—äH³jp"Rë [ªhêÆMRÐ 9À­þµ+>ŸÈgJ(ö¨”-0`ëØ°ëlw߸!¿ßîÚåÒü$>DÚ.Ò^" ÔV׌Çò|˯HUî¿ìÌÓÞeçlxSœª®¡� ~Òõ²ù—7& ÊEœwuJÒl>…~¶’½6ÝÙà„åááêѦLÈQó¨-jBR͘ÇcH¸kPÞ$B•†vrK@¶‡÷Ÿ•ôwV²£*¤ÑÂ(;'ÈPÉ„k†l�YâŽx¾Ï¾d$÷åùè™ÀçQûD*›ð�w¬0È"zV_mLÈ~zV—oÔ6Æ`Ø“ŽñÚ'Vø!àú;¶§6mÏ`„‰[—Ó¶¥ßIÖÐ6Êg©j…‹l´WΧ�¨°Í.†Dá8Ù[ç ?›ŒxóŠvDWÉy‹ÂÆx›{LD-Ÿ]�6ïRBL¨ÎgòyÕJ$ÌíxW—\ÖXÅ÷ç%í8ÁHm·[{%õÌΆàM<öÚ (†jÔ“39P‚œ¹ƒ>q¸mÝ��J¬ÚhØHX¶‚ U9uK"&ypG#¬zÜ4_mòcýR“Ï¡ö¦g›S¡ '� O%)ÊOÃeI[ƒ�ìÚ½ˆX¿î“Íz³9<ÔV «ßXU’$Áÿ³WÆXܦ …Ü$³[¢?9w9Å\®ýº¢ýÅpNòþ"¥íaëˆÁ�YG{ Ï$Œ©|gÈÀrÖOiÔ(›úÉîð¨tÛk~,jäÊ? ©•—ôŸà0{* óp阶L›ðãhN]ŒÀHDó ›p?±[fçChôÏ?\š…áþÄ0k¢iº´¦"Ê íúm œfHŠ Ó¯N\ö‰„íAž5=~ˆ�QõûÛ6Z%×䀕˜¦t7Ͼ«�Þ �1†á9ïyã•çÁôéÞN�p�D{Ê�– ¢tˆèá»_'àžÃÄÝÔNuô�àµÀãк’Ù^>ÊûÀ¥Éñ»“wW¬´½8hºk{Ìœ¹‹uh«9%Rá¾ñâ¹!8×¼KT ô¾"3@®'aÿ˜:qL�Àk`—CÔ¨†ü\ÇÏÓA’`nô …(��ŠmAu’,:lqélÙü¬§áw}mVªˆ3 æD÷'RWgÁ¸:ïÀ´úiÂàPY(_ÎÂÂ7­ˆ|ÆÏ� c_76á‘®P¸‚"wÕƒÍh(ÖÆS<É\åò¡…¦Ã�è¨ê-ß<ÀØi8g†Im°óÈþ\¥~þ�ÏìÚÆðI¼{Á(Ú»âÊ mÙ¯E¶gå)¦Û¼%1äŸð6Nt]»ÊPÉÖ¤!þ`˜+É>ô«»ÀzîVȪin˜OøµûÃ3ÑÊ%·€ºµzÌ|Î�$×,ÿ!ÅrÓµ™³íŸ ž\ ì{z3‚¡êxg•jÞ�F^“={ìÛãǤ™Ï.8œ)v‹ûËŒò1È'×Ë^¦DÖ�r j_L°Ÿ³5Ìx³&à½â÷®Ì?básBÈ•ÎâŠ~!ÛABͺ?ˆFõ¶=1k¢»n†=T`G–ì^0‰®óÄàÖq[ƒ=°~kÇb_)ÄHÇîÙ;tHÆß….uj؃ϣΦì.H±žIÌZa­\5[žW«¯³ÈÌNh´nˆ™`Ƥ–‡7ºÒÎÔþø£p_æC¸·OX…—6²dFø¯¯/Vtk…÷3~|o›C2 8/†¹}ŸWOkò0ñÞ+OkÇò°zMß—!\d4®*ЋC-òÕN$mª�©Ûm5õæÚ`¿µøœ#B}5r `‹ Ô~XvÞE@Ì»¨WèFÿèµ0_S¹-qQ32Ãçêx[µ€Y¾c!Ð]qû)KÀÞ†º%œz1:È�_´³ì6X+G‹OA»4¤aç_fÑh ßòm¶[?æJUƒ¼gÞ�†Ê÷¨}w?̦—GT\Ì 85Éãà«HóŸ’¹ª*“uwææÜ´Ò4†#ý>6Ï®økú*ˆ%‹.ÑBQBy÷‚ Ù�ÍW"ì}sšõ §žµ ´”ZY_éeƒZÞ�T —5aÃn×v`X!'-ÏÄ>ߊž=SÚÕ³okØkáú’–¹öPŸqŠ… ©±äÜ.µƒDå«mÒ$ä åÜOï$rtœb~Íü­ŧ}#2ç·Wÿ³õý—wŒËÒ–îíLZfÃXÔ2= ĹIøµ%¹ñ�³"ÅëŒp¨…çÖܳOF:‡Ô½tul•æc:¬³eGµ3R«Ë:>oM@Äëý"+Lõ´‡ ^Ù²¸hRøFŠYÈ“¢…Z±¬“%Š‹Ë4p›@-ßÄAeÊ ó7×± E�|ë”i'y"U¸Ò)¹s%é ù\ôÓUÖ.Ô7SgÒ¨7ªÙ 8VMGU6:ÿFÿm|BÛ˜º¥K9äÇ€/LúdŒ-­Xõ›`ü}l’¡œEŒêÒ ñ�sÏMĺ´Öy3ùŒ8¥Z´/ˆI›¼+î‘^v1$›N<÷w)�sy4u£5u6c8æ¹(“¶°vJçï+ìF;#H ˜qbvü¬,^$UnO eF#Ûã@ÿÙ³=óF–ò~A¿?džFõ8:|>«VÎXñþ0D¿¤϶�œ¹q_}ÐÙË7ŽÃo[LÐ] HsàÛ� B‹y¦Íù>€&´‡j«Iç9ÀÚ&znÉr£‡¥sëÏl�ò\*ZÉá´‡3Ì.©Ñ_ÿCÓ1Þ€~ƒú¸ñm\ ÓÌÍ™’ˆD¶{ØMÕOsH›Ûðw^ÕÆ¢/& ® B ‹Ý¹Ád]¹;õ>àJÿ$$ þª…µ Ê­î1�Ëòú84\5Lt“ÌÄ¡ó://»õûpMîÛò»ø<jâ,•u „£·I®©´³º³ŒÄ«ÊiÌIÕ�G¸T`qÁyþ§Zù-èÌ5¹¹&eïΤ€ïG߀&â>ÿþ.Lšxs„AÖV“‹5ÕRg¬‹r±C[P‚Y©e~uIpc[àæ! ¨®‹ŽŠó�‘Æçi&KæmxÄŒAGi§{�F-§V\ë¦ì°Øª˜R)£M¬‚òŒÅ׉›±MsE‰¸Jš· æY䇹ûê—Œ¼÷içÑc$u+_ek¥Ú\ ¼`ïÖUíí‘Á3ÅMöl”§ íW·#Õɾ^å¦Â@w#µ&‡V1Þïš4w·ejxB’ZY?ãÎyâGþj1/Wé™(»„�È7%ð±W$ê® †ã›ÖQ@³�ʆtlL,ISš¾vü³tD¾äHeBÞ¼ ¹½Û‰(Ù‹cv@Ô‹ÖŽ`v±Tsœ±³Á­ý­†nrv¤’Z騭ö¡C5‡ãCbþ5âdðí•zš%N»håÕÚÚ©³‘ hñá{8áï¼Á×Û–ï,­Ÿ.q=¨$¾‘‚Üþ,®J¯, S‡^3…�)NŸS@êí"p‘K¡§Å(Ò°XrÈr¾×@uÔ#Lz4Eä'²yKEß'@âÓl½ÆòSèjb]""§N u£{ä ë—ôÝ�ñ±þ.U-,Õo¶q{Ü�I¸Dšº6vÏ¡3v£SŽ»uÊÖiŸ'ßœ¦„ë:0ùÃU’ápÓÉLÈë[0bqÉÚv8ýôjãXÿ*Kþq«٠¢�=ß6^Ù}o{zû„áÆ Œ,v¬(˜ÈŠïõÞQŸm�µU(™ú+Y¤¿&ªÉÖŸjöBy\?³ò7Õñé.da]YP¨š„¶o>d_|O* aÀ‹­ ðŽIIÒa·‘?³Í'Iî[ªÍ“OÁx•¬•R ¼Fñó¦òψN­Y‡má5ž^�ÝðÝÀRaCgǘ\ ®�T«%"ÃdäÍ2ô_p’j‚ @s`Œ1¹jê’N5-tùÂ!v£tÑ—'ƒ`Er¼¾J6ŠÜ?_pÙÅ{”ê Ôskm°ç'H€o¥ÝTlWñצþÒ,Ê;np¨`ʸ¤Àîɶ�¬Áó¢Ï5 .^鞘éääöù�3†B¡5êüùëÀo"¸ø24A†(�üÔæIóë.|k|ˆÛ†¥îŒ½_v³�Æ5õæOë9çö=y“UõÜrW„îwP¯kHyQá»Ø*6=»ÄÒ?8÷†'ÏD,þA›ŒGø˜ßà_4­¼,�\Ú¥.ލDOgÄô,K�ÞšU¡ŽÛÎ{R‡a¼‡z¸¬`ŠPß4¥ìrjØ/ jù£°]ßI³DàøñÚî’“j~AOº)®]®\þƒÄ–½3×Ýiㆻ›þÙ‚C>¯ì½Û)Ñÿ"ÏŠ�î2k¼ü#¥U :ljÊèõ€Zxq‘9~œÙ?= \ûlN^2Ír’æÄíÒË:«‰ Z€†˜Ö(áç>,ÍJS¿bäDeD²Íàöß�^ÜB" ÉŸÞãòl�Ôí7¢Ïºoz�ÀX5¡07-(c7u­°,zf’åÍý¤ÖéȸT¬ÇUùsfwÀþÖÂwu‡ì,ÒÐÜhÑI1X"%d‚ù–(kXm«L‰ÿ…ãùÌ1]­#ÿÚ ¼ÿ‘¯ü£‡öÀ1@Ç�T=62º6ÖãFêFÙJÛ ÎC6ÕѸ¹¬s«šQ\¾¿r\ç?€1�¢x´v¸E—U…_ÐeÂã8…é0~=‹çq®ç/MÈáÖî‡ÂmZw‹Œç$\ys/0p³Ψ›³µÉSê�Ï.^»à.šAÅ»ED¹,§?k~3Y›GÅkA[š;Â2µÛªx§„&ïæ£:‚ÿSÎJ ‚ïÂ\©º�iËŒ«AµÍlÄP×5ý˜ñ‡’ªÞJýÅgÆH>ؤV¶SHë¼1u ÎuôGyò`éÉ]˜ÍE2Ë$Nåò�ˆ_ t)ÉF²×ùé˜Ðn`\‰…=©6âªÀ]4èöHÆêÛ¦zãïs i[pC&m´½Wó×F>¸ktGÌ:_y$¢€÷6Ú¯û…èl„Ø_Òë:¼èg°7U–ãûÐ>lÉUóà¡å„rí�=Ê™ô[æÐ9êL㨅O™Ë $ò“Ÿ‡gÙR²d"�;•AòÁɶ#œ¶ùÈÚ¦tpòi> ÷EyöþY¾/Ø4, •›•Tk…ŒÞöÌ”ÀÉîï)ƒœÚ¼5ƒÏµ_L­?ƒK ´¿]WfþRhÒSáÁýØÓ˜›£§ªÒ[MÒ¬y¢Æá&‚û ¾�s?»™È0Y±N4ðjv¢}—Àp*¤ôE¬·�NVÒòÉ€†¡lÈàÛž„,ƉMCkì–39"8BÈv:ÆôLý1K[÷éê�÷™YTÁÁõ¦ÙsÃKvµfCq)‚/Ä~«¿;¥�&øÂŒ„²Š÷„f•RãÙNù:óÇ ʱ®eH¥ÄÍ•;ïçלٕDH71ïàkëÞ†€,@K?ÚhÀ„ Y]-œåÇWð«¶Rn(-™ÔWmmò�l„�«Á?kos]§Hm«MøŒÉBamhi“ð)Ê<€¤ zFƶ=æÌáÝbú°ë³ð#$7Š’aIß”º§Ö�ƒ�Ç.• çåýyVž’¨H[H½ÖágÁ Üe9è²yæ:tÌ«öò¢³-8‚&7Þ蔇*¹ µ³ª{›{(l¿m7�¶šÒ:-S8¬‡:ít96B.k± ˜±¥– }5÷ÛøI�‹Xò=[ð÷�Ö¼^aA,ÍÑqyßkG½‡Çƒb°nQ›FZµ4ãGJl$mþ„ðègã+®íIHgŽ…ê_qÄ‹–nWšŽ\Á€ëW¨;VÁ¹„áÒèR%0Mg‚'c‘ð¼¥ü�‘‰OÈoKVï�/Bö�º´§ùã<=Ãq=_)½ EBÑAm?§9õF LuÎ wGÝé`Ö}ô {K¼ŸÂLl0Xp涸}©²lž&.˜¶ÛSÈML쟢ÃÜ£Tnµn12²82©lÎÌ£®X�a&ý2‰ w:Ume jq‰ð¼o·�u7Êí"Òk?©H“¼QþÿÝ€µiì¢ÍO›ßKö«úA2÷ýˆ‡�똑— B£ûñXà꫟·Øk Ï‚e"l?(IÍŸŒ›á­3C1p^•œz¡¶›ÓVì¥žÖ ¢q¡EtrÕì-¾Jû×7𼤾%7ï«æ­ ÆÅ@>C6ÃóöO• ¹ÛxòôñÚ´àxmA�K«ŸHµNů=eÌXgT™µóò‚Ó ›Ö=§ˆ/IÞ5K¨°ã“QΦ*•Œ¶@o±R<(qÉóŒÏ~‚wÀp)cÙ=Vû±oa£C3¦²Sež7\!àî#ð˜ž&¾ã‡™µ )G$¥´:ðšGïV�¦8�)îë¦/XF¥yÜiGc‹„ep7rfSå°"�k¸�è R އ®áЏ÷¼Ú»_ØÍõXÇ�õ¸«Ë@óMfXxð3°¢ÑAͽ<ÈqŸä¹ì[RÊéiµñy›åÅ>ŠM­�”yއ¼ð¤1Ÿwí’´€²5¶ÑtKqSáœRÎx}/a$“Rhõ[Îs¼c,Œ=z°ézbïn/g‰`|ß"é :c¹ë~9µ¯ T—Ù‡K¾‡é…Ô¾šå5àQØËq¿A�s’”_E3Ú6Í:‘1@Ó{EõœE¡ïHÕ»ôŒòË lî´Ð=íÁÇ@ñíÒ§;þåà¼Ò0(k‘®‡……çtÌÊŒŒªÙÒ“c¥Í±AÏxuöš.ʳGÔ½:Ù¢ åc*ÆþU[]çºæ@µ[á:¿aª™üú? xíÈP¡È�L^·µß�ÙÌuºräÇFÔ‡q#„ÞzüÇ9Îx?ñoæ€ qÜoƒ ’‡_€Í¿©Q¤â™U¯¡C¤Õ÷F1}uŠ99±Q-¡ÒÉt7§\>²Þ¢Ñ Æ�øÖÝ®ÃïÜá@k ·'.Ò¿,¸¡ŽVNƒf£Ü oÌz‰àëÊgÀÿûñrax}b,*LŽëºÔZñÅœÁAÈ�+4�îO©ZЃhë¬BmÆtÉ#ÕN`YõÈñ�¥O¬�ƒ†^$4Ûf§ÊÎ�'5^‰€ìÂwM!÷ÆÁ‹ïC̪’$”Hj�Ò_iãê¾Óì€_f@Tü墕¡F\•žX^¬ÊÔßG:d�þ¯ œÛ•¢ ‚ð©µvÖ&BX!¯*c9ýÖsòÅ+TMAdËvÔÝO.ȪÅÇ ÑÏ[Q>Q‘ªY«ümæèxÌ(hfÖð^gäÝ|�žà‘rª´÷AÏ[¯·�êZª ®]�úžAJÍ ùkbó�}ì–;áuÇ[pïRFª!CFxS¬úá”÷nj8iÞW⩊7ZâBec™Û öŸ)‹Ò¹ T>_ Ç8+ôÒp‰{[çG§ñüŒê{³Š…*Mÿ’«#ê£XÀÀgÅâÍ­”E+.J»ÆW“ÌH&=;ؽҒ级 YÄb]¸ÄŽxÂÆçÓ.‘òˆ" d·ù M¤ª²ãm„^X-¦BF÷ÆÚËxå3; ì{!a%zzFÙBqâ*06f²Áˆ …êÚØ„ð©ÉyÓ†Íü£¶€! i@cºVaéD3~8J…Ç*mÀÁaÝVØñép!ø¦.2P£–£Ïô¾ó4ô;8(z\� �†�ÙõçjDlqþB1uoáánª]¨”+: ‹îﮟ@ÿÝùåú¢ ¾ê‹¤>(—XìÞd×Ú·yL Sg- ‘Fv:ˊ굃å)ÂÏ9îg'Pn�b‘T|Ë÷xÜ÷¼I“KMÔ9.¾ùIçÏ›7—O-%¹Þã,•C_EFi81Š•"nѳÇÊò¡7â\?­·Š"h >ðÙ°‘­:u� :â9ûø=<ˆÄŠ- ØÛ'k¹²'#L¼]ÑUÇG¬.Pø =«"À;ðDàòè9™ˆ¤Ø/ÆgÑYþmÿMñüÅæxC¥z;|^«”bÈ °PУe×gFÃJ‹ËmòIª>®€K |Báæ9Öûþÿ*1ÁžÝm;ÂNïãæ·Á0Õ>Ù< }Êã•K‰-À\�),ôpIÀ¿!Ý’|ÿ$³]Lr¿•¼„m´˜jÉxžó®�À–;ñö¦Í­7'@ÏÇýa•–„òu1\¹RØG[Ò6@½HbÕ¬;Š€Â[&`N3? -´Øò\(ƹ›ä¥7à 8þ vu?K4Máé„Kk~gì;4¹NDÚºD,)dFÞÁ;áÔïi[t)ç©�9fþ‡@ö(‡-/¢*‘öñ\.„d–õ“Ñ=G¼J‹¥nƨF7Õ`<±ôå8¢#�⻘âÞ¬Ò'´ÀˆÕþ†”Åè¶¥€Cé·åŽº·@Ì.”ë"d,%àräâúf°}é¶ù´¾öpélJ�A‘O¿hH‰L¿Ê|mä’ ;'æ’Bú‰RÉi±|C™.µ½eÔëw€av¿ö¤@·ßÊ`5ÊžÎ%":c"ò«¸áP,Gé—ƒLôª„Ó€ ýÆ¿²Ò3¤ôÊáÈ£¯ß· ·j_gl@lT›ë¤‹8Î�0?‚Ïs÷9Ç,|ó ‚pBd;±(†-,G`‡®((ð5áC’âÁMy+çqí$4ZÂÐ$�Øõð²ˆ!ÅA¯t,Þ^ÿ­~‹¿õÇ[àeÁ­CFt;uò·ÃêÆ¹ .n£—È.ÑÆ“�_!(M0ÁÃAè®§:г–領=+Ï×K'ÿf Ú–¿'Ë\.Õ¤Se•öµ^'v5Þ$w›2ÿ¡ÊÊ )—qedœO”~Rm^ùû�³ ¾#ðÂ#Zë™\GZ¹¯á¯áèkOGHÛF•ç S¨aÑD³Œ¹+A]=ó)‹ƒh®Pu¨'~fÏK†Ù0’Ø‘BÙë0Ù�ÄÚvÔÃùú§,§?W–�„XbpX;µC`¾÷âaŸ�¾n\%�,{³[å;Ÿüñ•–pÖ%e ¨6qŒ5}°uåLöBcº_�8'rÑbA£Dƒ¨¥•O•NšVNúñyÿ `þç[ÇEpoSùn4þU? ¹ªhÖ±$9’ª1£/þUäU©ïóLþ‘�ˆ¦ð,Ø¢zÓéØF¤[YI:Ð™‰×Ü[jãÉ·» x¶I<š«AÔOên¼hÿƒ6‹ž3žñtÏ+˜QE[�izQÎ\@’=Š¿õœy4ºR­•ŒŸ1— $}K'䓈ñÿB6É„­±$i �‡°ÄÃT„5m£o²çµá­ÏAߨb/üGWçÀ/ÅšH"½›(4QÜÒùG‡A<ÄîÖwós-ÊäDl7³Zµ‘¯]{tµ`�UíCºêôe\§)Ìm–#q?4ê´–ðÿloº½äï xn ®Ú8ªCQÊÜ’Rí1+†:(·»DÜ ’AmCD×~[©X¬`(º�º¥p‹t/\{·;¡|p£XÔøi`‘)ÆâÌñœ Ñ EëÙÃN>Ç&´ ” ÙJ84ÞkŒ$R|Ò:ÏX€„÷á§é„âOª9 jo£AÕ2ß>1nVzÞÅ24ú6¬c‘ÒýÞNIqÑ¬Š ’k³äž$¯ ]mç¹±†GrÖIÔzÑ! "B2Àk¢âãk˜°¦$]‚×!ÊÎÆ¤¿`˜˜t…ƒ ·æãÞn}D¶WÝ¥¨è:vÒ¡$Ôà„�üªa�ªÈÏø–D°Æç鸭 ô=3ÊÄÆYß² c± Ù'MÞØ 7O€–µ¡¤n&ÉH¢òX¥cGÅ4«A0‹¦À ûn(\jϯàt†?ç¥Ûv¡H÷ü0 UkñØêY°ÆP�Ar¨+z!¾!µ�4DV€–ŒôÍkãK;ÿ,Ÿ@tœè¦’ƒ¶›@HN�ÂqAdºÇ¢¨7.£%)ÿÞÝëõw‘ÜùUI]ñÈNiSõŒ• Ö\>( ¨#s;(j 5’\fʀϽ§Nñ‡$‡³ÅËܰØÅ…-k°{ÔG9Œð¢âÝì˜å(Š&Ï<0ÿ›º�ì³3Û pýv+,}Þ4Gœð[Ü.ÂôJâ•��,Ó‹K‘’( ëòdë–¿ÓÚ)†Fê9à‚V³] o¶D_õ)*Ò5+Qü´Jg1�NÔ�üy‰Hm5ß{A+ˆ€¦ì,K«l×ùÝ‚"§Iò…Å^}ÏqÎ{’ÚáÓ}òIƒ�˜säsç+˜5€i7�DKÁä�d¾†šm5�œ˜¹LªôÁ)×(‘JÍZªëÌé¼ÞxGì¡;h's3ÇÌ¿xúý¼Y`À49£•=®Ü}ºaœîàÚ쯤sÊ¿r꾊BåVm±„ÛË‚Ó2"’zçÆ‚¢äm³>|–áÁ’ìæáÂâçü "ÿàžì#?úœ‡�H!Íq�¦îºÉþYû_���O@ªÓtªļlòÿMk›·­V£ƒª¦^ËŽÀÉi±iÍ -Ç]å.%«ö 0?ï èÒ÷´ÕR?{ÿÙ°°<=Äçdã|üýY(ê ¨ƒ.Æ"“Æ¶æÆ ÔD'5�YHÉzFú*SqëÏà÷õŒYl�LŒºõæegWaã}Ì¿”mæ®ØYrr™r‡O þøä¾sßÖø77îgŽ› ³"Ĭα8 ®ªÊN‰:§6g5ÓÎ1pq‚i£•.*Yœ"䊀Cº*lј1[¹¿_žì®}izc?üTÏøm¶Æàb&Ðwοʲ%î^(žw¦`”²ßÜx±×lyÅŸ­p˰ä[£×n`Ö˜¹ -Ðkž�ÝÈh}!§X‰Þ¼¿³Ì4ƒ¬²k.N:2$ȃ_Y+zaëà¾Åul7/rAýêÓ9æSG=œÊ"¿�ÁëQ!k-59¾E^í‰]`t…½óë?“8TÃ\¶D ¤1Œ;Ñ”ÏX†kB­V‰â!˜>½Ê „f€Œ¹l6[:‘hô`I*±órdËê˾e°]Zµx‘ Pã™F ï(~ÁÕ{Ê w×aféø=ân‰ìD°ýãmÄ☠––*Eqΰkfëti�tËqKB/ç)3Nxã|rÇÈR’ÎDìcH÷)äûi;¯ nSX À}-¬‚¾IíI¾‰]»/ÈcoßkŸG6UòJGï@ l?ë²¼9 †�ô´¹·{³—9” B%Ü“¥Â ²näqÝMF€° †O¯x¥ÊV7 Oè�_“5ãñ€y.�æ ÆZ1!2“ʳ³ÞµýÔšœ·¢ŒrËÛCî‹m…ªM/.kE‘!MͧÐÓŒãtº4šWbÜ÷"LñQÙÇ âG âÙ @||Î`Z¹ž©�s˜JWäo'´±ÈÆ$]¬³„nÍ´—£€pLèDª €YL8V¶E/hQÁ…(|#eÖß9�?炵¡·ZìA¹;mŽ{&ù?(J=—æPГ•lw# xØý½¹Œlk8ÖYbgDî!ç’Tšªx–°�Ñx»_t¦iàEÄ% ·Ù ²¤ÐÌíÛA¼£EDx0hzmE¹€µç‰H¬¡JÄ�‘NßhdUÐi±{}ði±¸éíAPÀÑq¥§»ënU±º‹ÝÇâ®EwöaÉvK¦Nó­8^{÷36¨ÓÆ4œ“-‹áE_†pÅî’Bƒ1²¶èò˜¥u3¼‡ê�]~èó�8ØŸÈq|FñГóR‹¼HGðþ·ìüÐ�^! Uø¨ ÅTm·­2S]p‘Hoýü*®�Étt£ËÎDz�:ŠO~‡[˜ÅÚ»•a™²íR‘!çÒT¯Äby¦¸ =Í:)ÈýÖ%¤ÞéÿÒQ¢,0údòŠ0f’` j:D+÷rpЉ;OëÙ)Á— X’kü «Õ@|½ûQ)f5Ë(cQ®˜Þ*:i‰Ú#÷G𧼫�°"¥îÇXCLð;Ëxqÿ×^œ‡”Ê„§�Q~*‹.–ÔÐjrf¤îˆãÉögÞ·üñb“mJY-Öæ:J*[±ÎR¿‚{‹¶ÓºÏÆ¢Ó9<ý}ñŽÏ…ýµs³…ãÙ1åµÜÊÀ)ˆŠ8OÜOí>u�¤õ[•µ×\*ˆž°±µß À%hÍÛ}Ý> ûñ¦Ï}ÿaR„ê°ý^’×Ùð_§R�ëúAøËŒ�L_ð+pcVÚr"QܰÐg%ñÌÆNZ!UÁÃ>»A�>ŽÏ±Ð‘ŠƒºäJ T•òVÄiêyIa^Öcñù‹‡î-Øë|†e#Aþ­ ÍzÿiBÞ/Gâ¾ÏdÚ…OŒ×+J^EÇ=fœÍn�û¶š‡áVÚ~6�BŠSÍÀ¼ù%Ø’uÝmzê‚0Ûz5MSú²ò�ÝíyNä:5." µF¹Œºˆõ®öœÇöºxxÆ�ñq­ê>UdZCô!®w¥Œõ3ÿu'ñáTýÛé¤pÃnªšU¨Ã¶7&æ�(÷Y?t¶ÒJ¿gCæhTI×H-(ªáê{NlýUÞ4n¼Ç#™ÝQy=/´Œ=�x 0ï­¥ô‰Èï�ì²;ACdãÂ=œ§KÝêÂÔÄEziäv{¶:ƒ)d7/ú6.L—¯L³¯ïlL�îl‘øéÕõdú ËíÇk¹ÙörrëMn›/ú´_U´ÕÁâ…Z1v‰G�„1&�H“g�6ˆ#j~9)Ü9Õr$y ïpÛ’ç‹Àj[q´^"Y>Òp†Φu¢~lÃÿ<ø"_Êêf)@Páž$O+_˜€žâ¤X’·y)=õs-83šùºqãõÙ¢u(Jrúd éƒCøÀ¢ê›"&Þ¾Dd{¯ÈÈóÿÅ÷ á½±\%ç½Yx*,Ù âñ¸ÐKXB¥ž wcúÙ�Ü[„r7ÛXz-Dÿç³eÔò¥§F«O‰¬Ãˆ<ñx*†V$Ï“÷\·È^ËwPQËÞÍÆ@x^è¦pbýdZrg(ʉQ¼ŠmîÅ’ià2R Ç\„$˜¯d©r "WÉGPD|½�Bî©5°mG}úf³‹0Õךð4ôê‡ ~üWê—\ëC…Þl¾ÂÛéB’l°÷Ö|ìj P�fJÚšuƒ›×Š«ƒýîüRÑËÒ4+ç|@,\Â¥cÖe|mRbì�¬PŸÄn¬M´þ†’m¯ [¨ç¬ÏÿÂ*©œì(  /áBMsSÒ¡L˜‰ûØlqë"/ 61÷Xj’†¹k4[¶- ²©¥,æðÓÒM•ùŠ›rY4RíR B´JLwe¢Ôi“GddT8‹¸u)æXöGéwd¹Ðq—>Îfnêߘ�šsïãc�N�'°’ê6ÌŽß¾ø[ÑÆHsís  „7’¬—Û�DO¡þPk¿ FBùårÃzm%“è$@­àǹT�…ý¨Gw¾v4½?QT÷•2#ÈK±¶‚[ªÎA;îƒã/W�^ÿhæP‹y~÷‹Àcjš�ûd{¦xÉ ¬YqÀij#–“Ó¬ÿfž§«æŸ½6:ÙÆ”i\`¥O¿¨OdãH#‡,Wé¼GvÌîdºàÉxfbâyc`™Õ •Eº/Õdùâ2ÒŸz¬ÛÙ.·–†ãš?Âk/çã0μ"nFx@ÓJ�¾¹A³ê[ƒÐÙ¦~Ú”ŠtO‚î`¹¬ÈttùaDhVm”›Àó%Ìxy°cUF’Ÿ¹åõ1ÛS V½R8?F#PŒÒ#i&kÏè–@þÊA¡´W·]¥\mŒµ²œ§ "_Ú¦�@Ôo}ã¥çô_Q”€´j§N¾Ïæê„ŠN+ݧ\Ŧ¨çn X@À‡Ry@y/5ó7Ól»ø(�MÐ%ôvaˆî~™wæ8Jsº7ÛnÎ.˜Ìik`_]ý˜îÛuº@¿q¶ }|À¥ÂQ5""c]%9WV¾ùöÉSë¥î9ã ï¤,(§© õ“€«ÉŽýh±…ã”$Ô¸§UF¾SΘ6¤È”Ä„6e­¬+ëës$ãzGK½-%ˆû�ó+ºÛÎÕxîGM5>ƒH¥%fês€0.(%Ôªw‚í@›Š+ºÉC6<Ž£8<5Çšà-̠ŧ;q¢—’Åc]Õ¾I͈\�Á]‹I÷Ó'u­é«èïÝî�¦Ã¬ÅÐTñDAøûíˆR�•=³/ëXž…ê–˾ÕÄPj&Rò“ ®ø+¬ï—WSÂîjzÕ)²»[þRQ…ÞáXÆ($Jý±˜ì¡c+;$¯Š¿“*iU5ñíÀ Ê}wB´žâãð¸%Ð>/Y�f2>›ŒaáŸËÄ�á#¬W¹8ÓLNª…g�=Æ�iHWüáðrSÇ¢h‡ìqÞÕtdp§Sœ!j‚´¤)·\>7Gy� øñSϵÂä�ÚëîšåôS­Ð†³Š=c¸Ðoa<ØA$/Ce,Ò�~4&@™04mX ð¾\Žæšú¸ùCîWÔ“¦‹‰¢ü÷~¸]·Ò<œaÉb(/ý}vÞ4CYB×ÿGë…Mu>Ç9�–+U¾JÚÅÓ%Yg®F7†%,@×T‰O´Ž‹ÏVY™¹)0ó¨£�‹Úw…"Y¯É:Þ»°"jãPdöŽ�² ³ßÖšYQó‚Õo`·É §eâYK½Oð_6c�N‰®°öQìÊT‹kY^„0$`HŬþC H ¦§w—‡ˆ>XŠð„†0SyÔ È4!Lì^4êsû×2ÊSjwÝo<³¢@ ›íA¼¢ªë5²Yô27žsÏÉ¢ª8‰§¯®;T˪¸Ûåtš 1*Tí&Mª·r•žt6Œ¯{Öç õ%êÜœœÛÕ[˜»7ªµŸ´b‘þ+^ì�ž âçÌ\lñâµéê>>YÖGˆ©Gô>¹t„�LYôò (FêCø¥¤4º€9ñ§å¢4š¬,&M°)8ö§/7rË5µóò eè–é‘¢œó«‚–áuÏ%Q5�o6Lñ…Øÿë#-poXctEÈàåùJ$!‡n¦IÊ;$z—Õļ]D lÔÎ<ÆèdKŸ;¡ÒZk¹„+„òe8iü+„=Í7<ôÎÑgÈK_Ž­ÍbíƒR‡¦uˆx †a"ˆ¾ Í{£n~âNŠi·|U8·¶ Ÿ•�ºõ#¼Øk’5<Ìæ ¶œ�É,/K|©Õ‰ÙýÙ²v�Ô3 ¬–Ö×ïžZ¥Êaš�°žHSnÛf'åæöÕM^{8j°µ+eß+¼¸[¤K†4#=î W�ëÚh ¬¿Á»¥æjë£o>?ÇrTD0½á_�¡ì½ë“Z|³ìµï"�­oЈ”î÷‡ýòÇSæ'äÙGÃÉa`Ê· Û]A8€GÊò‹á 6¬Ã°—�%9Q¢2ûÊÝ d³}*{¿Ö‚²NôùýãîŽî^ ]—¯ÏüÕ©]ÈN sœ¶F5øœ”W½xzBD¾-.J7'Œíãχ­ÉÜ�¬Ò~Ž"�`å’ü¨€¥/™C…x!¢¦&ÆRV98ë× –ÑYO5õA­†¦gíîrqá(.ayKÍ?/쮲òfÝÕ}ç[S9ww{ß~ä©¶#¡è}‚F èL²Ø¥®³a´}<Ìj]ú�3“§]l}ÌA¢~hx`f·Ø´:ä$)BBe/j�‹-nÑfª”Ô䯕Üc]<4»ÎL†ðß¾º×GW¥rüwN¸»w69Ù ±|¹¨¢_°i¸kºß6ê¤Ö½ÂßßN‘¢†ó-¨Á¢QÏ{ÿó•öPXvY;=)¾ÙÏ/»É<âÛÔ̬¦¸` Öm´ Px)’�UƒÝ8g1£?Ë�ê�Ü}€›ÓhYä:G») Ùà0ßf|‡;`»÷Juˆ^Ý Yw„½ÀÂÞ*à1¢ �q³¬)�c ‰hë;9‹ë[YYô«ùfGKU` Ž£ºù¤š[%½`Ì#¼�ÛŠ³6.ŸO‚áf{&×_/ü&ˆ:UúþæYŸ¼*5*àUÜÕˆ\Ö¦#õŠEâ§'�‰ùB®fás TÊÈÒŽd!Æ´T.¸îµٻä1 “丑Í`ÿmËÚ¸.f ~K­¿,(í É\¯±ËEÙ ܳsNÉ嵦:B¸º¾ðäxïÍw.ì§Ë%õ劎Xmÿøî0ÿíáÑò5Û„À–?ý¢½’H~ú'e¨/õšðÄ“#KNË„—ð»†`'7™ê"b_ObÞ1X·ÛÄðPçk±@HN?ª< &æ�M®ÚõQýͼ'†ƒ\ö¶7¡ÈeJ´´†¨¿p’råzÔ/@àÔè,)hÓÈz•ã8µi[fz\1/Ä3漟<Ùž…á¬-v‚DŸ‹¾SS±Ð\X÷Í翯¸HêÆT˼ы“Ea*|×l¨bE€À,WZØÞ«~Vzl 3ßð²$Ei¡%ÜèÇÉݪGîm†V(Fcöu³´ÛÒÁ×§éÂ/¸þÅÂ]MÊá€Q–,4dŸ m¸�b«FÉá�w’Ç÷�¢<ô;é.ª—u­#iwn-SÂbÜ–l‹ ¾ü¾þŽ?D¬­¿PW7™“j¿uvT¦÷¨÷ ô¸t©J (<[½Üñæò®¯þ¼ñvßãEEÒJ<ŽÂ§ãô›_õ¤Àÿ®-�ÂÄKÐ~lÒ”˜†HšØõXä ~Ö^bÅWŸÌhFõ4­¨½LÌ!Ÿ# Qk‰ jÿ¬·¾Í²­ v ¾¸NîrV ÛeKÛ”½6æ{Ü„�ß]•}ÎÆF –óOÀSâô2x%›�ŠQÅ8ŠbƒÅö¢€Uð\_äà!æÇcóx%D1ïŒ%icºª÷ËÁq�oëbjBÕç–3„Vì�û¼®f{;Íúi¥0zÝhÞ ÖFqY52L.â‹Q½ö~Ú6ãÎöD8‰^¹±ap:'l×ðcÿ(¤fá< pá«G@§¶œb—!½šã[ûHž¾>6)Ò0[Æ2EñÃ�¯â‚dçOëñb³�gizÝÑI˜$ë¨m§�Ò ,¹âš^ô3jåšÐ>@vMxa²(Eôè·Í ‹6¿>„cOœ º¥Ø…ÞbÊq’2Á \ÊV[úƒbv’‚¯h›8®O~hé¢x µ÷ö5h!öùÞ3ôz‹N9(óS0£ÝëÿMù¡>"]…†Âf1·Rùw¶[°I�?Ç˹}²Ï¯L1ª˜ëÈ„Sšã5[Xåx#”hí.c Ö*¡SíT3}’Eé““-È j.#/!C³ Lé þ]Ó˜Î-a”¹ÝfCN™DΑ¤Úg!õ2K£½ñJõgIsä‰DÃFÝ)Å^"}ŠXìK´ÞhÙq2)t±ÎMºÄ½Þã¤$ëåÊ |R!L(Ý—ñ*ß$$}@'üœ*—à‹•fKà4¨Âs!ÀwÆ—pRߣ‰9Ý«´n5~Ë9Î’ÝÔ}à‹!t}̘”²æ�§Ö5¦DNÆOògx8{gÏ!ÍTÅóÇ,#Ä4ÙïBdx n^•€“NáwÍå$L#‡êÁõ•w¶ZÁê‡ìÈN�)Lïhf]-ù£ây´—[+û²¯Íáû"Àï «šðø]âߨ{…@RŠÜ¢+«ïŽ0½v"ƒ¨ÞîxPMïÒv[”owU§U=¢” ëò 0`ád“‹clíµå8ž·0ñ»”lÎç¼ÿŒƒM=Þ$¡?Óý�‰ Q¿ÅŒÓ(RzÔËá#ø[Ô£8�2ÚbSÉ\@¥ ñ{E�ïKýX ïu^ü`2À\¨yÈæ½ö±t7ŸƒÐeÙÈ€s¦Ž)4x\€båyð;•aýTs8fbÏ£�ê¹ÎþkÀa.Þd׆C‹e¯ ·%HŸ‰r�ÙH¹�n„ÁB‘D�^}—š…\Ô†)݃ ÞóŒÇ3­÷YDFâXæÇ܆ÿP§Z&N¯L ¥â‰o‹Å {œ < 列ƒ0TufÒÊåš' Oß®7q�TXÅùá#c9Ë&ÏV«*-Ö8ã°£ ªù?î™Ñ$ò7UI8“oéºZ]à Ë×eüM§{d( åóU2!Û{˪ áºr&cÜ×À¥æk×*¥Ùã?Z¸=ÍKÚ©êŒ'Þn ¨³Àkbö'‡l°tXzî¹vºw?½N[6âû¬Í'€=�Žßq[¿¿åÒb÷üß}ô‹á’^×2K¶ù"Ù”uÄä|ú�ž±¤TÑoŽ/µóo²J¨ECüy$¡Uó6Ì£Sâ>B~N¾))8:#¬²¹#m¤+ÉeÆÈBîgÌD6Öt;´ªbDnKz0rÖù©V¸z‹{PÒ6E5NÝ­mÓv\°I6¬C:˜�‰Äœt¥)ORoZé}*g4#³)};1ßÑa™’)-²sÿ~õóÕ¨«U´.Óë.Ó  º+¿Y…< ÚÂL†�Ëpã,Aê Wº¤–úrÝp‹|ù…|·ë!+ú^±Šýç#¿Û¿ó—•ÇXÊyøp>AeºçÕW“š:dq¼&ü�Á5bÌA€8MÔ”™f%PZ(ôÿi‰ÃÒeT1š3ØœÊÊAÇ-ȧ Vüi»[ /}ÌÓD¢ÝñÂ\‚å&mAð¾´¬ŸG¬Nb´7H1˜tLSO$Ñ#NÊ„Yî-K{µÏ’¿ÔÄ™(w:{7Š^Ýût“ËÌ€jñƒª±ì) ÝîÍpÓ0|þ©(WvMôõ NK$bò�‡ÙšmÁM×£Ö�±í�y*cßcsHù›bq�•zƒ *�{D0®çœ- ûç¡­8O!l|˜¬¥¥¥5�5ÕƒlIéiõ1’3Ä>ýÄ´K "È’·vÆñU‰.Ö¯ÒéÍ:ÐíÎt“B/œéaWC3Áoÿì£âíú£ äev{Q‹ pd?f‰ÖyïÙžAêÿ(`Ú-<¡Â^ÈÛáxW(¦6ϺB‘ä›K’¼2p?TÏtKÕ¾~ÄWâ`[ÍÉQXW‡ˆóRÿ ÿ$'hYtªU?ÆÁxÀ³&àÓÂnYüxòÄÐð?þÛ7ÏÓFéDSïh™ki4¯[ †„? gªC24]ISè hº¸�ÂÇqŒ–�Œû]âÕ�(åžîÄ~Ê^8–‰Ž¦¤WÅE=û$%Š·Â µ›A~§ƒh:Ç;ÙBÙ¦ÇÇ®ÝE¼ê©ÚþZ}Æ86޳�ô‰ô!zç3h:›KZÝêÞ1Ë‹9”ܬ¯Åý´ÃÅw8¼¹¡uÿBúçT� {¬‚ðÕ/&ü:“b‘š±jE­ûº5eΛ?êc,( f§,7¬žïùÚ5ؤÿ—e>VkAøD¬Õ‡�io[†µ|å•‚<»­Ûßê?í³ácʬ¯½=u× ­-ÛÚ¦0ÿB—¹²ÈO½7@Þ˜¿hëÆ\˜8 C ¼�4ëNŠuUN dò(³�¼÷T„�›zóº¯3®ʸxWæøœYKÅ[̘þšàÂÑÑÈì'$«ïw"} �»°o,épV†tÒ[¸ þÁt2ä�%O�æ8˜»=7nx(I˜€Ã"·ÁÙǶX èi"îj‚*oFÚƒÊ1r 1ÛCœúÛQ‹à*òo€è-advSüÄ’OpC›KDCãn=ƒËÝûmi¥`33AêiÞ*‰Ì“¯�"1­›MÞ¶}n¶;Xõ b™÷y nŠöt”s¬(Ò}À°ý¡œbŠ"Lïv¢?ù/²ª1£T”}SŠáË!;†ICo/=ú} ãi=öô•Ù¸»¸frŒ Y´zäTbtåÜ>1•Åí]V?1–ž†*/ŽŒ’^D>-Ôã(�ÌWIsÛ7Rªžï>rx׆Î9¯d|tPØ ö„2F¦ÔW$ìíø¡è1÷Ò:AzŠΜlÁ7³ æ¢Uü2ê±÷iÒþ‡ô êð¦ÐK…€EˆcG²XFpŽ¿[•­Î0ˆ—Ýðeú© .lsÙ©(¶š7<aTm]f‡D$ us®^'Äqvé7”hŽÏlÕ Ð“úÏÞ¶ üÍš8æÙÉx½5×Ü{v‡†D=fØÁn‰Ó‘ CÔ Îc[E†,n¢•þà´ò!’ÔÑS­)Tù·áêÖ³ ñP¾oíœSE\3>� а˙DxjªßkR«KQü2Ÿ­ÀÎ}IË.DÏ"•! °2]ÅS+Ìõºí Ã£ê† #­Âú²íJi¾?sä$ŠBîzÃfª°HL‘L¢£cøg,ý;8ÇÓ‹”7÷å Fn�­ ��å ¯°ët\÷’)â}ãBé„ZI°~Æ™übþ·Ç6œ±š¹‚¨NJm§ú¤tËEUañË8¡ƒJ¶¥ŽXJ¨ÿù›~g|ƒÈa2±”ƒ¬lÞtbK`ýâ})~¶J (âËl¶ë%¿ 5§Ê¡s2ÍÃÇžž®aæ °ÆiÓÔþ Ù?´Ÿm"�!_åõ­O.Lœá«’ˆh-.1 ˆZ´·Ô´É`Ùåïuš¦¢þ;º&°¼Å@˜ÏpÜê-ótÂLXG5Æ8θ“U ¶!ú¢¦N&Dâeß Ø>Ìà ]pZCCtAœ¡CâvŸ? Wì´§“‡Ù�WÜîM<Ò" ôÆWÀ³b1.ž{‹õqGË\‘!™m²Ò^H‚>Ö2¡uêÕ¹�eÝ::Ó?|»Ínž–Í·âFЋ“Žôm5È"ºQñ§ òhl&áà,ô Ï0kbÀÄëkßw·µÀŠÎ2a¼,a0?:^O—  ,nÔ{÷¾#lù!õ2Œ�Æ~©/ÊI>:‚WD•Âj>ÒÁ3€™¿'�I¬"éÖ¶ÎH–ÝI«m-’Ù§ U¿«ñÇù’iþoÊ 0YÕô^¹ :í aÇ—~ Ÿ”-· {vÊ—›¡K![[ÒäÂÒ(ÅÉ·U³lFà Ú5øY.i;ØõÆJ$$|À~�ž"©M<�X ž°ñ%5Ú~¨¹Ñ†ì$¬žf¶¸ãFbZýDÙ˜ŠùÆ–ö¾†G£2õg­·´‘Uæª9âåQÖ™*nÂmi›uþøì±H™×¶q6m)Å—·ÈÀBþ+µ‡ÿÀe¾Á}üØÞØñSñú"4Þ•ByÞ69µËÜW$�¼4Ù݉ë~‡B†r‘ÒgAÒSé WBÕ”Á7È…Ô,®B° ]©ð;º¬tý�ù…½›øî%B¶K…B§{�çÎOd8Ì ƒ²VæÄ^Ù[ÅÙw(¯Ê†\×P)ÜÏ]|V'er�ZLä–ÆX¯T·4VÓ$x}bhSµPÄb‰– aw—\&¼Ô]Êç—oÓ@´øÊŠíÙ¡´­·”M½V]èLxU5­ÞIJ£Ød®L;¹RX"”֒׺ïI‡F3‹­X3­j% 3Ý­ÄŽ`V„y?WR,ÜKÖ�ß_›”Ÿ6dS7{ŽŽ¹¡w-¶¦¸¿%ÉVª’¹>A'¦D¡ÖF Ó8Á~wÿÊ< 7%¨#áR–ÌöÝ�²²â�¦pIâÚªZDKqˆ¯ÜXRmt=UÕN@ñêÆ¥GN#GÐDÙ¾1ò£<¡$SfÉàý|kð.~—Îà•1ö‚=»zÔÝ&Æ×Ýñ­ßÒmnœgín@¯~IzW�1Т8MjeU‘<–Œbb75} úéãÑ¥·6ž„³¬7Kv„)bÂDAÚž#D¾ŠŸjv‚â¹A,<ô6Z©GúžoE\½¸Å„i«†DèMÔ|BGkÿßœ²ùɪ.õ€Ð©ùAéð�ÅÍv­`e v ÊÁJ Ò”�v{7oœÂ¥L.0áÛ>nŠF¯žÝœHµ�ç‹é�Ô ®xò'EºÕ°µ6´�Þ&p¶=Ú@XΞ°ºl§ÕÙ`fU\½‰oƒÄ*"[„¶Îs4o‡U�Þ j¾ɹ‚L;c(FËûç—Ü:¹“`` —L0›•øZW9Ήð>úG-U¸Õ6ù�¯‡]Ä~0ð€ÍM–zºçp™^6ké5À¼ôä~IWÎ,´šbUûD2­D$?�Ñ…ï, ÞÒï×Tæ�­LÓn=}rG«fV½¶«ß¥?¢±«ÿ)ꨚ�«è² ¯|@˜",Ì œÑÉT¦djb—tž/Ê`̺ûát—œmum¯Ûµ€Cjý~¾×%�D\ýq–WÃÑîbVuÃêôÇüNkÛ:óYô{ˆ�Íðãc…3l.Rk‚¶E¥}&7 Ð LÍ:Ô߆Åz�gaîÚÙçê¿=Æ<  MÔùNÜ}‰‡KÝ:¥öŠ}ººb2Ñw'×#Ó(oQà”’ì;Uö$wlBÃIØY/Ѱ2U'™f4TÑâ.É€Ó½¥Õ½Åë7Ôí•·‡QŠöε£dåŸ.“�”¹/{^å0ÁµC u!ð}bxû&tškÉMT|/8êz±obr*,�Áìq-Q¶½RÆ1@©§…¶¼×1+Ÿíf‰.“D9ëlÃÆ#‰yR• À–|b´ìÔû{×ïío`êG¾GM©Æ±d ¿¡œØï{*FlMkÅ\ëcŠ~ZƒÖã΃5!/€î�Ø÷î´ ÇȾ$|‚üÙG©Èóž›å‹hDë›_�7:_ûÔ°½KÎ~Ç颰ü5¥¯$U„¸ZæâÉ¡ªE.ë»øô¡—ÍOñ Ãk8ë‹PÂÑ~  †ÒÀc‘bmÿ»"Có°~pseãvH›.X$Mð¬äí­d„šù;®Üާ!\Á††R^’ÒR3¢ÜOš¹º˜ðÏ®|çÓ™™_®»LR¥œ¹õb‹™ÿ6ÆqÙFDõeL� Y��ÑíÄÇGP+ß*‹žd�È 9Br ò;5æQ`üáú;– )™YlN‚‘·°7Ò·­#«?‚”½Ï€H¤z+ŒUI—·bÙ葊&#Ô3êþ“'=7 �^0K®«ï–ë“kâë¥.¼¹2=ÿÇ�t‡—âkzª³öm›¸>*‘› JºiAá.¬²`ð¸6·Ë'„ÄC:¯†%ñ3L¢ºìŸ9ÍT]:“»`E Xm‰ÊI|-µ´7ü3§Y‰±Ú‚Aqa@p}úTbâùÕ^2s):?Qý�yNuRA�Q¸ñue»“•·™‰4� ºýà§F´Í4Lu ™±÷‘Bƒ« AþDÁׂÌ(ˆÎ€2ìá´ßŒ3¶›”4CU£­ÏE„áôЫ;‹I"ì³�ÝPñ÷9ÉZFóÁš�Åø/jQ¤q¾;]=L–©~Í_VMøÓpç¬5H•(ËÆ^Í=©P–%y—Žõ?ecúIE UÁ6‹fVƒŸ`öY¦™¡Y¨—Ô¢®û¼�ÚI~ø�ö=lb LÅ©öi“LøD¢½,¶,óàR7ŽÎýfùÊvYDÄGMLEãòÚÔ™7?‡¢çY�ü–K2Ð~¦s.âcÐï”θWDÀŽWk+›9sÚ×hÝB}+ǶEm±þÁÈ—`}WOêË(yt6›½g hi…ö©Ã€wø˜×‘föÕ¥ÒF¼âи$Q᪸6æjè?óqÄõb¢ï;2xñ¶ÜçRàŸvµk.ωŒýq@”X[$5Ú#_(Ê-!gFoÐ�›iZ“M0R¬Ý€ÛÍgï@:4'ê!½x�.¨pB‹>A9ù~ùSÓ(�ùÓún(sÆùa/ãn@£Æ0 ,e •Ú4ó‘Ti2&0�É'¥›Â´ùn˜8þ09 ¥ìQâ¥c%/ŒÌš<ÛP§ �wÖ{Qš_º„aŽb�¡�Ý…¡¦+�Žâr}ðštbá €ig@ܬµãôt¬ˆñL—œ)³f:A§¯_›» …Ð…ž 7‚ lp<²í³„µ…¹wG[À<{ÅN)º-�È',ò'›™§8½uŒî8Ø3n]ÖÉ•»~á^9„ùÉ‚ff†Âò]¬¼dxì™d�¨¡š…þ„©Î…o§aT˜EÛIùLYŠyÔ©� ßhZ'ÒíæHÙ��ÿrâ�W~f¦E"æ]}1'U~ņñ×x!âÏø: äþ«ÒÅÅsù¼¤qb„U0óY0åVª�þ & Ÿ'­¢Rdµ&yÃ8KyTÒq™á¿]Ö,¤²6Œ‰“Ôý%Ué»Â¸ ÁRÌë^;8à8Ò&Ž8"Þ’yƒúþô¡ ŽS[ê%y·ãÌ¡ŠÉ_¸™ç3�«â÷þ´ Œú&L(-/MùIÀ«ÕÎ)9¥(Á¡�o`˜iƒ}M€¤| Õ¬»¯*liÁ_ØVƒ]©²X[ÄTØ7ï¹ÒŒ23;|ͼ]ĺãÝÜ% 7ÿ_4…6%kœ›Z±1Ib™aF�ÄPýVyÓ«�_`¨Y�;@w«Ð¨·ÞîKÄ_µ|a_âÉÖSrôƒv©å w 5œ$È™Ù]Àm PS¾sS§ë0º.#úWH¹$Ù ”Y]-@Ù�[ ‡i3´wžž, •½ÑÛ6*…Ç侩Ø;¡È]ÍVú�kÊhÔ¶Ë~ñÕµÙReø•U„Ó CKê9YÞÈ¿×7Mý+@l挭˜ &GÒÝÀµ¡ƒü †ºÏl¢oó½÷9�œIóõ)Î%b„ՒͪYP¬î†ÇVd…›®Ò´¯¦nØ€µ&eÏ/cn!,ˆU«ÎøS™ÝyzD|s\AðËm¹ Kž1óG9ƒžéG�(x¿,üWƳn)ý%»h§Ùý�lâ—-F=^9¢Ÿ.ìÃ.ñ~Ö¼±ÆW½–Ô¾¢Àã'�O�ˆó[È…ÿ#uËþŒaÃîE.kÕÝ÷¸Wüí0o! G®ñs!�Øÿ@êüÞš›‡÷…špÔ�½ �æˆæ>F•m0Rt”ôÍQç_¦&ÏDm~ÂÓC¢_'£U4‰åWÚ‘ šÀpñ”£(ZìCz^W«–+<Ðéë§ØXp²iËV@ª¸&˜>~›Ú•Œªï˜sXœý¶gú˜ö9Q°wÁ]÷/ÞsX#7À'ùô]ÐÉ0ö"1�©/�D'Iƒ«Ð3IÀšµ&å°üú€Z@!ñ“ð/Ÿ«¹RAd±ÃÖ¨aŸi³®“éç�´nµe€qgëîM°ðy#åŒðÄ+[÷RÃÊ¿å~Ðh)‰:{yh§¦%ÜÑØ)°jxÀ:±üOÄû&o°ó½šÏ=ß”²Ä¯h_·Ç¢¸/±¸ž³W[¸&sb³ú9d¤Eݧ$ãmÝ ƒ®dŸ;i/3î=´l@­s�~œÞdñ2ÊîÒÞ¡Šþçåqªôù‡ÛZKC™MÏáï§U£(iL¥�«k³!‹–H•À˜© 6€5e*-¹âj�²%_.-è¨`àÃ6¤Xã“ïxï£0Xöȵ¢¶þ�ã´øšTþy~êøñrŽi–`¯ç¿T Yv{�†ðSx÷ê]IÖ¸:Y¨eÅM"ɈŒÅüÝ„G,£\}ÒN�!\½ÇÔyŒ�å‡l½μϜˆØwîÏ ‡~ÑùÏZä²Ëi?åÆóõÈÛç zgI‚üªéP'RŒ <©I,4÷½"ÒÉ¢}A-N»‘ìæhЃwž½¤#•zQH¼ý#"úšß_Qø2&ü ¦KꦖK7_®ödê8ÆþÀ,Þ§~Âa1>Ë—Y gZ—C–½õ³4õ°%0ú¢+P°V{2û¯µé!Åð]lUû87Ë#(v„¥{5fθ8iHx‡é|ZŒÎ†Ã#r²¡gq ø'‘ñ{ è'ŠAE’=”Ó"ä£jÆS!ͺ)!5r2öŠhoû϶‘1Y)ƒL[×Þb!ë&wP e¡›BM-¸g“8-DD�m×ÑîˆÃ$ü å+ÈŸü¨ÃBÈÔ>2HøÞ÷ã[æ#Ón`»6”\5× ±›ó_û)£A"É,–âbâÅ«ãGõWo·yê†õºÜ®€ù.8{ÚT¥ˆo�}—>ÎLì|  *T¢Ð³Ä‘-þòBIf@Àoòàý9ˆn·µuÄ^Ïj UOœ_ûQN[À4× yÓ ¥jïÍÉgØäIoþ‘#8é¯Ó/‹À~ñ»˜]Ç9êÆl_̃s%ŠZt£6�€|$ÜÆvpªî{š©ôY­™àÍ�A�oÔ#�*ß0~%$ÐÁ(E—éæÚ£žJEÄXu#žÝë››2­*¿à‚0ðk®›T3 �ð^\s-ë'~ºsò•£ ±dm>+—½»] #4U‹h'ñtuªÌÒZ¨>Š D�Æ=MNØGŠ�íí5×Õu×RÔsÉ'Ásîâ…}m†&—f©%ad²)žŽGTŒ[Nð !Ñ5¨aË�O;õM]& Ðl\ß~wQQpb•ïêwÿQÖÙ]²$�\ïÙç÷�©•‰>p0TÚ§Z#¥}Å|BGÊ,Rj ¡êê„ŧÿØiS°z�ÁxqÅ;I£!íX5DÅR¥ÿ8‡™tè5o†CáJå¶„D„å Å•û<ÄJwŠ5ûLG¶Goøžm–>pó¶kÀbY£ºÀìî‡Å©ñ O0"IÁúáD°¦ïG#°ÿJ+  YPÓ‘`°Síþ뮵âvå ÆŠ(›é/ëâ•ï3fÊ^5'Ò®£\—©º �« Ú�'¥ H�:h€œØË ó¿oã>QwZù»²$xk5=F»€™Ó}`^Á¾µ€ûݼ%RûCÞòAj¥™9ñ aÙ)dyÖg+MËG•’þ¶…IføíáãNê§ŽTx©Þ¶"ÄÞŠA€ç˜d1ë}rˆµB¨?•óo‡ IÑ�)ßImú™AÂíNQóUÖSrD×Ä43ŒUoÔ.Øwû‹`„ò¡Çš}6í˜p·A•²|pxÉ!Ããœïà³пà²ëü =бÖÄÓJæ(b¬"7�[í?ïŒy“‰úsz[±³ðà+ÄâòÛ·ý£,R¥Ê”§m©7Vê/oùÐü$6@T3¸óë1ʼnž(!Qƒ LU¾øz¥™8è.­ï"ßär•ÈÙ8c]Ëyn2¬2æ¹±|y Z6’‰Ä|¼+ÖÇÄ� lhH 󹄪x�ãS[­ý³4¼ <±}*)†Ô�WrÁ@ŸYT�Åpf?!©YtáÖ�áëC¯†>ËEš*Àáldi­eØ>uÉÞŒO‡ìíÊ¥<úš¯Z‡ÌStiòóØÂ÷U¬�9~ª”Q@¼©éU¸§gUtÅû]Ènì&Cð©äpÐ4П£)ÕúÕ¥é�0{úŽ�gS!y­œ‘ö^ RùŠÊÔ(1¢Ô£¨3FÆDmãù3E€l5gfPkÆ` ÷ƒX 8d NÂ]×.ö0ƒ&€_U|j2rô_©Ãc«Aä_Ê`~÷Û† …7,\€yh‘¶ È”qá"Eׇ”|¡ïÏ —½[DÍÅ¡žéäÚ�i×ð2×üß;{ò�íå‰Ï ›Z¥¸�»Ä¾-J‘G}DŒrЧ�¦Ë')ª‘�¯2£�Ù×ŠÛø…Û:±(§ÆÆrï\À�òáHE·ƒjCˆZ}âIå‘òH3Åqn ¤;v_bÞÍ19F˜ëÌüeèù⦹@�h,auÔµ¡‰ˆlßòþ…„•�#®nÀ©Ž§À’ ò4W€Òõ¦™0å­ƒízn|ºÿ[Üâ› ¹M_íØÁÜû*§šþdeSäÝ%!ñ\öuÚÇ©¯ÛF•w›M�yaß¶ÊšßPr®>]¤q¹C¶[kPÃ/Ô|�D 3ÔBÉ VÌ~,^“êeXq¨Me%T¤#ýø·Mª@Ë—ÛbnÞ=ºÈ÷3|ŸL’dIÞ:‡& dë*ßSŒôn!_[ôƒ¡…c’GzÎÒp7ü•rì‚ãÖÊÌÒ¬ÔZ�ÛpF÷®ü�h�éÀƒ�`MÑé9ÿ$<ë†=NcŠ4!Ón|éNÕv®?b´r*èG‡ö¯R6ݺÈN¬bÎÂâ:y$_õ’‹· L„iô$Ãël�2ßÈ&}îkb�© ÄIïÏŽhÔc¾‡Õ…̺<±[ <}v© °ty…�%¤ÝjU’{—ðԶǨ àÍ7àè–¬E O�}�5Ì£$òÉù-A“>Edô1Hrþ0êGñ\<„?BR_Ù£XbÇoÞS†09AØ12!�€`3íiÏÃòG?�± XùÍ@gÔê'åmíÞ4Î;#@ø¹'wØã“`ãWmbH'�y—Þ³°ùÙÍmµ±ŒžIóiÃí›üÔäòµ´ÛBÆ w`ÚFá*`³‰b¡Ûå Íi¢ôü>cfé‹m=Ré{ý(¦�PÑïñŠpZ…5nŽM O@.ï©ÝOúk4ÐZ]HÌZaÚg¡À?6�8 StmµìÑãŒUÆ“«ØzûOªø¢�þ î`Ás\žèi7(¦ÎØ–ců*€\&~¹WÙ,æþ©·ê‹àL¹Ô"‹ ¬— Ͻ€‹>=!MöSª_K¿PÓo4CE°×ÎF¯H爑tå«u¾b‘á4£¹\MgØý£»ä ½ë™“õÿºßAGéú‘|ÍP§J‹@L#wüÑ 1Á¢ßL9JÒ¯£-+×U•†Ü P¹mÆû›#46{�ÞÌAñT¬ $eÊ»°m¬ \î·4J¹nö7ùæ„h#,l´/àÿ?xÉܼ«¡ÿæx‚¨ß 4ãsm‰¹h½Ug{¤²ý$ÀÓö -”Òt÷jÄH.Cihü•ù¬4+v?ß IÂ�àí@LìRˤ¼}ëo}mdy9¶‚1c¼õh ê¹¢6Éñ8n[BIÉê–ãK·mZŽ¥|L�ôª~•ïÓ»�'3|\bÛèFò»MæÂó@1¿ mèTÇú8QjÞ"z§òkÂê°³©6�Zu xíä´†‡¾×Òå`6ÑbB¶•¿ǽ–¢¤–mµô9 ûîp¯ÙÌ7�ÀxÔ$±wµˆ¼\@6t<–Áúk¨%‘.±Ã ¿ÊxiW¿Ž¶pÜšÊRÞ(^ú|àrN>5¹„ž G�îàá2]‘T¦Âjl�eñ/«�_“.¹×Ÿ)\+(Í‚9J`äù‹( Ýt8o¹{ÛŽ¤cÏž¾¤eP‰SÀÍÇP¸å–‚qyö¾ ¶×z+R‡Ùj8{[«�ÝoäiêšpE&on3£È�è)ü¯�—Ü«��Q¥òM÷ƒ¯©šðÛÆÂ'tœýÙY_Ï•*WbélˆŽc¢\° {Um—Ê&è7±êR/àóÆ"°26ðñÚx9”SöÇ^’€û’øGªp=Žñ]—ê…üݤzrŒ×±3E´ìEŠä4‚Õ,…”¤iÏ·G#|žjåe¿œûªæ r‘òíÐu®W²oItV$^EÉ•Åú;B¢W,+]r˜ó"‚Í‚…X¯£Ù¡ìýµv¥€V³IÚOo›m»œ£f|•ê-–…½T™`¨¾=ôŽi&4¡pFÀ$óè:OkÄzÏkçüÜà-jœ'Ž>ö¼ýÌÎ:«ýhh€fŠä¬aÒÌFÅTYYϰî-œ\¡Ä )�çn+˜ê«†&M©ë�ú!ªØ©f€@·P•d­â„ì7`âîÉžH¡�¶þ«¿iý/:¥—¾º'¤DÁ—0žùdp+e£ÿxT !àyq†À?z„þ¿þÃxChÖ'Ê/C½t;›CÞß3…¢q‡ÚtÀó»j-èÂO®h�, ÁnûïfÌÕhməݦ+Î-ÅÙ÷nrJÈPzí×ÐE¦¿3ñI4]í¶u ”^u¶™3 ؈§²xüSê‘v�‚Ïbç<`Œ!âì ¯Ó¢P2&îY¦È®R�yÉ"¥BÑAX@;Q°%;þák®'˜ç°“Ps9ƒ^ÄȼDü&wZøžEX9Ù6ÇÆ/vÎ `ÖϨ_�2ÕB}% ’�¨›�¸~R;c¶x>׆Ço*|Î]ë³x>Ìïʬ4jê¼æTê•VŠÐÝPLí^�”å��›k㤀ý8±ÅJäñô “+HÆ5NNk¯µ¥!ÅãmÃ#oüÿ~”‚¼ZK¾Ø§C?.I=7›;Áo:å,ZƒšÊXãƒõGZ^š^DSˆÊÇ#"4ïî{²¸^È$bº´Á‹žÍÕõàt·Ë§3|.d¶—Ûá(¶±êwÅP\�,lŠk¯¯ý­o‘ý¶Ó'£`{°Lt!Äú],ï)uù .ýÒ]ÑC�«Zlÿ±åC#W®Å¥m‡¶klLEžÃ=vƒK9‘w¦r”Ñ C›c˜–ˆ�k… 0Ó‡„D!åR\¤p=¹Ì¦3Ì´œÅÚôá«-õ O$¿7Í". ¡†¢·—‹I½Îð{ÿ²hVŒšç‹g§Ì0ŽZ€ðVσ�a°[3¶jƒígMJó•Ï0n1 daÿÏq³CrËBj‚`F¼o3ª�3·aËg!|ÓbÁQŸlóâî5y‰¦¬ÂSŽ ŠÙÍ\Yšõ¹´€¨Èßÿµ¬úƒF,xŸ&c ñÝ-Þq÷å<©Vý«¾¢ÂW%Ö^�ëq-—� u'®�W( D÷=•½šÛ{ñA… PåÆp�E�Î+Ħy-­Æ?ÍÅ­<!'E±zßΈ[=÷³;¼æÖ/Àå†ó�IÞ£�c# jh%ƒT\ÈÞ@ †-þÄiâp†Á9Õy(ÿ5Ò­ÝÜL¹#ÐGJ°õŠXqX£€—°¦<‰·Õ±¤nÉ>º…*<Ÿ§aâ H)q°˜ólspZí§ •âdžîd‰,„Pè+ÿ ¥É•vpŽógŠK0DËB¿¡ö`²È](Ù×�B¾ß¥ß¤Ñ*·T¹AÈ_S¨Šg=‚�|ÔÐý©Ò°\ïÖBgP¯¦Á/zê£;‹BC­`™Ð|GÏ\ùocM$[@7©@¹Vƒ•‹ gÝ“â_dý%�šWP˜ÚäAR´Ä‹•Ñ�ó ¦âþ�I¸ÌÓ8�@V*ïÇàÖÔô²Úóf¡EŒæÍá´rÂ5” ^¯{…×xÎUݤÅ|úúQ±‹—u�ÁlïØõ‘:}-:7ÏïÊJë—q…[ê†à n‰â€ºäʤ4ß¼Æñ~î-¶q:ùã?ôíró¦†-Pƒ¦¯tñ8¿ Cbº=åËgê—ît¬ R”3e$¸LIçÙ«‹ƒÈK”vIIÃõO3ÍòìW›ÇZÞ¸Eò1­=º¼ÃøÚ7 ÐÆ3ó´þ[ÊbqMrCò˜ßP‚T”}on¡ ÔÌ!û~^GFËÀv1Ucâ ÔGÓ1úM8* +(GeÞsœ/�œˆ�åR�Œ�Ùš™ÆT/,†ãàIqD+"R���o�…‹Ï¸±Ì£¬>ÌsTS<Õ*ÇÇf__¥|4Ùé»(Ž�,4f¸z!}<;¼T0Ø?Ó±úÿÛxÌ-`ŠfFÀ-¹ðËôSÆé.ž¬è¢i¯ØºùDº¹JæTÝÂвù¿@¯å>YA6©g+ üä~ç€ZÞÕɪBbjUßÙðYŒkÈ @²er±w±¾<Ô¥{–zÀ‚Ñ=äðûÝíË1¡Æ)Z��Íœ;ÖµŸ|îÙöà/ô¤UM±"G.£ŠÝ�  _×$À½M¬æ¡¡BÅ °FpsÂÈ ‹©õ&8êv�KH·÷‹ºà)ç  Æ3pµãíì´ÈÇ|7/9˜•uäX šìºRkó,¤en’ýɸR¿(„'ª”p`PËo'ì+|ø¥÷Ô7¦8Â66~ê €í7þçaÂ)ÐÛá8•DþwQ,xÏýaT¾9B¤øl•y9ÿ.�ª©¯¹O+ ÊB?&2ùÚžCL –-R˜sˆ e“Á(ÕÓ`=–~~{dïŸéVÿ¬-àQ|~jPçëÀÃm§{pêƒÂ垈XÀlkçžxËiõñ‰¾ŽTÑäþ$ áúðZ{(’ß.W¼?[†OÕ|&¯!ª?$˜Ï¸IA‰¡ôÆ;ÑŒÏËdÐ�£—B”_¤ÓÎ8hØÛ7Ó‘vÉòö¨›®Ütgpe8�ÐBÝ•µ;Ô�y…ÛÞø4U´ØµáI`”ÌE‰eÖ™•ô•t¸a’2‡‰9=âf5`‹œô‰ˆÂàÜT¿>Ôä•{Š,kë°!ø»_¤Q¥3oð|!cn6�ßmØ(e´oiE1jk+™ã›ûéÒdPãÂùÙãpϱl=\œ$"‡ ÄíY4:éä Å�äšeô° 1ôP¤�h_ Ú®]óð �§? 0`K *Æ–þm¿!={µ³ù¸ôñ¤.‚ WáH2�õ_Žzú•9Ð+xî³mµòÞ†½¦«'nV!‘È„Í_ Dw ²¯MŒFcŠ^Èè¦{„ úäͯ×1i”pk:œL\€Â@Ó[ÙÌL0ùrzé7r ÎϽÇ/>îרLÒœ|³fˆŠìî>¹EÞ³zï�5F€�� P;+?�ž«‰hó�s(¢—±QñM®-" *³nóq󈘵§Â:Ìš¸(1Å[NÄã=U'Q±ÌÊ'ô·WTÀ3¥[b˜xÓô‘=OEÇU=ç|à &O©dy ¼ð� ÅXp¤M´Öð-²nù6äàïÎk®Eþ½4+¥dŠ_²3§™»FZpJ[y1ÕÄEܹ÷\ƒÃ@ÉHô�«íñjÒ�Õ$«Ôøû2%#м]J]X¯‡Ñ0{M‹ÇÅA½kÎO–}/‚ çÂI'ü$ØI•5\!É%{äÆúï9UŒ²ÿL:ñ¹åTtÕNή®L¹y˰âø<2ÍìÎ*벺KÀ •AMµ÷èqC°§[ 2²§%µIêÆʕ±w c�ƒ¼$ßÓ|-„gª$oÝ4üЭq´ðæ{¸èØŽÊ�«} ‡ñŒôÖµŽm@Õªêð­ÜÚpIÎ>„ÒƒT›`1ËI»öi*³R­s ãòa²Þ áaN–vjŠ]£¼(¿Ï`N–˜žÔ@@�µy2Hã7Ê "÷ƒé{0<ªU©R¦È÷ŠB°fs'Æï€ËVÊå/D/{ :ðWå ´ô7[ãö2× £²Îf^œ‘[Ç~r„ÀþĹ×›&ë9%$`0A’Ê̯§‡ˆSÍjsò†‚*B ¾KkÊRtÝN§«D‡45”€yF&¥#›Ò÷М̴-2j¨«°I⢃b%’H†@öÈÍ`FæÕ+µ•Án„Ѿ¥øC�o¡qÎíqÄ’+ºêöc¨�®6/_>º�Å01!pmõŠáÕ(04Mc3EúÎlOâ€x�.j*�¨õ¬âè׺^ÃzŒpD –“µÜ"ÃT‹Ð�g`K‚ȶ£ïç�Úï°ä=‚€u4åýþ»jšDö  »¦u³r‚_óu‡E]sl¸³Tn Ï}€Œk¯ýß�Þ)c¦é>rxVX:‹õjŽ{.¹ôè‹3f }k­¿)81DL&²Y§%;¶ú{l]pó³²GÛÆÜD'ZŠýÚ•úC×Dœ­¹è}5LÕ‰â€�´ÍDÚ¼‡‰aÈrL>Q4Úqvýâñ™aZÞ–ˆu�Ÿ^¡XÈyÁ*Šp˜ÆŒYžnÌÁTæÂ3£ì8Ù˜ÿòS“„˜ôðWifÛ׿¹Ð°â\`—ã|xù»¼¨J&\%„÷Á9.{3¿F«Âq‡Ôï>K·íÀñçYß ÙÓŸp! ßF`“é:—í‡Ý#Œ«†/ãâ…¼Æ'. `©³IÈò"8&FéìU0Ó*³-XÁ ù§4D,j2×óì+#ô\î"r‡72CûŸ2‹Dá‰þ°<°â ÌÍân-JŽ  çí°gZGŒu"<® M}Ê®¢bþ5s ¸·�°²ªÇlSÑ¿A?¯ðþK¹¹ès+v~P#@_Ê}2�^Ëc­W¬l³üšè`±mƒ’˜5p©Ãù÷_¨|±O×àò&‘ζÄÌ_Ã'Q‰^‘DB}OÆ1íeƽY>âÎ-U½¦ x’‚fÑëlt-öY)�'l4û›æ`Ô^xWw>ze'¯êóýwž³ª8 B½•ÁÌ‘õ^0-ýíƒjSUÌ"MÏ}â<6Œ[®–Cà”cÿª‰ûB/²éwt”h,•Ó}Údðx½ÅPaPpY12gåµÔïÙ’Áœ�4HÆý<"W.ˆI,~d�«£íªÝ\b¸2PW+Ñ3„žÀ‰$;&TE vº)—ß®;Ï0®5 Ù˜q~ºîÏòËÙrþÓDf<"vWêa@èÚñq¸ë‰ZÁÿèw[J[‡4 âù4£<ÿûˆˆ8ç~%Çì¶}­ fb\Á mOXÉ퉑øš„›ô›ßrÔ¨çÉã'¢PmBàÉœ³Ü®j.Q’^qñc¡Ì¢��zNÚ¡Ö5y™wÆ÷*IK~òØ6o[Ò«ýE” @ô÷¢äâ ›=ÊÌEyßH Ú߸üám7Âç/ôWÛäŒl³k?XUÓRI᧤ɲ7Iig¼„ß !ïöù{ôq_×o@SÎàõ<…yæegdxz*ƒˆŽò¯3n0n:sþ_\f}ïòÓÔ:aw�t–`Xù3ˆEÐIEhYÙ½ßeh0×ëbež•µqˆý»$#a¶(Užuà]ê´CÖÀש(Sq ³Ð• ”&±Û™­29îoœ¡z–B„ÎèJ=[ {)h:<{¬Lnu²Š¥îX×N™Be™ÜÏ û™ÆŒŒ@°|­›=a›þˆ§QŒ«Œt¤µ³ýÜĶcypc]TŸ<`ö_ÏSäÌ*ê (Í'ùT¹q¤“&ÇYÜ_�ü¬ϰ–1 Òµf6Å“ñe®¾ì¤F;F÷Õ|óNI~y¡œzñ�/ˆHˆK&ÛâløyíúK›ÊÜÍ•`Ûú2QU>Z| Ðù$ñ©‰Qu�§¦ÂË‘¿+CPïˆMë¤á¼IeFƒ ùµ¶Át›ù|M{åÓdˆúoŠn`=‹¬Th·^¢…‘B¬ûJv~™—¬K+Ðý» 47ÅÀ¡$IõËðoþsôs•„&žyÓéÍ­YøPÌ,ì �Ìÿå@Éê‡G–¥­ï2CÕbV~Þ¡½ŠñÎêBÈ)ņ* ãEM^ß°‚äTîgã�1•‘õ”ì[¾± nïøЦA35SËÖªÚmuD̓ÃÊ勦e%a¿S¹ú†rwÎâêÆ3²ûÏÒ©2¸U£WÙÌoÖE¨³zïÿQþÑû0]ò~twº&ƒÉuöÁDŽ0²~ŸycŠÕYd/ ó8·3.E—<IÓ–a ÍÌJ±±ªÝà,�;aÎ4òAx#¡�l<àfÓó ˜=û÷âR ßln—räXÍî+Xô-ž@ÔGþZú¢`0ýO'�\Ð|ÿávšÒc¹vĽ¾·®­iƒúÆüã§vMˆàa¢ >ÿ$™ÞÎc°qK÷_®¼\õ¶JéÊx¤ÝÇ&rÚ^`.}àeB“Ûê™Ý/(ÍÅ,¾ÒŒv��¡âAÑ“LZð$ù{/ªE"þþ(½‹ÚõÁèxéMa…1ÂÁÁTÒL„…:–we”ø&ŽaØJÈóôŽSŽuÉW ê^[,C J M¬»¯o¹CÕ0ªâT{·G‚2sÄœœ‰®ÞV}³lÅC‡u.&ÇÉßü‚ÃXñYQ¹º¢”®5\àNã2·÷åÑ} �°’� ³§¢^�"ÆÖç�à2‹… &–Ù¶¯fhæÂ²ï~’b·�5…j|‘�:žœÇsv±»¿=Œ1B÷<}š¾ƒ4ôòo:‡@ylÚÕh¶_P]«þËA@d9IÝíêš{Ïüîb¯ôbBÙèD¨Ç¶ÏEç$)<¢‡ß´>þ¾¢�aï‡C½WŒÇfÂ"Ä2ANÎ8*I*µFXÝwÕýŠmeyø°XÄ÷m“ÌËiWAÓ¨ÛåR—0y 8©çHV…Øg/mŠ, �¶‚¾y�ŒL§þ ›ë Í(éBxãÛÉ[ÓlçMHŸÇP ¸<:4F“|Ä%º¿áú][î‰á¾PÛTÿh”:Û•îªÕøñ¬¾�a·.#ã13>â0G¯â¬XRJs†Rw,èg´æ±±H¶=%…‘–v qöþðI¤Ðßµ–oÖ¾u�î†+Ó�RXIð^˜,b¤ú’ �¢…×ÔKÜPsÔ“Yäáw:ÆQ{�m‚ïî·™²WpÓC›[Šˆ«FtÈùhÌ^Ñ@I(öðÑÇHYŃ÷ã^§:Ã@†3Ò«š²—’¤Ÿ3h £Ê¤£› ¿B5œÅ.švøßî„R$í|lÆGQûÓæ=z®bˆä®“þQtJ1æ7AÝ^À:l„-îÓî>Õ¤ÜÚ+êOd¥œÆÐžT !p·Ç¨ª“PÈcªRe+¯5òÞút)Ë ½3($�¼:b¬Ey¼mwé`àŸŸ)(…Y?1kµÛªžßæqø¥ÞŠ]Ù¨ŠšH bÒ]Nx· @êö4}íÌHÊ|Â̤¤½ôšE¶…x(þo5ïãUÛØî„Ê�ÿóWtÐeã{ º¡§I‹Y¨ƒÓ»_3®Þé¼á;BŸ2æ0ë×êÖ¹è1)ö÷ÿal2=‰`™{”©±ØiÎ £º4øív{EÛ™wVkÀ4l·2Ž„î·þK ™G‘#u›Öx ±#�í˜4îܨíub”i£ù~Éx&†QߢD·ÏÚ® ÿ€ÚI5¯a¨åvƒÙ]N~DoÒ`éðyƒ#bÄS¹ÝÝâ/�ºbÐVÖ¹ÁÔX«1ö^?�   ^Ä-èÇ’¿ƒ‚Èæq¾õqí®ñsBGã^ðI‡´›74DpïQ‰‚Ý ¥úO|'‡$ßü´ºWÕW˜'¸5ˆ3ú$s¿”||Ž‚qCz°:z˜òsÙGUg(æÏy‰õ0î*KGf?*ÈÉÿæì(f¾È_%5v:–”| j \œæâ·‡5I¹‡.o!@—>[á’Ý‹Ç@!Þ4šË�b "úR`õXŒ¨Ò7Þªr x˜Îƒ]—&­ éô<êc꽦l7 i¦ZTò�Ñâ:±¸ 8Öòxà§8CCž« 0™&G¼žÿ;_�ÅÍoˆÄŠ~ÑtLöHãa^±ëþÚ[þ×\¯�Nà5ë£ 1ŸYbeézL´Ó”'u~\ºÃk™\Çz]dWŠï¥,Ø�Z(,8èåPÅõJƽb_¢•¦-ù*zG'0º÷¾üúír~QDÕ±}ÒJqwiö´(êdöU/ ²”‹/læ�Î.BÊ2�dƒ6Þ°§ÿèŒotœì7Z×–þSyÐs«£eü¡‹&O#ÒmiÑ’w ‚FŽJÞ›"_aÑ“Œ8uν<Àlvv jîVäþ|º“'w_¥~²¨õÖãàM!‹ «ÊƒSÛ 2€ãcþáªC÷ú[gþ )VHDÔB€cÚLåSlh�Ò{pnµz½DWÔ€õåé&ˆ7¦–?Ïˇ¼Î�å:ï` ¦`%��›s]ìCžÑ»“,�+õkœ³ÝΆ*r¡»VZçڌßö|�Ê(Uq{hŠÑIEAÃ*³k_õ‡[7ý/”#f~¤›ËÆ–Êç!æõn]¤q_S·cv. 0Ì¢Áû”dýÎ!£«â*ÇRzåB¹ ʦ %�á/Üy}îëç c³Ö™D4h†?vPwWºÝe.èEH - #R‰[ï‡ì•§ƒâ'7‹z­6¼h’mªõÊ€¢)øMßÖ�Y³ÞJ$Âu„ÒÕ OîË 3f(ž©tñHHñå´÷TzÅd¨LÆX±i\é¦NÉûfŽÊHõÃ@Sƒé3ÈÛ}H‰Ì´>$�àÿMó× Öÿ׳Óxý‚®=¢‚|î�òTµá¥øþÑ2@_Po#Üz¿jžP@Ó6 ]I’Ç>„3¿œ‚-¥Ú}M›{=Óµ§Û˜Ùù¡Oå§xx^>ÿtº�ô|a©™ç¼obœm67èwÍUî-=²„¯ ¹}ÑÕG'Tšz¼q."ú±ófv—Ó¿ŽU†WÚE wŠþ1.yòPŒ£6½4–À%X»±¡ ÏN¶ù9|×¼ñ?¬÷j“·ðëÝáLÐÏn‚»á¯v“í–e?�*?µ^y*|#¨WÈÛ€PˆºÉÙkyçü:`6i‹¡tÉSçü20E‘)A‡÷X^E,£Wr²FUƒ„²ýQ‰2è”§ì/nÞZõHþg½KÏi^*9ŽŽî<%•–ØÏùçc[€l¶ög‰TÇ-´‰’ ‡�q®ÑMÝ“cÎàUn;Üѽ¿á¿|âx>ý-�3Öåpžãë4äÃúÅÍ[¿ ªü̺ãUa•0¾ROâÇ,%2ú.ÄÑÝ+ñ-à@žóâ?‰÷á”ÁÂÛv]©K`! íóy»®¾fW°#w¤�¾ ýG‰®¢½›rÃaÞ`+f¿�*çèÊ–·³ÍçÎ:¡‹»’ý,’D^ ,�â£t.ÙÕ„"�ð»²&(‡«çãõ qÅHV¶»Ò‚±ËÈPô•Ч M>»vŽÜÛ]P´x%¯ÿfñneÏ›ëƒÅ Y)ÀÜ‚GI3õa‚ìq½ŒÊ ¡ï]–(O¦‚ìfq!F~_9‘wQ@%Nó yÎñåÌ3¦ãån‹eãàU3üD7·óN{Þe¥X„äð< Á§£Ã@¿Ö®“r÷ìàŒ“S3ËéM±ç Í¥þ�Ëb®ñ±a,›D¡N0y(ÞhɆóJLo28Ps�9>Æ(ýåäÉy& LíÎ»ì Øè:÷uå¶l�V[ˆ‘ʘHƒ7è~ˆ1¥wbž‹¥pXHÌ9F�Ã7ª_ßrÖ;½M^GäÇ–�¹òŸã©{úÂÈSj¬ØaÑ/J¢ïr‚#�& )Áûö¸^^; ±ï¯·ˆÎ=_X"÷Ü’H-.�ÈlW¸cÆ×¨H]‡"ÿ¾ßj"f] QpÁ°iaÚ§à¥[?BS~qÎ@o\`óЍ¼¤Â 2£ mû("ëÃáΞþE±Ÿ¬‚íØ1V5¯蘜ˆj ³ÿ�P�íc‡Î9`‰Õv�H¯^üÑ=¸á¼_ÅC°Å‰Yü4l©¹A7ªkƒfw9% õžä‹#§�Ê;ŸžÊ·'e:É\®Tc0ÿFe`'‹™·S�ƒÉ #‚zlªi^÷6%Èü`÷>äA<°ëúžÓÝóñº"àé\ùåIÜH†Ê<ÓÍÄÊu¯p‚".>w±J:c{�q…Ý’¢#Æ�ãvòf.X_$| rè×Â7¤ÙØ’éÉëó¤’ÅÎ˳7N’Ž‘dÛµÆ4˜½¡›ÌF²t|c:@×™¿X<Ý l?Y‘‡b°Úvv�[¦‡Í`†3+²�—°�Ù\Hæ—- ŸA°C¿ÝÂ-°Ðx8DL=/µ0çî&˜oHÚ›xÌl ™ÏÏ£:ykøÊz¹ð}¸s~¡„veJØ�êCmÔvµ¶”1B +­CŠ!Ñß^À �âhP:¬NØŠ“Þ„µW«níq“Õ¢ÿvbF¯.áÚ¬Óʸõ<œ"ùS"YéT<�³/~F"ÃK{ÂÏê�ï ¦c¥›Ý\Òí•ðÒ²¬Fª¿¡­rVÕ{Û°ííŸbG~òtºf ‚îI%¢Æöjö¥FÔë¾ì?;ïéÏÔš˜YOaUþcð°;ðõ㈠£Óºgß„�¢â®j§FXº¯Å‘V}Û-™ÕÏqžøËô %%ÌLS¤3L<£3UõX\0ÑeB=ð9•ƒsöD ¼ñ¬‡Mí»ÊR^Éï“`ƶ”�tÆ?²³“Õ¢FˆnÓg“Ö þ–JÙÿ®†;™‘l›WL½>’ìl¹,œð?Ü"ž¿è[NGØ/oð‰ŠCÈ{»ÔPöŽ¢2�å9ô X„,÷Û¤:¯Ñ’i1»Ë¼6!1…íBÖ«RåØ$Ž1$� ÏµBÚ15i¤SÞ,;Q%ò{F)¾æö‹i²Ý5åeËmX´3XÅ¿OxÒÓîÅ}9ÙD�sÆS-D ²Œ‘Ökí¢L’&�ü±La#!ÿµ{‡²øäJÌ#^‘BOJ;χ¿¢CÎÏ "8D…I4Ê©KÊÎ`÷ U«½i0$¼Ž:OîüU%ËZéÈIéæÄDìfäJA ÚD$Ù æh”¡ Û°&ó�“�\¢ü¥ù vÔïV)6¥ ],6ÁïJX^æá8ª¢^µ{ ]r%¼Gê-Ó>›·ÄÏþu;t .¸;åƒ0ù3Êz¿E< å)¢ëdNª�é¼Ý¤—º-;(Gì›Ù|“¸§(EŠ"†8Ãÿ§åMè’8öÊ… R®.Â*ðÇV6VL…­^ùÏp3îk"Ý…ª˜F¨°—cCO®[”(+™`Òjøq¶»À±ñ,\F@Zì7 ˆ/pÀЯežÙ…P0xD³‡àG3ý² é’ëtöíF99á6è�Óè–”ÙÞ »ëËšÌSF­•ÉhB+`@|ÄÉÖÈ 3´7»Ûçj;7vD›¦áò˺§c9ôݬpáž äÝc”ú@,ì¥Ò><ÆÑ¡⤰J€@B’¯AÍξTVv ]G±)¯Àë.Ûpã.E¡ÿ0•«ÝšKñ§üqÔ°Äή@2 -ŸçPÌ÷ÕÈS¾j ò<‡Ÿ|xâì�7äGmë}'ƒ\´ò¾;Cc¨7E3ˆìñ’\e‚ªƒÜf!²¾cyÀ 8«»$­PlE°¨x8ªl^g“´ð@DD²rY ”é·¥?OH!ï­f¼œf úh'Ó†ªZNxÈ}ð¡yˆDŸ%*_ÌaÕöÉAŒp�— è�ªB4ÝõÑΔ 6K{eØ�ÍŠcr˜Ýº™)²˜h‚Ë`û5�ô]õâïÙ19;QìnÌa§‡è�Îq–ÕB»H‰Üwúq©6 à Ú ‡“YãÙ?ïè"‡!@è C¿‡�Ô~Ý¥ÒA ÅQÍÝÇÿÆwùVƒ†±3tì'-½Dï‚Íj¿õÜ®Ð]–-ãQ  Ï÷Ó7¼³êä}ßR·gi:maíQ-äGÖË„yDîªg/1I�#dÂ¾‘Ã) �. œ}SOÞ6¸ïGcªíøF({Èöd+� áA`'ËhÅsOc»ªÐP5�ι^—«=x”&{ú «¾=â5c[áI¬o0YÜ{YƒRÅ™G) A=Ï^Ÿ£6¶ÜVE-4B‘H=Jv�Ñe~Zcx›Ò©‹³ê;S… æ´Cþwûf™€L•íã»Î¹¹ܲ‰*š;3ç¶+~{¾F¯8v§E7b®#nû�õ}ý¥žúºA¤[ɃtÇí¥Šdϱ¶X†Ç[¯âOà‘U[!MÊ-( 1ß*ÜÓ[\ŠxÒ|2†šÌ#ü7w•–SHÃäÌÝ9Ôçú‡`ë•¥«1eˆž³fEÎ,­å�LBÑI=ç}Ó�5¨,λç5 ¬æ¸¬�¨÷ôžB%° ¯- úÚÿÓ:ƼÖÔ�ÿTRT@8q˜þ»Ù.L{ýí\Œ8ÿžÔiXÿuùoÖêÁX($wwî`Ч[DÚf*ïavk²B}øÆxÓŸ­!ú‹âæA$Ëp™Ñbã3w¸á5ž? –jC0œ]&‘d³o–…F:J¿…“¢eX9©'¶²ä�­Íÿrüdœ™ªÀú:A[Ñ™!gÒsãØÔŠ8ZôXèž²Ÿ|Tܳö:òöš—‘:v|³!=lïâk�ºúL”ÖT¬èqO^[«‚Ju\us€Ïõ­±¨Ïþ⢧â+E,m7¦tIÐ\ï�4ƶóƒœÄ‰(’ –—?ÿ“òbÖü Âð𬠄—ƒøñŽF'sP°GPXæw�‡hc°Ucf¬è\ÙL1wT9«tmõ "í1ŠFÌØËÕÛx5qâ®Ó³DÃ*<;é囩1&, âÃÑÜ$‰¿ü¦ÀÆûɨ>½j{ÂMÓö¢4»ú{îÙ©r?MDØÿ¸é‘½õÈHNêx5½p¤~Æ ÉŽ4‘ïÐ**==”R7"‘’«ÉÁ2ɃÂíØ#h뼾˿Q¢JlÕ¹¹ÐÆL­öE!ÀÒ-­K:žPp]{9ËßYSùMh¢0H`÷”õáf°Z±Ó§áhj ž‰ÄÜ~4î°ê#sAô�Ì�(Žò øÕë\Dú!¡¼½[ú1»X“‡4� 1•ubEÀà0«Âø#½€ù°Ž}–¿DA/á](*3ËQÝöZ�,Æ\6у ÍøàHðŒÃNg–R'™šÌŽT³Œò§ÂŸLI*0ç(UúÇŽ]ßGÖÝÓXyüáÏ�$§{œÉ\ÒfÿÑt2•Ñ8&ï�iݽQóâ¦CýŽnââh¢¯&6ä úËc–½4x�ƶ‹.Ü–�¸‹ó ØG<þiâÀíÝ'÷†äYDÀ=‘Û®ƒ D�KB~Ò�>ˆØ`ÀX¸Œç }¿'¢óÝþ]‹Y½ê'œŽb\DÖÞ<ÌYµzv¦àø#  åÃ\á°?~œ!mЃG�/X¢Ãã{É3ÖwXsY÷€)ùåqwÀ‡‡hò2Ym¦´øj€ß`€<=?Vd^¾c?�‹™Ó�ÃÎZË*>±9„V X}îįXLþ�6Þ]xQ‡/Ð'W³W?þ¿‰²Â ¶ Ý‚Ýb ÉXÆú7¿?¦�!v@ÌzÂ5V›:ÂFjJ }¨Kx,ôüâ&Ç‘žbÜ”Ê�c02¡×sŸÖ)Ÿ 6r£Ÿ\ÝaéWú,É ñPDΟ pdÓºG é^L åhÚg6ÌÕ>Jù$ÍY,¹éôô¾=WЪ‡)n¢Ì®ÔP}½ÈÉeÓ�ÏË´¿öÙß,¬L*£˜î˜Z–�7;cÁ.9­ÛUœwq‰ÊwuiJÁ'³]űЦÿ†&†«30Ñ@62÷6ÕìÉû�J$ÏæCÉY¸û\Ú…Ø%'ƒ_­&DB)¨Xçûùanò0´?qW0ùj®ññlj³,ÛOú Þ//>X;‚dÒNq¶ÜÂ7$üæâ¶­;V P¯ët"�Ö«Œ7g¤Ï—ײç¤8U„å²…Ž ƒ#�龿(uµ\†.#�A(�&3éCR¦I�JÙ‘%óNóô»ý]À¦ÒÛ’<óÒуm .‚(*€�–R ¦ Üâeß[õmwM‘`ž7wÿñA$Šk V¾Ù±¾¦èèÚ‡ÀÊ6 +®ñ 5ÆP1õ; N)å÷‹VDÚÕ°âªaÕk>‡‰.7þ�l²‡ÀtSÈ¥–h³Z¶†¤ I¶OCòr=й ò Ê{[f‡»ÙjG?%Õ¨™(ä*?|iâýÎÔq«ãŽ€¼l5 —>�×7‰ÚûO¶ºŸd@öÛ„žØè`‘O¤ò:Òu @¤£†ÛWݾ¥XdàÇiŸ°Ì:y|‘“” >F™}VRxSkCЕ-(¼ÜVˆðYš°O‹>«çýÏȲb¼…ñžk¾Ø"‡C|¶€Ìö¹‰¾áœþòÐÌ>3à�òÓªˆÆÍ´ê·¶Ê›G ?`ñ[Áz¦}ÎeöÀÊ `÷'*N‡çÚxÙ^¡¡�öyò~G-ï Ç *R½Py´ž3è—•#áH#“1N.s¶Ñ9á€$OŠ%3³¶‚z ÉíïùJ r’[Î"ÍÁçnøôïÅ>!dÅRò{ŠÐV�H8¤áÈò™}ÄjQ4Œ™SSð+4ÈZ¡þ‡ä_¹­÷¶êIÎ mÆ“Jóú,hg<fš¾çèÌÍ+òM:6ÙÞ┿€çQÙDz,{þ¾‰Eaß5:ŽÕº!\‡ó3FötMgiî²’ü€˜±ªÿˆr#¼Å7~ëæ/M8fJes!P]àéq4üü™Eë„f…,û4G#œóÊÙíy 6‰ù’]|wÉà’¥òv×±Ô6?b [éñ¶¦â=lÛp*áy.efg†ƒAY’™/,ÉìhÞKÏÏY †‡MÜ¥q™ÐÕ Ðf¡¢|0 Üñ!ÄÃn%0¤;_ïáz««ƒøFÈV™_©—篠~“”ËGÚá ú@½Dß×-’Ú»À‰÷×誫 Sßñ}Ássx/¤&µ¶ÊlsJæa”Ž({Êß¶v¿¬ÐÙ×­ÄÊÏî‘§ 롘ÿñ²ðu‚Ur™ýËN*ÂðU†õ¿ÄÂR¶ßùlR˜ÝP9gÃ�yW¦)^Žq�ƒ?¿ýÕTÛßÇoÜ…Äñ  ÍŸr±NÄÊÆ æSÌaW¥ìíÖ'ƒ¯¤†ÛàfÑÔ>ƒrIµfýÐärFøk*b‚âf¹K(Eô¦­MsúƦ{“‰«É`™2<£ž‡“.êD\Ê¥ŠÖL�h/Žò‡w¨¿P½$ä�‰ X¸÷‘úmÖm7n¤m!c®€dÇÏü¦ Ý8¥É�ÈÚqÇ—ˆ$å kÿ%;Ë;‰¾ØÆ¹ÈT]WŠŎ¤3eö®\W ^!äÜEÐÁúfIŸòðIípÎ3z6§[‡¥63³‰Ÿ4b¾æG ̱ý¸Áå{ Cæ*Ö7\èlÊŸãº2“Ö—¸úGº Ý—¢*æÔÕX¨–…Çàá_·æ E]U{+:˜`YoWøDHpK MG4C€JÊ�4g`zÑ Ùþˆ^�ʳê°ÅäÑ?c_ØRfÝ~ËÁ»Ö¡óRfó2·‹I²;0~O‹|w6§²&kƒ Ô¨:ƒ8LdéIà ‘ïÃäUwü/âµbð6:S“„¡‚ Ü)–²j%JX&©@—ƒä{¾Á¥}H®0áÐôNÈ¿B€`ÞÁ¶…ǧ{‰Å݉.X6òkK×yó˜.Á�¾×}úï Ø©ëÊðÜþ¹µNEÿ1?öt”|gsªä4d‡EqŸ=娽µg4ó@{ؽ‡ýõÅQ2x3Aò†k#Ÿ í9ø†0êiÚgÕ#þ"=r{ºp?¶ýX¡§ˆ:Æzà©�Í©0 äíâê,Nh`iÅÆ¡Ä ·(CË0TÂäúøÚßž¾*u�u=™oË7jƒu`{âµc�ó?�E%8'ä·„ÐþU‘‹HÚ(ßËAª\X/?®Y–­vëºd¨š'Ð !–¶ôg˜Ûà œwçÒðüu@(¯"ø,z·EnOsz]Fk�9¤ì H�Ê9Q²öŒì”`î2,nô–! v¨Ä/�†Ÿ‰…ìÜÕ‹ò9 ‹Š˜œL{:÷]&¾é G¡þüØ«PßfE#ú= ü»„0"�Ìt°ÒK¿ý#Àhò”�w,èÞSfôxŠ–MzX-ó¦›9„x|ª&ö®[G/‘×s\$- q „³Ã̇3Ã’@þ0.]J"}ãU9kü†M4#Ñ訠5õo¢À@´±„Ž€{|öðQ�ŒE%÷.δ&÷}Áú ™˜>‘e^}WòâÅŒÉ- ù¯ºáj"uªo�»C^–÷&tZUz%"sú—’Yù!ySÚ�Vo¦¥<°¸"Æ,G·Ö;VØ;X»µ’ZœDub]L‡ø¼L±cÀK”E@n¡«âjÚ‡êG`ÿÍ�âKÔ;& {¸�ý°#N^(¯aíŽûe„œqõªúhà  Ló„ƒG¹Gî3SGY9pì:ž{"düÛo×€˜é_.Ù?'ªÆÉ1Þ��Õ–1´`«÷Üî ­æß4âÂéòÆC'Ì.jœ©Ì#õ.š˜�ä�ï°�øEê¡Í¯ØÒ²0^ Ûzjê`“Ù<; h%Àn˜Ñãð†µ=z­Ÿtœ�…£¨ˆu´ê‹’c[mŠ_œ dµã³œ^¯ç÷÷FȺóýò�¶‹2 S Žù[•‰ ë‡ZÌw«vp‰|>l°Ñ…µm̰]ÃMâYš²E±Gsâûü6Ç›ˆ|M[ FLó ˆÜ¾xƒœ¦Ë{²óR±â±¶»²g�â ‰çNQÿÆûâ�Þ1�¹œ$ºWsKÛ™n PÔnÊa=;=|ÞsÓCŠ-™-…‘¶®sJ¥2YŸM2\p�"ݲXsèŠO„»•}È¥Ñ"h˜?�Ç&ÍGs Ö6Æ«¯û¶Ä1æ‰Ü!ë^½ýÛ~7#šª«^xËÌÝf(„¨°�·MŸ´›êÀ¼FWOWÔndï4¹’-˜å´³ÎÇ‚¼.“}pïàƒùÁHEÖ­fT(;Åú�Jç ëŸ]í߈ ò•t§Dº6M<Þ¸WfbùžZV7ǧ…ÊëæY­ª ÌèÎjèÛ<Ž&-j{½tg ¦" ;;—œNêÈY�]Ù�£ÛÂ&"é´ÝCL•†ýÄÄ!‘³’ÞÕǔôkûQ #>Ð2Y}¦ƒ•·XLÃiðšòï°]÷:R{eå¤Î!äúJÃèíÖR[äû<Ã]ž�4åÄ%<�IU¢yåã,feš¯Û”cÖ6à Ÿò81r¯‰so7¹�NûòÖ/êÎààeoþ½¿ìÿˆE1‚ p ("ÚˆéêÃØ�Ù¿o„>¦�îfß+¬CøHÖY×aAÍÃ�âɯÔ…àq#߹¦O—¯ lcÄpÿKÙ�o¯kÊ«‹ ¢�©BM¿R öÎ\T‚òUÛ`­—ª;’eËS¹+špl–[0¹GŦ Bc-Ô†¾ƒ+­��\ìËû2Ù×:7n?i…A_=Æ1.¡ìÝIq8¼õ“ªä.Ëiä¨v�­{æÉ[€Ã>_j{šÔßGLqå¶Àèö¢-fõ–ïiTŸ\†þ‰ •uÿÛûm ƒ²>é¬JÌÂFŠ„=Q$Â5EêÖ®TŒd² ~6ÓúPÉP€\²°æÍµ�޽Ýî»0 v;ÐS©D÷f<êO„²o‰íä\SrË'{Ȫ¢Ë3­7× ?Q JR �±v6¯ 3ßtÛPÃZVœZˆ 5�¤ñdæ^$Ø€ÿÈuZªW°ù÷®ôßïw#¦oæö[xÄi BÄ•M�£o\&M³ Ú¼³@*[+ZhÅRÔw}m[zk2¯½ó•‰'uI²°N&ìF ¼Á¶] e6–†kò£—ºáu?­ˆtS [!FW_>ü„¢¬q@=gOîr¦Y2¬žÑÐK÷ëýîÜh¦¶g4.u/wh‰Êç�‡_P‘µ”?2-æ”›þ]ÞË 1/zBkãÖk÷ÓïD¥Vè 6Ì[äDÔ0A;²*9î$rM—{BaÒ™G”4@ùd”õëîä ® ©§lEÕ©r|™^ßá™M¼ ì=ÎT‚Çã…ãÑÆ¯�¹òTÒWßY¦îQp€0 ©J}L +™ŠNõBvꨎÀ¬*�ÒõMü²³š5Óø¶­pÃ×E`’Q I`û²ŸÅBPétgQ¸ôø¦$J>²|¨:9ù¼a5Në0b²ó1\"ãnšòšlØ^óée]“3+zAfE®�_Æ&.d´�Í]6V#�Ÿ-Gü�¶2þmiÌi–Ó³-7Á„E«‚Ý·ÙÔ/ÉÜÜîë*¶VÄ¿~×�>ì¦gì½/lŸ¬Hp³KĹ¿j~v+õÞKêdi0 Ò†"÷ažL(5-¹[…‚}Þ+¥É*b³¬cÁ‹tflƒ¤~7�†^ søÌý ú È­ÝN úÞ´ÜÄ:Fm;3zD+à�‰ÒëÄú•»‘HÞæ†ôŠínÿÒ�ñ\"z˜õÑÅõ´F5†Àxj"´•5NŽ2Ö$ÏÍÕÆÕœÒâã-x#’×lUqðq{b,WcØd·~9ÿ­'�fä÷"e“Ýq%Á£‰ÓìûÜèjŒ"ö+§gàæµ©›Ù>…-}š§ú2ŠD¾$Á]tèYð^ÄÕMÈvè�MZk¹š›j«ñðÛz!ÕÃ"Dñ–¿+v}ƒ›¼ÂEÁáDí]ÁV¢gT·~ùJ^ÊÄž0Ì‘Ç.vÑ='ÁÉïÑ·ßáÞ˜à$‘ê– j¸ªRU�ÓqKtœX°sše‡G…ÌåÃ!¢ÛÊH*›F…%œRú%`S‚{ †Ø»(þ¸3/*2eˆS¬Y%4vý•ð¦fafð¾ÊÆtå8l´Í‚NB4ׇùUíH@6ºÅ2Eà�·“ßGhvþÓew«3ÿýúÖ@NÏË‹8-–߀6…¨¼3ïžµ½PãÖ*¨˜½”ÆÍ”v25ü`<…·�õ8Kh—5àŠ¶Öº(P JjØMላ`°ð7à\±æ…„¾íô U`à™½Þ²°[_fðã‰óÕõg°¦š/HÕÿÆ’¢§ µÔЮÝfòÛE'�:{° ¤ûY(qä¯A±Q¦¬ú»Ã]ìOÃ>&cJkÇ*4$¸M’G>ÖŽéêm>/››Îó'´d±•¬ ¢Ô8úÝ ¸NÄ2àßÒA€žLùóú”µ©·œEƒ�Ü5,và×qZAõø¡ÆùB c;Õh /‚ Ò“ãMê¤Iî¥ám:ûHßr®#MÌPÆqVBÿ­K ]’æÅgÑ1OȦG6h)Qæ*>¾üFeH¦ aäH+F¿è²Ø®+ú•±ŸêùhyJüÉÉÊoÚyt‚àPcÊæõ€‡`u[ÍÎÚ¢«Ç߸ëôkjÏ{ ³©¤ƒ”V³IBÎ�,ÃyäMSÏ�pòÛéÌj‚ô) ¸[GKM Àj®Daí\¿šØói=ŒËèφ¢î¶RÆ…ÕÞ=Çò/x;Jnšˆ-œFàž×MçÈí2nÐ2ððC‘z›ú}ÿû¯ÈU’ÃMÙ±[Ç1€2RÞÞU†ë¬@ú¦0&Ð?IHŒ._ˆö¶ÿIÝ =ùÈýÜNÛd"æñT„FFeZbs6£:•ÍnŒ{ñD€&gÅoºÒ(¡tfú¯µynMêåR ï$±è.¦Ws~•U 40×ãwä„>Ù[ Ïn\•úìаNZ/T45¨/•3f+(ÒÙÓ5qò»¤%­Š"(è”}üP«öÇúטõNœá÷·íÒlƒ9 ‘ÁY’‹¬…¿ ñG“2º­Ó¶ÐõdD1@ïùœ³|±0¨D‰‰L ´óy²t¹ æŸ:Í×*#ß]–Y0Dlöš3gpa» .�ËAû.71¾ÿËTú©ˆ™ ÔÔiè‘äÍ–LÀ–¢†ÆÜYc öþy(QËßz²h­ãG¥$‹ÔÀ,7ÈçÚs÷þ¹ñ¿�•Ž|¹Ë�ßs¸w^>q$fª�\Eí´t¹Ò5Ÿ|‰¹ºûºŽ#bF�fÕv”ëýÅ5‘¸‰<ð¡S,» ÿ3„ j“m‰~&äùŸTNŒµ¥õ“�šlΧ3'}Väz;:š ÏH*Õè 2(PÎÀ¿(¥Ä J’hfvÌ9¡–i8�|å“"nd7C×=ß#c¯>}&s?BJ à&|6a:¤¥´ráëžDWûûæÃ-hP®ò§a¯eTú)¾«6è‰`\8Sñóh°Êj| Lè+-Ž3ØQ¤, 0X®�§iC@†”¯6üþØM!¹þI%¤k†ŸùÕøÍ©Üh‹ ×NæôìÜ~k³ÈúV¦�º[‡ºàí3I�À·:Ò ½;/Às•È�ªxfå©îus]”¢á­qúz¥Ã[cÞ˜ý})[jibú–YiW=q\ÿ~Ìwß›ÓùWïÃô|Bˆœ“UbÉ|ŽÜì†V¨É èËSQr6YªqeÍ È-ø’€Ï+±�©š_³<ë dTA„@iBOEkž¹v³•ÁdIn$Éú=!÷±ÌIŒHªÔYs�µ8¤¤È Jw›¨ÛA}ˆÞ ¯Ž4ÇÒèrÚ¾PgŒ±U®,üÕ=ï€ÌKÒú1ËøŒæE^Ù%ôÚ`¦lËÀã¶´sAÕ :ªV³³L2�¥8™8‚h]òÐõ Ǹd ííÙ$Õ¬X°ö5Hå7ö¢¾1ú²ŒÅ+¾Xh9L%„‰®J̪l=û¹U( òœ ìÀÕCÃ/hKB ÝÔÆë2±[ì­p!gÄŒ±Õ G;ä¶a7r¤8Œ’˜?ìJ4¿pnLk¶ÖÑ|MõG BlÔ§HÓôÐ4À…ì×UÑ4OÏxŠ¢'.±Læ9¸ åYŸrëWÁZ Ìm‡¤!êèþÆ Ôã"b“­p!…€Ëf]WHaššL:Î8±«wvÑ7�Ùýx!­B¼Ôèv�½?<ö>¡•Ü®= °Ý7S-r¬!uøm•JÖà"VÛ!A™¿€3¼Â7�« '2ªF}¿,Rj¬'2¹â#õ¨q6RÅÑ‚;D{f€m pI QƒIoÛr[ ´|£Þ}â.Á5HV“üx.´ÄùO]�ÑßÀã–¸mX?� úêt¿äØz¶ÉI'í:–-5mÕ¹>ª½q°FªJCÔpd½b¨ÅP¢Ü“¼4x­®Þ›t (fš2'�/ûtô�ëN‘�ýNCM¦�µ,« çéð ¬ï Üöd}‚²¦§ûr‚éœTé±`ÂÃS¬”©ŸÈT¤•ƒˆ‘Ÿ O ˆÉ[ÏîÔ«ÇéA•KE#°x:4¥/;A^ÇÖ“†Gà¾FÙ�rgZºð˜›Qÿ-hzŠ�e¨9›’ùÅœù¹4Êß�¢}91OJ2qÆ5¹ÄÄçÑ«¼Ïz«ùý&ëŒËû�}ZTN•eÈws•{hßÌþ?ë±×/ý3›ÐO+hµ‰<’-»ÏÕ§ßm(þ…¯„'Àß‹3fÖ—tó§yuÿžá˜w»µ¿6�÷æ–ÊÁ9ÌÆ ö ³+ ëdÞ�7Næßo^/5”‚Ó:ãYæ›ùáŸ=qÈuÈkÜ®É+"ÿ“ ‡J'¤û,¼Í)Yâà«¡‘ˆ©†QÒ· TáGÉ­lÌØÊŠÑh6ø?†Õºˆ‹0}�ïæý$çi°œó!šî¸°–ÉS€ÓŒ4ËÓ�IœûFÆ“½'¥F.Ù7[ �\fÚ­è6dÔºpk€Ô­%Vµ¾©$!]�?±+‰®ªSÜîO&³Df™Yc}%ÃPJóíäj .‡%ÎTÞnž:DmUÀ7a¯¼�gJW~¨…4?6ydÙþòsv<Çq­¢b{ :B}�Û‘!�I,¦ô³pëÕ©_WeO1“áxs@ØÐ»ÅV|òRÿ E°ž�+V^¾è·?­Öý�  VDd°¡r†îÞÔ€`ÖàÁKs‡­Õ•LCê–©ŠÕÀK�ðA€¡]À›Ô ù[ Èp�ôäpM$-<'ñ!¾@ô �¸@€’ ÷m[¥ ýœÞʰÊQÝÅæ!À›Ö‰(�âŽÚZ$k¨€o_›q0W|³Ë Zg¾Þ„�ÛÏM—Šòy¼Ÿ|¹ƒm¶|¾DRJßAY8±0yHÙò`ßnR‚iä£à0‚.³©ÊZ&Mk„–=a-C&¶¹ý[„øèK-çA@o"1ò a×EFì K¼¨Àû €9+DãHÜP€Ñ8pÎjñ†¢ÏuŽÏ÷çãS²ŒÐ­ðÝ xy}èæ2…ñ”g!Nµ½s=NäpÊÔpÔ;;�^zÂnjâË× Ô ÷¸þfEüd=›JêäGÔ&±á“z±Â{NÑ„U©üxG ´»¡?Ü„\)À ~án¸¶hÎfi½BÔÒ¤›Ðk·�®_¢qì!œÁšPâ�!2mTÈ¿k ŒÝvRÖ´;UÂIïtˆ%.5Y ´É6Àe­ÉÑ~¬0pSk©jg‚„ |IEµŽú+CÈG¯¶±X¹³çF Œ툜¯2YRmXóÕ�=¢}±ôqæœ/sùdkßp6‰À>ÅÃÏ��Å%ƒPÄBêJrÛ¹DÓ×+vÙg€I -<OÉ ßpzøbj|Ü=ˆa;nR|‹Ý‚œ2ÊÒ÷ÓŒß7È›ÎÀPјýšptõ§H^XiˆÙ‰‰Ó‚ þ£‚„D^>BÖ †³©Ì`' Lï-ž Ã;ºÌ±^…jŒ×S~Z©OË*†‰:†–ŽG«°·™VþŸ‚Æ 5E¾(õÉ7“‡7czæ+Cga.ÿr±õloKwÌšïä–ÅûD($Ü_å{ùL®YýÔ�„XÔõó*¥“þ(% }FáY…/ E(å%B€Žì²l“cÒèw·‚·�¦Ó8q¨Àù¶äLzî~ó$õi 1©]ó÷ƒÒ]I;p·×WIªÐM&«ëôÔ’úJ¤ó ]ßï4þkU‘nà7b�›ª›bäTí²y¢=s$m„ºwÂHr¹o¨ÔØg�#s“ùñ~|ê�h=ñÌó8Í9Lïó +[ðAƒÂ•¹šs¡†^pI&è¥Z)éÊ8E˜í�7˜�Þ�ƒ„H÷�ã.84ÏcGÒŽ+Í:©9Õ[�"É>¨wmnÖ³BqTì•C‹3©ç%[ç�9ÙL„×ü1u³f¿¤!Ö¬â“c­%7,¤ÅJ¼°aëëC‹â/Hº ÎX¯\ wp‚|VÞX¼üÍæJÞ×pÏqÚCÿ?åΫMhåN‘“à|¸ËXÚÝö’‘vX‰hŸ›×Eøµ±tŽM¡HÛùUZ¹&¼>Y{Ò²äUˆÌk‚Ï�%’›§w.(<5¹{‚òÅQM–£€&]iñ£™¯Úò@<.ÁïćY¤ëb¹é…± a1£KºÂOxÏj¥Ã鬟²hòÚEö23m¤•ÎÞ�bNEmKÆ8-ž*Öàüi‹©w™äÑÜb¢¸¬y€w1q„h° .ëeÛý—›"UÁ*~×ù ýÜwø…´TY³¶ ­‰l’تa&ç’g*ô-4&á�HGs]ÍŠ†-×?mË* åÐáXÞhÇ9S¾-<�g&HŠ25 ËçÂÜd /µâ4ð3’.2}OR¬ô“{Òíþ2ÏøoIæÓ$ÜR�oˆ‰Â5í‹cp¦©ÐSµ5ÆnŸùáê‚ã™˜›ž×Æ*ÿŒ&8òðÅÓD‡û†§ ùÂŽ9·=hWeø²*qʺm±ûòƒ> �į´K °Î%n \/ñuC ù£Â1ÑAù©n¨²ò‰µœši6 ‚»ì!vgYðí6>J€7û§xI³nŒà}ë‚БË#VkpÚÀ:ü±æ•%@Hdv ÃXµg>¢@ìŒ)½g#6JCG�uN&{w?÷~ƒ@¨Ã¸ @xLò  QwT„Š’ÏÖ)ÃÜÀ·‰¶ÉÐF–¿Ì´˜k«˜�à“Ïw±s×€h9™T™þ ´ïMײoóº“쬎¼":"Ôqž›³BØÜîÃç÷�3å)D“rð_ˑ֒£+Âðçñ ¡vm/,/ŒŽ6²ï²Vîê¦üjí7œëÍÁ,q¡¸H<·bÓ{"oI¥²¦±h›ú(ÿN§<@ŒÞyÖ…§B<ÞWl,iG°GòŠflõÿ‚V›Fî{€#?S^з1Àm†ŒŠ,ëP›ç5ØRKÛh‘Çé…kPÜÁs‚…Z \5íß7š?uV ×@”øI÷íä¾*™Êq­m aE²YÉBå0·Ñ>ÖJÂ+I=kà7ÎjÙoB'Ä0ssœ[ˆFÌQ8ƒšiØ]‘Ó O^\ y'Ò6®݇ýú.•��C°Ø‘¬MBÔëÀÝ�¥?Þ¯Nüe`’½ÂÐxÎKµk…#ßì �VcÆvT(tDŠÝ³¿ ~¸M­�¼â"=ÞÔ8 ,Âζš éwDHöYú1Ó$gv°ê‘ ÀÖu¨&Œ¢xšæQ”Ì)÷qÐÂ+9/l{Øhñ޽Þˆ1œ3kËâ§-XÁ×÷ÃU4>…¸7×ÿ7(Ò{ Lf¸,¶–›› 5)ëà[«…jòþÊÑ<’¹ñÆ–ÅX�ê�ö“} -¦Qã»ý·ÊôNmm~/ëª&wbbRØ?©ú¥]Î�¥ç "òÛçNµ�=£½6]Eä¯ÌFã0%ûf¶7�þbå„_2­€æ1Ò&––ÕN]Bú«_‰²TÑø Â�q)ç§m¦ÎEÝu•í²Rhe8:url-listl94:https://dumps.wikimedia.org/enwiki/20231220/enwiki-20231220-pages-articles-multistream.xml.bz298:http://dumps.wikimedia.your.org/enwiki/20231220/enwiki-20231220-pages-articles-multistream.xml.bz2115:http://ftp.acc.umu.se/mirror/wikimedia.org/dumps/enwiki/20231220/enwiki-20231220-pages-articles-multistream.xml.bz296:http://wikipedia.c3sl.ufpr.br/enwiki/20231220/enwiki-20231220-pages-articles-multistream.xml.bz2eetremotesf-2.10.1/src/torrentfileparser.cpp000066400000000000000000000131431523411710200206460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "torrentfileparser.h" #include "fileutils.h" using namespace std::string_view_literals; namespace tremotesf { namespace { constexpr auto infoKey = "info"sv; constexpr auto filesKey = "files"sv; constexpr auto pathKey = "path"sv; constexpr auto lengthKey = "length"sv; constexpr auto nameKey = "name"sv; constexpr auto announceListKey = "announce-list"sv; constexpr auto announceKey = "announce"sv; template std::optional maybeTakeDictValue(bencode::Dictionary& dict, std::string_view key) { const auto found = dict.find(key); return found != dict.end() ? std::optional(std::move(found->second).takeValue()) : std::nullopt; } template Expected takeDictValue(bencode::Dictionary& dict, std::string_view key, std::string_view dictName) { auto found = dict.find(key); if (found == dict.end()) { throw bencode::Error( bencode::Error::Type::Parsing, fmt::format("{} dictionary does not contain key '{}'", dictName, key) ); } return std::move(found->second).takeValue(); } QString computeInfoHashV1(const QString& path, qint64 infoDictOffset, qint64 infoDictLength) { try { QFile file(path); openFile(file, QIODevice::ReadOnly); skipBytes(file, infoDictOffset); QCryptographicHash hash(QCryptographicHash::Sha1); static constexpr QByteArray::size_type maxBufferSize = 1024 * 1024 * 1024; // 1 MiB QByteArray buffer(std::min(maxBufferSize, static_cast(infoDictLength)), '\0'); auto remaining = static_cast(infoDictLength); while (remaining > 0) { if (remaining < buffer.size()) { buffer.resize(remaining); } readBytes(file, buffer); hash.addData(buffer); remaining -= buffer.size(); } return {hash.result().toHex()}; } catch (const QFileError&) { std::throw_with_nested(bencode::Error(bencode::Error::Type::Reading, "Failed to compute info hash")); } } } TorrentMetainfoFile::File::File(bencode::Value&& value) { auto dict = std::move(value).takeDictionary(); mPath = takeDictValue(dict, pathKey, "info"); size = takeDictValue(dict, lengthKey, "info"); } TorrentMetainfoFile::TorrentMetainfoFile(bencode::Value&& value, QString&& infoHashV1) : infoHashV1(std::move(infoHashV1)) { auto rootDict = std::move(value).takeDictionary(); if (auto announceList = maybeTakeDictValue(rootDict, announceListKey); announceList) { trackers = *announceList | std::views::transform([](bencode::Value& value) { return std::move(value).takeList() | std::views::as_rvalue | std::views::transform(&bencode::Value::takeString) | std::ranges::to(); }) | std::ranges::to(); } else if (auto announce = maybeTakeDictValue(rootDict, announceKey); announce) { trackers = std::vector{std::set{std::move(*announce)}}; } auto infoDict = takeDictValue(rootDict, infoKey, "root"); rootFileName = takeDictValue(infoDict, nameKey, "info"); if (auto size = maybeTakeDictValue(infoDict, lengthKey); size) { mSingleFileSizeOrFiles = *size; } else { mSingleFileSizeOrFiles = takeDictValue(infoDict, filesKey, "info"); } } TorrentMetainfoFile parseTorrentFile(const QString& path) { std::optional> infoDictOffsetAndLength{}; auto bencodeValue = bencode::parse(path, [&](const bencode::ByteArray& key, qint64 offset, qint64 length) { if (key == infoKey) { infoDictOffsetAndLength = std::pair(offset, length); } }); if (!infoDictOffsetAndLength.has_value()) { throw bencode::Error(bencode::Error::Type::Parsing, "root dictionary does not contain key 'info'"); } const auto [offset, length] = *infoDictOffsetAndLength; return TorrentMetainfoFile(std::move(bencodeValue), computeInfoHashV1(path, offset, length)); } QDebug operator<<(QDebug debug, const TorrentMetainfoFile& torrentFile) { const QDebugStateSaver saver(debug); debug.noquote() << fmt::format(singleArgumentFormatString, torrentFile).c_str(); return debug; } } fmt::format_context::iterator fmt::formatter::format( const tremotesf::TorrentMetainfoFile& torrentFile, format_context& ctx ) const { return fmt::format_to( ctx.out(), "TorrentMetainfoFile(infoHashV1={}, trackers={}, rootFileName={:?}, filesCount={})", torrentFile.infoHashV1, torrentFile.trackers, torrentFile.rootFileName, torrentFile.isSingleFile() ? 1 : std::get(torrentFile.mSingleFileSizeOrFiles).size() ); } tremotesf-2.10.1/src/torrentfileparser.h000066400000000000000000000040441523411710200203130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTFILEPARSER_H #define TREMOTESF_TORRENTFILEPARSER_H #include #include #include #include #include #include "log/formatters.h" #include "bencodeparser.h" namespace tremotesf { class TorrentMetainfoFile final { public: explicit TorrentMetainfoFile(bencode::Value&& value, QString&& infoHashV1); class File final { public: explicit File(bencode::Value&& value); bencode::Integer size; std::ranges::view auto path() { return mPath | std::views::transform([](bencode::Value& value) { return std::move(value).takeString(); }); } private: bencode::List mPath; }; QString infoHashV1; std::vector> trackers; QString rootFileName; inline bool isSingleFile() const { return std::holds_alternative(mSingleFileSizeOrFiles); } inline bencode::Integer singleFileSize() const { return std::get(mSingleFileSizeOrFiles); } inline std::ranges::view auto files() { return std::get(mSingleFileSizeOrFiles) | std::views::transform([](bencode::Value& value) { return File(std::move(value)); }); } private: std::variant mSingleFileSizeOrFiles; friend struct fmt::formatter; }; /** * @throws tremotesf::bencode::Error */ TorrentMetainfoFile parseTorrentFile(const QString& path); QDebug operator<<(QDebug debug, const TorrentMetainfoFile& torrentFile); } template<> struct fmt::formatter : tremotesf::SimpleFormatter { format_context::iterator format(const tremotesf::TorrentMetainfoFile& torrentFile, format_context& ctx) const; }; #endif // TREMOTESF_TORRENTFILEPARSER_H tremotesf-2.10.1/src/torrentfileparser_test.cpp000066400000000000000000000131011523411710200216770ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "torrentfileparser.h" using namespace Qt::StringLiterals; namespace tremotesf { class TorrentFileParserTest final : public QObject { Q_OBJECT private slots: void parseDebianTorrentFile() { auto torrentFile = parseTorrentFile(QDir::current().filePath("debian-10.9.0-amd64-netinst.iso.torrent"_L1)); QCOMPARE(torrentFile.infoHashV1, "9f292c93eb0dbdd7ff7a4aa551aaa1ea7cafe004"_L1); const std::vector> expectedTrackers{{"http://bttracker.debian.org:6969/announce"_L1}}; QCOMPARE(torrentFile.trackers, expectedTrackers); QCOMPARE(torrentFile.isSingleFile(), true); QCOMPARE(torrentFile.singleFileSize(), 353370112); QCOMPARE(torrentFile.rootFileName, "debian-10.9.0-amd64-netinst.iso"_L1); } void parseWikiTorrentFile() { auto torrentFile = parseTorrentFile( QDir::current().filePath("enwiki-20231220-pages-articles-multistream.xml.bz2.torrent"_L1) ); QCOMPARE(torrentFile.infoHashV1, "80fb3b384728e950f2fd09e5929970d3d576270d"_L1); const std::vector> expectedTrackers{ {"http://tracker.opentrackr.org:1337/announce"_L1}, {"udp://tracker.opentrackr.org:1337"_L1}, {"udp://tracker.openbittorrent.com:80/announce"_L1}, {"http://fosstorrents.com:6969/announce"_L1}, {"udp://fosstorrents.com:6969/announce"_L1} }; QCOMPARE(torrentFile.trackers, expectedTrackers); QCOMPARE(torrentFile.isSingleFile(), true); QCOMPARE(torrentFile.singleFileSize(), 22711545577); QCOMPARE(torrentFile.rootFileName, "enwiki-20231220-pages-articles-multistream.xml.bz2"_L1); } void parseFedoraTorrentFile() { auto torrentFile = parseTorrentFile(QDir::current().filePath("Fedora-Workstation-Live-x86_64-34.torrent"_L1)); QCOMPARE(torrentFile.infoHashV1, "2046e45fb6cf298cd25e4c0decbea40c6603d91b"_L1); const std::vector> expectedTrackers{ {"http://torrent.fedoraproject.org:6969/announce"_L1} }; QCOMPARE(torrentFile.trackers, expectedTrackers); QCOMPARE(torrentFile.isSingleFile(), false); QCOMPARE(torrentFile.rootFileName, "Fedora-Workstation-Live-x86_64-34"_L1); const auto files = torrentFile.files() | std::views::transform([](TorrentMetainfoFile::File file) { return std::pair{file.size, std::ranges::to(file.path())}; }) | std::ranges::to(); const std::set>> expected{ {1062, {"Fedora-Workstation-34-1.2-x86_64-CHECKSUM"_L1}}, {2007367680, {"Fedora-Workstation-Live-x86_64-34-1.2.iso"_L1}} }; QCOMPARE(files, expected); } void parseHybridV2TorrentFile() { auto torrentFile = parseTorrentFile(QDir::current().filePath("bittorrent-v2-hybrid-test.torrent"_L1)); QCOMPARE(torrentFile.infoHashV1, "631a31dd0a46257d5078c0dee4e66e26f73e42ac"_L1); const std::vector> expectedTrackers{}; QCOMPARE(torrentFile.trackers, expectedTrackers); QCOMPARE(torrentFile.isSingleFile(), false); QCOMPARE(torrentFile.rootFileName, "bittorrent-v1-v2-hybrid-test"_L1); const auto files = torrentFile.files() | std::views::transform([](TorrentMetainfoFile::File file) { return std::pair{file.size, std::ranges::to(file.path())}; }) | std::ranges::to(); const std::set>> expected{ {129434, {".pad"_L1, "129434"_L1}}, {227380, {".pad"_L1, "227380"_L1}}, {280339, {".pad"_L1, "280339"_L1}}, {442368, {".pad"_L1, "442368"_L1}}, {464896, {".pad"_L1, "464896"_L1}}, {507162, {".pad"_L1, "507162"_L1}}, {510995, {".pad"_L1, "510995"_L1}}, {524227, {".pad"_L1, "524227"_L1}}, {6535405, {"Darkroom (Stellar, 1994, Amiga ECS) HQ.mp4"_L1}}, {20506624, {"Spaceballs-StateOfTheArt.avi"_L1}}, {342230630, {"cncd_fairlight-ceasefire_(all_falls_down)-1080p.mp4"_L1}}, {61638604, {"eld-dust.mkv"_L1}}, {277889766, {"fairlight_cncd-agenda_circling_forth-1080p30lq.mp4"_L1}}, {44577773, {"meet the deadline - Still _ Evoke 2014.mp4"_L1}}, {61, {"readme.txt"_L1}}, {26296320, {"tbl-goa.avi"_L1}}, {115869700, {"tbl-tint.mpg"_L1}} }; QCOMPARE(files, expected); } void parseV2TorrentFile() { try { auto torrentFile = parseTorrentFile(QDir::current().filePath("bittorrent-v2-test.torrent"_L1)); QFAIL("parseTorrentFile must throw an exception"); } catch (const bencode::Error&) {} } }; } QTEST_GUILESS_MAIN(tremotesf::TorrentFileParserTest) #include "torrentfileparser_test.moc" tremotesf-2.10.1/src/translators.html000066400000000000000000000021151523411710200176270ustar00rootroot00000000000000

tremotesf-2.10.1/src/tremotesf.ico000066400000000000000000000226761523411710200171070ustar00rootroot0000000000000000 ¨%(0` ÿÿÿÿÿÿ77$;;4'7728885A>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M>?;M;<9H99/6770%... ÿÿÿÿÿÿÿÿÿÿÿÿ+++771*66.BJLI‹TXVóW[YøY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øY][øX\ZøTXVûSWUÛBDAd770%333ÿÿÿÿÿÿÿÿÿÿÿÿ@@@77/2*333ÿÿÿÿÿÿÿÿÿÿÿÿ---Y\Z˯²°ÿ¸»¹ÿ¸»¹ÿ®²°ÿw{yÿ[_]ÿdgfÿbgeÿcfdÿbedÿafcÿadbÿadbÿ`cbÿ_daÿ_caÿ^baÿ^caÿ]a_ÿ]a_ÿ]a_ÿ]a_ÿ[_]ÿ[`^ÿ[_]ÿY][ÿZ^\ÿY][ÿY][ÿY][ÿY][ÿX\ZÿVZXÿlomÿ¡£¡ÿ¸»¹ÿ»½»ÿ»½»ÿX\Yí77$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿVZX™¡¤£ÿ·º¸ÿµ¸¶ÿˆ‹‰ÿ…‰†ÿèëéÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿçêèÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿîñïÿª®¬ÿrusÿµ¸¶ÿ¹¼ºÿª¬«ÿZ^\ÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUn�”’øµ¸¶ÿ±´²ÿospÿÁÄÁÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿéíêÿª®¬ÿ…Šˆÿ¨¬ªÿåèæÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿáåâÿ\`^ÿ±´²ÿ¹¼ºÿ˜œšÿW[YšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUM~‚€ò¶¹·ÿ®±¯ÿuwvÿ¾ÁÀÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÏÒÑÿŠŽŒÿjmiÿ@>9ÿgieÿ†‹ˆÿ¼¿½ÿìïíÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿ×ÛÙÿafcÿ®±¯ÿ¹»ºÿ„ˆ†øSWUmÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWU+ospó´·µÿª®«ÿ|€}ÿ«¯­ÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿåèæÿ¢¦¤ÿ�…‚ÿLLGÿ:93ÿ:93ÿ:93ÿEE@ÿwzwÿ‹��ÿÎÑÐÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÁÄÁÿosqÿª®«ÿ¸¼¹ÿpsrñSWUGÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWU `dbû³¶´ÿ§ª¨ÿ�…ƒÿ˜›šÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿÆÉÈÿ‡ŒŠÿfieÿAA<ÿ??:ÿ??:ÿ??:ÿ??:ÿ??:ÿ??:ÿRTPÿ�…ƒÿ™�›ÿÜßÞÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿïòðÿ¨¬ªÿy}{ÿ§ª¨ÿ¸º¹ÿ^b`ôSWU"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]a_ò¬°®ÿ£§¥ÿˆ�‹ÿ‚‡ƒÿïòðÿïòðÿïòðÿïòðÿïòðÿáäâÿ™�›ÿƒ�ÿOQMÿEFBÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿNOLÿFGDÿad`ÿ…Šˆÿª®¬ÿãçåÿëïíÿëïíÿëïìÿëîìÿëîìÿŽ‘�ÿ�…ƒÿ¤¨¦ÿ³¶µÿTXVûSWUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZ^\Õ¤¨¥ÿ ¤¡ÿ�“‘ÿorpÿíðîÿìðîÿìïíÿêíëÿ½À¾ÿˆ�‹ÿ…Šˆÿ†‹‰ÿ‚‡…ÿSUSÿbedÿbedÿbedÿbedÿbedÿbedÿbedÿbedÿRUSÿ…‰‡ÿ†Šˆÿ…Šˆÿ‹�Žÿ¼Á¿ÿåéçÿçëéÿæëèÿæëèÿrxuÿ‹�Œÿ£§¤ÿ¥¨¦ÿX\ZäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX\Z°˜›™ÿž¢ ÿ•™—ÿ^a`ÿèìêÿèìêÿèìêÿèìéÿýýýÿÿÿÿÿÿÿÿÿûüûÿ~‚€ÿRUSÿVXXÿWYXÿWYXÿWYXÿWYXÿWYXÿWYXÿVXXÿW[Yÿˆ�‹ÿûûûÿýýýÿýýýÿûûûÿâçäÿâçåÿâçäÿâçäÿ_baÿ“˜•ÿ¤§¦ÿ”˜–ÿX\Z¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTXV…‹Ž‹þž¡Ÿÿ˜œ™ÿVZXÿàåâÿäèæÿäèæÿãèæÿãèåÿãèåÿãçåÿâçåÿƒˆ…ÿ]`^ÿnsqÿrwtÿrwuÿrwuÿrwuÿrwuÿrwtÿmrpÿ_caÿ‘•’ÿàæãÿáçäÿáæäÿàåâÿÞäáÿÞäáÿÞäáÿ×ÛÙÿUYWÿ™�šÿ£§¤ÿ†‰‡þUYW�ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUa|€~õ� Ÿÿ•™—ÿ`dbÿËÐÍÿàåâÿßåâÿßäâÿßäáÿßäáÿÞäáÿÞäáÿ“–”ÿ]`^ÿGGCÿ<;5ÿ>=7ÿ?>8ÿ?=8ÿ><7ÿ<:5ÿED@ÿ`daÿ ¥¡ÿÛáÞÿÛáÞÿÛáÞÿÚáÝÿÚàÝÿÚàÝÿÚàÝÿ¿ÄÂÿcfdÿ•™—ÿ£¦¥ÿvywõSWUeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWU@nqoñ� žÿ’–”ÿhmiÿ·¼ºÿÛáÞÿÛáÞÿÛáÞÿÛáÞÿÚàÝÿÚàÝÿÚàÝÿ¥©¦ÿ`a_ÿGGAÿgfbÿxwsÿwwrÿwwrÿxwsÿggaÿGF@ÿdfbÿ­²­ÿ×ÞÚÿ×ÝÚÿÖÝÚÿÖÝÚÿÖÝÙÿÖÝÙÿÕÜÙÿ§­ªÿjomÿ’–”ÿ£¦¤ÿeigñSWU@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUcfdöœŸ�ÿ�“�ÿmrnÿ¤ª§ÿ×ÞÚÿ×ÝÚÿ×ÝÚÿÖÝÚÿÖÝÚÿÖÝÙÿÖÜÙÿ´¹¶ÿ^`\ÿRSNÿ……‚ÿde`ÿbc^ÿbc^ÿde`ÿ„…�ÿTSOÿ`b^ÿ¹À¼ÿÓÚÖÿÒÚÖÿÒÙÖÿÒÙÖÿÒÙÖÿÑÙÕÿÑÙÕÿ’˜•ÿqtrÿ�“�ÿ£¦¤ÿX\ZöSWUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWU[_]ûš�›ÿ‹��ÿosqÿ’˜•ÿÓÚ×ÿÓÚÖÿÒÚÖÿÒÙÖÿÒÙÖÿÒÙÖÿÑÙÕÿÅÌÉÿRSOÿ_a]ÿ’”�ÿtvrÿtvrÿsuqÿsuqÿ�’�ÿ_a]ÿVVQÿÈÏÌÿÎÖÓÿÎÖÒÿÎÖÒÿÎÖÒÿÍÕÒÿÍÕÒÿÍÕÑÿy}ÿvzxÿŽ’�ÿ›žœÿTXVøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX\Zè”—•ÿˆŒ‰ÿtwtÿ~„€ÿÎÖÓÿÎÖÓÿÎÖÒÿÎÖÒÿÎÖÒÿÍÕÒÿÍÕÒÿÍÕÑÿMLHÿilgÿ ¢Ÿÿ…ˆ…ÿ…ˆ…ÿ…ˆ…ÿ…ˆ…ÿž¡žÿjliÿTTPÿÊÓÏÿÊÓÏÿÊÒÏÿÊÒÎÿÉÒÎÿÉÒÎÿÉÒÎÿÉÑÎÿfliÿ|€}ÿŽ‘ŽÿŽ‘�ÿW[XÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿW[YÈ‹ŽŒÿ†‹ˆÿw{xÿnspÿÊÓÏÿÊÓÏÿÊÒÏÿÊÒÎÿÉÒÎÿÉÒÎÿÉÒÎÿÉÑÍÿWXRÿswsÿ­±­ÿ–›—ÿ–›—ÿ–›—ÿ–›—ÿ¬°­ÿuxvÿ^^ZÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿW\Zÿ�…‚ÿŒ’�ÿ€„‚ÿV[X¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUYW €ƒ�ÿ†Šˆÿz~{ÿ`daÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿqtpÿ~‚€ÿº¿¼ÿ§®ªÿ§®ªÿ§®ªÿ§­©ÿ¹¾»ÿ~ƒ€ÿrtoÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿ¼ÅÁÿX\Zÿ�…ƒÿ�‘�ÿtxvýTXV…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUvuxwû„‰†ÿ{€}ÿUZWÿÇÐÌÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿŽ‘�ÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡ŒŠÿŠŽ‰ÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿÈÑÍÿª²¯ÿadcÿ}‚ÿŒ‘ŽÿhljôSWU]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUTkolòƒ‰†ÿz|ÿZ][ÿ¬´°ÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿœ¢�ÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡ŒŠÿ—š—ÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ¼ÅÁÿ’™–ÿeigÿz|ÿŒ‘Žÿ]a_ñSWU8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWU2afcòƒˆ…ÿv{xÿgliÿkpmÿœ£ ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿž¦¢ÿ–�šÿ‡‹‰ÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿ‡‹‰ÿ‘—”ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿŸ¦£ÿ�¤¡ÿ�•‘ÿZ^\ÿnspÿw|yÿŠ�ŒÿUYWúSWUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUX\Zú‡‹‰ÿv{xÿuzwÿfjhÿW\ZÿSWUÿSWUÿSWUÿSWUÿSWUÿSWUÿTXVÿ‡ŒŠÿÆËÉÿ¶½ºÿ¶½ºÿ¶½ºÿ¶½ºÿÅËÈÿˆ�‹ÿX\ZÿSWUÿSWUÿSWUÿSWUÿSWUÿSWUÿ_cbÿkomÿv{xÿ€„‚ÿ~ƒ€ÿTXVðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUYWã|~ÿ�…ƒÿy~{ÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿ‡ŒŠÿÈÎËÿ¹À½ÿ¹À½ÿ¹À½ÿ¹À½ÿÈÍËÿ‰ŽŒÿy}{ÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿw|yÿx}zÿ€…ƒÿ‡Œ‰ÿafcõTXV|ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUYZ^\ôvzxÿ„ˆ…ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿ…‰†ÿˆ�‹ÿËÑÎÿ½ÄÁÿ½ÄÁÿ½ÄÁÿ½ÄÁÿËÐÎÿŠ��ÿ…‰†ÿ„‰†ÿ„‰†ÿ„‰†ÿ„‰†ÿ„Їÿ„Їÿ„Їÿ„‰†ÿ{€~ÿ_daôTXVçSWU ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSWUSWU_UYWÍTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñUYWñ‡ŒŠÿÏÓÒÿÂÇÅÿÂÇÅÿÂÇÅÿÂÇÅÿÏÓÑÿˆŽŒÿY][òTXVñTXVñTXVñTXVñTXVñTXVñTXVñTXVñUYWéTXVsSWUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€ˆ�‹üÒÖÕÿÆËÉÿÆËÉÿÆËÉÿÆËÉÿÒÖÔÿ‹�ŽöŠŠŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€ˆ�‹üÕÙØÿÊÏÍÿÊÏÍÿÊÏÍÿÊÏÍÿÕÙ×ÿ‹�ŽöŠŠŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€ˆ�‹üÙÜÛÿÎÓÑÿÎÓÑÿÎÓÑÿÎÓÑÿØÜÛÿ‹�ŽöŠŠŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€ˆ�‹üÜßÞÿÒÖÕÿÒÖÕÿÒÖÕÿÒÖÕÿÛßÞÿ‹‘ŽöŠŠŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ• �����++Ї�‹üßâáÿÖÚÙÿÖÚÙÿÖÚÙÿÖÚÙÿßâáÿŒ‘�÷VVŒ&�����‰ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“N”¢•Ñ“ó”ú”ø”ø”ø”ø”ø“ø†‹‹ÿâåäÿÚÞÝÿÚÞÝÿÚÞÝÿÚÞÝÿâåäÿ‡Œ�ÿ “ù”ø”ø”ø”ø”ø“ú”õ•Ô–§“Wÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•—˜ô ¥ÿ©ÿ­ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ®ÿ†‹‹ÿåççÿÞááÿÞááÿÞááÿÞááÿåççÿˆ��ÿ¬ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ¯ÿ®ÿ©ÿ ¥ÿšô•°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“ý((Âÿ¾ÿºÿºÿºÿºÿºÿºÿºÿºÿ¸ÿ†‹‹ÿèëêÿâåäÿâåäÿâåäÿâåäÿèêêÿ‡Œ‘ÿ µÿºÿºÿºÿºÿºÿºÿºÿºÿ ½ÿ))Äÿ”ùŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“ÿKKåÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿ×ÿ†‹Œÿëîíÿæéèÿæéèÿæéèÿæéèÿëîíÿ‡�’ÿ ÒÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿÚÿDDäÿšö•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“û[[èÿ--éÿ åÿåÿåÿåÿåÿåÿåÿåÿ âÿ†‹Œÿïððÿêììÿêììÿêììÿêììÿîððÿˆ�“ÿÜÿåÿåÿåÿåÿåÿåÿåÿ åÿ((èÿ^^ëÿ—ø™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–…¢÷``Íþ~~Üÿáÿåÿåÿåÿåÿåÿåÿâÿ†‹‹ÿáäãÿÛßÞÿÚÞÝÿÚÞÝÿÛßÞÿáäãÿŠ�‘ÿ€€Üÿåÿåÿåÿåÿåÿåÿáÿ~~ÜÿccÎþ##§õ–œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”9”ƒ—¸—â”ü“ÿ“ÿ“ÿ“ÿ“ÿ“ÿ‚†Šÿ¸¼»ÿÆËÊÿÇÌÊÿÇÌÊÿÆËÊÿº¿½ÿƒ‡Šÿ’ÿ“ÿ“ÿ“ÿ“ÿ“ÿ”ü˜ã™»”ˆ‘Aÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰‹‹a‡�‹æ‡�‹ñ‡�‹ñ‡�‹ñ‡�‹ñˆ�Šè‰�‹nÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøðððððððøøøøøøøøü?ü?ü?ü?ü?ü?ü?þþþþþÿÿÿÿ€ÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtremotesf-2.10.1/src/tremotesf.manifest.in000066400000000000000000000012261523411710200205340ustar00rootroot00000000000000 UTF-8 tremotesf-2.10.1/src/tremotesf.manifest.rc000066400000000000000000000002641523411710200205330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: CC0-1.0 #include CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "tremotesf.manifest" tremotesf-2.10.1/src/tremotesf.rc.in000066400000000000000000000030251523411710200173310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: CC0-1.0 #include #define TREMOTESF_ICON 0 TREMOTESF_ICON ICON tremotesf.ico #define TORRENT_FILE_FRIENDLY_TYPE_NAME 0 #define MAGNET_LINK_FRIENDLY_TYPE_NAME 1 STRINGTABLE { TORRENT_FILE_FRIENDLY_TYPE_NAME, "Torrent file" MAGNET_LINK_FRIENDLY_TYPE_NAME, "Magnet link" } #ifdef _DEBUG #define TREMOTESF_FILEFLAGS VS_FF_DEBUG #else #define TREMOTESF_FILEFLAGS 0 #endif VS_VERSION_INFO VERSIONINFO FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS TREMOTESF_FILEFLAGS FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "CompanyName", "Tremotesf" VALUE "FileDescription", "Tremotesf - Remote GUI for transmission-daemon" VALUE "FileVersion", "@PROJECT_VERSION@" VALUE "InternalName", "@TREMOTESF_EXECUTABLE_NAME@" VALUE "LegalCopyright", "Copyright 2015-2025 Alexey Rochev" VALUE "OriginalFilename", "@TREMOTESF_EXECUTABLE_NAME@.exe" VALUE "ProductName", "@TREMOTESF_APP_NAME@" VALUE "ProductVersion", "@PROJECT_VERSION@" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END tremotesf-2.10.1/src/ui/000077500000000000000000000000001523411710200150035ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/darkthemeapplier_windows.cpp000066400000000000000000000306321523411710200226060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "darkthemeapplier_windows.h" #include #include #include #include #include #include #include #include #include #include #include "log/log.h" #include "settings.h" #include "windowshelpers.h" #include "systemcolorsprovider.h" namespace tremotesf { namespace { namespace DWMWINDOWATTRIBUTE_compat { inline constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_1809_UNTIL_2004 = 19; inline constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_SINCE_2004 = 20; inline constexpr DWORD DWMWA_CAPTION_COLOR = 35; } class TitleBarBackgroundEventFilter final : public QObject { Q_OBJECT public: explicit TitleBarBackgroundEventFilter( SystemColorsProvider* systemColorsProvider, QObject* parent = nullptr ) : QObject{parent}, mSystemColorsProvider{systemColorsProvider} {} bool eventFilter(QObject* watched, QEvent* event) override { switch (event->type()) { case QEvent::WinIdChange: { // Using QWindow::winId instead of QWidget::winId directly because // QWidget::winId may create native window prematurely which we don't want // QWidget::windowHandle will return nullptr if native window doesn't exist yet so we check for that QWindow* window{}; if (auto widget = qobject_cast(watched); widget) { window = widget->windowHandle(); } if (window) { switch (window->type()) { case Qt::Window: case Qt::Dialog: applyDarkThemeToTitleBarAndConnectSignal(window); break; default: break; } } break; } default: break; } return false; } private: static inline constexpr auto signalAddedProperty = "tremotesf::TitleBarBackgroundEventFilter"; void applyDarkThemeToTitleBarAndConnectSignal(QWindow* window) { applyDarkThemeToTitleBar(window); if (!window->property(signalAddedProperty).toBool()) { QObject::connect( mSystemColorsProvider, &SystemColorsProvider::darkThemeEnabledChanged, window, [this, window] { applyDarkThemeToTitleBar(window); } ); QObject::connect(Settings::instance(), &Settings::darkThemeModeChanged, window, [this, window] { applyDarkThemeToTitleBar(window); }); window->setProperty(signalAddedProperty, true); } } void applyDarkThemeToTitleBar(QWindow* window) { if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) { debug().log("Setting DWMWA_CAPTION_COLOR on {}", *window); const auto qcolor = QGuiApplication::palette().color(QPalette::Window); const auto color = RGB(qcolor.red(), qcolor.green(), qcolor.blue()); try { checkHResult( DwmSetWindowAttribute( // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) reinterpret_cast(window->winId()), DWMWINDOWATTRIBUTE_compat::DWMWA_CAPTION_COLOR, &color, sizeof(color) ), "DwmSetWindowAttribute" ); } catch (const winrt::hresult_error& e) { warning().logWithException(e, "Failed to set DWMWA_CAPTION_COLOR on {}", *window); } } else { debug().log("Setting DWMWA_USE_IMMERSIVE_DARK_MODE on {}", *window); const auto attribute = []() -> DWORD { if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_2004) { return DWMWINDOWATTRIBUTE_compat::DWMWA_USE_IMMERSIVE_DARK_MODE_SINCE_2004; } return DWMWINDOWATTRIBUTE_compat::DWMWA_USE_IMMERSIVE_DARK_MODE_1809_UNTIL_2004; }(); const bool darkTheme = [&] { switch (Settings::instance()->get_darkThemeMode()) { case Settings::DarkThemeMode::FollowSystem: return mSystemColorsProvider->isDarkThemeEnabled(); case Settings::DarkThemeMode::On: return true; case Settings::DarkThemeMode::Off: return false; } throw std::logic_error("Unknown DarkThemeMode value"); }(); const auto useImmersiveDarkMode = static_cast(darkTheme); try { checkHResult( DwmSetWindowAttribute( // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) reinterpret_cast(window->winId()), attribute, &useImmersiveDarkMode, sizeof(useImmersiveDarkMode) ), "DwmSetWindowAttribute" ); } catch (const winrt::hresult_error& e) { warning().logWithException(e, "Failed to set DWMWA_USE_IMMERSIVE_DARK_MODE on {}", *window); } } } SystemColorsProvider* mSystemColorsProvider{}; }; QColor blendAtop(QColor source, QColor background) { const int alpha = source.alpha(); if (alpha == 255) return source; const auto blend = [&](int s, int b) { return (s * alpha / 255 + b * (255 - alpha) / 255); }; return { blend(source.red(), background.red()), blend(source.green(), background.green()), blend(source.blue(), background.blue()) }; } double getRelativeLuminance(QColor color) { const auto trans = [](auto compF) { const auto comp = static_cast(compF); if (comp <= 0.03928) { return comp / 12.92; } return std::pow(((comp + 0.055) / 1.055), 2.4); }; return 0.2126 * trans(color.redF()) + 0.7152 * trans(color.greenF()) + 0.0722 * trans(color.blueF()); } double getContrastRatio(QColor lighterColor, QColor darkerColor) { return (getRelativeLuminance(lighterColor) + 0.05) / (getRelativeLuminance(darkerColor) + 0.05); } // From Web Content Accessibility Guidelines 2.2 bool isLegibleWithWhiteText(QColor backgroundColor) { const auto ratio = getContrastRatio(blendAtop(Qt::white, backgroundColor), backgroundColor); return std::ceil(ratio * 10.0) >= 45.0; } QColor withAlpha(QColor color, int alpha) { color.setAlpha(alpha); return color; } inline constexpr SystemColorsProvider::AccentColors defaultAccentColors{ .accentColor = QColor(48, 140, 198), .accentColorLight1 = QColor(58, 168, 238), .accentColorDark1 = QColor(40, 117, 165), .accentColorDark2 = QColor(33, 97, 137) }; void applyAccentToPalette(Settings* settings, SystemColorsProvider* systemColorsProvider) { info().log("Applying accent colors to palette"); SystemColorsProvider::AccentColors accentColors; if (settings->get_useSystemAccentColor()) { accentColors = systemColorsProvider->accentColors(); if (!accentColors.isValid()) { accentColors = defaultAccentColors; } } else { accentColors = defaultAccentColors; } info().log("Accent colors are {}", accentColors); QPalette palette{}; if (isLegibleWithWhiteText(accentColors.accentColor)) { palette.setColor(QPalette::Active, QPalette::Highlight, accentColors.accentColor); palette.setColor(QPalette::Disabled, QPalette::Highlight, withAlpha(accentColors.accentColor, 93)); } else { palette.setColor(QPalette::Active, QPalette::Highlight, accentColors.accentColorDark1); palette.setColor(QPalette::Disabled, QPalette::Highlight, withAlpha(accentColors.accentColorDark1, 93)); } palette.setColor(QPalette::Active, QPalette::HighlightedText, Qt::white); palette.setColor(QPalette::Disabled, QPalette::HighlightedText, withAlpha(Qt::white, 93)); palette.setColor( QPalette::Inactive, QPalette::Highlight, withAlpha(palette.color(QPalette::Active, QPalette::Highlight), 153) ); palette.setColor(QPalette::Inactive, QPalette::HighlightedText, Qt::white); palette.setColor(QPalette::Active, QPalette::Accent, palette.color(QPalette::Active, QPalette::Highlight)); palette .setColor(QPalette::Disabled, QPalette::Accent, palette.color(QPalette::Disabled, QPalette::Highlight)); QGuiApplication::setPalette(palette); if (qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark) { QPalette checkBoxPalette{}; checkBoxPalette.setColor(QPalette::Active, QPalette::Base, accentColors.accentColorDark1); checkBoxPalette.setColor(QPalette::Active, QPalette::Button, accentColors.accentColorLight1); checkBoxPalette.setColor(QPalette::Inactive, QPalette::Base, accentColors.accentColorDark2); QApplication::setPalette(checkBoxPalette, "QCheckBox"); QApplication::setPalette(checkBoxPalette, "QRadioButton"); } } } void applyDarkThemeToPalette(SystemColorsProvider* systemColorsProvider) { const auto settings = Settings::instance(); const auto apply = [=] { const bool darkTheme = [&] { switch (settings->get_darkThemeMode()) { case Settings::DarkThemeMode::FollowSystem: return systemColorsProvider->isDarkThemeEnabled(); case Settings::DarkThemeMode::On: return true; case Settings::DarkThemeMode::Off: return false; } throw std::logic_error("Unknown DarkThemeMode value"); }(); const auto colorScheme = darkTheme ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; info().log("Setting color scheme {}", colorScheme); qApp->styleHints()->setColorScheme(colorScheme); QMetaObject::invokeMethod( qApp, [=]() { applyAccentToPalette(settings, systemColorsProvider); }, Qt::QueuedConnection ); }; apply(); QObject::connect(systemColorsProvider, &SystemColorsProvider::darkThemeEnabledChanged, qApp, apply); QObject::connect(systemColorsProvider, &SystemColorsProvider::accentColorsChanged, qApp, apply); QObject::connect(settings, &Settings::darkThemeModeChanged, qApp, apply); QObject::connect(settings, &Settings::useSystemAccentColorChanged, qApp, apply); qApp->installEventFilter(new TitleBarBackgroundEventFilter(systemColorsProvider, QGuiApplication::instance())); } } #include "darkthemeapplier_windows.moc" tremotesf-2.10.1/src/ui/darkthemeapplier_windows.h000066400000000000000000000006051523411710200222500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_DARKTHEMEAPPLIER_WINDOWS_H #define TREMOTESF_DARKTHEMEAPPLIER_WINDOWS_H class QWindow; namespace tremotesf { class SystemColorsProvider; void applyDarkThemeToPalette(SystemColorsProvider* systemColorsProvider); } #endif // TREMOTESF_DARKTHEMEAPPLIER_WINDOWS_H tremotesf-2.10.1/src/ui/iconthemesetup.h000066400000000000000000000004641523411710200202140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_FALLBACK_ICON_THEME_H #define TREMOTESF_FALLBACK_ICON_THEME_H namespace tremotesf { void setupIconTheme(); void setFallbackIconTheme(); } #endif // TREMOTESF_FALLBACK_ICON_THEME_H tremotesf-2.10.1/src/ui/iconthemesetup_bundled.cpp000066400000000000000000000010571523411710200222430ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "iconthemesetup.h" #include "fileutils.h" #include "startup/recoloringsvgiconengineplugin.h" using namespace Qt::StringLiterals; namespace tremotesf { void setupIconTheme() { QIcon::setThemeSearchPaths({resolveExternalBundledResourcesPath("icons"_L1)}); QIcon::setThemeName(TREMOTESF_BUNDLED_ICON_THEME ""_L1); QApplication::setStyle(new RecoloringSvgIconStyle(qApp)); } } tremotesf-2.10.1/src/ui/iconthemesetup_freedesktop.cpp000066400000000000000000000015711523411710200231420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "iconthemesetup.h" #include "log/log.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { bool isPaletteDark() { // Can't use QStyleHints::colorScheme since it can lie const QPalette palette = QGuiApplication::palette(); const int windowBackgroundGray = qGray(palette.window().color().rgb()); return windowBackgroundGray < 192; } QLatin1String fallbackTheme() { return isPaletteDark() ? "breeze-dark"_L1 : "breeze"_L1; } } void setupIconTheme() { const auto theme = fallbackTheme(); info().log("Setting {} as fallback icon theme", theme); QIcon::setFallbackThemeName(theme); } } tremotesf-2.10.1/src/ui/itemmodels/000077500000000000000000000000001523411710200171455ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/itemmodels/baseproxymodel.cpp000066400000000000000000000042531523411710200227120ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "baseproxymodel.h" namespace tremotesf { BaseProxyModel::BaseProxyModel( QAbstractItemModel* sourceModel, int sortRole, std::optional fallbackColumn, QObject* parent ) : QSortFilterProxyModel(parent), mFallbackColumn(fallbackColumn) { setSourceModel(sourceModel); setSortRole(sortRole); mCollator.setCaseSensitivity(Qt::CaseInsensitive); mCollator.setNumericMode(true); } QModelIndexList BaseProxyModel::sourceIndexes(const QModelIndexList& proxyIndexes) const { QModelIndexList indexes; indexes.reserve(proxyIndexes.size()); for (const QModelIndex& index : proxyIndexes) { indexes.append(mapToSource(index)); } return indexes; } bool BaseProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const { std::partial_ordering ord = compare(source_left, source_right); if (ord == std::partial_ordering::equivalent && mFallbackColumn.has_value() && source_left.column() != *mFallbackColumn) { ord = compare(source_left.siblingAtColumn(*mFallbackColumn), source_right.siblingAtColumn(*mFallbackColumn)); } return ord == std::partial_ordering::less; } std::partial_ordering BaseProxyModel::compare(const QModelIndex& source_left, const QModelIndex& source_right) const { const auto role = sortRole(); const QVariant leftData = source_left.data(role); const QVariant rightData = source_right.data(role); if (leftData.typeId() == QMetaType::QString && rightData.typeId() == QMetaType::QString) { return mCollator.compare(leftData.toString(), rightData.toString()) <=> 0; } if (QSortFilterProxyModel::lessThan(source_left, source_right)) { return std::partial_ordering::less; } if (leftData.typeId() == rightData.typeId() && leftData == rightData) { return std::partial_ordering::equivalent; } return std::partial_ordering::unordered; } } tremotesf-2.10.1/src/ui/itemmodels/baseproxymodel.h000066400000000000000000000022361523411710200223560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_BASEPROXYMODEL_H #define TREMOTESF_BASEPROXYMODEL_H #ifndef Q_MOC_RUN // compare includes concepts and moc can't handle it :/ # include #endif #include #include #include #include namespace tremotesf { class BaseProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit BaseProxyModel( QAbstractItemModel* sourceModel = nullptr, int sortRole = Qt::DisplayRole, std::optional fallbackColumn = std::nullopt, QObject* parent = nullptr ); QModelIndexList sourceIndexes(const QModelIndexList& proxyIndexes) const; protected: bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; private: std::partial_ordering compare(const QModelIndex& source_left, const QModelIndex& source_right) const; std::optional mFallbackColumn{}; QCollator mCollator{}; }; } #endif // TREMOTESF_BASEPROXYMODEL_H tremotesf-2.10.1/src/ui/itemmodels/basetorrentfilesmodel.cpp000066400000000000000000000335611523411710200242550ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "basetorrentfilesmodel.h" #include #include #include #include #include #include "desktoputils.h" #include "formatutils.h" #include "stdutils.h" namespace tremotesf { BaseTorrentFilesModel::BaseTorrentFilesModel(std::vector&& columns, QObject* parent) : QAbstractItemModel(parent), mColumns(std::move(columns)) {} int BaseTorrentFilesModel::columnCount(const QModelIndex&) const { return static_cast(mColumns.size()); } QVariant BaseTorrentFilesModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } const auto* const entry = static_cast(index.internalPointer()); const Column column = fromColumnNumber(index.column()); switch (role) { case Qt::CheckStateRole: if (column == Column::Name) { using enum TorrentFilesModelEntry::WantedState; switch (entry->wantedState()) { case Wanted: return Qt::Checked; case Unwanted: return Qt::Unchecked; case Mixed: return Qt::PartiallyChecked; } } break; case Qt::DecorationRole: if (column == Column::Name) { return entry->icon(); } break; case Qt::DisplayRole: switch (column) { case Column::Name: return entry->name(); case Column::Size: return formatutils::formatByteSize(entry->size()); case Column::ProgressBar: case Column::Progress: return formatutils::formatProgress(entry->progress()); case Column::Priority: return entry->priorityString(); default: break; } break; case Qt::ToolTipRole: if (column == Column::Name) { return entry->name(); } break; case SortRole: switch (column) { case Column::Size: return entry->size(); case Column::ProgressBar: case Column::Progress: return entry->progress(); case Column::Priority: return QVariant::fromValue(entry->priority()); default: return data(index, Qt::DisplayRole); } default: return {}; } return {}; } Qt::ItemFlags BaseTorrentFilesModel::flags(const QModelIndex& index) const { if (!index.isValid()) { return {}; } if (fromColumnNumber(index.column()) == Column::Name) { return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; } return QAbstractItemModel::flags(index); } QVariant BaseTorrentFilesModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { return {}; } switch (fromColumnNumber(section)) { case Column::Name: //: Column title in torrent's file list return qApp->translate("tremotesf", "Name"); case Column::Size: //: Column title in torrent's file list return qApp->translate("tremotesf", "Size"); case Column::ProgressBar: //: Column title in torrent's file list return qApp->translate("tremotesf", "Progress Bar"); case Column::Progress: //: Column title in torrent's file list return qApp->translate("tremotesf", "Progress"); case Column::Priority: //: Column title in torrent's file list return qApp->translate("tremotesf", "Priority"); default: return {}; } } bool BaseTorrentFilesModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) { return false; } if (fromColumnNumber(index.column()) == Column::Name && role == Qt::CheckStateRole) { setFilesWanted({index}, (value.toInt() == Qt::Checked)); return true; } return false; } QModelIndex BaseTorrentFilesModel::index(int row, int column, const QModelIndex& parent) const { if (!parent.isValid()) { if (row == 0 && mRootEntry) { return createIndex(0, column, mRootEntry.get()); } return {}; } const auto* const parentEntry = static_cast(parent.internalPointer()); if (!parentEntry->isDirectory()) { return {}; } return createIndex(row, column, &parentEntry->children().at(static_cast(row))); } QModelIndex BaseTorrentFilesModel::parent(const QModelIndex& child) const { if (!child.isValid()) { return {}; } const auto* const parentDirectoryEntry = static_cast(child.internalPointer())->parentDirectory(); if (!parentDirectoryEntry) { return {}; } return createIndex(parentDirectoryEntry->row(), 0, parentDirectoryEntry); } int BaseTorrentFilesModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) { const auto* const entry = static_cast(parent.internalPointer()); if (entry->isDirectory()) { return static_cast(entry->children().size()); } } else if (mRootEntry) { return 1; } return 0; } namespace { class DataChangedDispatcher final { public: struct ColumnAndRoles { int column; QList roles; }; explicit DataChangedDispatcher(std::optional columnAndRoles = std::nullopt) : mColumnAndRoles(std::move(columnAndRoles)) {} void add(const QModelIndex& parent, int row) { // NOLINTNEXTLINE(clazy-detaching-member) if (const auto found = mPendingSignals.find(parent); found != mPendingSignals.end()) { found->push_back(row); } else { mPendingSignals.emplace(parent, std::vector{row}); } } void dispatchSignals(QAbstractItemModel& model) { for (auto&& [parent, rows] : mPendingSignals.asKeyValueRange()) { if (rows.size() == 1) { const int row = rows.front(); if (mColumnAndRoles.has_value()) { const auto index = model.index(row, static_cast(mColumnAndRoles->column), parent); emit model.dataChanged(index, index, mColumnAndRoles->roles); } else { emit model.dataChanged( model.index(row, 0, parent), model.index(row, model.columnCount() - 1, parent) ); } continue; } std::ranges::sort(rows); int firstRow = rows.front(); int lastRow = firstRow; const auto emitForRange = [&] { if (mColumnAndRoles.has_value()) { emit model.dataChanged( model.index(firstRow, mColumnAndRoles->column, parent), model.index(lastRow, mColumnAndRoles->column, parent), mColumnAndRoles->roles ); } else { emit model.dataChanged( model.index(firstRow, 0, parent), model.index(lastRow, model.columnCount() - 1, parent) ); } }; for (int row : rows | std::views::drop(1)) { if (row != (lastRow + 1)) { emitForRange(); firstRow = row; } lastRow = row; } emitForRange(); } } private: std::optional mColumnAndRoles; QHash> mPendingSignals{}; }; void recalculateDirectoryAndItsParents( TorrentFilesModelEntry* directory, QModelIndex index, DataChangedDispatcher& dispatcher ) { while (index.isValid()) { if (!directory->recalculateFromChildren()) { break; } dispatcher.add(index.parent(), index.row()); directory = directory->parentDirectory(); index = index.parent(); } } template UpdateState> requires std::same_as, bool> void updateDirectoryChildrenRecursively( TorrentFilesModelEntry* directory, const QModelIndex& directoryIndex, UpdateState updateState, BaseTorrentFilesModel& model, DataChangedDispatcher& dispatcher ) { for (auto& entry : directory->children()) { if (updateState(&entry)) { dispatcher.add(directoryIndex, entry.row()); } if (entry.isDirectory()) { updateDirectoryChildrenRecursively( &entry, model.index(entry.row(), 0, directoryIndex), updateState, model, dispatcher ); } } } template UpdateState> requires std::same_as, bool> void setWantedOrPriority( const QModelIndexList& indexes, DataChangedDispatcher::ColumnAndRoles columnAndRoles, UpdateState updateState, BaseTorrentFilesModel& model ) { if (indexes.empty()) return; if (!std::ranges::all_of(indexes, &QModelIndex::isValid)) return; DataChangedDispatcher dispatcher(std::move(columnAndRoles)); QSet> parentDirectoriesToRecalculate{}; for (const auto& index : indexes) { auto* const entry = static_cast(index.internalPointer()); if (updateState(entry)) { dispatcher.add(index.parent(), index.row()); if (entry->isDirectory()) { updateDirectoryChildrenRecursively(entry, index, updateState, model, dispatcher); } parentDirectoriesToRecalculate.insert({entry->parentDirectory(), index.parent()}); } } for (const auto& [directory, index] : parentDirectoriesToRecalculate) { recalculateDirectoryAndItsParents(directory, index, dispatcher); } dispatcher.dispatchSignals(model); } } void BaseTorrentFilesModel::setFilesWanted(const QModelIndexList& indexes, bool wanted) { setWantedOrPriority( indexes, {.column = columnNumber(Column::Name), .roles = {Qt::CheckStateRole}}, [&](TorrentFilesModelEntry* entry) { return entry->setWanted(wanted); }, *this ); } void BaseTorrentFilesModel::setFilesPriority(const QModelIndexList& indexes, TorrentFilesModelEntry::Priority priority) { setWantedOrPriority( indexes, {.column = columnNumber(Column::Priority), .roles = {Qt::DisplayRole, SortRole}}, [&](TorrentFilesModelEntry* entry) { return entry->setPriority(priority); }, *this ); } void BaseTorrentFilesModel::fileRenamed(TorrentFilesModelEntry* entry, const QString& newName) { entry->setName(newName); emit dataChanged(createIndex(entry->row(), 0, entry), createIndex(entry->row(), columnCount() - 1, entry)); } int BaseTorrentFilesModel::columnNumber(Column column) const { // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return indexOfCasted(mColumns, column).value(); } BaseTorrentFilesModel::Column BaseTorrentFilesModel::fromColumnNumber(int column) const { return mColumns.at(static_cast(column)); } void BaseTorrentFilesModel::updateFiles( std::span changedFiles, std::function&& updateFile ) { if (changedFiles.empty()) return; DataChangedDispatcher dispatcher{}; for (int index : changedFiles) { const auto sIndex = static_cast(index); auto* const file = mFiles.at(sIndex); updateFile(sIndex, file); auto* const parent = file->parentDirectory(); const auto parentIndex = [&] { if (!parent) { return QModelIndex{}; } return createIndex(parent->row(), 0, parent); }(); dispatcher.add(parentIndex, file->row()); recalculateDirectoryAndItsParents(parent, parentIndex, dispatcher); } dispatcher.dispatchSignals(*this); } } tremotesf-2.10.1/src/ui/itemmodels/basetorrentfilesmodel.h000066400000000000000000000042371523411710200237200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_BASETORRENTFILESMODEL_H #define TREMOTESF_BASETORRENTFILESMODEL_H #include #include #include #include #include "torrentfilesmodelentry.h" namespace tremotesf { class BaseTorrentFilesModel : public QAbstractItemModel { Q_OBJECT public: enum class Column { Name, Size, ProgressBar, Progress, Priority }; static constexpr auto SortRole = Qt::UserRole; explicit BaseTorrentFilesModel(std::vector&& columns, QObject* parent = nullptr); int columnCount(const QModelIndex& = {}) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent = {}) const override; virtual void setFilesWanted(const QModelIndexList& indexes, bool wanted); virtual void setFilesPriority(const QModelIndexList& indexes, TorrentFilesModelEntry::Priority priority); virtual void renameFile(const QModelIndex& index, const QString& newName) = 0; void fileRenamed(TorrentFilesModelEntry* entry, const QString& newName); Column fromColumnNumber(int column) const; int columnNumber(Column column) const; protected: void updateFiles( std::span changedFiles, std::function&& updateFile ); std::unique_ptr mRootEntry{}; std::vector mFiles{}; private: const std::vector mColumns; }; } #endif // TREMOTESF_BASETORRENTFILESMODEL_H tremotesf-2.10.1/src/ui/itemmodels/modelutils.h000066400000000000000000000030141523411710200214750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_MODELUTILS_H #define TREMOTESF_MODELUTILS_H #include #include #include "itemlistupdater.h" namespace tremotesf { template< std::derived_from Model, typename Item, std::ranges::forward_range NewItemsRange = std::vector> class ModelListUpdater : public ItemListUpdater { public: inline explicit ModelListUpdater(Model& model) : mModel(model) {} protected: void onChangedItems(size_t first, size_t last) override { emit mModel.dataChanged( mModel.index(static_cast(first), 0), mModel.index(static_cast(last - 1), lastColumn) ); } void onAboutToRemoveItems(size_t first, size_t last) override { mModel.beginRemoveRows({}, static_cast(first), static_cast(last - 1)); }; void onRemovedItems(size_t, size_t) override { mModel.endRemoveRows(); } void onAboutToAddItems(size_t count) override { const int first = mModel.rowCount(); mModel.beginInsertRows({}, first, first + static_cast(count) - 1); } void onAddedItems(size_t) override { mModel.endInsertRows(); }; Model& mModel; const int lastColumn = static_cast(mModel).columnCount() - 1; }; } #endif // TREMOTESF_MODELUTILS_H tremotesf-2.10.1/src/ui/itemmodels/stringlistmodel.cpp000066400000000000000000000006321523411710200230750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "stringlistmodel.h" #include "ui/itemmodels/modelutils.h" namespace tremotesf { void StringListModel::setStringList(const std::vector& stringList) { ModelListUpdater updater(*this); updater.update(mStringList, std::vector(stringList)); } } tremotesf-2.10.1/src/ui/itemmodels/stringlistmodel.h000066400000000000000000000036071523411710200225470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_STRINGLISTMODEL_H #define TREMOTESF_STRINGLISTMODEL_H #include #include #include #include #include namespace tremotesf { class StringListModel final : public QAbstractListModel { Q_OBJECT public: explicit inline StringListModel(std::optional header, std::optional decoration, QObject* parent) : QAbstractListModel(parent), mHeader(std::move(header)), mDecoration(std::move(decoration)) {}; inline QVariant data(const QModelIndex& index, int role) const override { if (!index.isValid()) return {}; switch (role) { case Qt::DisplayRole: return mStringList.at(static_cast(index.row())); case Qt::DecorationRole: if (mDecoration) { return *mDecoration; } break; default: break; } return {}; }; inline QVariant headerData(int, Qt::Orientation orientation, int role) const override { return (orientation == Qt::Horizontal && role == Qt::DisplayRole && mHeader) ? *mHeader : QVariant{}; }; inline int rowCount(const QModelIndex& = {}) const override { return static_cast(mStringList.size()); }; void setStringList(const std::vector& stringList); using QAbstractItemModel::beginInsertRows; using QAbstractItemModel::beginRemoveRows; using QAbstractItemModel::endInsertRows; using QAbstractItemModel::endRemoveRows; private: std::optional mHeader; std::optional mDecoration; std::vector mStringList; }; } #endif // TREMOTESF_STRINGLISTMODEL_H tremotesf-2.10.1/src/ui/itemmodels/torrentfilesmodel_test.cpp000066400000000000000000001105551523411710200244600ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #define QTEST_THROW_ON_FAIL #include #include #include #include #include "basetorrentfilesmodel.h" #include "torrentfilestreebuilder.h" #include "formatutils.h" #include "log/log.h" #include SPECIALIZE_FORMATTER_FOR_QDEBUG(QVariant) using namespace Qt::StringLiterals; namespace tremotesf { using formatutils::formatByteSize; using formatutils::formatProgress; namespace { QModelIndex indexForPath(QLatin1String path, QAbstractItemModel& model) { QModelIndex index{}; for (const auto& part : path.tokenize(u'/')) { const auto childIndexes = std::views::iota(0, model.rowCount(index)) | std::views::transform([&](int row) { return model.index(row, 0, index); }); const auto found = std::ranges::find(childIndexes, part, [&](const QModelIndex& childIndex) { return childIndex.data().toString(); }); if (found == childIndexes.end()) { warning().log("Did not find child with name {}", part); QFAIL("Nope"); } index = *found; } return index; } struct ExpectedDataChanged { QModelIndex topLeft; QModelIndex bottomRight{topLeft.siblingAtColumn(topLeft.model()->columnCount() - 1)}; QList roles{}; std::strong_ordering operator<=>(const ExpectedDataChanged&) const = default; static ExpectedDataChanged allColumnsAndRoles(QModelIndex topLeft, QModelIndex bottomRight) { return { .topLeft = topLeft, .bottomRight = bottomRight.siblingAtColumn(bottomRight.model()->columnCount() - 1) }; } static ExpectedDataChanged allColumnsAndRoles(QModelIndex index) { return allColumnsAndRoles(index, index); } static ExpectedDataChanged specificColumnAndRoles(QModelIndex topLeft, QModelIndex bottomRight, int column, QList roles) { return { .topLeft = topLeft.siblingAtColumn(column), .bottomRight = bottomRight.siblingAtColumn(column), .roles = std::move(roles) }; } static ExpectedDataChanged specificColumnAndRoles(QModelIndex index, int column, QList roles) { return specificColumnAndRoles(index, index, column, std::move(roles)); } }; [[maybe_unused]] QDebug operator<<(QDebug debug, const ExpectedDataChanged& dataChanged) { const QDebugStateSaver saver(debug); debug.nospace() << "{.topLeft = " << dataChanged.topLeft << ", bottomRight = " << dataChanged.bottomRight << ", roles = " << dataChanged.roles << "}"; return debug; } std::set actualDataChanged(QSignalSpy& spy) { return spy | std::views::transform([](QList args) { return ExpectedDataChanged{ .topLeft = args.at(0).toModelIndex(), .bottomRight = args.at(1).toModelIndex(), .roles = args.at(2).toList() | std::views::transform([](const QVariant& role) { return role.toInt(); }) | std::ranges::to() }; }) | std::ranges::to(); } } class TestTorrentFilesModel : public BaseTorrentFilesModel { Q_OBJECT public: TestTorrentFilesModel() : BaseTorrentFilesModel( {Column::Name, Column::Size, Column::ProgressBar, Column::Progress, Column::Priority} ) {} void renameFile(const QModelIndex&, const QString&) override {} struct File { QString path; qint64 size; qint64 completedSize; TorrentFilesModelEntry::Priority priority; bool wanted; }; void populateEverythingNormal() { const std::array files{ File{ .path = "topdir/subdir1/subsubddir/file1", .size = 666, .completedSize = 0, .priority = TorrentFilesModelEntry::Priority::Normal, .wanted = true }, File{ .path = "topdir/subdir1/subsubddir/file2", .size = 100000, .completedSize = 4234, .priority = TorrentFilesModelEntry::Priority::Normal, .wanted = true }, File{ .path = "topdir/subdir2/file1", .size = 3333333, .completedSize = 0, .priority = TorrentFilesModelEntry::Priority::Normal, .wanted = true }, File{ .path = "topdir/subdir2/file2", .size = 111, .completedSize = 0, .priority = TorrentFilesModelEntry::Priority::Normal, .wanted = true } }; TorrentFilesTreeBuilder builder(files.size()); for (const File& file : files) { builder.addFile( file.path.tokenize(u'/', Qt::SkipEmptyParts), true, file.size, file.completedSize, file.wanted, file.priority ); } builder.calculateDirectoriesRecursively(); mRootEntry = std::move(builder.rootEntry); mFiles = std::move(builder.files); } void populateMixedState() { const std::array files{ File{ .path = "topdir/subdir1/subsubddir/file1", .size = 666, .completedSize = 0, .priority = TorrentFilesModelEntry::Priority::Normal, .wanted = true }, File{ .path = "topdir/subdir1/subsubddir/file2", .size = 100000, .completedSize = 4234, .priority = TorrentFilesModelEntry::Priority::Low, .wanted = false }, File{ .path = "topdir/subdir2/file1", .size = 3333333, .completedSize = 0, .priority = TorrentFilesModelEntry::Priority::Normal, .wanted = true }, File{ .path = "topdir/subdir2/file2", .size = 111, .completedSize = 0, .priority = TorrentFilesModelEntry::Priority::Normal, .wanted = true } }; TorrentFilesTreeBuilder builder(files.size()); for (const File& file : files) { builder.addFile( file.path.tokenize(u'/', Qt::SkipEmptyParts), true, file.size, file.completedSize, file.wanted, file.priority ); } builder.calculateDirectoriesRecursively(); mRootEntry = std::move(builder.rootEntry); mFiles = std::move(builder.files); } using BaseTorrentFilesModel::updateFiles; }; class TorrentFilesModelTest : public QObject { Q_OBJECT private slots: void checkInitialStateNormal() { TestTorrentFilesModel model{}; model.populateEverythingNormal(); checkTree( model, {.name = "topdir"_L1, .size = 3434110, .progress = 4234.0 / 3434110.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = { {.name = "subdir1"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = {{.name = "subsubddir"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = {{.name = "file1"_L1, .size = 666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 100000, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}}}}}, {.name = "subdir2"_L1, .size = 3333444, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = { {.name = "file1"_L1, .size = 3333333, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 111, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked} }} }} ); } void checkInitialStateMixed() { TestTorrentFilesModel model{}; model.populateMixedState(); checkTree( model, {.name = "topdir"_L1, .size = 3434110, .progress = 4234.0 / 3434110.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::PartiallyChecked, .children = { {.name = "subdir1"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::PartiallyChecked, .children = {{.name = "subsubddir"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::PartiallyChecked, .children = {{.name = "file1"_L1, .size = 666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 100000, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Unchecked}}}}}, {.name = "subdir2"_L1, .size = 3333444, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = { {.name = "file1"_L1, .size = 3333333, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 111, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked} }} }} ); } void checkSetFilesWantedFromRoot() { TestTorrentFilesModel model{}; model.populateMixedState(); QSignalSpy dataChanged(&model, &QAbstractItemModel::dataChanged); model.setFilesWanted({model.index(0, 0)}, false); const auto actualSignals = actualDataChanged(dataChanged); const int nameColumn = model.columnNumber(BaseTorrentFilesModel::Column::Name); const QList expectedRoles{Qt::CheckStateRole}; const auto expectedSignals = std::set{ ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir/file1"_L1, model), nameColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir"_L1, model), nameColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir2/file1"_L1, model), indexForPath("topdir/subdir2/file2"_L1, model), nameColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1"_L1, model), indexForPath("topdir/subdir2"_L1, model), nameColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles(indexForPath("topdir"_L1, model), nameColumn, expectedRoles) }; QCOMPARE(actualSignals, expectedSignals); checkTree( model, {.name = "topdir"_L1, .size = 3434110, .progress = 4234.0 / 3434110.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Unchecked, .children = { {.name = "subdir1"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Unchecked, .children = {{.name = "subsubddir"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Unchecked, .children = {{.name = "file1"_L1, .size = 666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Unchecked}, {.name = "file2"_L1, .size = 100000, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Unchecked}}}}}, {.name = "subdir2"_L1, .size = 3333444, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Unchecked, .children = { {.name = "file1"_L1, .size = 3333333, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Unchecked}, {.name = "file2"_L1, .size = 111, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Unchecked} }} }} ); } void checkSetFilesWantedFromFile() { TestTorrentFilesModel model{}; model.populateMixedState(); QSignalSpy dataChanged(&model, &QAbstractItemModel::dataChanged); model.setFilesWanted({indexForPath("topdir/subdir1/subsubddir/file2"_L1, model)}, true); const auto actualSignals = actualDataChanged(dataChanged); const int nameColumn = model.columnNumber(BaseTorrentFilesModel::Column::Name); const QList expectedRoles{Qt::CheckStateRole}; const auto expectedSignals = std::set{ ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir/file2"_L1, model), nameColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir"_L1, model), nameColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1"_L1, model), nameColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles(indexForPath("topdir"_L1, model), nameColumn, expectedRoles) }; QCOMPARE(actualSignals, expectedSignals); checkTree( model, {.name = "topdir"_L1, .size = 3434110, .progress = 4234.0 / 3434110.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Checked, .children = { {.name = "subdir1"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Checked, .children = {{.name = "subsubddir"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Checked, .children = {{.name = "file1"_L1, .size = 666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 100000, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Checked}}}}}, {.name = "subdir2"_L1, .size = 3333444, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = { {.name = "file1"_L1, .size = 3333333, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 111, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked} }} }} ); } void checkSetFilesPriorityFromRoot() { TestTorrentFilesModel model{}; model.populateMixedState(); QSignalSpy dataChanged(&model, &QAbstractItemModel::dataChanged); model.setFilesPriority({model.index(0, 0)}, TorrentFilesModelEntry::Priority::Low); const auto actualSignals = actualDataChanged(dataChanged); const int priorityColumn = model.columnNumber(BaseTorrentFilesModel::Column::Priority); const QList expectedRoles{Qt::DisplayRole, BaseTorrentFilesModel::SortRole}; const auto expectedSignals = std::set{ ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir/file1"_L1, model), priorityColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir"_L1, model), priorityColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir2/file1"_L1, model), indexForPath("topdir/subdir2/file2"_L1, model), priorityColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1"_L1, model), indexForPath("topdir/subdir2"_L1, model), priorityColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir"_L1, model), priorityColumn, expectedRoles ) }; QCOMPARE(actualSignals, expectedSignals); checkTree( model, {.name = "topdir"_L1, .size = 3434110, .progress = 4234.0 / 3434110.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::PartiallyChecked, .children = { {.name = "subdir1"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::PartiallyChecked, .children = {{.name = "subsubddir"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::PartiallyChecked, .children = {{.name = "file1"_L1, .size = 666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 100000, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Unchecked}}}}}, {.name = "subdir2"_L1, .size = 3333444, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Checked, .children = { {.name = "file1"_L1, .size = 3333333, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 111, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Low, .checkState = Qt::CheckState::Checked} }} }} ); } void checkSetFilesPriorityFromFile() { TestTorrentFilesModel model{}; model.populateMixedState(); QSignalSpy dataChanged(&model, &QAbstractItemModel::dataChanged); model.setFilesPriority( {indexForPath("topdir/subdir1/subsubddir/file2"_L1, model)}, TorrentFilesModelEntry::Priority::Normal ); const auto actualSignals = actualDataChanged(dataChanged); const int priorityColumn = model.columnNumber(BaseTorrentFilesModel::Column::Priority); const QList expectedRoles{Qt::DisplayRole, BaseTorrentFilesModel::SortRole}; const auto expectedSignals = std::set{ ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir/file2"_L1, model), priorityColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1/subsubddir"_L1, model), priorityColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir/subdir1"_L1, model), priorityColumn, expectedRoles ), ExpectedDataChanged::specificColumnAndRoles( indexForPath("topdir"_L1, model), priorityColumn, expectedRoles ) }; QCOMPARE(actualSignals, expectedSignals); checkTree( model, {.name = "topdir"_L1, .size = 3434110, .progress = 4234.0 / 3434110.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::PartiallyChecked, .children = { {.name = "subdir1"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::PartiallyChecked, .children = {{.name = "subsubddir"_L1, .size = 100666, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::PartiallyChecked, .children = {{.name = "file1"_L1, .size = 666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 100000, .progress = 4234.0 / 100666.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Unchecked}}}}}, {.name = "subdir2"_L1, .size = 3333444, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = { {.name = "file1"_L1, .size = 3333333, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 111, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked} }} }} ); } void checkUpdateFiles() { TestTorrentFilesModel model{}; model.populateMixedState(); std::array changed{TestTorrentFilesModel::File{}}; QSignalSpy dataChanged(&model, &QAbstractItemModel::dataChanged); model.updateFiles(std::array{0, 1}, [](size_t index, TorrentFilesModelEntry* file) { switch (index) { case 0: file->update(true, TorrentFilesModelEntry::Priority::High, 0); break; case 1: file->update(true, TorrentFilesModelEntry::Priority::Normal, 0); break; } }); const auto actualSignals = actualDataChanged(dataChanged); const auto expectedSignals = std::set{ ExpectedDataChanged::allColumnsAndRoles( indexForPath("topdir/subdir1/subsubddir/file1"_L1, model), indexForPath("topdir/subdir1/subsubddir/file2"_L1, model) ), ExpectedDataChanged::allColumnsAndRoles(indexForPath("topdir/subdir1/subsubddir"_L1, model)), ExpectedDataChanged::allColumnsAndRoles(indexForPath("topdir/subdir1"_L1, model)), ExpectedDataChanged::allColumnsAndRoles(indexForPath("topdir"_L1, model)) }; QCOMPARE(actualSignals, expectedSignals); checkTree( model, {.name = "topdir"_L1, .size = 3434110, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Checked, .children = { {.name = "subdir1"_L1, .size = 100666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Checked, .children = {{.name = "subsubddir"_L1, .size = 100666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Mixed, .checkState = Qt::CheckState::Checked, .children = {{.name = "file1"_L1, .size = 666, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::High, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 100000, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}}}}}, {.name = "subdir2"_L1, .size = 3333444, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked, .children = { {.name = "file1"_L1, .size = 3333333, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked}, {.name = "file2"_L1, .size = 111, .progress = 0.0, .priority = TorrentFilesModelEntry::Priority::Normal, .checkState = Qt::CheckState::Checked} }} }} ); } private: struct ExpectedData { QLatin1String name; long long size; double progress; TorrentFilesModelEntry::Priority priority; Qt::CheckState checkState; std::vector children{}; }; void checkTree(BaseTorrentFilesModel& model, const ExpectedData& expectedRootEntry) { checkEntryPresentation(model, {}, 0, expectedRootEntry, 0); } void checkEntryPresentation( BaseTorrentFilesModel& model, const QModelIndex& parent, int row, const ExpectedData& expected, int depth ) { info().log("{}* {}", u" "_s.repeated(depth), expected.name); const auto data = [&](BaseTorrentFilesModel::Column column, int role = Qt::DisplayRole) { return model.index(row, static_cast(column), parent).data(role); }; QCOMPARE(data(BaseTorrentFilesModel::Column::Name).toString(), expected.name); QCOMPARE(data(BaseTorrentFilesModel::Column::Size).toString(), formatByteSize(expected.size)); QCOMPARE(data(BaseTorrentFilesModel::Column::Progress).toString(), formatProgress(expected.progress)); QCOMPARE( data(BaseTorrentFilesModel::Column::Name, Qt::CheckStateRole).value(), expected.checkState ); const auto expectedPriorityString = [&] { switch (expected.priority) { case TorrentFilesModelEntry::Priority::Low: return qApp->translate("tremotesf", "Low"); case TorrentFilesModelEntry::Priority::Normal: return qApp->translate("tremotesf", "Normal"); case TorrentFilesModelEntry::Priority::High: return qApp->translate("tremotesf", "High"); case TorrentFilesModelEntry::Priority::Mixed: return qApp->translate("tremotesf", "Mixed"); } return QString{}; }(); QCOMPARE(data(BaseTorrentFilesModel::Column::Priority).toString(), expectedPriorityString); const auto thisIndex = model.index(row, 0, parent); QCOMPARE(model.rowCount(thisIndex), static_cast(expected.children.size())); if (!expected.children.empty()) { int i = 0; for (const auto& child : expected.children) { checkEntryPresentation(model, thisIndex, i, child, depth + 1); ++i; } } } }; } QTEST_GUILESS_MAIN(tremotesf::TorrentFilesModelTest) #include "torrentfilesmodel_test.moc" tremotesf-2.10.1/src/ui/itemmodels/torrentfilesmodelentry.cpp000066400000000000000000000136431523411710200245030ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include "desktoputils.h" #include "stdutils.h" #include "torrentfilesmodelentry.h" namespace tremotesf { TorrentFilesModelEntry::Priority TorrentFilesModelEntry::fromFilePriority(TorrentFile::Priority priority) { switch (priority) { case TorrentFile::Priority::Low: return Priority::Low; case TorrentFile::Priority::Normal: return Priority::Normal; case TorrentFile::Priority::High: return Priority::High; default: return Priority::Normal; } } TorrentFile::Priority TorrentFilesModelEntry::toFilePriority(Priority priority) { switch (priority) { case Priority::Low: return TorrentFile::Priority::Low; case Priority::Normal: return TorrentFile::Priority::Normal; case Priority::High: return TorrentFile::Priority::High; default: return TorrentFile::Priority::Normal; } } QString TorrentFilesModelEntry::path() const { QString path(mName); const auto* parent = mParentDirectory; while (parent && !parent->name().isEmpty()) { path.prepend('/'); path.prepend(parent->name()); parent = parent->parentDirectory(); } return path; } bool TorrentFilesModelEntry::setWanted(bool wanted) { WantedState wantedState{}; if (wanted) { wantedState = WantedState::Wanted; } else { wantedState = WantedState::Unwanted; } if (wantedState != mWantedState) { mWantedState = wantedState; return true; } return false; } QString TorrentFilesModelEntry::priorityString() const { switch (mPriority) { case Priority::Low: //: Torrent's file loading priority return qApp->translate("tremotesf", "Low"); case Priority::Normal: //: Torrent's file loading priority return qApp->translate("tremotesf", "Normal"); case Priority::High: //: Torrent's file loading priority return qApp->translate("tremotesf", "High"); case Priority::Mixed: //: Torrent's file loading priority return qApp->translate("tremotesf", "Mixed"); } return {}; } bool TorrentFilesModelEntry::setPriority(Priority priority) { if (priority != mPriority) { mPriority = priority; return true; } return false; } bool TorrentFilesModelEntry::update(bool wanted, Priority priority, long long completedSize) { return update(wanted ? WantedState::Wanted : WantedState::Unwanted, priority, completedSize); } bool TorrentFilesModelEntry::update(WantedState wantedState, Priority priority, long long completedSize) { bool changed = false; setChanged(mWantedState, wantedState, changed); setChanged(mPriority, priority, changed); setChanged(mCompletedSize, completedSize, changed); return changed; } namespace { QIcon determineFileIcon(const QString& fileName) { static const QMimeDatabase mimeDb{}; const auto mimeType = mimeDb.mimeTypeForFile(fileName, QMimeDatabase::MatchExtension); if (!mimeType.isValid()) { return desktoputils::standardFileIcon(); } if (const auto icon = QIcon::fromTheme(mimeType.iconName()); !icon.isNull()) { return icon; } if (const auto icon = QIcon::fromTheme(mimeType.genericIconName()); !icon.isNull()) { return icon; } return desktoputils::standardFileIcon(); } } QIcon TorrentFilesModelEntry::icon() const { return std::visit( [&](auto&& data) { if constexpr (std::same_as>) { if (!data.initializedIcon) { data.icon = determineFileIcon(name()); data.initializedIcon = true; } return data.icon; } else { return desktoputils::standardDirIcon(); } }, mFileOrDirectoryData ); } void TorrentFilesModelEntry::getFileIds(std::vector& ids) const { std::visit( [&](auto&& data) { if constexpr (std::same_as>) { ids.push_back(data.id); } else { for (const auto& child : data.children) { child.getFileIds(ids); } } }, mFileOrDirectoryData ); } bool TorrentFilesModelEntry::recalculateFromChildren() { const auto& children = std::get(mFileOrDirectoryData).children; if (children.empty()) return false; const auto& firstChild = children.front(); long long completedSize = firstChild.completedSize(); WantedState wantedState = firstChild.wantedState(); Priority priority = firstChild.priority(); for (const auto& child : children | std::views::drop(1)) { completedSize += child.completedSize(); if (wantedState != TorrentFilesModelEntry::WantedState::Mixed && child.wantedState() != wantedState) { wantedState = TorrentFilesModelEntry::WantedState::Mixed; } if (priority != TorrentFilesModelEntry::Priority::Mixed && child.priority() != priority) { priority = TorrentFilesModelEntry::Priority::Mixed; } } return update(wantedState, priority, completedSize); } } tremotesf-2.10.1/src/ui/itemmodels/torrentfilesmodelentry.h000066400000000000000000000105671523411710200241520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTFILESMODELENTRY_H #define TREMOTESF_TORRENTFILESMODELENTRY_H #include #include #include #include #include "rpc/torrentfile.h" namespace tremotesf { class TorrentFilesModelEntry { Q_GADGET public: enum class WantedState : char { Wanted, Unwanted, Mixed }; Q_ENUM(WantedState) enum class Priority : char { Low, Normal, High, Mixed }; Q_ENUM(Priority) static Priority fromFilePriority(TorrentFile::Priority priority); static TorrentFile::Priority toFilePriority(Priority priority); inline int row() const { return mRow; } inline TorrentFilesModelEntry* parentDirectory() const { return mParentDirectory; } inline void setParentDirectory(TorrentFilesModelEntry* directory) { mParentDirectory = directory; } inline QString name() const { return mName; } inline void setName(const QString& name) { mName = name; } QString path() const; inline long long size() const { return mSize; } void setSize(long long size) { mSize = size; } inline long long completedSize() const { return mCompletedSize; } inline double progress() const { return mSize > 0 ? static_cast(mCompletedSize) / static_cast(mSize) : 0.0; } inline WantedState wantedState() const { return mWantedState; } bool setWanted(bool wanted); inline Priority priority() const { return mPriority; } QString priorityString() const; bool setPriority(Priority priority); bool update(bool wanted, Priority priority, long long completedSize); bool update(WantedState wantedState, Priority priority, long long completedSize); QIcon icon() const; void getFileIds(std::vector& ids) const; inline bool isDirectory() const { return std::holds_alternative(mFileOrDirectoryData); } inline std::vector& children() { return std::get(mFileOrDirectoryData).children; } inline const std::vector& children() const { return std::get(mFileOrDirectoryData).children; } bool recalculateFromChildren(); inline int fileId() const { return std::get(mFileOrDirectoryData).id; } inline static TorrentFilesModelEntry createFile( int id, int row, QString name, long long size, long long completedSize, bool wanted, Priority priority ) { return TorrentFilesModelEntry( row, std::move(name), size, completedSize, wanted, priority, FileData{.id = id} ); } inline static TorrentFilesModelEntry createDirectory(int row, QString name) { return TorrentFilesModelEntry(row, std::move(name), DirectoryData{}); } private: TorrentFilesModelEntry* mParentDirectory{}; QString mName; long long mSize{}; long long mCompletedSize{}; int mRow; WantedState mWantedState{}; Priority mPriority{}; struct FileData { mutable QIcon icon{}; mutable bool initializedIcon{}; int id; }; struct DirectoryData { std::vector children; }; std::variant mFileOrDirectoryData; inline TorrentFilesModelEntry( int row, QString name, long long size, long long completedSize, bool wanted, Priority priority, FileData data ) : mName(std::move(name)), mSize(size), mCompletedSize(completedSize), mRow(row), mWantedState(wanted ? WantedState::Wanted : WantedState::Unwanted), mPriority(priority), mFileOrDirectoryData(std::move(data)) {} inline TorrentFilesModelEntry(int row, QString name, DirectoryData data) : mName(std::move(name)), mRow(row), mFileOrDirectoryData(std::move(data)) {} }; } #endif // TREMOTESF_TORRENTFILESMODELENTRY_H tremotesf-2.10.1/src/ui/itemmodels/torrentfilesproxymodel.cpp000066400000000000000000000020151523411710200245120ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentfilesproxymodel.h" #include "ui/itemmodels/basetorrentfilesmodel.h" namespace tremotesf { TorrentFilesProxyModel::TorrentFilesProxyModel( BaseTorrentFilesModel* sourceModel, int sortRole, int fallbackColumn, QObject* parent ) : BaseProxyModel(sourceModel, sortRole, fallbackColumn, parent) {} bool TorrentFilesProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { const bool leftIsDirectory = static_cast(left.internalPointer())->isDirectory(); const bool rightIsDirectory = static_cast(right.internalPointer())->isDirectory(); if (leftIsDirectory != rightIsDirectory) { if (sortOrder() == Qt::AscendingOrder) { return leftIsDirectory; } return rightIsDirectory; } return BaseProxyModel::lessThan(left, right); } } tremotesf-2.10.1/src/ui/itemmodels/torrentfilesproxymodel.h000066400000000000000000000012751523411710200241660ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTFILESPROXYMODEL_H #define TREMOTESF_TORRENTFILESPROXYMODEL_H #include "ui/itemmodels/baseproxymodel.h" namespace tremotesf { class BaseTorrentFilesModel; class TorrentFilesProxyModel final : public BaseProxyModel { Q_OBJECT public: explicit TorrentFilesProxyModel( BaseTorrentFilesModel* sourceModel, int sortRole, int fallbackColumn, QObject* parent = nullptr ); protected: bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; }; } #endif // TREMOTESF_TORRENTFILESPROXYMODEL_H tremotesf-2.10.1/src/ui/itemmodels/torrentfilestreebuilder.h000066400000000000000000000172161523411710200242740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTFILESTREEBUILDER_H #define TREMOTESF_TORRENTFILESTREEBUILDER_H #include #include #include #include #include "torrentfilesmodelentry.h" namespace tremotesf { namespace impl { template concept QStringOrView = std::same_as || std::same_as; inline QString toString(QStringView view) { return view.toString(); } inline const QString& toString(const QString& string) { return string; } } class TorrentFilesTreeBuilder final { public: explicit TorrentFilesTreeBuilder(size_t reserveFilesCount) { files.reserve(reserveFilesCount); } ~TorrentFilesTreeBuilder() = default; Q_DISABLE_COPY_MOVE(TorrentFilesTreeBuilder) void initializeRootDirectory(QString name) { rootEntry = std::make_unique(TorrentFilesModelEntry::createDirectory(0, std::move(name))); } template requires(std::ranges::input_range && impl::QStringOrView>) void addFile( PathParts&& pathParts, bool pathIncludesFirstPart, long long size, long long completedSize, bool wanted, TorrentFilesModelEntry::Priority priority ) { auto iter = pathParts.begin(); if (iter == pathParts.end()) return; if (pathIncludesFirstPart) { if (!rootEntry) { auto firstPart = *iter; ++iter; if (iter == pathParts.end()) { // This is a file not in any directory. We don't expect any addFile() calls rootEntry = std::make_unique(TorrentFilesModelEntry::createFile( 0, 0, impl::toString(firstPart), size, completedSize, wanted, priority )); files.push_back(rootEntry.get()); return; } initializeRootDirectory(impl::toString(firstPart)); } else { if (!rootEntry->isDirectory()) { // Root entry must be a directory return; } ++iter; if (iter == pathParts.end()) { // This is a file not in any directory, but we already have root entry? This shouldn't happen return; } } } else if (!rootEntry || !rootEntry->isDirectory()) { // If path doesn't include first part then root entry must be initialized using initializeRootDirectory() return; } // This a file in a directory(ies) TorrentFilesModelEntry* currentDirectory = rootEntry.get(); DirectoryCacheEntry* currentDirectoryCacheEntry = &mRootDirectoryCacheEntry; while (true) { auto part = *iter; ++iter; if (iter != pathParts.end()) { // This was not the last part, therefore a directory name const auto found = currentDirectoryCacheEntry->childDirectoriesCache.find(part); if (found != currentDirectoryCacheEntry->childDirectoriesCache.constEnd()) { currentDirectory = ¤tDirectory->children()[found.value().indexInParent]; currentDirectoryCacheEntry = &found.value(); } else { const auto partString = impl::toString(part); auto& children = currentDirectory->children(); children.push_back( TorrentFilesModelEntry::createDirectory(static_cast(children.size()), partString) ); const auto indexInParent = children.size() - 1; currentDirectory = &children.back(); const auto inserted = currentDirectoryCacheEntry->childDirectoriesCache.emplace( partString, DirectoryCacheEntry{.indexInParent = indexInParent} ); currentDirectoryCacheEntry = &inserted.value(); } } else { // This was the last part, therefore a file name auto& children = currentDirectory->children(); children.push_back( TorrentFilesModelEntry::createFile( mFileId, static_cast(children.size()), impl::toString(part), size, completedSize, wanted, priority ) ); ++mFileId; break; } } } void calculateDirectoriesRecursively() { if (!rootEntry) return; if (!rootEntry->isDirectory()) return; calculateFromChildrenRecursively(rootEntry.get()); std::ranges::sort(files, std::ranges::less{}, &TorrentFilesModelEntry::fileId); } std::unique_ptr rootEntry{}; std::vector files{}; private: void calculateFromChildrenRecursively(TorrentFilesModelEntry* directory) { auto& children = directory->children(); if (children.empty()) return; long long size{}; long long completedSize{}; TorrentFilesModelEntry::WantedState wantedState{}; TorrentFilesModelEntry::Priority priority{}; bool isFirst = true; for (auto& child : children) { child.setParentDirectory(directory); if (child.isDirectory()) { calculateFromChildrenRecursively(&child); } else { files.push_back(&child); } size += child.size(); completedSize += child.completedSize(); if (isFirst) { wantedState = child.wantedState(); priority = child.priority(); isFirst = false; } else { if (wantedState != TorrentFilesModelEntry::WantedState::Mixed && child.wantedState() != wantedState) { wantedState = TorrentFilesModelEntry::WantedState::Mixed; } if (priority != TorrentFilesModelEntry::Priority::Mixed && child.priority() != priority) { priority = TorrentFilesModelEntry::Priority::Mixed; } } } directory->setSize(size); directory->update(wantedState, priority, completedSize); } struct DirectoryCacheEntry { size_t indexInParent; QHash childDirectoriesCache{}; }; DirectoryCacheEntry mRootDirectoryCacheEntry{}; int mFileId{}; }; } #endif // TREMOTESF_TORRENTFILESTREEBUILDER_H tremotesf-2.10.1/src/ui/notificationscontroller.cpp000066400000000000000000000125651523411710200224750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "notificationscontroller.h" #include #include #include "log/log.h" #include "rpc/rpc.h" #include "rpc/servers.h" #include "settings.h" namespace tremotesf { void NotificationsController::showNotification(const QString& title, const QString& message) { fallbackToSystemTrayIcon(title, message); } NotificationsController::NotificationsController(QSystemTrayIcon* trayIcon, const Rpc* rpc, QObject* parent) : QObject(parent), mTrayIcon(trayIcon) { QObject::connect(mTrayIcon, &QSystemTrayIcon::messageClicked, this, [this] { info().log("NotificationsController: notification clicked"); emit notificationClicked({}); }); QObject::connect(rpc, &Rpc::connectedChanged, this, [rpc, this] { if (rpc->isConnected()) { onConnected(rpc); } else { onDisconnected(rpc); } }); QObject::connect(rpc, &Rpc::torrentAdded, this, [this](const auto* torrent) { if (Settings::instance()->get_notificationOnAddingTorrent()) { showAddedTorrentsNotification({torrent->data().name}); } }); QObject::connect(rpc, &Rpc::torrentFinished, this, [this](const auto* torrent) { if (Settings::instance()->get_notificationOfFinishedTorrents()) { showFinishedTorrentsNotification({torrent->data().name}); } }); } void NotificationsController::fallbackToSystemTrayIcon(const QString& title, const QString& message) { if (mTrayIcon->isVisible()) { info().log("NotificationsController: executing QSystemTrayIcon::showMessage()"); mTrayIcon->showMessage(title, message, QSystemTrayIcon::Information, 0); } else { warning().log("NotificationsController: system tray icon is not visible, don't show notification"); } } void NotificationsController::onConnected(const Rpc* rpc) { const bool notifyOnAdded = Settings::instance()->get_notificationsOnAddedTorrentsSinceLastConnection(); const bool notifyOnFinished = Settings::instance()->get_notificationsOnFinishedTorrentsSinceLastConnection(); if (!notifyOnAdded && !notifyOnFinished) { return; } const LastTorrents lastTorrents(Servers::instance()->currentServerLastTorrents()); if (!lastTorrents.saved) { return; } QStringList addedNames{}; QStringList finishedNames{}; for (const auto& torrent : rpc->torrents()) { const auto found = std::ranges::find( lastTorrents.torrents, torrent->data().hashString, &LastTorrents::Torrent::hashString ); if (found == lastTorrents.torrents.cend()) { if (notifyOnAdded) { addedNames.push_back(torrent->data().name); } } else { if (notifyOnFinished && !found->finished && torrent->data().isFinished()) { finishedNames.push_back(torrent->data().name); } } } if (notifyOnAdded && !addedNames.isEmpty()) { showAddedTorrentsNotification(addedNames); } if (notifyOnFinished && !finishedNames.isEmpty()) { showFinishedTorrentsNotification(finishedNames); } } void NotificationsController::onDisconnected(const Rpc* rpc) { const auto& status = rpc->status(); if ((status.error != Rpc::Error::NoError) && Settings::instance()->get_notificationOnDisconnecting()) { showNotification( //: Notification title when disconnected from server qApp->translate("tremotesf", "Disconnected"), status.toString() ); } } void NotificationsController::showFinishedTorrentsNotification(const QStringList& torrentNames) { showTorrentsNotification( torrentNames.size() == 1 //: Notification title ? qApp->translate("tremotesf", "Torrent finished") //: Notification title, %Ln is number of finished torrents : qApp->translate("tremotesf", "%Ln torrents finished", nullptr, static_cast(torrentNames.size())), torrentNames ); } void NotificationsController::showAddedTorrentsNotification(const QStringList& torrentNames) { showTorrentsNotification( torrentNames.size() == 1 //: Notification title ? qApp->translate("tremotesf", "Torrent added") //: Notification title, %Ln is number of added torrents : qApp->translate("tremotesf", "%Ln torrents added", nullptr, static_cast(torrentNames.size())), torrentNames ); } void NotificationsController::showTorrentsNotification(const QString& title, const QStringList& torrentNames) { if (torrentNames.size() == 1) { showNotification(title, torrentNames.first()); } else { auto names = torrentNames; for (auto& name : names) { name.prepend(u"\u2022 "); } showNotification(title, names.join('\n')); } } } tremotesf-2.10.1/src/ui/notificationscontroller.h000066400000000000000000000025161523411710200221350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_NOTIFICATIONSCONTROLLER_H #define TREMOTESF_NOTIFICATIONSCONTROLLER_H #include #include class QSystemTrayIcon; namespace tremotesf { class Rpc; class NotificationsController : public QObject { Q_OBJECT public: static NotificationsController* createInstance(QSystemTrayIcon* trayIcon, const Rpc* rpc, QObject* parent = nullptr); protected: explicit NotificationsController(QSystemTrayIcon* trayIcon, const Rpc* rpc, QObject* parent = nullptr); virtual void showNotification(const QString& title, const QString& message); void fallbackToSystemTrayIcon(const QString& title, const QString& message); private: void onConnected(const Rpc* rpc); void onDisconnected(const Rpc* rpc); void showFinishedTorrentsNotification(const QStringList& torrentNames); void showAddedTorrentsNotification(const QStringList& torrentNames); void showTorrentsNotification(const QString& title, const QStringList& torrentNames); QSystemTrayIcon* mTrayIcon{}; signals: void notificationClicked(const std::optional& windowActivationToken); }; } #endif // TREMOTESF_NOTIFICATIONSCONTROLLER_H tremotesf-2.10.1/src/ui/notificationscontroller_fallback.cpp000066400000000000000000000005621523411710200243060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "notificationscontroller.h" namespace tremotesf { NotificationsController* NotificationsController::createInstance(QSystemTrayIcon* trayIcon, const Rpc* rpc, QObject* parent) { return new NotificationsController(trayIcon, rpc, parent); } } tremotesf-2.10.1/src/ui/notificationscontroller_freedesktop.cpp000066400000000000000000000170441523411710200250650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "notificationscontroller.h" #include #include #include #include #include "coroutines/dbus.h" #include "coroutines/scope.h" #include "desktoputils.h" #include "log/log.h" #include "tremotesf_dbus_generated/org.freedesktop.portal.Notification.h" #include "tremotesf_dbus_generated/org.freedesktop.Notifications.h" #include "rpc/servers.h" using namespace Qt::StringLiterals; SPECIALIZE_FORMATTER_FOR_QDEBUG(QDBusError) namespace tremotesf { namespace { constexpr auto flatpakEnvVariable = "FLATPAK_ID"; class PortalNotificationsController final : public NotificationsController { Q_OBJECT public: explicit PortalNotificationsController(QSystemTrayIcon* trayIcon, const Rpc* rpc, QObject* parent = nullptr) : NotificationsController(trayIcon, rpc, parent) { qDBusRegisterMetaType>(); mPortalInterface.setTimeout(desktoputils::defaultDbusTimeout); } protected: void showNotification(const QString& title, const QString& message) override { mCoroutineScope.launch(showNotificationViaPortal(title, message)); } private: Coroutine<> showNotificationViaPortal(QString title, QString message) { info().log( "PortalNotificationsController: executing " "org.freedesktop.portal.Notification.AddNotification() D-Bus call" ); const auto reply = mPortalInterface.AddNotification( QUuid::createUuid().toString(QUuid::WithoutBraces), { {"title"_L1, title}, {"body"_L1, message}, {"icon"_L1, QVariant::fromValue( QPair( "themed"_L1, QDBusVariant(QStringList{TREMOTESF_APP_ID ""_L1}) ) )}, {"default-action"_L1, "app.default"_L1}, // We just need to put something here {"default-action-target", true}, } ); co_await reply; if (!reply.isError()) { info().log( "PortalNotificationsController: executed " "org.freedesktop.portal.Notification.AddNotification() D-Bus call" ); co_return; } warning().log( "PortalNotificationsController: org.freedesktop.portal.Notification.AddNotification() D-Bus " "call failed: " "{}", reply.error() ); fallbackToSystemTrayIcon(title, message); } OrgFreedesktopPortalNotificationInterface mPortalInterface{ "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, QDBusConnection::sessionBus() }; CoroutineScope mCoroutineScope{}; }; class LegacyFreedesktopNotificationsController final : public NotificationsController { Q_OBJECT public: explicit LegacyFreedesktopNotificationsController( QSystemTrayIcon* trayIcon, const Rpc* rpc, QObject* parent = nullptr ) : NotificationsController(trayIcon, rpc, parent) { qDBusRegisterMetaType>(); mLegacyInterface.setTimeout(desktoputils::defaultDbusTimeout); QObject::connect(&mLegacyInterface, &OrgFreedesktopNotificationsInterface::ActionInvoked, this, [this] { info().log("LegacyFreedesktopNotificationsController: notification clicked"); emit notificationClicked(mActivationToken); mActivationToken.reset(); }); QObject::connect( &mLegacyInterface, &OrgFreedesktopNotificationsInterface::ActivationToken, this, [this](uint, const QString& activationToken) { info().log("LegacyFreedesktopNotificationsController: activationToken = {}", activationToken); mActivationToken = activationToken.toUtf8(); } ); } protected: void showNotification(const QString& title, const QString& message) override { mCoroutineScope.launch(showNotificationViaLegacyInterface(title, message)); } private: Coroutine<> showNotificationViaLegacyInterface(QString title, QString message) { info().log( "LegacyFreedesktopNotificationsController: executing org.freedesktop.Notifications.Notify() " "D-Bus call" ); const auto reply = mLegacyInterface.Notify( TREMOTESF_APP_NAME ""_L1, 0, TREMOTESF_APP_ID ""_L1, title, message, //: Button on notification {"default"_L1, qApp->translate("tremotesf", "Show Tremotesf")}, {{"desktop-entry"_L1, TREMOTESF_APP_ID ""_L1}, {"x-kde-origin-name"_L1, Servers::instance()->currentServerName()}}, -1 ); co_await reply; if (!reply.isError()) { info().log( "LegacyFreedesktopNotificationsController: executed " "org.freedesktop.Notifications.Notify() D-Bus call" ); co_return; } warning().log( "LegacyFreedesktopNotificationsController: org.freedesktop.Notifications.Notify() D-Bus call " "failed: " "{}", reply.error() ); fallbackToSystemTrayIcon(title, message); } OrgFreedesktopNotificationsInterface mLegacyInterface{ "org.freedesktop.Notifications"_L1, "/org/freedesktop/Notifications"_L1, QDBusConnection::sessionBus() }; std::optional mActivationToken{}; CoroutineScope mCoroutineScope{}; }; } NotificationsController* NotificationsController::createInstance(QSystemTrayIcon* trayIcon, const Rpc* rpc, QObject* parent) { // We can technically use Notification portal outside of Flatpak, // however it only works properly if we were launched in a very special way through systemd // (https://systemd.io/DESKTOP_ENVIRONMENTS, "XDG standardization for applications" section) // If we were launched from command line or any other way, then notification won't be properly associated with the app // Just use legacy interface instead, it still works if (qEnvironmentVariableIsSet(flatpakEnvVariable)) { return new PortalNotificationsController(trayIcon, rpc, parent); } return new LegacyFreedesktopNotificationsController(trayIcon, rpc, parent); } } #include "notificationscontroller_freedesktop.moc" tremotesf-2.10.1/src/ui/recoloringsvgiconengine.cpp000066400000000000000000000423031523411710200224330ustar00rootroot00000000000000// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LGPL-3.0-only #include "recoloringsvgiconengine.h" // clang-format off #include "qpainter.h" #include "qpixmap.h" #include "qsvgrenderer.h" #include "qpixmapcache.h" #include "qfileinfo.h" #if QT_CONFIG(mimetype) #include #include #endif #include #include "qdebug.h" #include #include #include #include #include #include namespace tremotesf { namespace { QString STYLESHEET_TEMPLATE() { return QStringLiteral(".ColorScheme-Text { color:%1; }\ .ColorScheme-Background{ color:%2; }\ .ColorScheme-Highlight{ color:%3; }\ .ColorScheme-HighlightedText{ color:%4; }\ .ColorScheme-PositiveText{ color:%5; }\ .ColorScheme-NeutralText{ color:%6; }\ .ColorScheme-NegativeText{ color:%7; }\ .ColorScheme-ActiveText{ color:%8; }\ .ColorScheme-Complement{ color:%9; }\ .ColorScheme-Contrast{ color:%10; }\ .ColorScheme-Accent{ color:%11; }\ "); } qreal luma(const QColor &color) { return (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255; } QPalette paletteForStylesheet() { return QApplication::palette("QMenu"); } } enum FileType { OtherFile, SvgFile, CompressedSvgFile }; static FileType fileType(const QFileInfo &fi); class RecoloringSvgIconEnginePrivate : public QSharedData { public: RecoloringSvgIconEnginePrivate() { stepSerialNum(); } static int hashKey(QIcon::Mode mode, QIcon::State state) { return ((mode << 4) | state); } QString pmcKey(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) const { return QLatin1String("$qt_svgicon_") % HexString(serialNum) % HexString(mode) % HexString(state) % HexString(size.width()) % HexString(size.height()) % HexString(static_cast(qRound(scale * 1000))) % HexString(paletteForStylesheet().cacheKey()); } void stepSerialNum() { serialNum = lastSerialNum.fetchAndAddRelaxed(1); } bool tryLoad(QSvgRenderer *renderer, QIcon::Mode tryMode, QIcon::State tryState, QIcon::Mode actualMode); QIcon::Mode loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state); QHash svgFiles; QHash svgBuffers; QMultiHash addedPixmaps; int serialNum = 0; static QAtomicInt lastSerialNum; }; QAtomicInt RecoloringSvgIconEnginePrivate::lastSerialNum; RecoloringSvgIconEngine::RecoloringSvgIconEngine() : d(new RecoloringSvgIconEnginePrivate) { } RecoloringSvgIconEngine::RecoloringSvgIconEngine(const RecoloringSvgIconEngine &other) : QIconEngine(other), d(new RecoloringSvgIconEnginePrivate) { d->svgFiles = other.d->svgFiles; d->svgBuffers = other.d->svgBuffers; d->addedPixmaps = other.d->addedPixmaps; } RecoloringSvgIconEngine::~RecoloringSvgIconEngine() { } QSize RecoloringSvgIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) { if (!d->addedPixmaps.isEmpty()) { const auto key = d->hashKey(mode, state); auto it = d->addedPixmaps.constFind(key); while (it != d->addedPixmaps.end() && it.key() == key) { const auto &pm = it.value(); if (!pm.isNull() && pm.size() == size) return size; ++it; } } QPixmap pm = pixmap(size, mode, state); if (pm.isNull()) return QSize(); return pm.size(); } static inline QByteArray maybeUncompress(const QByteArray &ba) { #ifndef QT_NO_COMPRESS return qUncompress(ba); #else return ba; #endif } bool RecoloringSvgIconEnginePrivate::tryLoad(QSvgRenderer *renderer, QIcon::Mode tryMode, QIcon::State tryState, QIcon::Mode actualMode) { const auto key = hashKey(tryMode, tryState); QByteArray buf = svgBuffers.value(key); if (!buf.isEmpty()) { if (renderer->load(maybeUncompress(buf))) return true; svgBuffers.remove(key); } QString svgFile = svgFiles.value(key); if (!svgFile.isEmpty()) { if (fileType(QFileInfo(svgFile)) == CompressedSvgFile) { qWarning() << "Can't recolor compressed svg" << svgFile; return renderer->load(svgFile); } const auto pal = paletteForStylesheet(); const QColor complement = luma(pal.window().color()) > 0.5 ? Qt::white : Qt::black; const QColor contrast = luma(pal.window().color()) > 0.5 ? Qt::black : Qt::white; QColor accentColor = pal.accent().color(); // When selected, tint the accent color with a small portion of highlighted text color, // because since the accent color used to be the same as the highlight color, it might cause // icons, especially folders to "disappear" against the background if (actualMode == QIcon::Selected) { const float tintRatio = 0.85f; const auto highlightedText = pal.highlightedText().color(); const float r = accentColor.redF() * tintRatio + highlightedText.redF() * (1.0f - tintRatio); const float g = accentColor.greenF() * tintRatio + highlightedText.greenF() * (1.0f - tintRatio); const float b = accentColor.blueF() * tintRatio + highlightedText.blueF() * (1.0f - tintRatio); accentColor.setRgbF(r, g, b, accentColor.alphaF()); } const QString styleSheet = STYLESHEET_TEMPLATE().arg( // ColorScheme-Text actualMode == QIcon::Selected ? pal.highlightedText().color().name() : pal.windowText().color().name(), // ColorScheme-Background actualMode == QIcon::Selected ? pal.highlight().color().name() : pal.window().color().name(), // ColorScheme-Highlight actualMode == QIcon::Selected ? pal.highlightedText().color().name() : pal.highlight().color().name(), // ColorScheme-HighlightedText actualMode == QIcon::Selected ? pal.highlight().color().name() : pal.highlightedText().color().name(), // ColorScheme-PositiveText actualMode == QIcon::Selected ? pal.highlightedText().color().name() : pal.windowText().color().name(), // ColorScheme-NeutralText actualMode == QIcon::Selected ? pal.highlightedText().color().name() : pal.windowText().color().name(), // ColorScheme-NegativeText actualMode == QIcon::Selected ? pal.highlightedText().color().name() : pal.windowText().color().name(), // ColorScheme-ActiveText actualMode == QIcon::Selected ? pal.highlightedText().color().name() : pal.windowText().color().name(), // ColorScheme-Complement complement.name(), // ColorScheme-Contrast contrast.name(), // ColorScheme-Accent accentColor.name() ); QFile file(svgFile); if (!file.open(QIODevice::ReadOnly)) { return false; } QByteArray processedContents; QXmlStreamReader reader(&file); QBuffer buffer(&processedContents); buffer.open(QIODevice::WriteOnly); QXmlStreamWriter writer(&buffer); while (!reader.atEnd()) { if (reader.readNext() == QXmlStreamReader::StartElement // && reader.qualifiedName() == QLatin1String("style") // && reader.attributes().value(QLatin1String("id")) == QLatin1String("current-color-scheme")) { writer.writeStartElement(QStringLiteral("style")); writer.writeAttributes(reader.attributes()); writer.writeCharacters(styleSheet); writer.writeEndElement(); while (reader.tokenType() != QXmlStreamReader::EndElement) { reader.readNext(); } } else if (reader.tokenType() != QXmlStreamReader::Invalid) { writer.writeCurrentToken(reader); } } return renderer->load(processedContents); } return false; } QIcon::Mode RecoloringSvgIconEnginePrivate::loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state) { if (tryLoad(renderer, mode, state, mode)) return mode; const QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On; if (mode == QIcon::Disabled || mode == QIcon::Selected) { const QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled; if (tryLoad(renderer, QIcon::Normal, state, mode)) return QIcon::Normal; if (tryLoad(renderer, QIcon::Active, state, mode)) return QIcon::Active; if (tryLoad(renderer, mode, oppositeState, mode)) return mode; if (tryLoad(renderer, QIcon::Normal, oppositeState, mode)) return QIcon::Normal; if (tryLoad(renderer, QIcon::Active, oppositeState, mode)) return QIcon::Active; if (tryLoad(renderer, oppositeMode, state, mode)) return oppositeMode; if (tryLoad(renderer, oppositeMode, oppositeState, mode)) return oppositeMode; } else { const QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal; if (tryLoad(renderer, oppositeMode, state, mode)) return oppositeMode; if (tryLoad(renderer, mode, oppositeState, mode)) return mode; if (tryLoad(renderer, oppositeMode, oppositeState, mode)) return oppositeMode; if (tryLoad(renderer, QIcon::Disabled, state, mode)) return QIcon::Disabled; if (tryLoad(renderer, QIcon::Selected, state, mode)) return QIcon::Selected; if (tryLoad(renderer, QIcon::Disabled, oppositeState, mode)) return QIcon::Disabled; if (tryLoad(renderer, QIcon::Selected, oppositeState, mode)) return QIcon::Selected; } return QIcon::Normal; } QPixmap RecoloringSvgIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) { return scaledPixmap(size, mode, state, 1.0); } QPixmap RecoloringSvgIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) { QPixmap pm; QString pmckey(d->pmcKey(size, mode, state, scale)); if (QPixmapCache::find(pmckey, &pm)) return pm; if (!d->addedPixmaps.isEmpty()) { const auto realSize = size * scale; const auto key = d->hashKey(mode, state); auto it = d->addedPixmaps.constFind(key); while (it != d->addedPixmaps.end() && it.key() == key) { const auto &pm = it.value(); if (!pm.isNull()) { // we don't care about dpr here - don't use RecoloringSvgIconEngine when // there are a lot of raster images are to handle. if (pm.size() == realSize) return pm; } ++it; } } QSvgRenderer renderer; const QIcon::Mode loadmode = d->loadDataForModeAndState(&renderer, mode, state); if (!renderer.isValid()) return pm; QSize actualSize = renderer.defaultSize(); if (!actualSize.isNull()) actualSize.scale(size * scale, Qt::KeepAspectRatio); if (actualSize.isEmpty()) return pm; pm = QPixmap(actualSize); pm.fill(Qt::transparent); QPainter p(&pm); renderer.render(&p); p.end(); if (qobject_cast(QCoreApplication::instance())) { if (loadmode != mode && mode != QIcon::Normal) { const QPixmap generated = QGuiApplicationPrivate::instance()->applyQIconStyleHelper(mode, pm); if (!generated.isNull()) pm = generated; } } if (!pm.isNull()) { pm.setDevicePixelRatio(scale); QPixmapCache::insert(pmckey, pm); } return pm; } void RecoloringSvgIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) { d->stepSerialNum(); d->addedPixmaps.insert(d->hashKey(mode, state), pixmap); } static FileType fileType(const QFileInfo &fi) { const QString &suffix = fi.completeSuffix(); if (suffix.endsWith(QLatin1String("svg"), Qt::CaseInsensitive)) return SvgFile; if (suffix.endsWith(QLatin1String("svgz"), Qt::CaseInsensitive) || suffix.endsWith(QLatin1String("svg.gz"), Qt::CaseInsensitive)) { return CompressedSvgFile; } #if QT_CONFIG(mimetype) const QString &mimeTypeName = QMimeDatabase().mimeTypeForFile(fi).name(); if (mimeTypeName == QLatin1String("image/svg+xml")) return SvgFile; if (mimeTypeName == QLatin1String("image/svg+xml-compressed")) return CompressedSvgFile; #endif return OtherFile; } void RecoloringSvgIconEngine::addFile(const QString &fileName, const QSize &, QIcon::Mode mode, QIcon::State state) { if (!fileName.isEmpty()) { const QFileInfo fi(fileName); const QString abs = fi.absoluteFilePath(); const FileType type = fileType(fi); #ifndef QT_NO_COMPRESS if (type == SvgFile || type == CompressedSvgFile) { #else if (type == SvgFile) { #endif QSvgRenderer renderer(abs); if (renderer.isValid()) { d->stepSerialNum(); d->svgFiles.insert(d->hashKey(mode, state), abs); } } else if (type == OtherFile) { QPixmap pm(abs); if (!pm.isNull()) addPixmap(pm, mode, state); } } } void RecoloringSvgIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) { QSize pixmapSize = rect.size(); if (painter->device()) pixmapSize *= painter->device()->devicePixelRatio(); painter->drawPixmap(rect, pixmap(pixmapSize, mode, state)); } bool RecoloringSvgIconEngine::isNull() { return d->svgFiles.isEmpty() && d->addedPixmaps.isEmpty() && d->svgBuffers.isEmpty(); } QString RecoloringSvgIconEngine::key() const { return QLatin1String("svg"); } QIconEngine *RecoloringSvgIconEngine::clone() const { return new RecoloringSvgIconEngine(*this); } bool RecoloringSvgIconEngine::read(QDataStream &in) { d = new RecoloringSvgIconEnginePrivate; if (in.version() >= QDataStream::Qt_4_4) { int isCompressed; QHash fileNames; // For memoryoptimization later in >> fileNames >> isCompressed >> d->svgBuffers; #ifndef QT_NO_COMPRESS if (!isCompressed) { for (auto &svgBuf : d->svgBuffers) svgBuf = qCompress(svgBuf); } #else if (isCompressed) { qWarning("RecoloringSvgIconEngine: Can not decompress SVG data"); d->svgBuffers.clear(); } #endif int hasAddedPixmaps; in >> hasAddedPixmaps; if (hasAddedPixmaps) { in >> d->addedPixmaps; } } else { QPixmap pixmap; QByteArray data; uint mode; uint state; int num_entries; in >> data; if (!data.isEmpty()) { #ifndef QT_NO_COMPRESS data = qUncompress(data); #endif if (!data.isEmpty()) d->svgBuffers.insert(d->hashKey(QIcon::Normal, QIcon::Off), data); } in >> num_entries; for (int i=0; i> pixmap; in >> mode; in >> state; // The pm list written by 4.3 is buggy and/or useless, so ignore. //addPixmap(pixmap, QIcon::Mode(mode), QIcon::State(state)); } } return true; } bool RecoloringSvgIconEngine::write(QDataStream &out) const { if (out.version() >= QDataStream::Qt_4_4) { int isCompressed = 0; #ifndef QT_NO_COMPRESS isCompressed = 1; #endif QHash svgBuffers = d->svgBuffers; for (const auto &it : d->svgFiles.asKeyValueRange()) { QByteArray buf; QFile f(it.second); if (f.open(QIODevice::ReadOnly)) buf = f.readAll(); #ifndef QT_NO_COMPRESS buf = qCompress(buf); #endif svgBuffers.insert(it.first, buf); } out << d->svgFiles << isCompressed << svgBuffers; if (d->addedPixmaps.isEmpty()) out << 0; else out << 1 << d->addedPixmaps; } else { const auto key = d->hashKey(QIcon::Normal, QIcon::Off); QByteArray buf = d->svgBuffers.value(key); if (buf.isEmpty()) { QString svgFile = d->svgFiles.value(key); if (!svgFile.isEmpty()) { QFile f(svgFile); if (f.open(QIODevice::ReadOnly)) buf = f.readAll(); } } #ifndef QT_NO_COMPRESS buf = qCompress(buf); #endif out << buf; // 4.3 has buggy handling of added pixmaps, so don't write any out << (int)0; } return true; } } tremotesf-2.10.1/src/ui/recoloringsvgiconengine.h000066400000000000000000000030201523411710200220710ustar00rootroot00000000000000// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LGPL-3.0-only #ifndef TREMOTESF_RECOLORINGSVGICONENGINE_H #define TREMOTESF_RECOLORINGSVGICONENGINE_H #include #include // clang-format off namespace tremotesf { class RecoloringSvgIconEnginePrivate; // QSvgIconEngine fork that injects CSS derived from current QPalette class RecoloringSvgIconEngine : public QIconEngine { public: RecoloringSvgIconEngine(); RecoloringSvgIconEngine(const RecoloringSvgIconEngine&other); ~RecoloringSvgIconEngine(); void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override; QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override; QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override; QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override; void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override; void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) override; bool isNull() override; QString key() const override; QIconEngine *clone() const override; bool read(QDataStream &in) override; bool write(QDataStream &out) const override; private: QSharedDataPointer d; }; } #endif tremotesf-2.10.1/src/ui/savewindowstatedispatcher.cpp000066400000000000000000000072531523411710200230140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "savewindowstatedispatcher.h" #include #include #include #include "settings.h" #include "log/log.h" #include "rpc/servers.h" namespace tremotesf { namespace { constexpr auto windowHasSaveStateHandlerProperty = "tremotesf_windowHasSaveStateHandler"; QEvent::Type saveStateOnQuitEventType() { static const auto type = static_cast(QEvent::registerEventType()); return type; } } SaveWindowStateDispatcher::SaveWindowStateDispatcher() { QObject::connect( qApp, &QCoreApplication::aboutToQuit, this, &SaveWindowStateDispatcher::onAboutToQuit, Qt::DirectConnection ); } void SaveWindowStateDispatcher::onAboutToQuit() { debug().log("Received aboutToQuit signal"); for (QWidget* window : QApplication::topLevelWidgets()) { if (window->property(windowHasSaveStateHandlerProperty).toBool()) { debug().log("Sending save state event to window {}", *window); QEvent event(saveStateOnQuitEventType()); QCoreApplication::sendEvent(window, &event); } } // On Windows our process might be terminated immediately after returning from this function // and QSettings destructors won't be called, so sync them immediately debug().log("Syncing settings"); Settings::instance()->sync(); Servers::instance()->sync(); } ApplicationQuitEventFilter::ApplicationQuitEventFilter(QObject* parent) : QObject(parent) { qApp->installEventFilter(this); } ApplicationQuitEventFilter::~ApplicationQuitEventFilter() { qApp->removeEventFilter(this); } bool ApplicationQuitEventFilter::eventFilter(QObject*, QEvent* event) { if (event->type() == QEvent::Quit) { isQuittingApplication = true; QMetaObject::invokeMethod(qApp, [this] { isQuittingApplication = false; }, Qt::QueuedConnection); } return false; } SaveWindowStateHandler::SaveWindowStateHandler(QWidget* window, std::function saveState, QObject* parent) : QObject(parent), mWindow(window), mSaveState(std::move(saveState)) { mWindow->installEventFilter(this); mWindow->setProperty(windowHasSaveStateHandlerProperty, true); } SaveWindowStateHandler::~SaveWindowStateHandler() { mWindow->removeEventFilter(this); mWindow->setProperty(windowHasSaveStateHandlerProperty, QVariant{}); } bool SaveWindowStateHandler::eventFilter(QObject* watched, QEvent* event) { const auto window = static_cast(watched); // Window can be moved without activation, so we also need to handle Hide event switch (event->type()) { case QEvent::WindowDeactivate: case QEvent::Hide: debug().log("Received {} event for {}", event->type(), *window); if (mApplicationEventFilter.isQuittingApplication) { debug().log("Already quitting application, ignore"); break; } if (event->type() == QEvent::WindowDeactivate && window->isHidden()) { debug().log("Window is hidden, ignore"); break; } mSaveState(); break; default: if (event->type() == saveStateOnQuitEventType()) { debug().log("Received save state event for {}", *window); mSaveState(); } break; } return false; } } tremotesf-2.10.1/src/ui/savewindowstatedispatcher.h000066400000000000000000000025561523411710200224620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SAVEWINDOWSTATEDISPATCHER_H #define TREMOTESF_SAVEWINDOWSTATEDISPATCHER_H #include #include class QWidget; namespace tremotesf { class SaveWindowStateDispatcher : public QObject { Q_OBJECT public: SaveWindowStateDispatcher(); private: void onAboutToQuit(); }; class ApplicationQuitEventFilter : public QObject { Q_OBJECT public: explicit ApplicationQuitEventFilter(QObject* parent = nullptr); ~ApplicationQuitEventFilter() override; Q_DISABLE_COPY_MOVE(ApplicationQuitEventFilter); bool eventFilter(QObject* watched, QEvent* event) override; bool isQuittingApplication{}; }; class SaveWindowStateHandler : public QObject { Q_OBJECT public: explicit SaveWindowStateHandler(QWidget* window, std::function saveState, QObject* parent = nullptr); ~SaveWindowStateHandler() override; Q_DISABLE_COPY_MOVE(SaveWindowStateHandler) bool eventFilter(QObject* watched, QEvent* event) override; private: QWidget* mWindow{}; std::function mSaveState{}; ApplicationQuitEventFilter mApplicationEventFilter{}; }; } #endif //TREMOTESF_SAVEWINDOWSTATEDISPATCHER_H tremotesf-2.10.1/src/ui/screens/000077500000000000000000000000001523411710200164455ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/screens/aboutdialog.cpp000066400000000000000000000065701523411710200214530ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "aboutdialog.h" #include #include #include #include #include #include #include #include #include #include "fileutils.h" using namespace Qt::StringLiterals; namespace tremotesf { AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) { //: "About" dialog title setWindowTitle(qApp->translate("tremotesf", "About")); auto layout = new QVBoxLayout(this); auto titleWidget = new KTitleWidget(this); titleWidget->setIcon(qApp->windowIcon(), KTitleWidget::ImageLeft); titleWidget->setText(QString("%1 %2"_L1).arg(TREMOTESF_APP_NAME ""_L1, qApp->applicationVersion())); layout->addWidget(titleWidget); auto tabWidget = new QTabWidget(this); auto aboutPage = new QWidget(this); auto aboutPageLayout = new QVBoxLayout(aboutPage); const auto aboutPageTemplate = uR"(

© 2015-2025 Alexey Rochev <equeim@gmail.com>

%1 https://github.com/equeim/tremotesf2

%2 https://www.transifex.com/equeim/tremotesf

)"_s; auto aboutPageLabel = new QLabel( aboutPageTemplate.arg( qApp->translate("tremotesf", "Source code:").toHtmlEscaped(), qApp->translate("tremotesf", "Translations:").toHtmlEscaped() ), this ); QObject::connect(aboutPageLabel, &QLabel::linkActivated, this, &QDesktopServices::openUrl); aboutPageLayout->addWidget(aboutPageLabel); //: "About" dialog's "About" tab title tabWidget->addTab(aboutPage, qApp->translate("tremotesf", "About")); auto authorsWidget = new QTextBrowser(this); authorsWidget->setHtml( QString(readFile(":/authors.html")) .arg(qApp->translate("tremotesf", "Maintainer"), qApp->translate("tremotesf", "Contributor")) ); authorsWidget->setOpenExternalLinks(true); //: "About" dialog's "Authors" tab title tabWidget->addTab(authorsWidget, qApp->translate("tremotesf", "Authors")); auto translatorsWidget = new QTextBrowser(this); translatorsWidget->setHtml(readFile(":/translators.html")); translatorsWidget->setOpenExternalLinks(true); //: "About" dialog's "Translators" tab title tabWidget->addTab(translatorsWidget, qApp->translate("tremotesf", "Translators")); auto licenseWidget = new QTextBrowser(this); licenseWidget->setHtml(readFile(":/license.html")); licenseWidget->setOpenExternalLinks(true); //: "About" dialog's "License" tab title tabWidget->addTab(licenseWidget, qApp->translate("tremotesf", "License")); layout->addWidget(tabWidget); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &AboutDialog::reject); layout->addWidget(dialogButtonBox); dialogButtonBox->button(QDialogButtonBox::Close)->setDefault(true); resize(sizeHint().expandedTo(QSize(420, 384))); } } tremotesf-2.10.1/src/ui/screens/aboutdialog.h000066400000000000000000000006011523411710200211050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_ABOUTDIALOG_H #define TREMOTESF_ABOUTDIALOG_H #include namespace tremotesf { class AboutDialog final : public QDialog { Q_OBJECT public: explicit AboutDialog(QWidget* parent = nullptr); }; } #endif // TREMOTESF_ABOUTDIALOG_H tremotesf-2.10.1/src/ui/screens/addtorrent/000077500000000000000000000000001523411710200206135ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/screens/addtorrent/addtorrentdialog.cpp000066400000000000000000000504061523411710200246520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // SPDX-FileCopyrightText: 2022 Alex // // SPDX-License-Identifier: GPL-3.0-or-later #include "addtorrentdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "addtorrenthelpers.h" #include "fileutils.h" #include "formatutils.h" #include "magnetlinkparser.h" #include "settings.h" #include "stdutils.h" #include "torrentfileparser.h" #include "coroutines/threadpool.h" #include "log/log.h" #include "rpc/rpc.h" #include "rpc/serversettings.h" #include "rpc/torrent.h" #include "ui/widgets/editlabelswidget.h" #include "ui/widgets/torrentremotedirectoryselectionwidget.h" #include "ui/widgets/torrentfilesview.h" #include "localtorrentfilesmodel.h" namespace tremotesf { namespace { constexpr std::array priorityComboBoxItems{ TorrentData::Priority::High, TorrentData::Priority::Normal, TorrentData::Priority::Low }; TorrentData::Priority priorityFromComboBoxIndex(int index) { if (index == -1) { return TorrentData::Priority::Normal; } return priorityComboBoxItems.at(static_cast(index)); } int rowForWidget(QFormLayout* layout, QWidget* widget) { int row{}; QFormLayout::ItemRole role{}; layout->getWidgetPosition(widget, &row, &role); if (row == -1) { throw std::logic_error(fmt::format("Did not find widget {} in layout {}", *widget, *layout)); } return row; } Rpc::DeleteFileMode determineDeleteFileMode(const AddTorrentDialog::AddTorrentParametersWidgets& widgets) { if (!widgets.deleteTorrentFileGroupBox || !widgets.moveTorrentFileToTrashCheckBox) { return Rpc::DeleteFileMode::No; } if (!widgets.deleteTorrentFileGroupBox->isChecked()) { return Rpc::DeleteFileMode::No; } if (widgets.moveTorrentFileToTrashCheckBox->isChecked()) { return Rpc::DeleteFileMode::MoveToTrash; } return Rpc::DeleteFileMode::Delete; } } AddTorrentDialog::AddTorrentDialog(Rpc* rpc, std::variant params, QWidget* parent) : QDialog(parent), mRpc(rpc), mParams(std::move(params)), mFilesModel(isAddingFile() ? new LocalTorrentFilesModel(this) : nullptr) { setupUi(); QSize size(512, 0); if (isAddingFile()) { size.setHeight(512); } resize(sizeHint().expandedTo(size)); if (isAddingFile()) { mParseTorrentFileCoroutineScope.launch(parseTorrentFile()); } } void AddTorrentDialog::accept() { if (isAddingFile()) { if (!checkIfTorrentFileExists()) { mRpc->addTorrentFile( std::get(mParams).filePath, mAddTorrentParametersWidgets.downloadDirectoryWidget->path(), mFilesModel->unwantedFiles(), mFilesModel->highPriorityFiles(), mFilesModel->lowPriorityFiles(), mFilesModel->renamedFiles(), priorityFromComboBoxIndex(mAddTorrentParametersWidgets.priorityComboBox->currentIndex()), mAddTorrentParametersWidgets.startTorrentCheckBox->isChecked(), determineDeleteFileMode(mAddTorrentParametersWidgets), mEditLabelsWidget->enabledLabels() ); } } else { QStringList urls = mTorrentLinkTextField->toPlainText().split('\n', Qt::SkipEmptyParts); parseMagnetLinksAndCheckIfTorrentsExist(urls); if (!urls.empty()) { mRpc->addTorrentLinks( std::move(urls), mAddTorrentParametersWidgets.downloadDirectoryWidget->path(), priorityFromComboBoxIndex(mAddTorrentParametersWidgets.priorityComboBox->currentIndex()), mAddTorrentParametersWidgets.startTorrentCheckBox->isChecked(), mEditLabelsWidget->enabledLabels() ); } } const auto settings = Settings::instance(); if (settings->get_rememberOpenTorrentDir() && isAddingFile()) { settings->set_lastOpenTorrentDirectory(QFileInfo(std::get(mParams).filePath).path()); } if (settings->get_rememberAddTorrentParameters()) { mAddTorrentParametersWidgets.saveToSettings(); } QDialog::accept(); } AddTorrentDialog::AddTorrentParametersWidgets AddTorrentDialog::createAddTorrentParametersWidgets(bool forTorrentFile, QFormLayout* layout, Rpc* rpc) { const auto parameters = getAddTorrentParameters(rpc); auto* const downloadDirectoryWidget = new TorrentDownloadDirectoryDirectorySelectionWidget{}; downloadDirectoryWidget->setup(parameters.downloadDirectory, rpc); //: Input field's label layout->addRow(qApp->translate("tremotesf", "Download directory:"), downloadDirectoryWidget); auto* const priorityComboBox = new QComboBox{}; priorityComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); for (const auto priority : priorityComboBoxItems) { switch (priority) { case TorrentData::Priority::High: //: Torrent's loading priority priorityComboBox->addItem(qApp->translate("tremotesf", "High")); break; case TorrentData::Priority::Normal: //: Torrent's loading priority priorityComboBox->addItem(qApp->translate("tremotesf", "Normal")); break; case TorrentData::Priority::Low: //: Torrent's loading priority priorityComboBox->addItem(qApp->translate("tremotesf", "Low")); break; } } // NOLINTNEXTLINE(bugprone-unchecked-optional-access) priorityComboBox->setCurrentIndex(indexOfCasted(priorityComboBoxItems, parameters.priority).value()); //: Combo box label layout->addRow(qApp->translate("tremotesf", "Torrent priority:"), priorityComboBox); auto* const startTorrentCheckBox = new QCheckBox(qApp->translate("tremotesf", "Start downloading after adding")); startTorrentCheckBox->setChecked(parameters.startAfterAdding); layout->addRow(startTorrentCheckBox); QGroupBox* deleteTorrentFileGroupBox{}; QCheckBox* moveTorrentFileToTrashCheckBox{}; if (forTorrentFile) { deleteTorrentFileGroupBox = new QGroupBox(qApp->translate("tremotesf", "Delete .torrent file")); layout->addWidget(deleteTorrentFileGroupBox); deleteTorrentFileGroupBox->setCheckable(true); const auto groupBoxLayout = new QVBoxLayout(deleteTorrentFileGroupBox); moveTorrentFileToTrashCheckBox = new QCheckBox(qApp->translate("tremotesf", "Move .torrent file to trash")); groupBoxLayout->addWidget(moveTorrentFileToTrashCheckBox); deleteTorrentFileGroupBox->setChecked(parameters.deleteTorrentFile); moveTorrentFileToTrashCheckBox->setChecked(parameters.moveTorrentFileToTrash); layout->addRow(deleteTorrentFileGroupBox); } return { .downloadDirectoryWidget = downloadDirectoryWidget, .priorityComboBox = priorityComboBox, .startTorrentCheckBox = startTorrentCheckBox, .deleteTorrentFileGroupBox = deleteTorrentFileGroupBox, .moveTorrentFileToTrashCheckBox = moveTorrentFileToTrashCheckBox }; } bool AddTorrentDialog::isAddingFile() const { return std::holds_alternative(mParams); } void AddTorrentDialog::setupUi() { if (isAddingFile()) { //: Dialog title setWindowTitle(qApp->translate("tremotesf", "Add Torrent File")); } else { //: Dialog title setWindowTitle(qApp->translate("tremotesf", "Add Torrent Link")); } auto layout = new QFormLayout(this); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); layout->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy::AllNonFixedFieldsGrow); mMessageWidget = new KMessageWidget(this); mMessageWidget->setCloseButtonVisible(false); mMessageWidget->hide(); layout->addRow(mMessageWidget); if (isAddingFile()) { mTorrentFilePathTextField = new QLineEdit(std::get(mParams).filePath, this); mTorrentFilePathTextField->setReadOnly(true); //: Input field's label layout->addRow(qApp->translate("tremotesf", "Torrent file:"), mTorrentFilePathTextField); } else { mTorrentLinkTextField = new QPlainTextEdit(std::get(mParams).urls.join('\n'), this); mTorrentLinkTextField->textCursor().setPosition(0); //: Input field's label layout->addRow(qApp->translate("tremotesf", "Torrent link:"), mTorrentLinkTextField); QObject::connect( mTorrentLinkTextField, &QPlainTextEdit::textChanged, this, &AddTorrentDialog::canAcceptUpdate ); } mAddTorrentParametersWidgets = createAddTorrentParametersWidgets(isAddingFile(), layout, mRpc); mFreeSpaceLabel = new QLabel(this); QObject::connect( mAddTorrentParametersWidgets.downloadDirectoryWidget, &RemoteDirectorySelectionWidget::pathChanged, this, [=, this] { onDownloadDirectoryPathChanged(mAddTorrentParametersWidgets.downloadDirectoryWidget->path()); } ); onDownloadDirectoryPathChanged(mAddTorrentParametersWidgets.downloadDirectoryWidget->path()); layout->insertRow( rowForWidget(layout, mAddTorrentParametersWidgets.downloadDirectoryWidget) + 1, nullptr, mFreeSpaceLabel ); if (isAddingFile()) { mTorrentFilesView = new TorrentFilesView(mFilesModel, mRpc); layout->insertRow(rowForWidget(layout, mFreeSpaceLabel) + 1, mTorrentFilesView); } mEditLabelsGroupBox = new QGroupBox(qApp->translate("tremotesf", "Labels"), this); layout->addRow(mEditLabelsGroupBox); auto labelsGroupBoxLayout = new QVBoxLayout(mEditLabelsGroupBox); mEditLabelsWidget = new EditLabelsWidget({}, mRpc, this); labelsGroupBoxLayout->addWidget(mEditLabelsWidget); if (!isAddingFile()) { layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); } mDialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QObject::connect(mDialogButtonBox, &QDialogButtonBox::accepted, this, [this] { if (!mEditLabelsWidget->comboBoxHasFocus()) { accept(); } }); QObject::connect(mDialogButtonBox, &QDialogButtonBox::rejected, this, &AddTorrentDialog::reject); layout->addRow(mDialogButtonBox); QObject::connect(mRpc, &Rpc::connectedChanged, this, &AddTorrentDialog::updateUi); QObject::connect( mAddTorrentParametersWidgets.downloadDirectoryWidget, &RemoteDirectorySelectionWidget::pathChanged, this, &AddTorrentDialog::canAcceptUpdate ); // If call updateUi() right now and need to show messageWidget, it will be // shown without animation, because we are not visible yet // Call it at the next available event loop iteration QTimer::singleShot(0, this, &AddTorrentDialog::updateUi); } void AddTorrentDialog::updateUi() { const bool enabled = mRpc->isConnected() && (mFilesModel ? mFilesModel->isLoaded() : true); if (enabled) { // Update parameters which initial values depend on server state const auto parameters = getAddTorrentParameters(mRpc); mAddTorrentParametersWidgets.downloadDirectoryWidget->resetPath(parameters.downloadDirectory); mAddTorrentParametersWidgets.startTorrentCheckBox->setChecked(parameters.startAfterAdding); } for (int i = 1, max = layout()->count(); i < max; ++i) { auto* const widget = layout()->itemAt(i)->widget(); if (widget) { widget->setEnabled(enabled); } } mAddTorrentParametersWidgets.startTorrentCheckBox->setEnabled(enabled); if (enabled) { if (mMessageWidget->isShowAnimationRunning()) { mMessageWidget->hide(); } else { mMessageWidget->animatedHide(); } mMessageWidget->animatedHide(); } else if (mFilesModel && !mFilesModel->isLoaded()) { mMessageWidget->setMessageType(KMessageWidget::Information); //: Placeholder shown when torrent file is being read/parsed mMessageWidget->setText(qApp->translate("tremotesf", "Loading")); mMessageWidget->animatedShow(); } else { mMessageWidget->setMessageType(KMessageWidget::Warning); //: Server connection status mMessageWidget->setText(qApp->translate("tremotesf", "Disconnected")); mMessageWidget->animatedShow(); } mEditLabelsGroupBox->setVisible( mRpc->isConnected() ? mRpc->serverSettings()->data().hasLabelsProperty() : true ); canAcceptUpdate(); } void AddTorrentDialog::canAcceptUpdate() { bool can = mRpc->isConnected() && (mFilesModel ? mFilesModel->isLoaded() : true); if (mTorrentLinkTextField && mTorrentLinkTextField->document()->isEmpty()) { can = false; } if (mAddTorrentParametersWidgets.downloadDirectoryWidget->path().isEmpty()) { can = false; } mDialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(can); } void AddTorrentDialog::saveState() { if (mTorrentFilesView) { mTorrentFilesView->saveState(); } } void AddTorrentDialog::onDownloadDirectoryPathChanged(QString path) { mFreeSpaceLabel->hide(); mFreeSpaceLabel->clear(); mFreeSpaceCoroutineScope.cancelAll(); if (!path.isEmpty()) { mFreeSpaceCoroutineScope.launch(getFreeSpaceForPath(std::move(path))); } } Coroutine<> AddTorrentDialog::getFreeSpaceForPath(QString path) { const auto freeSpace = co_await mRpc->getFreeSpaceForPath(std::move(path)); if (freeSpace) { mFreeSpaceLabel->setText( //: %1 is a amount of free space in a directory, e.g. 1 GiB qApp->translate("tremotesf", "Free space: %1").arg(formatutils::formatByteSize(*freeSpace)) ); } else { mFreeSpaceLabel->setText(qApp->translate("tremotesf", "Error getting free space")); } mFreeSpaceLabel->show(); } Coroutine<> AddTorrentDialog::parseTorrentFile() { const auto& filePath = std::get(mParams).filePath; try { info().log("Parsing torrent file {}", filePath); auto torrentFile = co_await runOnThreadPool(&tremotesf::parseTorrentFile, filePath); info().log("Parsed, result = {}", torrentFile); mTorrentFileInfoHashAndTrackers = std::pair{std::move(torrentFile.infoHashV1), std::move(torrentFile.trackers)}; if (checkIfTorrentFileExists()) { if (isAddingFile()) { co_await deleteTorrentFileIfEnabled(); } close(); co_return; } co_await mFilesModel->load(std::move(torrentFile)); updateUi(); } catch (const bencode::Error& e) { warning().logWithException(e, "Failed to parse torrent file {}", filePath); showTorrentParsingError(bencodeErrorString(e.type())); close(); } } void AddTorrentDialog::showTorrentParsingError(const QString& errorString) { auto* const messageBox = new QMessageBox( QMessageBox::Critical, //: Dialog title qApp->translate("tremotesf", "Error"), errorString, QMessageBox::Close, parentWidget() ); messageBox->setAttribute(Qt::WA_DeleteOnClose); messageBox->show(); } void AddTorrentDialog::parseMagnetLinksAndCheckIfTorrentsExist(QStringList& urls) { auto toErase = std::ranges::remove_if(urls, [&](const QString& url) { try { info().log("Parsing {} as a magnet link", url); auto magnetLink = tremotesf::parseMagnetLink(QUrl(url)); info().log("Parsed, result = {}", magnetLink); auto* const torrent = mRpc->torrentByHash(magnetLink.infoHashV1); if (torrent) { createAskForMergingTrackersDialog(torrent, std::move(magnetLink.trackers), parentWidget())->show(); return true; } } catch (const std::runtime_error& e) { warning().logWithException(e, "Failed to parse {} as a magnet link", url); } return false; }); if (!toErase.empty()) { urls.erase(toErase.begin(), toErase.end()); } } bool AddTorrentDialog::checkIfTorrentFileExists() { if (!mTorrentFileInfoHashAndTrackers.has_value()) return false; auto& [infoHashV1, trackers] = *mTorrentFileInfoHashAndTrackers; auto* const torrent = mRpc->torrentByHash(infoHashV1); if (torrent) { createAskForMergingTrackersDialog(torrent, std::move(trackers), parentWidget())->show(); return true; } return false; } Coroutine<> AddTorrentDialog::deleteTorrentFileIfEnabled() { const auto deleteFileMode = determineDeleteFileMode(mAddTorrentParametersWidgets); if (deleteFileMode == Rpc::DeleteFileMode::No) { co_return; } co_await runOnThreadPool([deleteFileMode, filePath = std::get(mParams).filePath] { try { if (deleteFileMode == Rpc::DeleteFileMode::MoveToTrash) { moveFileToTrashOrDelete(filePath); } else { deleteFile(filePath); } } catch (const QFileError& e) { warning().logWithException(e, "Failed to delete torrent file"); } }); } void AddTorrentDialog::AddTorrentParametersWidgets::reset(Rpc* rpc) const { const auto initialParameters = getInitialAddTorrentParameters(rpc); downloadDirectoryWidget->resetPath(initialParameters.downloadDirectory); priorityComboBox->setCurrentIndex( // NOLINTNEXTLINE(bugprone-unchecked-optional-access) indexOfCasted(priorityComboBoxItems, initialParameters.priority).value() ); startTorrentCheckBox->setChecked(initialParameters.startAfterAdding); if (deleteTorrentFileGroupBox) { deleteTorrentFileGroupBox->setChecked(initialParameters.deleteTorrentFile); moveTorrentFileToTrashCheckBox->setChecked(initialParameters.moveTorrentFileToTrash); } } void AddTorrentDialog::AddTorrentParametersWidgets::saveToSettings() const { downloadDirectoryWidget->saveDirectories(); auto* const settings = Settings::instance(); settings->set_lastAddTorrentPriority(priorityFromComboBoxIndex(priorityComboBox->currentIndex())); settings->set_lastAddTorrentStartAfterAdding(startTorrentCheckBox->isChecked()); if (deleteTorrentFileGroupBox) { settings->set_lastAddTorrentDeleteTorrentFile(deleteTorrentFileGroupBox->isChecked()); settings->set_lastAddTorrentMoveTorrentFileToTrash(moveTorrentFileToTrashCheckBox->isChecked()); } } } tremotesf-2.10.1/src/ui/screens/addtorrent/addtorrentdialog.h000066400000000000000000000060461523411710200243200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_ADDTORRENTDIALOG_H #define TREMOTESF_ADDTORRENTDIALOG_H #include #include #include #include #include #include #include #include "coroutines/scope.h" #include "ui/savewindowstatedispatcher.h" class QCheckBox; class QComboBox; class QDialogButtonBox; class QFormLayout; class QGroupBox; class QLabel; class QLineEdit; class QPlainTextEdit; class KMessageWidget; namespace tremotesf { class EditLabelsWidget; class LocalTorrentFilesModel; class TorrentDownloadDirectoryDirectorySelectionWidget; class TorrentFilesView; class Rpc; class AddTorrentDialog final : public QDialog { Q_OBJECT public: struct FileParams { QString filePath; }; struct UrlParams { QStringList urls; }; explicit AddTorrentDialog(Rpc* rpc, std::variant params, QWidget* parent = nullptr); void accept() override; struct AddTorrentParametersWidgets { TorrentDownloadDirectoryDirectorySelectionWidget* downloadDirectoryWidget; QComboBox* priorityComboBox; QCheckBox* startTorrentCheckBox; QGroupBox* deleteTorrentFileGroupBox; QCheckBox* moveTorrentFileToTrashCheckBox; void reset(Rpc* rpc) const; void saveToSettings() const; }; static AddTorrentParametersWidgets createAddTorrentParametersWidgets(bool forTorrentFile, QFormLayout* layout, Rpc* rpc); private: bool isAddingFile() const; void setupUi(); void updateUi(); void canAcceptUpdate(); void saveState(); void onDownloadDirectoryPathChanged(QString path); Coroutine<> getFreeSpaceForPath(QString path); Coroutine<> parseTorrentFile(); void showTorrentParsingError(const QString& errorString); void parseMagnetLinksAndCheckIfTorrentsExist(QStringList& urls); bool checkIfTorrentFileExists(); Coroutine<> deleteTorrentFileIfEnabled(); Rpc* mRpc; std::variant mParams; LocalTorrentFilesModel* mFilesModel{}; CoroutineScope mParseTorrentFileCoroutineScope{}; std::optional>>> mTorrentFileInfoHashAndTrackers{}; KMessageWidget* mMessageWidget{}; QLineEdit* mTorrentFilePathTextField{}; QPlainTextEdit* mTorrentLinkTextField{}; QLabel* mFreeSpaceLabel{}; TorrentFilesView* mTorrentFilesView{}; AddTorrentParametersWidgets mAddTorrentParametersWidgets{}; QGroupBox* mEditLabelsGroupBox{}; EditLabelsWidget* mEditLabelsWidget{}; QDialogButtonBox* mDialogButtonBox{}; CoroutineScope mFreeSpaceCoroutineScope{}; SaveWindowStateHandler mSaveStateHandler{this, [this] { saveState(); }}; }; } #endif // TREMOTESF_ADDTORRENTDIALOG_H tremotesf-2.10.1/src/ui/screens/addtorrent/addtorrenthelpers.cpp000066400000000000000000000110011523411710200250410ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include "addtorrenthelpers.h" #include "settings.h" #include "rpc/rpc.h" #include "rpc/servers.h" #include "rpc/serversettings.h" namespace tremotesf { AddTorrentParameters getAddTorrentParameters(Rpc* rpc) { auto* const settings = Settings::instance(); auto* const serverSettings = rpc->serverSettings(); return { .downloadDirectory = [&] { const auto lastDir = Servers::instance()->currentServerLastDownloadDirectory(serverSettings); return !lastDir.isEmpty() ? lastDir : serverSettings->data().downloadDirectory; }(), .priority = settings->get_lastAddTorrentPriority(), .startAfterAdding = settings->get_lastAddTorrentStartAfterAdding(), .deleteTorrentFile = settings->get_lastAddTorrentDeleteTorrentFile(), .moveTorrentFileToTrash = settings->get_lastAddTorrentMoveTorrentFileToTrash() }; } AddTorrentParameters getInitialAddTorrentParameters(Rpc* rpc) { auto* const serverSettings = rpc->serverSettings(); return { .downloadDirectory = serverSettings->data().downloadDirectory, .priority = TorrentData::Priority::Normal, .startAfterAdding = serverSettings->data().startAddedTorrents, .deleteTorrentFile = false, .moveTorrentFileToTrash = true }; } QDialog* createAskForMergingTrackersDialog(Torrent* torrent, std::vector> trackers, QWidget* parent) { auto* const settings = Settings::instance(); QMessageBox* messageBox{}; if (settings->get_askForMergingTrackersWhenAddingExistingTorrent()) { messageBox = new QMessageBox( QMessageBox::Question, //: Dialog title qApp->translate("tremotesf", "Merge trackers?"), qApp->translate("tremotesf", "Torrent «%1» is already added, merge trackers?") .arg(torrent->data().name), QMessageBox::Ok | QMessageBox::Cancel, parent ); messageBox->button(QMessageBox::Ok)->setText(qApp->translate("tremotesf", "Merge")); messageBox->setCheckBox(new QCheckBox(qApp->translate("tremotesf", "Do not ask again"), messageBox)); QObject::connect( messageBox, &QDialog::finished, messageBox, [=, torrent = QPointer(torrent), trackers = std::move(trackers)](int result) { if (result == QMessageBox::Ok && torrent) { torrent->addTrackers(trackers); } if (messageBox->checkBox()->isChecked()) { settings->set_mergeTrackersWhenAddingExistingTorrent(result == QMessageBox::Ok); settings->set_askForMergingTrackersWhenAddingExistingTorrent(false); } } ); } else if (settings->get_mergeTrackersWhenAddingExistingTorrent()) { torrent->addTrackers(trackers); messageBox = new QMessageBox( QMessageBox::Information, //: Dialog title qApp->translate("tremotesf", "Merged trackers"), qApp->translate("tremotesf", "Merged trackers for torrent «%1»").arg(torrent->data().name), QMessageBox::Close, parent ); } else { messageBox = new QMessageBox( QMessageBox::Warning, //: Dialog title qApp->translate("tremotesf", "Torrent already exists"), qApp->translate("tremotesf", "Torrent «%1» already exists").arg(torrent->data().name), QMessageBox::Close, parent ); } messageBox->setAttribute(Qt::WA_DeleteOnClose); messageBox->setModal(false); return messageBox; } QString bencodeErrorString(bencode::Error::Type type) { switch (type) { case bencode::Error::Type::Reading: return qApp->translate("tremotesf", "Error reading torrent file"); case bencode::Error::Type::Parsing: return qApp->translate("tremotesf", "Error parsing torrent file"); default: return {}; } } } tremotesf-2.10.1/src/ui/screens/addtorrent/addtorrenthelpers.h000066400000000000000000000015771523411710200245270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_ADDTORRENTHELPERS_H #define TREMOTESF_ADDTORRENTHELPERS_H #include #include "bencodeparser.h" #include "rpc/torrent.h" class QDialog; class QWidget; namespace tremotesf { class Rpc; struct AddTorrentParameters { QString downloadDirectory; TorrentData::Priority priority; bool startAfterAdding; bool deleteTorrentFile; bool moveTorrentFileToTrash; }; AddTorrentParameters getAddTorrentParameters(Rpc* rpc); AddTorrentParameters getInitialAddTorrentParameters(Rpc* rpc); QDialog* createAskForMergingTrackersDialog(Torrent* torrent, std::vector> trackers, QWidget* parent); QString bencodeErrorString(bencode::Error::Type type); } #endif // TREMOTESF_ADDTORRENTHELPERS_H tremotesf-2.10.1/src/ui/screens/addtorrent/droppedtorrents.cpp000066400000000000000000000036751523411710200245700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "droppedtorrents.h" #include #include #include "log/log.h" #include "magnetlinkparser.h" using namespace Qt::StringLiterals; SPECIALIZE_FORMATTER_FOR_QDEBUG(QUrl) namespace tremotesf { namespace { constexpr auto torrentFileSuffix = ".torrent"_L1; constexpr auto httpScheme = "http"_L1; constexpr auto httpsScheme = "https"_L1; } DroppedTorrents::DroppedTorrents(const QMimeData* mime) { // We need to validate whether we are actually opening torrent file (based on extension) // or BitTorrent magnet link in order to not accept event and tell OS that we don't want it if (mime->hasUrls()) { const auto mimeUrls = mime->urls(); for (const auto& url : mimeUrls) { processUrl(url); } } else if (mime->hasText()) { const auto text = mime->text(); const auto lines = QStringView(text).split(u'\n', Qt::SkipEmptyParts); for (auto line : lines) { processUrl(QUrl(line.toString())); } } } void DroppedTorrents::processUrl(const QUrl& url) { if (url.isLocalFile()) { if (auto path = url.toLocalFile(); path.endsWith(torrentFileSuffix)) { files.push_back(path); } } else { const auto scheme = url.scheme(); if (scheme == magnetScheme) { try { parseMagnetLink(url); urls.push_back(url.toString()); } catch (const std::runtime_error& e) { warning().logWithException(e, "Failed to parse URL {} as magnet link", url); } } else if (scheme == httpScheme || scheme == httpsScheme) { urls.push_back(url.toString()); } } } } tremotesf-2.10.1/src/ui/screens/addtorrent/droppedtorrents.h000066400000000000000000000011131523411710200242160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_DROPPEDTORRENTS_H #define TREMOTESF_DROPPEDTORRENTS_H #include class QMimeData; class QUrl; namespace tremotesf { struct DroppedTorrents { explicit DroppedTorrents(const QMimeData* mime); [[nodiscard]] bool isEmpty() const { return files.isEmpty() && urls.isEmpty(); } QStringList files{}; QStringList urls{}; private: void processUrl(const QUrl& url); }; } #endif // TREMOTESF_DROPPEDTORRENTS_H tremotesf-2.10.1/src/ui/screens/addtorrent/localtorrentfilesmodel.cpp000066400000000000000000000074161523411710200261030ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "localtorrentfilesmodel.h" #include "coroutines/threadpool.h" #include "ui/itemmodels/torrentfilesmodelentry.h" #include "ui/itemmodels/torrentfilestreebuilder.h" #include "torrentfileparser.h" namespace tremotesf { namespace { struct CreateTreeResult { std::unique_ptr rootEntry; std::vector files; }; CreateTreeResult createTree(TorrentMetainfoFile torrentFile) { if (torrentFile.isSingleFile() == 1) { auto rootEntry = std::make_unique(TorrentFilesModelEntry::createFile( 0, 0, std::move(torrentFile.rootFileName), torrentFile.singleFileSize(), 0, true, TorrentFilesModelEntry::Priority::Normal )); auto rootEntryPtr = rootEntry.get(); return {.rootEntry = std::move(rootEntry), .files = {rootEntryPtr}}; } auto files = torrentFile.files(); TorrentFilesTreeBuilder builder(files.size()); builder.initializeRootDirectory(std::move(torrentFile.rootFileName)); for (TorrentMetainfoFile::File file : files) { builder.addFile(file.path(), false, file.size, 0, true, TorrentFilesModelEntry::Priority::Normal); } builder.calculateDirectoriesRecursively(); return {.rootEntry = std::move(builder.rootEntry), .files = std::move(builder.files)}; } } LocalTorrentFilesModel::LocalTorrentFilesModel(QObject* parent) : BaseTorrentFilesModel({Column::Name, Column::Size, Column::Priority}, parent) {} Coroutine<> LocalTorrentFilesModel::load(TorrentMetainfoFile torrentFile) { beginResetModel(); try { auto [rootEntry, files] = co_await runOnThreadPool(&createTree, std::move(torrentFile)); mRootEntry = std::move(rootEntry); mFiles = std::move(files); mLoaded = true; endResetModel(); } catch (const bencode::Error&) { endResetModel(); throw; } } bool LocalTorrentFilesModel::isLoaded() const { return mLoaded; } std::vector LocalTorrentFilesModel::unwantedFiles() const { std::vector files; for (const TorrentFilesModelEntry* file : mFiles) { if (file->wantedState() == TorrentFilesModelEntry::WantedState::Unwanted) { files.push_back(file->fileId()); } } return files; } std::vector LocalTorrentFilesModel::highPriorityFiles() const { std::vector files; for (const TorrentFilesModelEntry* file : mFiles) { if (file->priority() == TorrentFilesModelEntry::Priority::High) { files.push_back(file->fileId()); } } return files; } std::vector LocalTorrentFilesModel::lowPriorityFiles() const { std::vector files; for (const TorrentFilesModelEntry* file : mFiles) { if (file->priority() == TorrentFilesModelEntry::Priority::Low) { files.push_back(file->fileId()); } } return files; } const std::map& LocalTorrentFilesModel::renamedFiles() const { return mRenamedFiles; } void LocalTorrentFilesModel::renameFile(const QModelIndex& index, const QString& newName) { auto entry = static_cast(index.internalPointer()); mRenamedFiles.emplace(entry->path(), newName); fileRenamed(entry, newName); } } tremotesf-2.10.1/src/ui/screens/addtorrent/localtorrentfilesmodel.h000066400000000000000000000021521523411710200255400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_LOCALTORRENTFILESMODEL_H #define TREMOTESF_LOCALTORRENTFILESMODEL_H #include #include #include "ui/itemmodels/basetorrentfilesmodel.h" #include "coroutines/coroutinefwd.h" namespace tremotesf { class TorrentMetainfoFile; class LocalTorrentFilesModel final : public BaseTorrentFilesModel { Q_OBJECT public: explicit LocalTorrentFilesModel(QObject* parent = nullptr); /** * @throws bencode::Error */ Coroutine<> load(TorrentMetainfoFile torrentFile); bool isLoaded() const; std::vector unwantedFiles() const; std::vector highPriorityFiles() const; std::vector lowPriorityFiles() const; const std::map& renamedFiles() const; void renameFile(const QModelIndex& index, const QString& newName) override; private: bool mLoaded{}; std::map mRenamedFiles{}; }; } #endif // TREMOTESF_LOCALTORRENTFILESMODEL_H tremotesf-2.10.1/src/ui/screens/connectionsettings/000077500000000000000000000000001523411710200223655ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/screens/connectionsettings/connectionsettingsdialog.cpp000066400000000000000000000134611523411710200301760ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "connectionsettingsdialog.h" #include #include #include #include #include #include #include #include #include #include #include "serversmodel.h" #include "servereditdialog.h" #include "ui/itemmodels/baseproxymodel.h" #include "ui/widgets/listplaceholder.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { constexpr auto editIconName = "document-properties"_L1; constexpr auto removeIconName = "list-remove"_L1; } ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget* parent) : QDialog(parent), //: Servers list placeholder mModel(new ServersModel(this)), mProxyModel(new BaseProxyModel(mModel, Qt::DisplayRole, std::nullopt, this)), mServersView(new QListView(this)) { //: Dialog title setWindowTitle(qApp->translate("tremotesf", "Connection Settings")); mProxyModel->sort(0); auto layout = new QGridLayout(this); mServersView->setContextMenuPolicy(Qt::CustomContextMenu); mServersView->setSelectionMode(QListView::ExtendedSelection); mServersView->setModel(mProxyModel); QObject::connect(mServersView, &QListView::activated, this, &ConnectionSettingsDialog::showEditDialogs); auto removeAction = new QAction( QIcon::fromTheme(removeIconName), //: Server's context menu item qApp->translate("tremotesf", "&Remove"), this ); removeAction->setShortcut(QKeySequence::Delete); mServersView->addAction(removeAction); QObject::connect(removeAction, &QAction::triggered, this, &ConnectionSettingsDialog::removeServers); QObject::connect(mServersView, &QListView::customContextMenuRequested, this, [=, this](QPoint pos) { if (mServersView->indexAt(pos).isValid()) { QMenu contextMenu; const auto* const editAction = contextMenu.addAction( QIcon::fromTheme(editIconName), //: Server's context menu item qApp->translate("tremotesf", "&Edit...") ); QObject::connect(editAction, &QAction::triggered, this, &ConnectionSettingsDialog::showEditDialogs); contextMenu.addAction(removeAction); contextMenu.exec(mServersView->viewport()->mapToGlobal(pos)); } }); auto placeholder = createListPlaceholderLabel(qApp->translate("tremotesf", "No servers")); addListPlaceholderLabelToViewportAndManageVisibility(mServersView, placeholder); layout->addWidget(mServersView, 0, 0); auto buttonsLayout = new QVBoxLayout(); layout->addLayout(buttonsLayout, 0, 1); auto addServerButton = new QPushButton( QIcon::fromTheme("list-add"_L1), //: Button qApp->translate("tremotesf", "Add Server..."), this ); QObject::connect(addServerButton, &QPushButton::clicked, this, [=, this] { auto dialog = new ServerEditDialog(mModel, -1, this); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); }); buttonsLayout->addWidget(addServerButton); auto editButton = new QPushButton( QIcon::fromTheme(editIconName), //: Button qApp->translate("tremotesf", "Edit..."), this ); editButton->setEnabled(false); QObject::connect(editButton, &QPushButton::clicked, this, &ConnectionSettingsDialog::showEditDialogs); buttonsLayout->addWidget(editButton); auto removeButton = new QPushButton( QIcon::fromTheme(removeIconName), //: Button qApp->translate("tremotesf", "Remove"), this ); removeButton->setEnabled(false); QObject::connect(removeButton, &QPushButton::clicked, this, &ConnectionSettingsDialog::removeServers); buttonsLayout->addWidget(removeButton); buttonsLayout->addStretch(); QObject::connect(mServersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [=, this] { const bool hasSelection = mServersView->selectionModel()->hasSelection(); editButton->setEnabled(hasSelection); removeButton->setEnabled(hasSelection); }); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &ConnectionSettingsDialog::accept); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &ConnectionSettingsDialog::reject); layout->addWidget(dialogButtonBox, 1, 0, 1, 2); resize(sizeHint().expandedTo(QSize(384, 320))); } void ConnectionSettingsDialog::accept() { Servers::instance()->saveServers(mModel->servers(), mModel->currentServerName()); QDialog::accept(); } void ConnectionSettingsDialog::showEditDialogs() { const QModelIndexList indexes(mServersView->selectionModel()->selectedIndexes()); for (const QModelIndex& index : indexes) { auto dialog = new ServerEditDialog(mModel, mProxyModel->mapToSource(index).row(), this); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } } void ConnectionSettingsDialog::removeServers() { while (mServersView->selectionModel()->hasSelection()) { mModel->removeServerAtIndex( mProxyModel->mapToSource(mServersView->selectionModel()->selectedIndexes().first()) ); } } } tremotesf-2.10.1/src/ui/screens/connectionsettings/connectionsettingsdialog.h000066400000000000000000000012671523411710200276440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SERVERSDIALOG_H #define TREMOTESF_SERVERSDIALOG_H #include class QListView; namespace tremotesf { class ServersModel; class BaseProxyModel; class ConnectionSettingsDialog final : public QDialog { Q_OBJECT public: explicit ConnectionSettingsDialog(QWidget* parent = nullptr); void accept() override; private: void showEditDialogs(); void removeServers(); ServersModel* mModel; BaseProxyModel* mProxyModel; QListView* mServersView; }; } #endif // TREMOTESF_SERVERSDIALOG_H tremotesf-2.10.1/src/ui/screens/connectionsettings/servereditdialog.cpp000066400000000000000000000734031523411710200264340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "servereditdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fileutils.h" #include "log/log.h" #include "rpc/pathutils.h" #include "stdutils.h" #include "target_os.h" #include "ui/widgets/tooltipwhenelideddelegate.h" #include "rpc/servers.h" #include "serversmodel.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { constexpr auto removeIconName = "list-remove"_L1; constexpr std::array proxyTypeComboBoxValues{ ConnectionConfiguration::ProxyType::Default, ConnectionConfiguration::ProxyType::Http, ConnectionConfiguration::ProxyType::Socks5, ConnectionConfiguration::ProxyType::None, }; ConnectionConfiguration::ProxyType proxyTypeFromComboBoxIndex(int index) { if (index == -1) { return ConnectionConfiguration::ProxyType::Default; } return proxyTypeComboBoxValues.at(static_cast(index)); } constexpr std::array serverCertificateModeComboBoxValues{ ConnectionConfiguration::ServerCertificateMode::None, ConnectionConfiguration::ServerCertificateMode::SelfSigned, ConnectionConfiguration::ServerCertificateMode::CustomRoot }; ConnectionConfiguration::ServerCertificateMode serverCertificateModeFromComboBoxIndex(int index) { if (index == -1) { return ConnectionConfiguration::ServerCertificateMode::None; } return serverCertificateModeComboBoxValues.at(static_cast(index)); } } class CertificateTextField final : public QWidget { Q_OBJECT public: explicit CertificateTextField(const QString& labelText = {}, QWidget* parent = nullptr) : QWidget(parent) { auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->addWidget(&label); layout->addWidget(&textEdit); layout->addWidget(&mLoadFromFileButton); label.setText(labelText); textEdit.setMinimumHeight(192); QObject::connect( &mLoadFromFileButton, &QPushButton::clicked, this, &CertificateTextField::loadCertificateFromFile ); } QLabel label; QPlainTextEdit textEdit; private: void loadCertificateFromFile() { auto* fileDialog = new QFileDialog( nativeParentWidget(), //: File chooser dialog title qApp->translate("tremotesf", "Select Files") ); fileDialog->setAttribute(Qt::WA_DeleteOnClose); fileDialog->setFileMode(QFileDialog::ExistingFile); fileDialog->setMimeTypeFilters({"application/x-pem-file"_L1}); QObject::connect(fileDialog, &QFileDialog::accepted, this, [=, this] { try { textEdit.setPlainText(readFile(fileDialog->selectedFiles().constFirst())); } catch (const QFileError& e) { warning().logWithException(e, "Failed to read certificate from file"); } }); if constexpr (targetOs == TargetOs::Windows) { fileDialog->open(); } else { fileDialog->show(); } } QPushButton mLoadFromFileButton{//: Button qApp->translate("tremotesf", "Load from file...") }; }; class MountedDirectoriesWidget final : public QTableWidget { Q_OBJECT public: MountedDirectoriesWidget(int rows, int columns, QWidget* parent = nullptr) : QTableWidget(rows, columns, parent) { setMinimumHeight(192); setSelectionMode(QAbstractItemView::SingleSelection); setItemDelegate(new TooltipWhenElidedDelegate(this)); setHorizontalHeaderLabels( {//: Column title in the list of mounted directories qApp->translate("tremotesf", "Local directory"), //: Column title in the list of mounted directories qApp->translate("tremotesf", "Remote directory") } ); horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); horizontalHeader()->setSectionsClickable(false); verticalHeader()->setVisible(false); setContextMenuPolicy(Qt::CustomContextMenu); //: Context menu item auto removeAction = new QAction(QIcon::fromTheme(removeIconName), qApp->translate("tremotesf", "&Remove"), this); removeAction->setShortcut(QKeySequence::Delete); addAction(removeAction); QObject::connect(removeAction, &QAction::triggered, this, [=, this] { const auto items(selectionModel()->selectedIndexes()); if (!items.isEmpty()) { removeRow(items.first().row()); } }); QObject::connect(this, &QWidget::customContextMenuRequested, this, [=, this](QPoint pos) { const QModelIndex index(indexAt(pos)); if (!index.isValid()) { return; } QMenu contextMenu; const QAction* selectAction = nullptr; if (index.column() == 0) { //: Context menu item to open directory chooser selectAction = contextMenu.addAction(qApp->translate("tremotesf", "&Select...")); } contextMenu.addAction(removeAction); auto executed = contextMenu.exec(viewport()->mapToGlobal(pos)); if (executed && executed == selectAction) { const QString directory(QFileDialog::getExistingDirectory(this)); if (!directory.isEmpty()) { const auto item = this->item(index.row(), index.column()); if (item) { item->setText(directory); item->setToolTip(directory); } } } }); } void addRow(const QString& localDirectory, const QString& remoteDirectory) { const int row = rowCount(); insertRow(row); const auto localItem = new QTableWidgetItem(localDirectory); localItem->setToolTip(localDirectory); setItem(row, 0, localItem); const auto remoteItem = new QTableWidgetItem(remoteDirectory); remoteItem->setToolTip(remoteDirectory); setItem(row, 1, remoteItem); } protected: void keyPressEvent(QKeyEvent* event) override { QTableWidget::keyPressEvent(event); switch (event->key()) { case Qt::Key_Return: case Qt::Key_Enter: event->accept(); if (state() != EditingState) { edit(currentIndex()); } break; default: break; } } }; ServerEditDialog::ServerEditDialog(ServersModel* serversModel, int row, QWidget* parent) : QDialog(parent), mServersModel(serversModel) { const auto setInitialDependentState = setupUi(); resize(sizeHint().expandedTo(QSize(384, 512))); if (row == -1) { //: Dialog title setWindowTitle(qApp->translate("tremotesf", "Add Server")); mPortSpinBox->setValue(9091); mApiPathLineEdit->setText("/transmission/rpc"_L1); mProxyTypeComboBox->setCurrentIndex( // NOLINTNEXTLINE(bugprone-unchecked-optional-access) indexOfCasted(proxyTypeComboBoxValues, ConnectionConfiguration::ProxyType::Default).value() ); mHttpsGroupBox->setChecked(false); mServerCertificateModeComboBox->setCurrentIndex( // NOLINTBEGIN(bugprone-unchecked-optional-access) indexOfCasted( serverCertificateModeComboBoxValues, ConnectionConfiguration::ServerCertificateMode::None ) .value() // NOLINTEND(bugprone-unchecked-optional-access) ); mAuthenticationGroupBox->setChecked(false); mUpdateIntervalSpinBox->setValue(5); mTimeoutSpinBox->setValue(30); mAutoReconnectGroupBox->setChecked(false); mAutoReconnectSpinBox->setValue(30); } else { const Server& server = mServersModel->servers().at(static_cast(row)); mServerName = server.name; setWindowTitle(mServerName); mNameLineEdit->setText(mServerName); mAddressLineEdit->setText(server.connectionConfiguration.address); mPortSpinBox->setValue(server.connectionConfiguration.port); mApiPathLineEdit->setText(server.connectionConfiguration.apiPath); mProxyTypeComboBox->setCurrentIndex( // NOLINTNEXTLINE(bugprone-unchecked-optional-access) indexOfCasted(proxyTypeComboBoxValues, server.connectionConfiguration.proxyType).value() ); mProxyHostnameLineEdit->setText(server.connectionConfiguration.proxyHostname); mProxyPortSpinBox->setValue(server.connectionConfiguration.proxyPort); mProxyUserLineEdit->setText(server.connectionConfiguration.proxyUser); mProxyPasswordLineEdit->setText(server.connectionConfiguration.proxyPassword); mHttpsGroupBox->setChecked(server.connectionConfiguration.https); mServerCertificateModeComboBox->setCurrentIndex( // NOLINTBEGIN(bugprone-unchecked-optional-access) indexOfCasted( serverCertificateModeComboBoxValues, server.connectionConfiguration.serverCertificateMode ) .value() // NOLINTEND(bugprone-unchecked-optional-access) ); mServerRootCertificateField->textEdit.setPlainText(server.connectionConfiguration.serverRootCertificate); mServerLeafCertificateField->textEdit.setPlainText(server.connectionConfiguration.serverLeafCertificate); mClientCertificateCheckBox->setChecked(server.connectionConfiguration.clientCertificateEnabled); mClientCertificateField->textEdit.setPlainText(server.connectionConfiguration.clientCertificate); mAuthenticationGroupBox->setChecked(server.connectionConfiguration.authentication); mUsernameLineEdit->setText(server.connectionConfiguration.username); mPasswordLineEdit->setText(server.connectionConfiguration.password); mUpdateIntervalSpinBox->setValue(server.connectionConfiguration.updateInterval); mTimeoutSpinBox->setValue(server.connectionConfiguration.timeout); mAutoReconnectGroupBox->setChecked(server.connectionConfiguration.autoReconnectEnabled); mAutoReconnectSpinBox->setValue(server.connectionConfiguration.autoReconnectInterval); for (const auto& [localDirectory, remoteDirectory] : server.mountedDirectories) { mMountedDirectoriesWidget->addRow(localDirectory, remoteDirectory); } } setInitialDependentState(); } void ServerEditDialog::accept() { if (mServersModel) { const QString newName(mNameLineEdit->text()); if (newName != mServerName && mServersModel->hasServer(newName)) { QMessageBox messageBox( QMessageBox::Warning, //: Dialog title qApp->translate("tremotesf", "Overwrite Server"), qApp->translate("tremotesf", "Server already exists"), QMessageBox::Ok | QMessageBox::Cancel, this ); messageBox.setDefaultButton(QMessageBox::Cancel); //: Dialog's confirmation button messageBox.button(QMessageBox::Ok)->setText(qApp->translate("tremotesf", "Overwrite")); if (messageBox.exec() != QMessageBox::Ok) { return; } } } setServer(); QDialog::accept(); } std::function ServerEditDialog::setupUi() { auto topLayout = new QVBoxLayout(this); auto scrollArea = new QScrollArea(this); scrollArea->setFrameShape(QFrame::NoFrame); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea->setWidgetResizable(true); topLayout->addWidget(scrollArea); auto widget = new QWidget(this); auto formLayout = new QFormLayout(widget); formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mNameLineEdit = new QLineEdit(this); mNameLineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(R"(^\S.*)"_L1), this)); formLayout->addRow(qApp->translate("tremotesf", "Name:"), mNameLineEdit); mAddressLineEdit = new QLineEdit(this); auto addressValidator = new QRegularExpressionValidator(QRegularExpression(R"(^\S+)"_L1), this); mAddressLineEdit->setValidator(addressValidator); formLayout->addRow(qApp->translate("tremotesf", "Address:"), mAddressLineEdit); mPortSpinBox = new QSpinBox(this); const int maxPort = 65535; mPortSpinBox->setMaximum(maxPort); formLayout->addRow(qApp->translate("tremotesf", "Port:"), mPortSpinBox); mApiPathLineEdit = new QLineEdit(this); formLayout->addRow(qApp->translate("tremotesf", "API path:"), mApiPathLineEdit); auto proxyGroupBox = new QGroupBox(qApp->translate("tremotesf", "Proxy"), this); auto proxyLayout = new QFormLayout(proxyGroupBox); proxyLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mProxyTypeComboBox = new QComboBox(this); mProxyTypeComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); for (const auto type : proxyTypeComboBoxValues) { switch (type) { case ConnectionConfiguration::ProxyType::Default: //: Default proxy option mProxyTypeComboBox->addItem(qApp->translate("tremotesf", "Default")); break; case ConnectionConfiguration::ProxyType::Http: //: HTTP proxy option mProxyTypeComboBox->addItem(qApp->translate("tremotesf", "HTTP")); break; case ConnectionConfiguration::ProxyType::Socks5: //: SOCKS5 proxy option mProxyTypeComboBox->addItem(qApp->translate("tremotesf", "SOCKS5")); break; case ConnectionConfiguration::ProxyType::None: //: None proxy option mProxyTypeComboBox->addItem(qApp->translate("tremotesf", "None")); break; } } proxyLayout->addRow(qApp->translate("tremotesf", "Proxy type:"), mProxyTypeComboBox); mProxyHostnameLineEdit = new QLineEdit(this); mProxyHostnameLineEdit->setValidator(addressValidator); proxyLayout->addRow(qApp->translate("tremotesf", "Address:"), mProxyHostnameLineEdit); mProxyPortSpinBox = new QSpinBox(this); mProxyPortSpinBox->setMaximum(maxPort); proxyLayout->addRow(qApp->translate("tremotesf", "Port:"), mProxyPortSpinBox); mProxyUserLineEdit = new QLineEdit(this); proxyLayout->addRow(qApp->translate("tremotesf", "Username:"), mProxyUserLineEdit); mProxyPasswordLineEdit = new QLineEdit(this); mProxyPasswordLineEdit->setEchoMode(QLineEdit::Password); proxyLayout->addRow(qApp->translate("tremotesf", "Password:"), mProxyPasswordLineEdit); formLayout->addRow(proxyGroupBox); const auto updateProxyFieldsVisibility = [=, this] { bool visible{}; switch (proxyTypeFromComboBoxIndex(mProxyTypeComboBox->currentIndex())) { case ConnectionConfiguration::ProxyType::Default: case ConnectionConfiguration::ProxyType::None: visible = false; break; default: visible = true; } for (int i = 1, max = proxyLayout->rowCount(); i < max; ++i) { proxyLayout->itemAt(i, QFormLayout::LabelRole)->widget()->setVisible(visible); proxyLayout->itemAt(i, QFormLayout::FieldRole)->widget()->setVisible(visible); } }; QObject::connect(mProxyTypeComboBox, &QComboBox::currentTextChanged, this, updateProxyFieldsVisibility); mHttpsGroupBox = new QGroupBox(qApp->translate("tremotesf", "HTTPS"), this); mHttpsGroupBox->setCheckable(true); auto httpsGroupBoxLayout = new QFormLayout(mHttpsGroupBox); httpsGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mServerCertificateModeComboBox = new QComboBox(this); mServerCertificateModeComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); for (const auto type : serverCertificateModeComboBoxValues) { switch (type) { case ConnectionConfiguration::ServerCertificateMode::None: //: Server does not use custom certificates mServerCertificateModeComboBox->addItem(qApp->translate("tremotesf", "No")); break; case ConnectionConfiguration::ServerCertificateMode::SelfSigned: mServerCertificateModeComboBox->addItem(qApp->translate("tremotesf", "Self-signed certificate")); break; case ConnectionConfiguration::ServerCertificateMode::CustomRoot: mServerCertificateModeComboBox->addItem(qApp->translate("tremotesf", "Custom CA root certificate")); break; } } httpsGroupBoxLayout->addRow( qApp->translate("tremotesf", "Server uses custom certificates:"), mServerCertificateModeComboBox ); mServerRootCertificateField = new CertificateTextField({}, this); httpsGroupBoxLayout->addRow(mServerRootCertificateField); auto serverLeafCertificateNotice = new QLabel( qApp->translate( "tremotesf", "If server's leaf certificate does not have correct host name, you need to provide it too for " "certificate validation to pass" ), this ); serverLeafCertificateNotice->setWordWrap(true); httpsGroupBoxLayout->addRow(serverLeafCertificateNotice); mServerLeafCertificateField = new CertificateTextField(qApp->translate("tremotesf", "Server's leaf certificate in PEM format:"), this); httpsGroupBoxLayout->addRow(mServerLeafCertificateField); const auto updateServerCertificateFieldsState = [=, this] { switch (serverCertificateModeFromComboBoxIndex(mServerCertificateModeComboBox->currentIndex())) { case ConnectionConfiguration::ServerCertificateMode::SelfSigned: mServerRootCertificateField->label.setText( qApp->translate("tremotesf", "Server's leaf certificate in PEM format:") ); mServerRootCertificateField->show(); serverLeafCertificateNotice->hide(); mServerLeafCertificateField->hide(); break; case ConnectionConfiguration::ServerCertificateMode::CustomRoot: mServerRootCertificateField->label.setText( qApp->translate("tremotesf", "Server's CA root certificate in PEM format:") ); mServerRootCertificateField->show(); serverLeafCertificateNotice->show(); mServerLeafCertificateField->show(); break; case ConnectionConfiguration::ServerCertificateMode::None: default: mServerRootCertificateField->hide(); serverLeafCertificateNotice->hide(); mServerLeafCertificateField->hide(); break; } }; QObject::connect( mServerCertificateModeComboBox, &QComboBox::currentTextChanged, this, updateServerCertificateFieldsState ); mClientCertificateCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Use client certificate authentication"), this ); httpsGroupBoxLayout->addRow(mClientCertificateCheckBox); mClientCertificateField = new CertificateTextField( qApp->translate("tremotesf", "Client certificate with private key in PEM format:"), this ); httpsGroupBoxLayout->addRow(mClientCertificateField); const auto updateClientCertificateFieldVisibility = [this] { mClientCertificateField->setVisible(mClientCertificateCheckBox->isChecked()); }; QObject::connect(mClientCertificateCheckBox, &QCheckBox::toggled, this, updateClientCertificateFieldVisibility); formLayout->addRow(mHttpsGroupBox); //: Check box label mAuthenticationGroupBox = new QGroupBox(qApp->translate("tremotesf", "Authentication"), this); mAuthenticationGroupBox->setCheckable(true); auto authenticationGroupBoxLayout = new QFormLayout(mAuthenticationGroupBox); authenticationGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mUsernameLineEdit = new QLineEdit(this); authenticationGroupBoxLayout->addRow(qApp->translate("tremotesf", "Username:"), mUsernameLineEdit); mPasswordLineEdit = new QLineEdit(this); mPasswordLineEdit->setEchoMode(QLineEdit::Password); authenticationGroupBoxLayout->addRow(qApp->translate("tremotesf", "Password:"), mPasswordLineEdit); formLayout->addRow(mAuthenticationGroupBox); mUpdateIntervalSpinBox = new QSpinBox(this); mUpdateIntervalSpinBox->setMinimum(1); mUpdateIntervalSpinBox->setMaximum(3600); //: Suffix that is added to input field with number of seconds, e.g. "30 s" mUpdateIntervalSpinBox->setSuffix(qApp->translate("tremotesf", " s")); formLayout->addRow(qApp->translate("tremotesf", "Update interval:"), mUpdateIntervalSpinBox); mTimeoutSpinBox = new QSpinBox(this); mTimeoutSpinBox->setMinimum(5); mTimeoutSpinBox->setMaximum(60); //: Suffix that is added to input field with number of seconds, e.g. "30 s" mTimeoutSpinBox->setSuffix(qApp->translate("tremotesf", " s")); formLayout->addRow(qApp->translate("tremotesf", "Timeout:"), mTimeoutSpinBox); //: Check box label mAutoReconnectGroupBox = new QGroupBox(qApp->translate("tremotesf", "Auto reconnect on error"), this); mAutoReconnectGroupBox->setCheckable(true); formLayout->addRow(mAutoReconnectGroupBox); auto* autoReconnectFormLayout = new QFormLayout(mAutoReconnectGroupBox); autoReconnectFormLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mAutoReconnectSpinBox = new QSpinBox(this); mAutoReconnectSpinBox->setMinimum(1); mAutoReconnectSpinBox->setMaximum(3600); //: Suffix that is added to input field with number of seconds, e.g. "30 s" mAutoReconnectSpinBox->setSuffix(qApp->translate("tremotesf", " s")); autoReconnectFormLayout->addRow( qApp->translate("tremotesf", "Auto reconnect interval:"), mAutoReconnectSpinBox ); auto mountedDirectoriesGroupBox = new QGroupBox(qApp->translate("tremotesf", "Mounted directories"), this); auto mountedDirectoriesLayout = new QGridLayout(mountedDirectoriesGroupBox); mMountedDirectoriesWidget = new MountedDirectoriesWidget(0, 2); mountedDirectoriesLayout->addWidget(mMountedDirectoriesWidget, 0, 0, 1, 2); auto addDirectoriesButton = new QPushButton( QIcon::fromTheme("list-add"_L1), //: Button qApp->translate("tremotesf", "Add"), this ); QObject::connect(addDirectoriesButton, &QPushButton::clicked, this, [=, this] { const QUrl directory = QFileDialog::getExistingDirectoryUrl(this); const auto normalized = normalizeLocalPathOrNetworkShareUrl(directory.toString()); if (!normalized.isEmpty()) { mMountedDirectoriesWidget->addRow(normalized, QString()); } }); mountedDirectoriesLayout->addWidget(addDirectoriesButton, 1, 0); auto removeDirectoriesButton = new QPushButton( QIcon::fromTheme(removeIconName), //: Button qApp->translate("tremotesf", "Remove"), this ); QObject::connect(removeDirectoriesButton, &QPushButton::clicked, this, [=, this] { const auto items(mMountedDirectoriesWidget->selectionModel()->selectedIndexes()); if (!items.isEmpty()) { mMountedDirectoriesWidget->removeRow(items.first().row()); } }); mountedDirectoriesLayout->addWidget(removeDirectoriesButton, 1, 1); formLayout->addRow(mountedDirectoriesGroupBox); scrollArea->setWidget(widget); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &ServerEditDialog::accept); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &ServerEditDialog::reject); topLayout->addWidget(dialogButtonBox); const auto updateAcceptButtonState = [=, this] { dialogButtonBox->button(QDialogButtonBox::Ok) ->setEnabled(mNameLineEdit->hasAcceptableInput() && mAddressLineEdit->hasAcceptableInput()); }; QObject::connect(mNameLineEdit, &QLineEdit::textChanged, this, updateAcceptButtonState); QObject::connect(mAddressLineEdit, &QLineEdit::textChanged, this, updateAcceptButtonState); return [=] { updateProxyFieldsVisibility(); updateServerCertificateFieldsState(); updateClientCertificateFieldVisibility(); updateAcceptButtonState(); }; } void ServerEditDialog::setServer() { std::vector mountedDirectories{}; mountedDirectories.reserve(static_cast(mMountedDirectoriesWidget->rowCount())); for (int i = 0, max = mMountedDirectoriesWidget->rowCount(); i < max; ++i) { const auto localItem = mMountedDirectoriesWidget->item(i, 0); const QString localDirectory = localItem ? normalizeLocalPathOrNetworkShareUrl(localItem->text().trimmed()) : QString{}; const auto remoteItem = mMountedDirectoriesWidget->item(i, 1); const QString remoteDirectory = remoteItem ? remoteItem->text().trimmed() : QString{}; if (!localDirectory.isEmpty() && !remoteDirectory.isEmpty()) { mountedDirectories.push_back({.localPath = localDirectory, .remotePath = remoteDirectory}); } } ConnectionConfiguration connectionConfiguration{ .address = mAddressLineEdit->text(), .port = mPortSpinBox->value(), .apiPath = mApiPathLineEdit->text(), .proxyType = proxyTypeFromComboBoxIndex(mProxyTypeComboBox->currentIndex()), .proxyHostname = mProxyHostnameLineEdit->text(), .proxyPort = mProxyPortSpinBox->value(), .proxyUser = mProxyUserLineEdit->text(), .proxyPassword = mProxyPasswordLineEdit->text(), .https = mHttpsGroupBox->isChecked(), .serverCertificateMode = serverCertificateModeFromComboBoxIndex(mServerCertificateModeComboBox->currentIndex()), .serverRootCertificate = mServerRootCertificateField->textEdit.toPlainText().toUtf8(), .serverLeafCertificate = mServerLeafCertificateField->textEdit.toPlainText().toUtf8(), .clientCertificateEnabled = mClientCertificateCheckBox->isChecked(), .clientCertificate = mClientCertificateField->textEdit.toPlainText().toUtf8(), .authentication = mAuthenticationGroupBox->isChecked(), .username = mUsernameLineEdit->text(), .password = mPasswordLineEdit->text(), .updateInterval = mUpdateIntervalSpinBox->value(), .timeout = mTimeoutSpinBox->value(), .autoReconnectEnabled = mAutoReconnectGroupBox->isChecked(), .autoReconnectInterval = mAutoReconnectSpinBox->value(), }; if (mServersModel) { mServersModel->setServer( mServerName, mNameLineEdit->text(), std::move(connectionConfiguration), std::move(mountedDirectories) ); } else { Servers::instance() ->setServer(mServerName, mNameLineEdit->text(), connectionConfiguration, std::move(mountedDirectories)); } } } #include "servereditdialog.moc" tremotesf-2.10.1/src/ui/screens/connectionsettings/servereditdialog.h000066400000000000000000000041501523411710200260720ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SERVEREDITDIALOG_H #define TREMOTESF_SERVEREDITDIALOG_H #include #include class QCheckBox; class QComboBox; class QDialogButtonBox; class QFormLayout; class QGroupBox; class QLineEdit; class QSpinBox; namespace tremotesf { class CertificateTextField; class MountedDirectoriesWidget; class ServersModel; class ServerEditDialog final : public QDialog { Q_OBJECT public: explicit ServerEditDialog(ServersModel* serversModel, int row, QWidget* parent = nullptr); void accept() override; private: std::function setupUi(); void setServer(); ServersModel* mServersModel; QString mServerName; QLineEdit* mNameLineEdit = nullptr; QLineEdit* mAddressLineEdit = nullptr; QSpinBox* mPortSpinBox = nullptr; QLineEdit* mApiPathLineEdit = nullptr; QComboBox* mProxyTypeComboBox = nullptr; QLineEdit* mProxyHostnameLineEdit = nullptr; QSpinBox* mProxyPortSpinBox = nullptr; QLineEdit* mProxyUserLineEdit = nullptr; QLineEdit* mProxyPasswordLineEdit = nullptr; QGroupBox* mHttpsGroupBox = nullptr; QComboBox* mServerCertificateModeComboBox = nullptr; CertificateTextField* mServerRootCertificateField = nullptr; CertificateTextField* mServerLeafCertificateField = nullptr; QCheckBox* mClientCertificateCheckBox = nullptr; CertificateTextField* mClientCertificateField = nullptr; QGroupBox* mAuthenticationGroupBox = nullptr; QLineEdit* mUsernameLineEdit = nullptr; QLineEdit* mPasswordLineEdit = nullptr; QSpinBox* mUpdateIntervalSpinBox = nullptr; QSpinBox* mTimeoutSpinBox = nullptr; QGroupBox* mAutoReconnectGroupBox = nullptr; QSpinBox* mAutoReconnectSpinBox = nullptr; MountedDirectoriesWidget* mMountedDirectoriesWidget = nullptr; QDialogButtonBox* mDialogButtonBox = nullptr; }; } #endif // TREMOTESF_SERVEREDITDIALOG_H tremotesf-2.10.1/src/ui/screens/connectionsettings/serversmodel.cpp000066400000000000000000000117001523411710200256020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "serversmodel.h" namespace tremotesf { ServersModel::ServersModel(QObject* parent) : QAbstractListModel(parent), mServers(Servers::instance()->servers()), mCurrentServer(Servers::instance()->currentServerName()) {} QVariant ServersModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } const Server& server = mServers.at(static_cast(index.row())); switch (role) { case Qt::CheckStateRole: if (server.name == mCurrentServer) { return Qt::Checked; } return Qt::Unchecked; case Qt::DisplayRole: return server.name; default: return {}; } } Qt::ItemFlags ServersModel::flags(const QModelIndex& index) const { if (!index.isValid()) { return {}; } return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable; } int ServersModel::rowCount(const QModelIndex&) const { return static_cast(mServers.size()); } bool ServersModel::setData(const QModelIndex& modelIndex, const QVariant& value, int role) { if (!modelIndex.isValid() || role != Qt::CheckStateRole || value.value() != Qt::Checked) { return false; } const auto& current = mServers.at(static_cast(modelIndex.row())); if (current.name != mCurrentServer) { mCurrentServer = current.name; emit dataChanged(index(0), index(static_cast(mServers.size()) - 1)); return true; } return false; } const std::vector& ServersModel::servers() const { return mServers; } const QString& ServersModel::currentServerName() const { return mCurrentServer; } bool ServersModel::hasServer(const QString& name) const { return serverRow(name) != -1; } void ServersModel::setServer( const QString& oldName, const QString& name, ConnectionConfiguration connectionConfiguration, std::vector mountedDirectories ) { const int oldRow = serverRow(oldName); int row = serverRow(name); Server* const server = [=, this]() -> Server* { if (oldRow != -1) { return &mServers.at(static_cast(oldRow)); } if (row != -1) { return &mServers.at(static_cast(row)); } return nullptr; }(); if (server) { // Overwrite an existing server server->name = name; server->connectionConfiguration = std::move(connectionConfiguration); server->mountedDirectories = std::move(mountedDirectories); const QModelIndex modelIndex(index(oldRow)); emit dataChanged(modelIndex, modelIndex); if (oldRow != -1 && row != -1 && row != oldRow) { // Remove overwritten server if we overwrite when renaming beginRemoveRows(QModelIndex(), row, row); mServers.erase(mServers.begin() + row); endRemoveRows(); } if (oldName == mCurrentServer) { mCurrentServer = name; } } else { row = static_cast(mServers.size()); beginInsertRows(QModelIndex(), row, row); mServers.push_back( Server{ .name = name, .connectionConfiguration = std::move(connectionConfiguration), .mountedDirectories = std::move(mountedDirectories), .lastTorrents = {}, .lastDownloadDirectories = {}, .lastDownloadDirectory = {} } ); endInsertRows(); if (row == 0) { mCurrentServer = name; } } } void ServersModel::removeServerAtIndex(const QModelIndex& index) { removeServerAtRow(index.row()); } void ServersModel::removeServerAtRow(int row) { const bool current = (mServers.at(static_cast(row)).name == mCurrentServer); beginRemoveRows(QModelIndex(), row, row); mServers.erase(mServers.begin() + row); endRemoveRows(); if (current) { if (mServers.empty()) { mCurrentServer.clear(); } else { mCurrentServer = mServers.front().name; const QModelIndex modelIndex(index(0, 0)); emit dataChanged(modelIndex, modelIndex); } } } int ServersModel::serverRow(const QString& name) const { for (size_t i = 0, max = mServers.size(); i < max; ++i) { if (mServers[i].name == name) { return static_cast(i); } } return -1; } } tremotesf-2.10.1/src/ui/screens/connectionsettings/serversmodel.h000066400000000000000000000026011523411710200252470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SERVERSMODEL_H #define TREMOTESF_SERVERSMODEL_H #include #include #include "rpc/servers.h" namespace tremotesf { class ServersModel final : public QAbstractListModel { Q_OBJECT public: explicit ServersModel(QObject* parent = nullptr); QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; int rowCount(const QModelIndex& = {}) const override; bool setData(const QModelIndex& modelIndex, const QVariant& value, int role = Qt::EditRole) override; const std::vector& servers() const; const QString& currentServerName() const; bool hasServer(const QString& name) const; void setServer( const QString& oldName, const QString& name, ConnectionConfiguration connectionConfiguration, std::vector mountedDirectories ); void removeServerAtIndex(const QModelIndex& index); void removeServerAtRow(int row); private: int serverRow(const QString& name) const; std::vector mServers; QString mCurrentServer; }; } #endif // TREMOTESF_SERVERSMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/000077500000000000000000000000001523411710200206215ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/screens/mainwindow/alltrackersmodel.cpp000066400000000000000000000103021523411710200246510ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "alltrackersmodel.h" #include #include #include #include "rpc/torrent.h" #include "rpc/tracker.h" #include "rpc/rpc.h" #include "ui/itemmodels/modelutils.h" #include "torrentsproxymodel.h" #include "desktoputils.h" using namespace Qt::StringLiterals; namespace tremotesf { QVariant AllTrackersModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } const TrackerItem& item = mTrackers.at(static_cast(index.row())); switch (role) { case TrackerRole: return item.tracker; case Qt::DecorationRole: { if (item.tracker.isEmpty()) { return desktoputils::standardDirIcon(); } static const auto networkServerIcon = QIcon::fromTheme("network-server"_L1); return networkServerIcon; } case Qt::DisplayRole: case Qt::ToolTipRole: if (item.tracker.isEmpty()) { //: Filter option of torrents list's tracker filter. %L1 is total number of torrents return qApp->translate("tremotesf", "All (%L1)").arg(item.torrents); } //: Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker return qApp->translate("tremotesf", "%1 (%L2)").arg(item.tracker).arg(item.torrents); default: return {}; } } int AllTrackersModel::rowCount(const QModelIndex&) const { return static_cast(mTrackers.size()); } QModelIndex AllTrackersModel::indexForTorrentsProxyModelFilter() const { if (!torrentsProxyModel()) { return {}; } const auto filter = torrentsProxyModel()->trackerFilter(); for (size_t i = 0, max = mTrackers.size(); i < max; ++i) { const auto& item = mTrackers[i]; if (item.tracker == filter) { return index(static_cast(i)); } } return {}; } void AllTrackersModel::resetTorrentsProxyModelFilter() const { if (torrentsProxyModel()) { torrentsProxyModel()->setTrackerFilter({}); } } class AllTrackersModelUpdater : public ModelListUpdater> { public: inline explicit AllTrackersModelUpdater(AllTrackersModel& model) : ModelListUpdater(model) {} protected: std::map::iterator findNewItemForItem(std::map& newTrackers, const AllTrackersModel::TrackerItem& tracker) override { return newTrackers.find(tracker.tracker); } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) bool updateItem(AllTrackersModel::TrackerItem& tracker, std::pair&& newTracker) override { const auto& [site, torrents] = newTracker; if (tracker.torrents != torrents) { tracker.torrents = torrents; return true; } return false; } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) AllTrackersModel::TrackerItem createItemFromNewItem(std::pair&& newTracker) override { return AllTrackersModel::TrackerItem{.tracker = newTracker.first, .torrents = newTracker.second}; } }; void AllTrackersModel::update() { std::map trackers; trackers.emplace(QString(), rpc()->torrentsCount()); for (const auto& torrent : rpc()->torrents()) { for (const Tracker& tracker : torrent->data().trackers) { const QString& site = tracker.site(); auto found = trackers.find(site); if (found == trackers.end()) { trackers.emplace(site, 1); } else { ++(found->second); } } } AllTrackersModelUpdater updater(*this); updater.update(mTrackers, std::move(trackers)); } } tremotesf-2.10.1/src/ui/screens/mainwindow/alltrackersmodel.h000066400000000000000000000020741523411710200243250ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_ALLTRACKERSMODEL_H #define TREMOTESF_ALLTRACKERSMODEL_H #include #include "basetorrentsfilterssettingsmodel.h" namespace tremotesf { class AllTrackersModel final : public BaseTorrentsFiltersSettingsModel { Q_OBJECT public: static constexpr auto TrackerRole = Qt::UserRole; inline explicit AllTrackersModel(QObject* parent = nullptr) : BaseTorrentsFiltersSettingsModel(parent) {}; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& = {}) const override; QModelIndex indexForTorrentsProxyModelFilter() const override; struct TrackerItem { QString tracker; int torrents; }; protected: void resetTorrentsProxyModelFilter() const override; private: void update() override; std::vector mTrackers; }; } #endif // TREMOTESF_ALLTRACKERSMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/basetorrentsfilterssettingsmodel.cpp000066400000000000000000000031351523411710200302350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "basetorrentsfilterssettingsmodel.h" #include #include "rpc/rpc.h" namespace tremotesf { Rpc* BaseTorrentsFiltersSettingsModel::rpc() const { return mRpc; } void BaseTorrentsFiltersSettingsModel::setRpc(Rpc* rpc) { if (rpc != mRpc.data()) { if (const auto oldRpc = mRpc.data(); oldRpc) { QObject::disconnect(oldRpc, nullptr, this, nullptr); } mRpc = rpc; if (rpc) { QObject::connect(rpc, &Rpc::torrentsUpdated, this, &BaseTorrentsFiltersSettingsModel::updateImpl); QTimer::singleShot(0, this, &BaseTorrentsFiltersSettingsModel::updateImpl); } } } TorrentsProxyModel* BaseTorrentsFiltersSettingsModel::torrentsProxyModel() const { return mTorrentsProxyModel; } void BaseTorrentsFiltersSettingsModel::setTorrentsProxyModel(TorrentsProxyModel* model) { mTorrentsProxyModel = model; } bool BaseTorrentsFiltersSettingsModel::isPopulated() const { return mPopulated; } void BaseTorrentsFiltersSettingsModel::updateImpl() { bool populatedChanged = false; if (mPopulated != mRpc->isConnected()) { mPopulated = mRpc->isConnected(); populatedChanged = true; } update(); if (mPopulated && !indexForTorrentsProxyModelFilter().isValid()) { resetTorrentsProxyModelFilter(); } if (populatedChanged) { emit this->populatedChanged(); } } } tremotesf-2.10.1/src/ui/screens/mainwindow/basetorrentsfilterssettingsmodel.h000066400000000000000000000026611523411710200277050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_BASETORRENTSFILTERSSETTINGSMODEL_H #define TREMOTESF_BASETORRENTSFILTERSSETTINGSMODEL_H #include #include namespace tremotesf { class Rpc; class TorrentsProxyModel; class BaseTorrentsFiltersSettingsModel : public QAbstractListModel { Q_OBJECT public: inline explicit BaseTorrentsFiltersSettingsModel(QObject* parent = nullptr) : QAbstractListModel(parent) {}; Rpc* rpc() const; void setRpc(Rpc* rpc); TorrentsProxyModel* torrentsProxyModel() const; void setTorrentsProxyModel(TorrentsProxyModel* model); bool isPopulated() const; virtual QModelIndex indexForTorrentsProxyModelFilter() const = 0; // Needed for ModelListUpdater using QAbstractItemModel::beginInsertRows; using QAbstractItemModel::beginRemoveRows; using QAbstractItemModel::endInsertRows; using QAbstractItemModel::endRemoveRows; protected: virtual void update() = 0; virtual void resetTorrentsProxyModelFilter() const = 0; private: void updateImpl(); QPointer mRpc{}; TorrentsProxyModel* mTorrentsProxyModel{}; bool mPopulated = false; signals: void populatedChanged(); }; } #endif // TREMOTESF_BASETORRENTSFILTERSSETTINGSMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/downloaddirectoriesmodel.cpp000066400000000000000000000130011523411710200264050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "downloaddirectoriesmodel.h" #include #include #include #include "rpc/pathutils.h" #include "rpc/rpc.h" #include "rpc/torrent.h" #include "rpc/serversettings.h" #include "ui/itemmodels/modelutils.h" #include "ui/screens/mainwindow/downloaddirectoriesmodel.h" #include "desktoputils.h" #include "settings.h" #include "torrentsproxymodel.h" namespace tremotesf { DownloadDirectoriesModel::DownloadDirectoriesModel(QObject* parent) : BaseTorrentsFiltersSettingsModel(parent) { const auto settings = Settings::instance(); mDisplayFullDownloadDirectoryPath = settings->get_displayFullDownloadDirectoryPath(); QObject::connect(settings, &Settings::displayFullDownloadDirectoryPathChanged, this, [this, settings] { mDisplayFullDownloadDirectoryPath = settings->get_displayFullDownloadDirectoryPath(); }); } QVariant DownloadDirectoriesModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } const DirectoryItem& item = mDirectories.at(static_cast(index.row())); switch (role) { case static_cast(Role::Directory): return item.directory; case Qt::DecorationRole: { return desktoputils::standardDirIcon(); } case Qt::DisplayRole: { if (item.directory.isEmpty()) { //: Filter option of torrents list's download directory filter. %L1 is total number of torrents return qApp->translate("tremotesf", "All (%L1)").arg(item.torrents); } //: Filter option of torrents list's download directory filter. %1 is download directory, %L2 is number of torrents with that download directory const auto& text = mDisplayFullDownloadDirectoryPath ? item.displayDirectory : lastPathSegment(item.directory); return qApp->translate("tremotesf", "%1 (%L2)").arg(text).arg(item.torrents); } case Qt::ToolTipRole: if (item.directory.isEmpty()) { return data(index, Qt::DisplayRole); } return item.displayDirectory; default: return {}; } } int DownloadDirectoriesModel::rowCount(const QModelIndex&) const { return static_cast(mDirectories.size()); } QModelIndex DownloadDirectoriesModel::indexForTorrentsProxyModelFilter() const { if (!torrentsProxyModel()) { return {}; } const auto filter = torrentsProxyModel()->downloadDirectoryFilter(); for (size_t i = 0, max = mDirectories.size(); i < max; ++i) { const auto& item = mDirectories[i]; if (item.directory == filter) { return index(static_cast(i)); } } return {}; } void DownloadDirectoriesModel::resetTorrentsProxyModelFilter() const { if (torrentsProxyModel()) { torrentsProxyModel()->setDownloadDirectoryFilter({}); } } class DownloadDirectoriesModelUpdater final : public ModelListUpdater< DownloadDirectoriesModel, DownloadDirectoriesModel::DirectoryItem, std::vector> { public: inline explicit DownloadDirectoriesModelUpdater(DownloadDirectoriesModel& model) : ModelListUpdater(model) {} protected: std::vector::iterator findNewItemForItem( std::vector& newItems, const DownloadDirectoriesModel::DirectoryItem& item ) override { return std::ranges::find(newItems, item.directory, &DownloadDirectoriesModel::DirectoryItem::directory); } bool updateItem( DownloadDirectoriesModel::DirectoryItem& item, // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) DownloadDirectoriesModel::DirectoryItem&& newItem ) override { if (item.torrents != newItem.torrents) { item.torrents = newItem.torrents; return true; } return false; } DownloadDirectoriesModel::DirectoryItem createItemFromNewItem(DownloadDirectoriesModel::DirectoryItem&& newItem) override { return DownloadDirectoriesModel::DirectoryItem{std::move(newItem)}; } }; void DownloadDirectoriesModel::update() { std::vector directories; directories.push_back({.torrents = rpc()->torrentsCount()}); for (const auto& torrent : rpc()->torrents()) { const QString& directory = torrent->data().downloadDirectory; auto found = std::ranges::find(directories, directory, &DirectoryItem::directory); if (found == directories.end()) { directories.push_back( {.directory = directory, .displayDirectory = toNativeSeparators(directory, rpc()->serverSettings()->data().pathOs), .torrents = 1} ); } else { ++(found->torrents); } } DownloadDirectoriesModelUpdater updater(*this); updater.update(mDirectories, std::move(directories)); } } tremotesf-2.10.1/src/ui/screens/mainwindow/downloaddirectoriesmodel.h000066400000000000000000000022241523411710200260570ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_DOWNLOADDIRECTORIESMODEL_H #define TREMOTESF_DOWNLOADDIRECTORIESMODEL_H #include #include "basetorrentsfilterssettingsmodel.h" namespace tremotesf { class DownloadDirectoriesModel final : public BaseTorrentsFiltersSettingsModel { Q_OBJECT public: enum class Role { Directory = Qt::UserRole }; explicit DownloadDirectoriesModel(QObject* parent = nullptr); QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& = {}) const override; QModelIndex indexForTorrentsProxyModelFilter() const override; struct DirectoryItem { QString directory{}; QString displayDirectory{}; int torrents{}; }; protected: void resetTorrentsProxyModelFilter() const override; private: void update() override; std::vector mDirectories{}; bool mDisplayFullDownloadDirectoryPath{}; }; } #endif // TREMOTESF_DOWNLOADDIRECTORIESMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/downloaddirectorydelegate.cpp000066400000000000000000000016141523411710200265560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "downloaddirectorydelegate.h" #include "settings.h" namespace tremotesf { DownloadDirectoryDelegate::DownloadDirectoryDelegate(QObject* parent) : TooltipWhenElidedDelegate(parent) { const auto settings = Settings::instance(); mAlwaysShowTooltip = !settings->get_displayFullDownloadDirectoryPath(); QObject::connect(settings, &Settings::displayFullDownloadDirectoryPathChanged, this, [this, settings] { mAlwaysShowTooltip = !settings->get_displayFullDownloadDirectoryPath(); }); } void DownloadDirectoryDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const { TooltipWhenElidedDelegate::initStyleOption(option, index); option->textElideMode = Qt::ElideMiddle; } } // namespace tremotesf tremotesf-2.10.1/src/ui/screens/mainwindow/downloaddirectorydelegate.h000066400000000000000000000012501523411710200262170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_DOWNLOADDIRECTORYDELEGATE_H #define TREMOTESF_DOWNLOADDIRECTORYDELEGATE_H #include "ui/widgets/tooltipwhenelideddelegate.h" namespace tremotesf { class DownloadDirectoryDelegate : public TooltipWhenElidedDelegate { Q_OBJECT public: explicit DownloadDirectoryDelegate(QObject* parent = nullptr); // QStyledItemDelegate interface protected: void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override; }; } // namespace tremotesf #endif // TREMOTESF_DOWNLOADDIRECTORYDELEGATE_H tremotesf-2.10.1/src/ui/screens/mainwindow/editlabelsdialog.cpp000066400000000000000000000040261523411710200246170ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include "editlabelsdialog.h" #include "rpc/rpc.h" #include "rpc/torrent.h" #include "ui/widgets/editlabelswidget.h" namespace tremotesf { EditLabelsDialog::EditLabelsDialog(const std::vector& selectedTorrents, Rpc* rpc, QWidget* parent) : QDialog(parent) { setWindowTitle(qApp->translate("tremotesf", "Edit Labels")); auto layout = new QVBoxLayout(this); auto enabledLabels = selectedTorrents.at(0)->data().labels; if (!enabledLabels.empty() && selectedTorrents.size() > 1) { for (const Torrent* torrent : std::views::drop(selectedTorrents, 1)) { if (torrent->data().labels != enabledLabels) { enabledLabels.clear(); break; } } } auto editLabelsWidget = new EditLabelsWidget(enabledLabels, rpc, this); layout->addWidget(editLabelsWidget); auto torrentIds = selectedTorrents | std::views::transform([](Torrent* t) { return t->data().id; }) | std::ranges::to(); const auto saveLabels = [=, torrentIds = std::move(torrentIds)] { rpc->setTorrentsLabels(torrentIds, editLabelsWidget->enabledLabels()); }; auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); layout->addWidget(dialogButtonBox); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, this, [=, this] { if (!editLabelsWidget->comboBoxHasFocus()) { saveLabels(); accept(); } }); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); editLabelsWidget->setFocusOnComboBox(); resize(sizeHint().expandedTo(QSize(512, 0))); } } tremotesf-2.10.1/src/ui/screens/mainwindow/editlabelsdialog.h000066400000000000000000000007701523411710200242660ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_EDITLABELSDIALOG_H #define TREMOTESF_EDITLABELSDIALOG_H #include #include namespace tremotesf { class Rpc; class Torrent; class EditLabelsDialog : public QDialog { Q_OBJECT public: explicit EditLabelsDialog(const std::vector& selectedTorrents, Rpc* rpc, QWidget* parent); }; } #endif // TREMOTESF_EDITLABELSDIALOG_H tremotesf-2.10.1/src/ui/screens/mainwindow/labelsmodel.cpp000066400000000000000000000102231523411710200236060ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "labelsmodel.h" #include #include #include #include "rpc/rpc.h" #include "rpc/serversettings.h" #include "rpc/torrent.h" #include "ui/itemmodels/modelutils.h" #include "torrentsproxymodel.h" #include "desktoputils.h" using namespace Qt::StringLiterals; namespace tremotesf { QVariant LabelsModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } const LabelItem& item = mLabels.at(static_cast(index.row())); switch (role) { case LabelRole: return item.label; case Qt::DecorationRole: { if (item.label.isEmpty()) { return desktoputils::standardDirIcon(); } static const auto tagIcon = QIcon::fromTheme("tag"_L1); return tagIcon; } case Qt::DisplayRole: case Qt::ToolTipRole: if (item.label.isEmpty()) { //: Filter option of torrents list's label filter. %L1 is total number of torrents return qApp->translate("tremotesf", "All (%L1)").arg(item.torrents); } //: Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label return qApp->translate("tremotesf", "%1 (%L2)").arg(item.label).arg(item.torrents); default: return {}; } } int LabelsModel::rowCount(const QModelIndex&) const { return static_cast(mLabels.size()); } QModelIndex LabelsModel::indexForTorrentsProxyModelFilter() const { if (!torrentsProxyModel()) { return {}; } const auto filter = torrentsProxyModel()->labelFilter(); for (size_t i = 0, max = mLabels.size(); i < max; ++i) { const auto& item = mLabels[i]; if (item.label == filter) { return index(static_cast(i)); } } return {}; } void LabelsModel::resetTorrentsProxyModelFilter() const { if (torrentsProxyModel()) { torrentsProxyModel()->setLabelFilter({}); } } class LabelsModelUpdater : public ModelListUpdater> { public: inline explicit LabelsModelUpdater(LabelsModel& model) : ModelListUpdater(model) {} protected: std::map::iterator findNewItemForItem(std::map& newLabels, const LabelsModel::LabelItem& item) override { return newLabels.find(item.label); } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) bool updateItem(LabelsModel::LabelItem& item, std::pair&& newItem) override { const auto& [label, torrents] = newItem; if (item.torrents != torrents) { item.torrents = torrents; return true; } return false; } // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) LabelsModel::LabelItem createItemFromNewItem(std::pair&& newItem) override { return LabelsModel::LabelItem{.label = newItem.first, .torrents = newItem.second}; } }; void LabelsModel::update() { std::map labels{}; const bool serverSupportsLabels = rpc()->isConnected() ? rpc()->serverSettings()->data().hasLabelsProperty() : true; if (serverSupportsLabels) { labels.emplace(QString(), rpc()->torrentsCount()); for (const auto& torrent : rpc()->torrents()) { for (const QString& label : torrent->data().labels) { auto found = labels.find(label); if (found == labels.end()) { labels.emplace(label, 1); } else { ++(found->second); } } } } LabelsModelUpdater updater(*this); updater.update(mLabels, std::move(labels)); } } tremotesf-2.10.1/src/ui/screens/mainwindow/labelsmodel.h000066400000000000000000000020311523411710200232510ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_LABELSMODEL_H #define TREMOTESF_LABELSMODEL_H #include #include "basetorrentsfilterssettingsmodel.h" namespace tremotesf { class LabelsModel final : public BaseTorrentsFiltersSettingsModel { Q_OBJECT public: static constexpr auto LabelRole = Qt::UserRole; inline explicit LabelsModel(QObject* parent = nullptr) : BaseTorrentsFiltersSettingsModel(parent) {}; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& = {}) const override; QModelIndex indexForTorrentsProxyModelFilter() const override; struct LabelItem { QString label; int torrents; }; protected: void resetTorrentsProxyModelFilter() const override; private: void update() override; std::vector mLabels; }; } #endif // TREMOTESF_LABELSMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindow.cpp000066400000000000000000002244601523411710200235110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // SPDX-FileCopyrightText: 2021 LuK1337 // // SPDX-License-Identifier: GPL-3.0-or-later #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TREMOTESF_UNIX_FREEDESKTOP # include # include # include "unixhelpers.h" #endif #include "log/log.h" #include "rpc/serverstats.h" #include "rpc/mounteddirectoriesutils.h" #include "rpc/torrent.h" #include "rpc/servers.h" #include "ui/savewindowstatedispatcher.h" #include "ui/stylehelpers.h" #include "ui/screens/aboutdialog.h" #include "ui/screens/addtorrent/addtorrentdialog.h" #include "ui/screens/addtorrent/addtorrenthelpers.h" #include "ui/screens/connectionsettings/servereditdialog.h" #include "ui/screens/connectionsettings/connectionsettingsdialog.h" #include "ui/screens/serversettings/serversettingsdialog.h" #include "ui/screens/serverstatsdialog.h" #include "ui/screens/settingsdialog.h" #include "ui/screens/torrentproperties/torrentpropertiesdialog.h" #include "ui/screens/torrentproperties/torrentpropertieswidget.h" #include "ui/widgets/listplaceholder.h" #include "ui/widgets/torrentremotedirectoryselectionwidget.h" #include "ui/widgets/torrentfilesview.h" #include "desktoputils.h" #include "editlabelsdialog.h" #include "filemanagerlauncher.h" #include "formatutils.h" #include "macoshelpers.h" #include "mainwindowsidebar.h" #include "mainwindowstatusbar.h" #include "mainwindowviewmodel.h" #include "settings.h" #include "target_os.h" #include "torrentsmodel.h" #include "torrentsproxymodel.h" #include "torrentsview.h" using namespace Qt::StringLiterals; SPECIALIZE_FORMATTER_FOR_QDEBUG(QRect) namespace tremotesf { namespace { class SetLocationDialog final : public QDialog { Q_OBJECT public: explicit SetLocationDialog(const QString& downloadDirectory, Rpc* rpc, QWidget* parent = nullptr) : QDialog(parent), mDirectoryWidget(new TorrentDownloadDirectoryDirectorySelectionWidget(this)), mMoveFilesCheckBox( //: Check box label new QCheckBox(qApp->translate("tremotesf", "Move files from current directory"), this) ) { //: Dialog title for changing torrent's download directory setWindowTitle(qApp->translate("tremotesf", "Set Location")); auto layout = new QVBoxLayout(this); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); auto label = new QLabel(qApp->translate("tremotesf", "Download directory:"), this); label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); layout->addWidget(label); mDirectoryWidget->setup(downloadDirectory, rpc); mDirectoryWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); layout->addWidget(mDirectoryWidget); mMoveFilesCheckBox->setChecked(true); layout->addWidget(mMoveFilesCheckBox); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, this, [this] { accept(); }); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &SetLocationDialog::reject); QObject::connect( mDirectoryWidget, &RemoteDirectorySelectionWidget::pathChanged, this, [dialogButtonBox, this] { dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(!mDirectoryWidget->path().isEmpty()); } ); layout->addWidget(dialogButtonBox); QObject::connect(rpc, &Rpc::connectedChanged, this, [rpc, this] { if (!rpc->isConnected()) { reject(); } }); resize(sizeHint().expandedTo(QSize(720, 0))); } [[nodiscard]] QString downloadDirectory() const { return mDirectoryWidget->path(); } [[nodiscard]] bool moveFiles() const { return mMoveFilesCheckBox->isChecked(); } private: TorrentDownloadDirectoryDirectorySelectionWidget* const mDirectoryWidget; QCheckBox* const mMoveFilesCheckBox; }; #ifndef Q_OS_MACOS bool isAllowedToHide(const QWidget* window) { static constexpr std::array classNames{ // Managed by QFileDialog "KDEPlatformFileDialog"_L1, "KDirSelectDialog"_L1, // Managed by QSystemTrayIcon "QSystemTrayIconSys"_L1 }; auto* const metaObject = window->metaObject(); return metaObject && !std::ranges::contains(classNames, QLatin1String(metaObject->className())); } [[nodiscard]] std::vector> toQPointers(const QWidgetList& widgets) { return {widgets.begin(), widgets.end()}; } #endif void unminimizeAndRaiseWindow(QWidget* window) { if (window->isMinimized()) { info().log("Unminimizing window {}", *window); window->setWindowState(window->windowState().setFlag(Qt::WindowMinimized, false)); } info().log("Raising window {}", *window); window->raise(); } void consumeActivationToken( [[maybe_unused]] QWidget* window, [[maybe_unused]] std::optional& windowActivationToken ) { #ifdef TREMOTESF_UNIX_FREEDESKTOP if (!windowActivationToken.has_value()) { return; } switch (KWindowSystem::platform()) { case KWindowSystem::Platform::X11: debug().log("Windowing system is X11"); info().log("Removing startup notification with id {}", *windowActivationToken); KStartupInfo::setNewStartupId(window->windowHandle(), *windowActivationToken); KStartupInfo::appStarted(*windowActivationToken); break; case KWindowSystem::Platform::Wayland: debug().log("Windowing system is Wayland"); info().log("Activating window with token {}", *windowActivationToken); // Qt gets new token from XDG_ACTIVATION_TOKEN environment variable // It wll be read and unset in QWidget::activateWindow() or QWidget::show() qputenv(xdgActivationTokenEnvVariable, *windowActivationToken); break; default: warning().log("Unknown windowing system"); break; } windowActivationToken.reset(); #endif } } class MainWindow::Impl : public QObject { Q_OBJECT public: explicit Impl(QStringList&& commandLineFiles, QStringList&& commandLineUrls, MainWindow* window) : mWindow(window), mViewModel{std::move(commandLineFiles), std::move(commandLineUrls)} { mHorizontalSplitter.setChildrenCollapsible(false); if (!Settings::instance()->get_sideBarVisible()) { mSideBar.hide(); } mHorizontalSplitter.addWidget(&mSideBar); mHorizontalSplitter.addWidget(&mVerticalSplitter); mHorizontalSplitter.setStretchFactor(1, 1); mVerticalSplitter.setChildrenCollapsible(false); mVerticalSplitter.setOrientation(Qt::Vertical); mVerticalSplitter.addWidget(&mTorrentsView); mVerticalSplitter.setStretchFactor(0, 1); QObject::connect(&mTorrentsView, &TorrentsView::customContextMenuRequested, this, [this](QPoint pos) { if (mTorrentsView.indexAt(pos).isValid()) { mTorrentMenu->popup(mTorrentsView.viewport()->mapToGlobal(pos)); } }); QObject::connect( &mTorrentsView, &TorrentsView::activated, this, &MainWindow::Impl::performTorrentDoubleClickAction ); QObject::connect(mViewModel.rpc(), &Rpc::connectedChanged, this, [this] { if (mViewModel.rpc()->isConnected() && mTorrentsProxyModel.rowCount() > 0) { mTorrentsView.setCurrentIndex(mTorrentsProxyModel.index(0, 0)); } }); setupTorrentsPlaceholder(); setupTorrentPropertiesWidget(); mHorizontalSplitter.restoreState(Settings::instance()->get_horizontalSplitterState()); mVerticalSplitter.restoreState(Settings::instance()->get_verticalSplitterState()); mWindow->setCentralWidget(&mHorizontalSplitter); auto* const statusBar = new MainWindowStatusBar(mViewModel.rpc()); mWindow->setStatusBar(statusBar); if (!Settings::instance()->get_statusBarVisible()) { statusBar->hide(); } QObject::connect(statusBar, &MainWindowStatusBar::showConnectionSettingsDialog, this, [this] { showSingleInstanceDialog([this] { return new ConnectionSettingsDialog(mWindow); }); }); setupActions(); updateTorrentActions(); QObject::connect(mViewModel.rpc(), &Rpc::torrentsUpdated, this, &MainWindow::Impl::updateTorrentActions); QObject::connect( mTorrentsView.selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::Impl::updateTorrentActions ); setupMenuBar(); setupToolBar(); setupTrayIcon(); mViewModel.setupNotificationsController(&mTrayIcon); updateRpcActions(); QObject::connect(mViewModel.rpc(), &Rpc::connectionStateChanged, this, &MainWindow::Impl::updateRpcActions); QObject::connect( Servers::instance(), &Servers::hasServersChanged, this, &MainWindow::Impl::updateRpcActions ); mWindow->restoreState(Settings::instance()->get_mainWindowState()); mToolBarAction->setChecked(!mToolBar.isHidden()); QObject::connect( &mViewModel, &MainWindowViewModel::showWindow, this, &MainWindow::Impl::showWindowsOrActivateMainWindow ); QObject::connect( &mViewModel, &MainWindowViewModel::showAddTorrentDialogs, this, &MainWindow::Impl::showAddTorrentDialogs ); QObject::connect( &mViewModel, &MainWindowViewModel::askForMergingTrackers, this, &MainWindow::Impl::askForMergingTrackers ); QObject::connect( &mViewModel, &MainWindowViewModel::showDelayedTorrentAddDialog, this, &MainWindow::Impl::showDelayedTorrentAddDialog ); showAddTorrentErrors(); auto pasteShortcut = new QShortcut(QKeySequence::Paste, mWindow); QObject::connect( pasteShortcut, &QShortcut::activated, &mViewModel, &MainWindowViewModel::pasteShortcutActivated ); if (mViewModel.performStartupAction() == MainWindowViewModel::StartupActionResult::ShowAddServerDialog) { QMetaObject::invokeMethod( this, [this] { auto* const dialog = new ServerEditDialog(nullptr, -1, mWindow); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); }, Qt::QueuedConnection ); } QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, [this] { mViewModel.rpc()->disconnect(); mTrayIcon.hide(); }); if constexpr (targetOs == TargetOs::UnixMacOS) { QObject::connect( qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::Impl::updateShowHideAction ); } info().log("Restoring main window geometry"); if (!mWindow->restoreGeometry(Settings::instance()->get_mainWindowGeometry())) { info().log("Did not restore geometry"); mWindow->resize(mWindow->sizeHint().expandedTo(QSize(896, 640))); } else { info().log("Restored geometry {}", mWindow->geometry()); } } ~Impl() override = default; Q_DISABLE_COPY_MOVE(Impl) void updateShowHideAction() { QObject::disconnect(&mShowHideAppAction, &QAction::triggered, nullptr, nullptr); if (isMainWindowHiddenOrMinimized()) { mShowHideAppAction.setText(qApp->translate("tremotesf", "&Show Tremotesf")); QObject::connect(&mShowHideAppAction, &QAction::triggered, this, [this] { showWindowsOrActivateMainWindow(); }); } else { mShowHideAppAction.setText(qApp->translate("tremotesf", "&Hide Tremotesf")); QObject::connect(&mShowHideAppAction, &QAction::triggered, this, [this] { hideWindows(); }); } } /** * @return true if event should be ignored, false otherwise */ bool onCloseEvent() { if (mAppQuitEventFilter.isQuittingApplication) { debug().log("Received close event on main window while quitting app, just close window"); return false; } // Do stuff at the next event loop iteration since we are in the middle of event handling if (mTrayIcon.isVisible() && QSystemTrayIcon::isSystemTrayAvailable()) { info().log("Closed main window but tray icon is active, hide windows without quitting app"); QMetaObject::invokeMethod(this, &MainWindow::Impl::hideWindows, Qt::QueuedConnection); return true; } info().log("Closed main window when tray icon is not active, quitting app"); QMetaObject::invokeMethod(qApp, &QCoreApplication::quit, Qt::QueuedConnection); return false; } void onDragEnterEvent(QDragEnterEvent* event) { MainWindowViewModel::processDragEnterEvent(event); } void onDropEvent(QDropEvent* event) { mViewModel.processDropEvent(event); } void saveState() { debug().log("Saving MainWindow state, window geometry is {}", mWindow->geometry()); Settings::instance()->set_mainWindowGeometry(mWindow->saveGeometry()); Settings::instance()->set_mainWindowState(mWindow->saveState()); Settings::instance()->set_horizontalSplitterState(mHorizontalSplitter.saveState()); Settings::instance()->set_verticalSplitterState(mVerticalSplitter.saveState()); mTorrentsView.saveState(); if (mTorrentPropertiesWidget) { mTorrentPropertiesWidget->saveState(); } } private: MainWindow* mWindow; MainWindowViewModel mViewModel; QSplitter mHorizontalSplitter{}; QSplitter mVerticalSplitter{}; TorrentsModel mTorrentsModel{mViewModel.rpc()}; TorrentsProxyModel mTorrentsProxyModel{&mTorrentsModel}; TorrentsView mTorrentsView{&mTorrentsProxyModel}; TorrentPropertiesWidget* mTorrentPropertiesWidget{}; MainWindowSideBar mSideBar{mViewModel.rpc(), &mTorrentsProxyModel}; std::unordered_map mTorrentPropertiesDialogs{}; QAction mShowHideAppAction{}; //: Button / menu item to connect to server QAction mConnectAction{qApp->translate("tremotesf", "&Connect")}; //: Button / menu item to disconnect from server QAction mDisconnectAction{qApp->translate("tremotesf", "&Disconnect")}; QAction mAddTorrentFileAction{ QIcon::fromTheme("list-add"_L1), //: Menu item qApp->translate("tremotesf", "&Add Torrent File...") }; QAction mAddTorrentLinkAction{ QIcon::fromTheme("insert-link"_L1), //: Menu item qApp->translate("tremotesf", "Add Torrent &Link..."), }; QMenu* mTorrentMenu{}; QAction* mStartTorrentAction{}; QAction* mStartTorrentNowAction{}; QAction* mPauseTorrentAction{}; QAction* mRemoveTorrentAction{}; QAction* mRenameTorrentAction{}; QAction* mOpenTorrentFilesAction{}; QAction* mOpenTorrentDownloadDirectoryAction{}; QAction* mHighPriorityAction{}; QAction* mNormalPriorityAction{}; QAction* mLowPriorityAction{}; std::vector mConnectionDependentActions; QMenu* mFileMenu{}; QToolBar mToolBar{}; QAction* mToolBarAction{}; QSystemTrayIcon mTrayIcon{QIcon::fromTheme("tremotesf-tray-icon"_L1, mWindow->windowIcon())}; #ifndef Q_OS_MACOS std::vector> mOtherWindowsHiddenByUs; #endif SaveWindowStateHandler mSaveStateHandler{mWindow, [this] { saveState(); }}; ApplicationQuitEventFilter mAppQuitEventFilter{}; void setupActions() { updateShowHideAction(); QObject::connect(&mConnectAction, &QAction::triggered, mViewModel.rpc(), &Rpc::connect); QObject::connect(&mDisconnectAction, &QAction::triggered, mViewModel.rpc(), &Rpc::disconnect); const auto connectIcon = QIcon::fromTheme("network-connect"_L1); const auto disconnectIcon = QIcon::fromTheme("network-disconnect"_L1); if (connectIcon.name() != disconnectIcon.name()) { mConnectAction.setIcon(connectIcon); mDisconnectAction.setIcon(disconnectIcon); } mAddTorrentFileAction.setShortcuts(QKeySequence::Open); QObject::connect(&mAddTorrentFileAction, &QAction::triggered, this, &MainWindow::Impl::openTorrentFiles); mConnectionDependentActions.push_back(&mAddTorrentFileAction); QObject::connect(&mAddTorrentLinkAction, &QAction::triggered, this, [this] { if (Settings::instance()->get_showMainWindowWhenAddingTorrent() && isMainWindowHiddenOrMinimized()) { showWindowsOrActivateMainWindow(); } mViewModel.triggeredAddTorrentLinkAction(); }); mConnectionDependentActions.push_back(&mAddTorrentLinkAction); // // Torrent menu // //: Menu bar item mTorrentMenu = new QMenu(qApp->translate("tremotesf", "&Torrent"), mWindow); QAction* torrentPropertiesAction = mTorrentMenu->addAction( QIcon::fromTheme("document-properties"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "&Properties") ); QObject::connect( torrentPropertiesAction, &QAction::triggered, this, &MainWindow::Impl::showTorrentsPropertiesDialogs ); torrentPropertiesAction->setVisible(!Settings::instance()->get_showTorrentPropertiesInMainWindow()); QObject::connect( Settings::instance(), &Settings::showTorrentPropertiesInMainWindowChanged, this, [torrentPropertiesAction] { torrentPropertiesAction->setVisible(!Settings::instance()->get_showTorrentPropertiesInMainWindow()); } ); mTorrentMenu->addSeparator(); mStartTorrentAction = mTorrentMenu->addAction( QIcon::fromTheme("media-playback-start"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "&Start") ); QObject::connect(mStartTorrentAction, &QAction::triggered, this, [this] { mViewModel.rpc()->startTorrents(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); mStartTorrentNowAction = mTorrentMenu->addAction( QIcon::fromTheme("media-playback-start"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Start &Now") ); QObject::connect(mStartTorrentNowAction, &QAction::triggered, this, [this] { mViewModel.rpc()->startTorrentsNow(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); mPauseTorrentAction = mTorrentMenu->addAction( QIcon::fromTheme("media-playback-pause"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "P&ause") ); QObject::connect(mPauseTorrentAction, &QAction::triggered, this, [this] { mViewModel.rpc()->pauseTorrents(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); mTorrentMenu->addSeparator(); const auto* const copyMagnetLinkAction = mTorrentMenu->addAction( QIcon::fromTheme("edit-copy"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Copy &Magnet Link") ); QObject::connect(copyMagnetLinkAction, &QAction::triggered, this, [this] { const auto indexes(mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows())); QStringList links; links.reserve(indexes.size()); for (const auto& index : indexes) { links.push_back(mTorrentsModel.torrentAtIndex(index)->data().magnetLink); } qApp->clipboard()->setText(links.join('\n')); }); mTorrentMenu->addSeparator(); mRemoveTorrentAction = mTorrentMenu->addAction( QIcon::fromTheme("edit-delete"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "&Delete") ); mRemoveTorrentAction->setShortcut(QKeySequence::Delete); QObject::connect(mRemoveTorrentAction, &QAction::triggered, this, [this] { removeSelectedTorrents(false); }); const auto removeTorrentWithFilesShortcut = new QShortcut(QKeyCombination(Qt::ShiftModifier, Qt::Key_Delete), mWindow); QObject::connect(removeTorrentWithFilesShortcut, &QShortcut::activated, this, [this] { removeSelectedTorrents(true); }); const auto* const setLocationAction = mTorrentMenu->addAction( QIcon::fromTheme("mark-location"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Set &Location") ); QObject::connect(setLocationAction, &QAction::triggered, this, [this] { if (mTorrentsView.selectionModel()->hasSelection()) { QModelIndexList indexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) ); auto dialog = new SetLocationDialog( mTorrentsModel.torrentAtIndex(indexes.first())->data().downloadDirectory, mViewModel.rpc(), mWindow ); dialog->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(dialog, &SetLocationDialog::accepted, this, [indexes, dialog, this] { mViewModel.rpc()->setTorrentsLocation( mTorrentsModel.idsFromIndexes(indexes), dialog->downloadDirectory(), dialog->moveFiles() ); }); dialog->show(); } }); mRenameTorrentAction = mTorrentMenu->addAction( QIcon::fromTheme("edit-rename"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "&Rename") ); mRenameTorrentAction->setShortcut(Qt::Key_F2); QObject::connect(mRenameTorrentAction, &QAction::triggered, this, [this] { const auto indexes = mTorrentsView.selectionModel()->selectedRows(); if (indexes.size() == 1) { const auto torrent = mTorrentsModel.torrentAtIndex(mTorrentsProxyModel.mapToSource(indexes.first())); const auto id = torrent->data().id; const auto name = torrent->data().name; TorrentFilesView::showFileRenameDialog(name, mWindow, [id, name, this](const auto& newName) { mViewModel.rpc()->renameTorrentFile(id, name, newName); }); } }); const auto* const editLabelsAction = mTorrentMenu->addAction( QIcon::fromTheme("tag"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Edi&t Labels") ); QObject::connect(editLabelsAction, &QAction::triggered, this, [this] { if (mTorrentsView.selectionModel()->hasSelection()) { const auto selectedTorrents = mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) | std::views::transform([this](const QModelIndex& index) { return mTorrentsModel.torrentAtIndex(index); }) | std::ranges::to(); auto dialog = new EditLabelsDialog(selectedTorrents, mViewModel.rpc(), mWindow); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } }); mTorrentMenu->addSeparator(); mOpenTorrentFilesAction = mTorrentMenu->addAction( QIcon::fromTheme("document-open"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "&Open") ); QObject::connect(mOpenTorrentFilesAction, &QAction::triggered, this, &MainWindow::Impl::openTorrentsFiles); mOpenTorrentDownloadDirectoryAction = mTorrentMenu->addAction( QIcon::fromTheme("go-jump"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Op&en Download Directory") ); QObject::connect( mOpenTorrentDownloadDirectoryAction, &QAction::triggered, this, &MainWindow::Impl::showTorrentsInFileManager ); mTorrentMenu->addSeparator(); const auto* const checkTorrentAction = mTorrentMenu->addAction( QIcon::fromTheme("document-preview"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "&Check Local Data") ); QObject::connect(checkTorrentAction, &QAction::triggered, this, [this] { mViewModel.rpc()->checkTorrents(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); const auto* const reannounceAction = mTorrentMenu->addAction( QIcon::fromTheme("view-refresh"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Reanno&unce") ); QObject::connect(reannounceAction, &QAction::triggered, this, [this] { mViewModel.rpc()->reannounceTorrents(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); mTorrentMenu->addSeparator(); auto* const queueMenu = mTorrentMenu->addMenu( //: Torrent's context menu item qApp->translate("tremotesf", "&Queue") ); const auto* const moveTorrentToTopAction = queueMenu->addAction( QIcon::fromTheme("go-top"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Move To &Top") ); QObject::connect(moveTorrentToTopAction, &QAction::triggered, this, [this] { mViewModel.rpc()->moveTorrentsToTop(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); const auto* const moveTorrentUpAction = queueMenu->addAction( QIcon::fromTheme("go-up"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Move &Up") ); QObject::connect(moveTorrentUpAction, &QAction::triggered, this, [this] { mViewModel.rpc()->moveTorrentsUp(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); const auto* const moveTorrentDownAction = queueMenu->addAction( QIcon::fromTheme("go-down"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Move &Down") ); QObject::connect(moveTorrentDownAction, &QAction::triggered, this, [this] { mViewModel.rpc()->moveTorrentsDown(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); const auto* const moveTorrentToBottomAction = queueMenu->addAction( QIcon::fromTheme("go-bottom"_L1), //: Torrent's context menu item qApp->translate("tremotesf", "Move To &Bottom") ); QObject::connect(moveTorrentToBottomAction, &QAction::triggered, this, [this] { mViewModel.rpc()->moveTorrentsToBottom(mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) )); }); const auto priorityMenu = mTorrentMenu->addMenu( //: Torrent's context menu item qApp->translate("tremotesf", "&Priority") ); const auto priorityGroup = new QActionGroup(priorityMenu); priorityGroup->setExclusive(true); const auto setTorrentsPriority = [this](TorrentData::Priority priority) { for (const auto& index : mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows())) { mTorrentsModel.torrentAtIndex(index)->setBandwidthPriority(priority); } }; //: Torrent's loading priority mHighPriorityAction = priorityMenu->addAction(qApp->translate("tremotesf", "High")); QObject::connect( mHighPriorityAction, &QAction::triggered, this, std::bind_front(setTorrentsPriority, TorrentData::Priority::High) ); mHighPriorityAction->setCheckable(true); priorityGroup->addAction(mHighPriorityAction); //: Torrent's loading priority mNormalPriorityAction = priorityMenu->addAction(qApp->translate("tremotesf", "Normal")); QObject::connect( mNormalPriorityAction, &QAction::triggered, this, std::bind_front(setTorrentsPriority, TorrentData::Priority::Normal) ); mNormalPriorityAction->setCheckable(true); priorityGroup->addAction(mNormalPriorityAction); //: Torrent's loading priority mLowPriorityAction = priorityMenu->addAction(qApp->translate("tremotesf", "Low")); QObject::connect( mLowPriorityAction, &QAction::triggered, this, std::bind_front(setTorrentsPriority, TorrentData::Priority::Low) ); mLowPriorityAction->setCheckable(true); priorityGroup->addAction(mLowPriorityAction); } QAction* createQuitAction() { const auto action = new QAction( QIcon::fromTheme("application-exit"_L1), //: Menu item qApp->translate("tremotesf", "&Quit"), this ); if constexpr (targetOs == TargetOs::Windows) { action->setShortcut(QKeyCombination(Qt::ControlModifier, Qt::Key_Q)); } else { action->setShortcuts(QKeySequence::Quit); } QObject::connect(action, &QAction::triggered, this, &QCoreApplication::quit); return action; } void updateRpcActions() { if (Servers::instance()->hasServers()) { const bool disconnected = mViewModel.rpc()->connectionState() == Rpc::ConnectionState::Disconnected; mConnectAction.setEnabled(disconnected); mDisconnectAction.setEnabled(!disconnected); } else { mConnectAction.setEnabled(false); mDisconnectAction.setEnabled(false); } const bool connected = mViewModel.rpc()->isConnected(); for (auto action : mConnectionDependentActions) { action->setEnabled(connected); } } void openTorrentFiles() { auto* const settings = Settings::instance(); if (settings->get_showMainWindowWhenAddingTorrent() && isMainWindowHiddenOrMinimized()) { showWindowsOrActivateMainWindow(); } auto directory = settings->get_rememberOpenTorrentDir() ? settings->get_lastOpenTorrentDirectory() : QString{}; if (directory.isEmpty()) { directory = QDir::homePath(); } auto* const fileDialog = new QFileDialog( settings->get_showMainWindowWhenAddingTorrent() ? mWindow : nullptr, //: File chooser dialog title qApp->translate("tremotesf", "Select Files"), directory, //: Torrent file type. Parentheses and text within them must remain unchanged qApp->translate("tremotesf", "Torrent Files (*.torrent)") ); fileDialog->setAttribute(Qt::WA_DeleteOnClose); fileDialog->setFileMode(QFileDialog::ExistingFiles); QObject::connect(fileDialog, &QFileDialog::accepted, this, [fileDialog, this] { mViewModel.acceptedFileDialog(fileDialog->selectedFiles()); }); if constexpr (targetOs == TargetOs::Windows) { fileDialog->open(); } else { fileDialog->show(); } } void setupTorrentPropertiesWidget() { const auto setup = [this] { if (Settings::instance()->get_showTorrentPropertiesInMainWindow()) { useTorrentPropertiesWidget(); } else { useTorrentPropertiesDialogs(); } }; setup(); QObject::connect(Settings::instance(), &Settings::showTorrentPropertiesInMainWindowChanged, this, setup); } void useTorrentPropertiesDialogs() { if (!mTorrentPropertiesWidget) { return; } mTorrentPropertiesWidget->deleteLater(); mTorrentPropertiesWidget = nullptr; } void useTorrentPropertiesWidget() { if (mTorrentPropertiesWidget) { return; } if (!mTorrentPropertiesDialogs.empty()) { // Don't iterate over mTorrentPropertiesDialogs directly since call to reject() will modify it (through QDialog::finished slot) const auto dialogs = mTorrentPropertiesDialogs; mTorrentPropertiesDialogs.clear(); for (const auto& [hashString, dialog] : dialogs) { dialog->reject(); } } mTorrentPropertiesWidget = new TorrentPropertiesWidget(mViewModel.rpc(), true, mWindow); mVerticalSplitter.addWidget(mTorrentPropertiesWidget); const auto updateCurrentTorrent = [this] { const auto currentIndex = mTorrentsView.selectionModel()->currentIndex(); if (currentIndex.isValid()) { auto source = mTorrentsProxyModel.mapToSource(currentIndex); mTorrentPropertiesWidget->setTorrent(mTorrentsModel.torrentAtIndex(source)); } else { mTorrentPropertiesWidget->setTorrent(nullptr); } }; updateCurrentTorrent(); QObject::connect( mTorrentsView.selectionModel(), &QItemSelectionModel::currentChanged, mTorrentPropertiesWidget, updateCurrentTorrent ); } void updateTorrentActions() { const auto actions = mTorrentMenu->actions(); if (!mTorrentsView.selectionModel()->hasSelection()) { for (QAction* action : actions) { action->setEnabled(false); } return; } for (QAction* action : actions) { action->setEnabled(true); } mHighPriorityAction->setChecked(false); mNormalPriorityAction->setChecked(false); mLowPriorityAction->setChecked(false); const QModelIndexList selectedRows = mTorrentsView.selectionModel()->selectedRows(); if (selectedRows.size() == 1) { const auto torrent = mTorrentsModel.torrentAtIndex(mTorrentsProxyModel.mapToSource(selectedRows.first())); if (torrent->data().status == TorrentData::Status::Paused) { mPauseTorrentAction->setEnabled(false); } else { mStartTorrentAction->setEnabled(false); mStartTorrentNowAction->setEnabled(false); } switch (torrent->data().bandwidthPriority) { case TorrentData::Priority::High: mHighPriorityAction->setChecked(true); break; case TorrentData::Priority::Normal: mNormalPriorityAction->setChecked(true); break; case TorrentData::Priority::Low: mLowPriorityAction->setChecked(true); break; } } else { mRenameTorrentAction->setEnabled(false); } bool localOrMounted = true; if (!mViewModel.rpc()->isLocal()) { for (const QModelIndex& index : selectedRows) { const auto* const torrent = mTorrentsModel.torrentAtIndex(mTorrentsProxyModel.mapToSource(index)); if (!isServerLocalOrTorrentIsMounted(mViewModel.rpc(), torrent)) { localOrMounted = false; break; } } } mOpenTorrentFilesAction->setEnabled(localOrMounted); mOpenTorrentDownloadDirectoryAction->setEnabled(localOrMounted); } void performTorrentDoubleClickAction() { switch (Settings::instance()->get_torrentDoubleClickAction()) { case Settings::TorrentDoubleClickAction::OpenPropertiesDialog: if (Settings::instance()->get_showTorrentPropertiesInMainWindow()) { warning().log( "torrentDoubleClickAction is OpenPropertiesDialog, but " "showTorrentPropertiesInMainWindow is true" ); } else { showTorrentsPropertiesDialogs(); } break; case Settings::TorrentDoubleClickAction::OpenTorrentFile: if (mOpenTorrentFilesAction->isEnabled()) { openTorrentsFiles(); } break; case Settings::TorrentDoubleClickAction::OpenDownloadDirectory: showTorrentsInFileManager(); break; default: break; } } void showTorrentsPropertiesDialogs() { const QModelIndexList selectedRows(mTorrentsView.selectionModel()->selectedRows()); for (const auto& index : selectedRows) { auto* const torrent = mTorrentsModel.torrentAtIndex(mTorrentsProxyModel.mapToSource(index)); const auto hashString = torrent->data().hashString; const auto existingDialog = mTorrentPropertiesDialogs.find(hashString); if (existingDialog != mTorrentPropertiesDialogs.end()) { unminimizeAndRaiseWindow(existingDialog->second); existingDialog->second->activateWindow(); } else { auto dialog = new TorrentPropertiesDialog(torrent, mViewModel.rpc(), mWindow); dialog->setAttribute(Qt::WA_DeleteOnClose); mTorrentPropertiesDialogs.emplace(hashString, dialog); QObject::connect(dialog, &TorrentPropertiesDialog::finished, this, [hashString, this] { mTorrentPropertiesDialogs.erase(hashString); }); dialog->show(); } } } void removeSelectedTorrents(bool deleteFiles) { const auto ids = mTorrentsModel.idsFromIndexes( mTorrentsProxyModel.sourceIndexes(mTorrentsView.selectionModel()->selectedRows()) ); if (ids.empty()) { return; } QMessageBox dialog(mWindow); dialog.setIcon(QMessageBox::Warning); dialog.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); dialog.setDefaultButton(QMessageBox::Cancel); const auto okButton = dialog.button(QMessageBox::Ok); if (dialog.style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons)) { okButton->setIcon(QIcon::fromTheme("edit-delete"_L1)); } //: Check box label QCheckBox deleteFilesCheckBox(qApp->translate("tremotesf", "Also delete the files on the hard disk")); deleteFilesCheckBox.setChecked(deleteFiles); dialog.setCheckBox(&deleteFilesCheckBox); auto setRemoveText = [&] { okButton->setText( deleteFilesCheckBox.isChecked() //: Check box label ? qApp->translate("tremotesf", "Delete with files") //: Check box label : qApp->translate("tremotesf", "Delete") ); }; setRemoveText(); QObject::connect(&deleteFilesCheckBox, &QCheckBox::toggled, this, setRemoveText); if (ids.size() == 1) { //: Dialog title dialog.setWindowTitle(qApp->translate("tremotesf", "Delete Torrent")); dialog.setText(qApp->translate("tremotesf", "Are you sure you want to delete this torrent?")); } else { //: Dialog title dialog.setWindowTitle(qApp->translate("tremotesf", "Delete Torrents")); // Don't put static_cast in qApp->translate() - lupdate doesn't like it const auto count = static_cast(ids.size()); //: %Ln is a number of torrents selected for deletion dialog.setText(qApp->translate( "tremotesf", "Are you sure you want to delete %Ln selected torrents?", nullptr, count )); } if (dialog.exec() == QMessageBox::Ok) { mViewModel.rpc()->removeTorrents(ids, deleteFilesCheckBox.checkState() == Qt::Checked); } } void setupTorrentsPlaceholder() { auto layout = new QVBoxLayout(mTorrentsView.viewport()); layout->addStretch(); auto status = createListPlaceholderLabel(); layout->addWidget(status); layout->setAlignment(status, Qt::AlignCenter); { auto font = status->font(); constexpr int minFontSize = 12; font.setPointSize(std::max(minFontSize, static_cast(std::round(font.pointSize() * 1.3)))); status->setFont(font); } auto error = createListPlaceholderLabel(); layout->addWidget(error); layout->setAlignment(error, Qt::AlignCenter); { auto font = error->font(); constexpr int minFontSize = 10; font.setPointSize(std::max(minFontSize, font.pointSize())); error->setFont(font); } layout->addStretch(); const auto updatePlaceholder = [status, error, this] { QString statusText{}; QString errorText{}; if (mViewModel.rpc()->isConnected()) { if (mTorrentsProxyModel.rowCount() == 0) { if (mTorrentsModel.rowCount() == 0) { //: Torrents list placeholder statusText = qApp->translate("tremotesf", "No torrents"); } else { //: Torrents list placeholder statusText = qApp->translate("tremotesf", "No torrents matching filters"); } } } else if (Servers::instance()->hasServers()) { statusText = mViewModel.rpc()->status().toString(); if (mViewModel.rpc()->error() != Rpc::Error::NoError) { errorText = mViewModel.rpc()->errorMessage(); } } else { statusText = qApp->translate("tremotesf", "No servers"); } status->setText(statusText); status->setVisible(!statusText.isEmpty()); error->setText(errorText); error->setVisible(!errorText.isEmpty()); }; updatePlaceholder(); QObject::connect(mViewModel.rpc(), &Rpc::statusChanged, this, updatePlaceholder); QObject::connect( &mTorrentsProxyModel, &TorrentsModel::rowsInserted, this, [updatePlaceholder, this](const QModelIndex&, int first, int last) { if ((last - first) + 1 == mTorrentsProxyModel.rowCount()) { // Model was empty updatePlaceholder(); } } ); QObject::connect(&mTorrentsProxyModel, &TorrentsModel::rowsRemoved, this, [updatePlaceholder, this] { if (mTorrentsProxyModel.rowCount() == 0) { // Model is now empty updatePlaceholder(); } }); } template Dialog, typename CreateDialogFunction> requires std::is_invocable_r_v void showSingleInstanceDialog(CreateDialogFunction createDialog) { auto existingDialog = mWindow->findChild({}, Qt::FindDirectChildrenOnly); if (existingDialog) { unminimizeAndRaiseWindow(existingDialog); existingDialog->activateWindow(); } else { auto dialog = createDialog(); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } } void setupMenuBar() { //: Menu bar item mFileMenu = mWindow->menuBar()->addMenu(qApp->translate("tremotesf", "&File")); mFileMenu->addAction(&mConnectAction); mFileMenu->addAction(&mDisconnectAction); mFileMenu->addSeparator(); mFileMenu->addAction(&mAddTorrentFileAction); mFileMenu->addAction(&mAddTorrentLinkAction); mFileMenu->addSeparator(); if constexpr (targetOs == TargetOs::UnixMacOS) { auto closeWindowAction = mFileMenu->addAction(qApp->translate("tremotesf", "&Close Window")); closeWindowAction->setShortcuts(QKeySequence::Close); QObject::connect(closeWindowAction, &QAction::triggered, this, [] { auto window = QApplication::activeWindow(); if (window) { window->close(); } }); } auto* const quitAction = createQuitAction(); quitAction->setMenuRole(QAction::QuitRole); mFileMenu->addAction(quitAction); //: Menu bar item auto* const editMenu = mWindow->menuBar()->addMenu(qApp->translate("tremotesf", "&Edit")); auto* const selectAllAction = editMenu->addAction( QIcon::fromTheme("edit-select-all"_L1), qApp->translate("tremotesf", "Select &All") ); selectAllAction->setShortcut(QKeySequence::SelectAll); QObject::connect(selectAllAction, &QAction::triggered, &mTorrentsView, &TorrentsView::selectAll); const auto* const invertSelectionAction = editMenu->addAction( QIcon::fromTheme("edit-select-invert"_L1), qApp->translate("tremotesf", "&Invert Selection") ); QObject::connect(invertSelectionAction, &QAction::triggered, this, [this] { mTorrentsView.selectionModel()->select( QItemSelection( mTorrentsProxyModel.index(0, 0), mTorrentsProxyModel .index(mTorrentsProxyModel.rowCount() - 1, mTorrentsProxyModel.columnCount() - 1) ), QItemSelectionModel::Toggle ); }); mWindow->menuBar()->addMenu(mTorrentMenu); //: Menu bar item QMenu* viewMenu = mWindow->menuBar()->addMenu(qApp->translate("tremotesf", "&View")); mToolBarAction = viewMenu->addAction(qApp->translate("tremotesf", "&Toolbar")); mToolBarAction->setCheckable(true); QObject::connect(mToolBarAction, &QAction::triggered, &mToolBar, &QToolBar::setVisible); QAction* sideBarAction = viewMenu->addAction(qApp->translate("tremotesf", "&Sidebar")); sideBarAction->setCheckable(true); sideBarAction->setChecked(Settings::instance()->get_sideBarVisible()); QObject::connect(sideBarAction, &QAction::triggered, this, [this](bool checked) { mSideBar.setVisible(checked); Settings::instance()->set_sideBarVisible(checked); }); QAction* statusBarAction = viewMenu->addAction(qApp->translate("tremotesf", "St&atusbar")); statusBarAction->setCheckable(true); statusBarAction->setChecked(Settings::instance()->get_statusBarVisible()); QObject::connect(statusBarAction, &QAction::triggered, this, [this](bool checked) { mWindow->statusBar()->setVisible(checked); Settings::instance()->set_statusBarVisible(checked); }); QAction* torrentPropertiesWidgetAction = viewMenu->addAction(qApp->translate("tremotesf", "Torrent properties &panel")); torrentPropertiesWidgetAction->setCheckable(true); torrentPropertiesWidgetAction->setChecked(Settings::instance()->get_showTorrentPropertiesInMainWindow()); QObject::connect(torrentPropertiesWidgetAction, &QAction::triggered, this, [](bool checked) { Settings::instance()->set_showTorrentPropertiesInMainWindow(checked); using enum Settings::TorrentDoubleClickAction; Settings::instance()->set_torrentDoubleClickAction(checked ? OpenTorrentFile : OpenPropertiesDialog); }); QObject::connect(Settings::instance(), &Settings::showTorrentPropertiesInMainWindowChanged, this, [=] { torrentPropertiesWidgetAction->setChecked( Settings::instance()->get_showTorrentPropertiesInMainWindow() ); }); viewMenu->addSeparator(); QAction* lockToolBarAction = viewMenu->addAction(qApp->translate("tremotesf", "&Lock Toolbar")); lockToolBarAction->setCheckable(true); lockToolBarAction->setChecked(Settings::instance()->get_toolBarLocked()); QObject::connect(lockToolBarAction, &QAction::triggered, &mToolBar, [this](bool checked) { mToolBar.setMovable(!checked); Settings::instance()->set_toolBarLocked(checked); }); //: Menu bar item QMenu* toolsMenu = mWindow->menuBar()->addMenu(qApp->translate("tremotesf", "T&ools")); QAction* settingsAction = toolsMenu->addAction( QIcon::fromTheme("configure"_L1, QIcon::fromTheme("preferences-system"_L1)), qApp->translate("tremotesf", "&Options") ); settingsAction->setShortcut(QKeySequence::Preferences); settingsAction->setMenuRole(QAction::PreferencesRole); QObject::connect(settingsAction, &QAction::triggered, this, [this] { showSingleInstanceDialog([this] { return new SettingsDialog(mViewModel.rpc(), mWindow); }); }); QAction* serversAction = toolsMenu->addAction( QIcon::fromTheme("network-server"_L1), qApp->translate("tremotesf", "&Connection Settings") ); serversAction->setMenuRole(QAction::NoRole); QObject::connect(serversAction, &QAction::triggered, this, [this] { showSingleInstanceDialog([this] { return new ConnectionSettingsDialog(mWindow); }); }); toolsMenu->addSeparator(); auto serverSettingsAction = new QAction( QIcon::fromTheme("preferences-system-network"_L1, QIcon::fromTheme("preferences-system"_L1)), qApp->translate("tremotesf", "&Server Options"), this ); serverSettingsAction->setMenuRole(QAction::NoRole); QObject::connect(serverSettingsAction, &QAction::triggered, this, [this] { showSingleInstanceDialog([this] { return new ServerSettingsDialog(mViewModel.rpc(), mWindow); }); }); mConnectionDependentActions.push_back(serverSettingsAction); toolsMenu->addAction(serverSettingsAction); auto serverStatsAction = new QAction( QIcon::fromTheme("view-statistics"_L1), qApp->translate("tremotesf", "Server S&tats"), this ); QObject::connect(serverStatsAction, &QAction::triggered, this, [this] { showSingleInstanceDialog([this] { return new ServerStatsDialog(mViewModel.rpc(), mWindow); }); }); mConnectionDependentActions.push_back(serverStatsAction); toolsMenu->addAction(serverStatsAction); auto shutdownServerAction = new QAction( QIcon::fromTheme("system-shutdown"_L1), qApp->translate("tremotesf", "S&hutdown Server"), this ); QObject::connect(shutdownServerAction, &QAction::triggered, this, [this] { auto dialog = new QMessageBox( QMessageBox::Warning, //: Dialog title qApp->translate("tremotesf", "Shutdown Server"), qApp->translate("tremotesf", "Are you sure you want to shutdown remote Transmission instance?"), QMessageBox::Cancel | QMessageBox::Ok, mWindow ); auto okButton = dialog->button(QMessageBox::Ok); okButton->setIcon(QIcon::fromTheme("system-shutdown"_L1)); //: Dialog confirmation button okButton->setText(qApp->translate("tremotesf", "Shutdown")); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setModal(true); QObject::connect(dialog, &QDialog::accepted, this, [this] { mViewModel.rpc()->shutdownServer(); }); dialog->show(); }); mConnectionDependentActions.push_back(shutdownServerAction); toolsMenu->addAction(shutdownServerAction); //: Menu bar item QMenu* helpMenu = mWindow->menuBar()->addMenu(qApp->translate("tremotesf", "&Help")); QAction* aboutAction = helpMenu->addAction( QIcon::fromTheme("help-about"_L1), //: Menu item opening "About" dialog qApp->translate("tremotesf", "&About") ); aboutAction->setMenuRole(QAction::AboutRole); QObject::connect(aboutAction, &QAction::triggered, this, [this] { showSingleInstanceDialog([this] { return new AboutDialog(mWindow); }); }); } void setupToolBar() { mToolBar.setObjectName("toolBar"_L1); mToolBar.setContextMenuPolicy(Qt::CustomContextMenu); mToolBar.setMovable(!Settings::instance()->get_toolBarLocked()); mWindow->addToolBar(Qt::TopToolBarArea, &mToolBar); mToolBar.addAction(&mConnectAction); mToolBar.addAction(&mDisconnectAction); mToolBar.addSeparator(); mToolBar.addAction(&mAddTorrentFileAction); mToolBar.addAction(&mAddTorrentLinkAction); mToolBar.addSeparator(); mToolBar.addAction(mStartTorrentAction); mToolBar.addAction(mPauseTorrentAction); mToolBar.addAction(mRemoveTorrentAction); QObject::connect(&mToolBar, &QToolBar::customContextMenuRequested, this, [this](QPoint pos) { QMenu contextMenu; QActionGroup group(this); //: Toolbar mode group.addAction(qApp->translate("tremotesf", "Icon Only"))->setCheckable(true); //: Toolbar mode group.addAction(qApp->translate("tremotesf", "Text Only"))->setCheckable(true); //: Toolbar mode group.addAction(qApp->translate("tremotesf", "Text Beside Icon"))->setCheckable(true); //: Toolbar mode group.addAction(qApp->translate("tremotesf", "Text Under Icon"))->setCheckable(true); //: Toolbar mode group.addAction(qApp->translate("tremotesf", "Follow System Style"))->setCheckable(true); group.actions().at(mWindow->toolButtonStyle())->setChecked(true); contextMenu.addActions(group.actions()); const auto* const action = contextMenu.exec(mToolBar.mapToGlobal(pos)); if (action) { const auto style = static_cast(contextMenu.actions().indexOf(action)); mWindow->setToolButtonStyle(style); Settings::instance()->set_toolButtonStyle(style); } }); } void setupTrayIcon() { auto contextMenu = new QMenu(mWindow); contextMenu->addAction(&mShowHideAppAction); contextMenu->addSeparator(); if constexpr (targetOs != TargetOs::UnixMacOS) { QObject::connect(&mTrayIcon, &QSystemTrayIcon::activated, this, [this](auto reason) { if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick) { if (isMainWindowHiddenOrMinimized()) { showWindowsOrActivateMainWindow(); } else { hideWindows(); } } }); } contextMenu->addAction(&mConnectAction); contextMenu->addAction(&mDisconnectAction); contextMenu->addSeparator(); contextMenu->addAction(&mAddTorrentFileAction); contextMenu->addAction(&mAddTorrentLinkAction); contextMenu->addSeparator(); contextMenu->addAction(createQuitAction()); mTrayIcon.setContextMenu(contextMenu); mTrayIcon.setToolTip(mViewModel.rpc()->status().toString()); QObject::connect(mViewModel.rpc(), &Rpc::statusChanged, this, [this] { mTrayIcon.setToolTip(mViewModel.rpc()->status().toString()); }); QObject::connect(mViewModel.rpc()->serverStats(), &ServerStats::updated, this, [this] { mTrayIcon.setToolTip( QString("\u25be %1\n\u25b4 %2") .arg( formatutils::formatByteSpeed(mViewModel.rpc()->serverStats()->downloadSpeed()), formatutils::formatByteSpeed(mViewModel.rpc()->serverStats()->uploadSpeed()) ) ); }); if (Settings::instance()->get_showTrayIcon()) { mTrayIcon.show(); } QObject::connect(Settings::instance(), &Settings::showTrayIconChanged, this, [this] { if (Settings::instance()->get_showTrayIcon()) { mTrayIcon.show(); } else { mTrayIcon.hide(); showWindowsOrActivateMainWindow(); } }); } bool isMainWindowHiddenOrMinimized() const { if constexpr (targetOs == TargetOs::UnixMacOS) { if (isNSAppHidden()) return true; } return mWindow->isHidden() || mWindow->isMinimized(); } void showWindowsOrActivateMainWindow([[maybe_unused]] std::optional windowActivationToken = {}) { info().log("Showing windows"); #ifdef Q_OS_MACOS if (isNSAppHidden()) { info().log("NSApp is hidden, unhiding it"); unhideNSApp(); } else { info().log("NSApp is not hidden, activating main window"); unminimizeAndRaiseWindow(mWindow); mWindow->activateWindow(); } #else if (!mWindow->isHidden()) { info().log("Main window is not hidden, activating it"); unminimizeAndRaiseWindow(mWindow); consumeActivationToken(mWindow, windowActivationToken); mWindow->activateWindow(); return; } consumeActivationToken(mWindow, windowActivationToken); info().log("Showing window {}", *mWindow); mWindow->show(); unminimizeAndRaiseWindow(mWindow); for (const auto& window : mOtherWindowsHiddenByUs) { if (window) { info().log("Showing window {}", *window); window->show(); unminimizeAndRaiseWindow(window); } } mOtherWindowsHiddenByUs.clear(); #endif } void hideWindows() { info().log("Hiding windows"); #ifdef Q_OS_MACOS if (isNSAppHidden()) { info().log("NSApp is already hidden, do nothing"); return; } // Hiding application doesn't work in fullscreen mode if (mWindow->isFullScreen()) { info().log("Exiting fullscreen"); mWindow->setWindowState(mWindow->windowState().setFlag(Qt::WindowFullScreen, false)); } info().log("Hiding NSApp"); hideNSApp(); #else if (mWindow->isHidden()) { info().log("Main window is already hidden, do nothing"); return; } info().log("Hiding {}", *mWindow); mWindow->hide(); mOtherWindowsHiddenByUs.clear(); for (auto&& widget : toQPointers(qApp->topLevelWidgets())) { if (widget != mWindow && widget->isWindow() && !widget->isHidden() && isAllowedToHide(widget)) { info().log("Hiding {}", *widget); widget->hide(); mOtherWindowsHiddenByUs.push_back(std::move(widget)); } } #endif } void openTorrentsFiles() { const QModelIndexList selectedRows(mTorrentsView.selectionModel()->selectedRows()); for (const QModelIndex& index : selectedRows) { desktoputils::openFile( localTorrentRootFilePath( mViewModel.rpc(), mTorrentsModel.torrentAtIndex(mTorrentsProxyModel.mapToSource(index)) ), mWindow ); } } void showTorrentsInFileManager() { std::vector files{}; const QModelIndexList selectedRows(mTorrentsView.selectionModel()->selectedRows()); files.reserve(static_cast(selectedRows.size())); for (const QModelIndex& index : selectedRows) { const auto* const torrent = mTorrentsModel.torrentAtIndex(mTorrentsProxyModel.mapToSource(index)); files.push_back(localTorrentRootFilePath(mViewModel.rpc(), torrent)); } launchFileManagerAndSelectFiles(files, mWindow); } void showAddTorrentDialogs( const QStringList& files, const QStringList& urls, std::optional windowActivationToken ) { if (!files.isEmpty()) { showAddTorrentFileDialogs(files, std::move(windowActivationToken)); windowActivationToken = std::nullopt; } if (!urls.isEmpty()) { showAddTorrentLinksDialog(urls, windowActivationToken); } } void showAddTorrentFileDialogs(const QStringList& files, std::optional windowActivationToken) { const bool setParent = Settings::instance()->get_showMainWindowWhenAddingTorrent(); for (const QString& filePath : files) { showAddTorrentFileDialog(filePath, setParent, windowActivationToken); } } void showAddTorrentFileDialog( const QString& filePath, bool setParent, std::optional& windowActivationToken ) { auto* const dialog = new AddTorrentDialog( mViewModel.rpc(), AddTorrentDialog::FileParams{filePath}, setParent ? mWindow : nullptr ); dialog->setAttribute(Qt::WA_DeleteOnClose); consumeActivationToken(dialog, windowActivationToken); dialog->show(); } void showAddTorrentLinksDialog(const QStringList& urls, std::optional& windowActivationToken) { auto* const dialog = new AddTorrentDialog( mViewModel.rpc(), AddTorrentDialog::UrlParams{urls}, Settings::instance()->get_showMainWindowWhenAddingTorrent() ? mWindow : nullptr ); dialog->setAttribute(Qt::WA_DeleteOnClose); consumeActivationToken(dialog, windowActivationToken); dialog->show(); } void askForMergingTrackers( std::vector>>> existingTorrents, std::optional windowActivationToken ) { const bool setParent = Settings::instance()->get_showMainWindowWhenAddingTorrent(); for (auto& [torrent, trackers] : existingTorrents) { auto* const dialog = tremotesf::createAskForMergingTrackersDialog( torrent, std::move(trackers), setParent ? mWindow : nullptr ); consumeActivationToken(dialog, windowActivationToken); dialog->show(); } } void showAddTorrentErrors() { const auto showError = [this](const QString& title, const QString& text) { QWidget* parent{}; if (Settings::instance()->get_showMainWindowWhenAddingTorrent()) { parent = mWindow; showWindowsOrActivateMainWindow(); } auto* const dialog = new QMessageBox(QMessageBox::Warning, title, text, QMessageBox::Close, parent); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(false); dialog->show(); }; QObject::connect(mViewModel.rpc(), &Rpc::torrentAddDuplicate, this, [=] { showError( qApp->translate("tremotesf", "Error adding torrent"), qApp->translate("tremotesf", "This torrent is already added") ); }); QObject::connect(mViewModel.rpc(), &Rpc::torrentAddError, this, [=](const QString& filePathOrUrl) { showError( qApp->translate("tremotesf", "Error adding torrent"), qApp->translate("tremotesf", "Error adding torrent «%1»").arg(filePathOrUrl) ); }); } void showDelayedTorrentAddDialog(const QStringList& torrents, std::optional windowActivationToken) { debug().log("MainWindow: showing delayed torrent add dialog"); const auto dialog = new QMessageBox( QMessageBox::Information, qApp->translate("tremotesf", "Disconnected"), //: Message shown when user attempts to add torrent while disconnect from server. qApp->translate("tremotesf", "Torrents will be added after connection to server"), QMessageBox::Close, Settings::instance()->get_showMainWindowWhenAddingTorrent() ? mWindow : nullptr ); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(false); QString detailedText{}; for (const auto& torrent : torrents) { detailedText += "\u2022 "; detailedText += torrent; detailedText += '\n'; } dialog->setDetailedText(detailedText); consumeActivationToken(dialog, windowActivationToken); dialog->show(); QObject::connect(mViewModel.rpc(), &Rpc::connectedChanged, dialog, [=, this] { if (mViewModel.rpc()->isConnected()) dialog->close(); }); } }; MainWindow::MainWindow(QStringList&& commandLineFiles, QStringList&& commandLineUrls, QWidget* parent) : QMainWindow(parent), mImpl(new Impl(std::move(commandLineFiles), std::move(commandLineUrls), this)) { setWindowTitle(TREMOTESF_APP_NAME ""_L1); setMinimumSize(minimumSizeHint().expandedTo(QSize(384, 256))); setContextMenuPolicy(Qt::NoContextMenu); setToolButtonStyle(Settings::instance()->get_toolButtonStyle()); setAcceptDrops(true); if constexpr (targetOs == TargetOs::UnixMacOS) { if (determineStyle() == KnownStyle::macOS) { setUnifiedTitleAndToolBarOnMac(true); } } } MainWindow::~MainWindow() = default; void MainWindow::initialShow(bool minimized) { if (!(minimized && Settings::instance()->get_showTrayIcon() && QSystemTrayIcon::isSystemTrayAvailable())) { show(); } } bool MainWindow::event(QEvent* event) { if (event->type() == QEvent::WindowStateChange) { // This may be called in Impl constructor from restoreGeometry(), when mImpl is still uninitialized, so access it inside the lambda QMetaObject::invokeMethod(this, [this] { mImpl->updateShowHideAction(); }, Qt::QueuedConnection); } return QMainWindow::event(event); } void MainWindow::showEvent(QShowEvent* event) { QMetaObject::invokeMethod(this, [this] { mImpl->updateShowHideAction(); }, Qt::QueuedConnection); QMainWindow::showEvent(event); } void MainWindow::hideEvent(QHideEvent* event) { QMetaObject::invokeMethod(this, [this] { mImpl->updateShowHideAction(); }, Qt::QueuedConnection); QMainWindow::hideEvent(event); } void MainWindow::closeEvent(QCloseEvent* event) { if (mImpl->onCloseEvent()) { event->ignore(); } else { QMainWindow::closeEvent(event); } } void MainWindow::dragEnterEvent(QDragEnterEvent* event) { mImpl->onDragEnterEvent(event); } void MainWindow::dropEvent(QDropEvent* event) { mImpl->onDropEvent(event); } } #include "mainwindow.moc" tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindow.h000066400000000000000000000017221523411710200231500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_MAINWINDOW_H #define TREMOTESF_MAINWINDOW_H #include #include namespace tremotesf { class MainWindow final : public QMainWindow { Q_OBJECT public: MainWindow(QStringList&& commandLineFiles, QStringList&& commandLineUrls, QWidget* parent = nullptr); ~MainWindow() override; Q_DISABLE_COPY_MOVE(MainWindow) void initialShow(bool minimized); protected: bool event(QEvent* event) override; void showEvent(QShowEvent* event) override; void hideEvent(QHideEvent* event) override; void closeEvent(QCloseEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; private: class Impl; std::unique_ptr mImpl; }; } #endif // TREMOTESF_MAINWINDOW_H tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindowsidebar.cpp000066400000000000000000000321161523411710200250360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "mainwindowsidebar.h" #include #include #include #include #include #include #include #include #include "rpc/rpc.h" #include "rpc/serversettings.h" #include "ui/itemmodels/baseproxymodel.h" #include "ui/stylehelpers.h" #include "ui/widgets/tooltipwhenelideddelegate.h" #include "alltrackersmodel.h" #include "downloaddirectoriesmodel.h" #include "downloaddirectorydelegate.h" #include "labelsmodel.h" #include "statusfiltersmodel.h" #include "torrentsproxymodel.h" namespace tremotesf { namespace { class BaseListView : public QListView { Q_OBJECT public: explicit BaseListView( TorrentsProxyModel* torrentsProxyModel, BaseTorrentsFiltersSettingsModel* model, QAbstractItemDelegate* delegate, QWidget* parent ) : QListView(parent), mTorrentsProxyModel(torrentsProxyModel), mModel(model) { mModel->setParent(this); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setIconSize(QSize(16, 16)); if (delegate) { delegate->setParent(this); setItemDelegate(delegate); } else { setItemDelegate(new TooltipWhenElidedDelegate(this)); } setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setTextElideMode(Qt::ElideMiddle); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); makeScrollAreaTransparent(this); } [[nodiscard]] QSize minimumSizeHint() const override { return {8, 0}; } [[nodiscard]] QSize sizeHint() const override { int height = 0; for (int i = 0, max = model()->rowCount(); i < max; ++i) { height += sizeHintForRow(i); height += spacing(); } height += spacing(); return {sizeHintForColumn(0), height}; } void setFilterEnabled(bool enabled) { setVisible(enabled); setTorrentsModelFilterEnabled(enabled); } protected: void init(Rpc* rpc, std::optional sortByRole = {}) { QAbstractItemModel* actualModel{}; if (sortByRole.has_value()) { mProxyModel = new BaseProxyModel(mModel, *sortByRole, std::nullopt, this); mProxyModel->sort(0); actualModel = mProxyModel; } else { actualModel = mModel; } setModel(actualModel); QObject::connect( mModel, &BaseTorrentsFiltersSettingsModel::populatedChanged, this, &BaseListView::updateCurrentIndex ); QObject::connect( selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex& current) { if (mModel->isPopulated()) { setTorrentsModelFilterFromIndex(current); } } ); QObject::connect( actualModel, &BaseTorrentsFiltersSettingsModel::rowsInserted, this, &BaseListView::updateGeometry ); QObject::connect( actualModel, &BaseTorrentsFiltersSettingsModel::rowsRemoved, this, &BaseListView::updateGeometry ); mModel->setTorrentsProxyModel(mTorrentsProxyModel); mModel->setRpc(rpc); if (mModel->isPopulated()) { updateCurrentIndex(); } } void updateCurrentIndex() { auto index = mModel->indexForTorrentsProxyModelFilter(); if (index.isValid() && mProxyModel) { index = mProxyModel->mapFromSource(index); } if (!index.isValid() && model()->rowCount() > 0) { index = model()->index(0, 0); } if (index.isValid()) { setCurrentIndex(index); } } virtual void setTorrentsModelFilterFromIndex(const QModelIndex& index) = 0; virtual void setTorrentsModelFilterEnabled(bool enabled) = 0; TorrentsProxyModel* mTorrentsProxyModel; BaseTorrentsFiltersSettingsModel* mModel; BaseProxyModel* mProxyModel{}; }; class StatusFiltersListView final : public BaseListView { Q_OBJECT public: StatusFiltersListView(Rpc* rpc, TorrentsProxyModel* torrentsModel, QWidget* parent = nullptr) : BaseListView(torrentsModel, new StatusFiltersModel(), nullptr, parent) { init(rpc); QObject::connect(torrentsModel, &TorrentsProxyModel::statusFilterChanged, this, [=, this] { updateCurrentIndex(); }); } protected: void setTorrentsModelFilterFromIndex(const QModelIndex& index) override { mTorrentsProxyModel->setStatusFilter( index.data(StatusFiltersModel::FilterRole).value() ); } void setTorrentsModelFilterEnabled(bool enabled) override { mTorrentsProxyModel->setStatusFilterEnabled(enabled); } }; class LabelsListView final : public BaseListView { Q_OBJECT public: LabelsListView(Rpc* rpc, TorrentsProxyModel* torrentsModel, QWidget* parent = nullptr) : BaseListView(torrentsModel, new LabelsModel(), nullptr, parent) { init(rpc, LabelsModel::LabelRole); QObject::connect(torrentsModel, &TorrentsProxyModel::labelFilterChanged, this, [=, this] { updateCurrentIndex(); }); } protected: void setTorrentsModelFilterFromIndex(const QModelIndex& index) override { mTorrentsProxyModel->setLabelFilter(index.data(LabelsModel::LabelRole).toString()); } void setTorrentsModelFilterEnabled(bool enabled) override { mTorrentsProxyModel->setLabelFilterEnabled(enabled); } }; class TrackersListView final : public BaseListView { Q_OBJECT public: TrackersListView(Rpc* rpc, TorrentsProxyModel* torrentsModel, QWidget* parent = nullptr) : BaseListView(torrentsModel, new AllTrackersModel(), nullptr, parent) { init(rpc, AllTrackersModel::TrackerRole); QObject::connect(torrentsModel, &TorrentsProxyModel::trackerFilterChanged, this, [=, this] { updateCurrentIndex(); }); } protected: void setTorrentsModelFilterFromIndex(const QModelIndex& index) override { mTorrentsProxyModel->setTrackerFilter(index.data(AllTrackersModel::TrackerRole).toString()); } void setTorrentsModelFilterEnabled(bool enabled) override { mTorrentsProxyModel->setTrackerFilterEnabled(enabled); } }; class DirectoriesListView final : public BaseListView { Q_OBJECT public: DirectoriesListView(Rpc* rpc, TorrentsProxyModel* torrentsModel, QWidget* parent = nullptr) : BaseListView(torrentsModel, new DownloadDirectoriesModel(), new DownloadDirectoryDelegate(), parent) { init(rpc, static_cast(DownloadDirectoriesModel::Role::Directory)); QObject::connect(torrentsModel, &TorrentsProxyModel::downloadDirectoryFilterChanged, this, [=, this] { updateCurrentIndex(); }); } protected: void setTorrentsModelFilterFromIndex(const QModelIndex& index) override { mTorrentsProxyModel->setDownloadDirectoryFilter( index.data(static_cast(DownloadDirectoriesModel::Role::Directory)).toString() ); } void setTorrentsModelFilterEnabled(bool enabled) override { mTorrentsProxyModel->setDownloadDirectoryFilterEnabled(enabled); } }; } MainWindowSideBar::MainWindowSideBar(Rpc* rpc, TorrentsProxyModel* proxyModel, QWidget* parent) : QScrollArea(parent) { setFrameShape(QFrame::NoFrame); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setWidgetResizable(true); auto contentWidget = new QWidget(this); auto layout = new QVBoxLayout(contentWidget); auto searchLineEdit = new QLineEdit(this); searchLineEdit->setClearButtonEnabled(true); //: Search field placeholder searchLineEdit->setPlaceholderText(qApp->translate("tremotesf", "Search...")); QObject::connect(searchLineEdit, &QLineEdit::textChanged, this, [proxyModel](const auto& text) { proxyModel->setSearchString(text); }); layout->addWidget(searchLineEdit); auto* const searchShortcut = new QShortcut(QKeyCombination(Qt::ControlModifier, Qt::Key_F), this); QObject::connect(searchShortcut, &QShortcut::activated, this, [searchLineEdit] { searchLineEdit->selectAll(); searchLineEdit->setFocus(); }); QFont checkBoxFont(QApplication::font()); checkBoxFont.setBold(true); checkBoxFont.setCapitalization(QFont::AllUppercase); //: Title of torrents status filters list auto statusCheckBox = new QCheckBox(qApp->translate("tremotesf", "Status"), this); statusCheckBox->setChecked(true); statusCheckBox->setFont(checkBoxFont); layout->addWidget(statusCheckBox); auto statusFiltersListView = new StatusFiltersListView(rpc, proxyModel, this); QObject::connect( statusCheckBox, &QCheckBox::toggled, statusFiltersListView, &StatusFiltersListView::setFilterEnabled ); layout->addWidget(statusFiltersListView); statusCheckBox->setChecked(proxyModel->isStatusFilterEnabled()); //: Title of torrents label filters list auto labelsCheckBox = new QCheckBox(qApp->translate("tremotesf", "Labels"), this); labelsCheckBox->setChecked(proxyModel->isLabelFilterEnabled()); labelsCheckBox->setFont(checkBoxFont); layout->addWidget(labelsCheckBox); auto labelsListView = new LabelsListView(rpc, proxyModel, this); QObject::connect(labelsCheckBox, &QCheckBox::toggled, labelsListView, &LabelsListView::setFilterEnabled); layout->addWidget(labelsListView); QObject::connect(rpc, &Rpc::connectedChanged, this, [=] { const bool serverSupportsLabels = rpc->isConnected() ? rpc->serverSettings()->data().hasLabelsProperty() : true; if (serverSupportsLabels) { labelsCheckBox->setVisible(true); labelsListView->setVisible(labelsCheckBox->isChecked()); } else { labelsCheckBox->setVisible(false); labelsListView->setVisible(false); } }); //: Title of torrents download directory filters list auto directoriesCheckBox = new QCheckBox(qApp->translate("tremotesf", "Directories"), this); directoriesCheckBox->setChecked(true); directoriesCheckBox->setFont(checkBoxFont); layout->addWidget(directoriesCheckBox); auto directoriesListView = new DirectoriesListView(rpc, proxyModel, this); QObject::connect( directoriesCheckBox, &QCheckBox::toggled, directoriesListView, &DirectoriesListView::setFilterEnabled ); layout->addWidget(directoriesListView); directoriesCheckBox->setChecked(proxyModel->isDownloadDirectoryFilterEnabled()); //: Title of torrents tracker filters list auto trackersCheckBox = new QCheckBox(qApp->translate("tremotesf", "Trackers"), this); trackersCheckBox->setChecked(true); trackersCheckBox->setFont(checkBoxFont); layout->addWidget(trackersCheckBox); auto trackersListView = new TrackersListView(rpc, proxyModel, this); QObject::connect(trackersCheckBox, &QCheckBox::toggled, trackersListView, &TrackersListView::setFilterEnabled); layout->addWidget(trackersListView); trackersCheckBox->setChecked(proxyModel->isTrackerFilterEnabled()); layout->addStretch(); setWidget(contentWidget); } } #include "mainwindowsidebar.moc" tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindowsidebar.h000066400000000000000000000007771523411710200245130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_MAINWINDOWSIDEBAR_H #define TREMOTESF_MAINWINDOWSIDEBAR_H #include namespace tremotesf { class Rpc; class TorrentsProxyModel; class MainWindowSideBar final : public QScrollArea { Q_OBJECT public: explicit MainWindowSideBar(Rpc* rpc, TorrentsProxyModel* proxyModel, QWidget* parent = nullptr); }; } #endif // TREMOTESF_MAINWINDOWSIDEBAR_H tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindowstatusbar.cpp000066400000000000000000000212771523411710200254430ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "mainwindowstatusbar.h" #include #include #include #include #include #include #include #include #include "log/log.h" #include "rpc/serverstats.h" #include "rpc/servers.h" #include "rpc/rpc.h" #include "formatutils.h" using namespace Qt::StringLiterals; namespace tremotesf { MainWindowStatusBar::MainWindowStatusBar(const Rpc* rpc, QWidget* parent) : QStatusBar(parent), mRpc(rpc) { setSizeGripEnabled(false); setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(this, &QWidget::customContextMenuRequested, this, &MainWindowStatusBar::showContextMenu); auto container = new QWidget(this); addPermanentWidget(container, 1); auto layout = new QHBoxLayout(container); // Top/bottom margins are set on mServerLabel below so that they don't affect separators layout->setContentsMargins(8, 0, 8, 0); mNoServersErrorImage = new QLabel(this); mNoServersErrorImage->setPixmap(QIcon::fromTheme("dialog-error"_L1).pixmap(16, 16)); mNoServersErrorImage->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); layout->addWidget(mNoServersErrorImage); mServerLabel = new QLabel(this); mServerLabel->setContentsMargins(0, 5, 0, 5); layout->addWidget(mServerLabel); mFirstSeparator = new StatusBarSeparator(this); layout->addWidget(mFirstSeparator); mStatusLabel = new QLabel(this); layout->addWidget(mStatusLabel); mSecondSeparator = new StatusBarSeparator(this); layout->addWidget(mSecondSeparator); mDownloadSpeedImage = new QLabel(this); mDownloadSpeedImage->setPixmap(QIcon::fromTheme("go-down"_L1).pixmap(16, 16)); mDownloadSpeedImage->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); layout->addWidget(mDownloadSpeedImage); mDownloadSpeedLabel = new QLabel(this); layout->addWidget(mDownloadSpeedLabel); mThirdSeparator = new StatusBarSeparator(this); layout->addWidget(mThirdSeparator); mUploadSpeedImage = new QLabel(this); mUploadSpeedImage->setPixmap(QIcon::fromTheme("go-up"_L1).pixmap(16, 16)); mUploadSpeedImage->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); layout->addWidget(mUploadSpeedImage); mUploadSpeedLabel = new QLabel(this); layout->addWidget(mUploadSpeedLabel); mFourthSeparator = new StatusBarSeparator(this); layout->addWidget(mFourthSeparator); mFreeSpaceLabel = new QLabel(this); mFreeSpaceLabel->setContentsMargins(8, 0, 0, 0); layout->addWidget(mFreeSpaceLabel); updateLayout(); QObject::connect(mRpc, &Rpc::connectionStateChanged, this, &MainWindowStatusBar::updateLayout); QObject::connect(Servers::instance(), &Servers::hasServersChanged, this, &MainWindowStatusBar::updateLayout); updateServerLabel(); QObject::connect( Servers::instance(), &Servers::currentServerChanged, this, &MainWindowStatusBar::updateServerLabel ); QObject::connect( Servers::instance(), &Servers::hasServersChanged, this, &MainWindowStatusBar::updateServerLabel ); updateStatusLabels(); QObject::connect(mRpc, &Rpc::statusChanged, this, &MainWindowStatusBar::updateStatusLabels); QObject::connect(mRpc->serverStats(), &ServerStats::updated, this, [=, this] { mDownloadSpeedLabel->setText(formatutils::formatByteSpeed(mRpc->serverStats()->downloadSpeed())); mUploadSpeedLabel->setText(formatutils::formatByteSpeed(mRpc->serverStats()->uploadSpeed())); mFreeSpaceLabel->setText(qApp->translate("tremotesf", "Free space: %1") .arg(formatutils::formatByteSize(mRpc->serverStats()->freeSpace()))); }); } void MainWindowStatusBar::updateLayout() { if (Servers::instance()->hasServers()) { mNoServersErrorImage->hide(); mServerLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); mFirstSeparator->show(); mStatusLabel->show(); if (mRpc->connectionState() == Rpc::ConnectionState::Connected) { mSecondSeparator->show(); mDownloadSpeedImage->show(); mDownloadSpeedLabel->show(); mThirdSeparator->show(); mUploadSpeedImage->show(); mUploadSpeedLabel->show(); mFourthSeparator->show(); mFreeSpaceLabel->show(); } else { mSecondSeparator->hide(); mDownloadSpeedImage->hide(); mDownloadSpeedLabel->hide(); mThirdSeparator->hide(); mUploadSpeedImage->hide(); mUploadSpeedLabel->hide(); mFourthSeparator->hide(); mFreeSpaceLabel->hide(); } } else { mNoServersErrorImage->show(); mServerLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); mFirstSeparator->hide(); mStatusLabel->hide(); mSecondSeparator->hide(); mDownloadSpeedImage->hide(); mDownloadSpeedLabel->hide(); mThirdSeparator->hide(); mUploadSpeedImage->hide(); mUploadSpeedLabel->hide(); mFreeSpaceLabel->hide(); } } void MainWindowStatusBar::updateServerLabel() { if (Servers::instance()->hasServers()) { mServerLabel->setText( QString("%1 (%2)"_L1) .arg(Servers::instance()->currentServerName(), Servers::instance()->currentServerAddress()) ); } else { mServerLabel->setText(qApp->translate("tremotesf", "No servers")); } } void MainWindowStatusBar::updateStatusLabels() { mStatusLabel->setText(mRpc->status().toString()); if (mRpc->error() != RpcError::NoError) { mStatusLabel->setToolTip(mRpc->errorMessage()); } else { mStatusLabel->setToolTip({}); } } void MainWindowStatusBar::showContextMenu(QPoint pos) { auto* const menu = new QMenu(this); auto* const group = new QActionGroup(menu); group->setExclusive(true); menu->setAttribute(Qt::WA_DeleteOnClose, true); const auto servers = Servers::instance()->servers(); const auto currentServerName = Servers::instance()->currentServerName(); for (const auto& server : servers) { auto* const action = menu->addAction(server.name); action->setData(server.name); action->setCheckable(true); group->addAction(action); if (server.name == currentServerName) { action->setChecked(true); } } menu->addSeparator(); auto* const connectionSettingsAction = menu->addAction( QIcon::fromTheme("network-server"_L1), qApp->translate("tremotesf", "&Connection Settings") ); QObject::connect(menu, &QMenu::triggered, this, [this, connectionSettingsAction](QAction* action) { if (action == connectionSettingsAction) { emit showConnectionSettingsDialog(); } else { const auto selectedName = action->data().toString(); const auto servers = Servers::instance()->servers(); if (std::ranges::contains(servers, selectedName, &Server::name)) { Servers::instance()->setCurrentServer(selectedName); } else { warning().log("Selected server {} which no longer exists", selectedName); } } }); menu->popup(mapToGlobal(pos)); } StatusBarSeparator::StatusBarSeparator(QWidget* parent) : QWidget(parent) { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); } QSize StatusBarSeparator::sizeHint() const { QStyleOption opt{}; opt.initFrom(this); opt.state.setFlag(QStyle::State_Horizontal); const int extent = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, this); return {extent, extent}; } void StatusBarSeparator::paintEvent(QPaintEvent*) { QPainter p(this); QStyleOption opt{}; opt.initFrom(this); opt.state.setFlag(QStyle::State_Horizontal); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p, this); } } tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindowstatusbar.h000066400000000000000000000027311523411710200251020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_MAINWINDOWSTATUSBAR_H #define TREMOTESF_MAINWINDOWSTATUSBAR_H #include class QLabel; namespace tremotesf { class Rpc; class StatusBarSeparator; class MainWindowStatusBar final : public QStatusBar { Q_OBJECT public: explicit MainWindowStatusBar(const Rpc* rpc, QWidget* parent = nullptr); private: void updateLayout(); void updateServerLabel(); void updateStatusLabels(); void showContextMenu(QPoint pos); const Rpc* mRpc{}; QLabel* mNoServersErrorImage{}; QLabel* mServerLabel{}; StatusBarSeparator* mFirstSeparator{}; QLabel* mStatusLabel{}; StatusBarSeparator* mSecondSeparator{}; QLabel* mDownloadSpeedImage{}; QLabel* mDownloadSpeedLabel{}; StatusBarSeparator* mThirdSeparator{}; QLabel* mUploadSpeedImage{}; QLabel* mUploadSpeedLabel{}; StatusBarSeparator* mFourthSeparator{}; QLabel* mFreeSpaceLabel{}; signals: void showConnectionSettingsDialog(); }; class StatusBarSeparator final : public QWidget { Q_OBJECT public: explicit StatusBarSeparator(QWidget* parent = nullptr); QSize sizeHint() const override; protected: void paintEvent(QPaintEvent* event) override; }; } #endif // TREMOTESF_MAINWINDOWSTATUSBAR_H tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindowviewmodel.cpp000066400000000000000000000336221523411710200254230ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "mainwindowviewmodel.h" #include #include #include #include #include #include #include #include #include "coroutines/qobjectsignal.h" #include "coroutines/timer.h" #include "coroutines/threadpool.h" #include "coroutines/waitall.h" #include "log/log.h" #include "ipc/ipcserver.h" #include "rpc/servers.h" #include "ui/screens/addtorrent/addtorrenthelpers.h" #include "ui/screens/addtorrent/droppedtorrents.h" #include "ui/notificationscontroller.h" #include "magnetlinkparser.h" #include "settings.h" #include "torrentfileparser.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QUrl) SPECIALIZE_FORMATTER_FOR_QDEBUG(Qt::DropActions) namespace tremotesf { namespace { std::string formatDropEvent(const QDropEvent* event) { const auto mime = event->mimeData(); return fmt::format( R"( proposedAction = {} possibleActions = {} formats = {} urls = {} text = {})", event->proposedAction(), event->possibleActions(), mime->formats(), mime->urls(), mime->text() ); } std::string formatMimeData(const QMimeData* mime) { return fmt::format( R"( formats = {} urls = {} text = {})", mime->formats(), mime->urls(), mime->text() ); } using namespace std::chrono_literals; constexpr auto initialDelayedTorrentAddMessageDelay = 500ms; } MainWindowViewModel::MainWindowViewModel( QStringList&& commandLineFiles, QStringList&& commandLineUrls, QObject* parent ) : QObject(parent) { if (!commandLineFiles.isEmpty() || !commandLineUrls.isEmpty()) { QMetaObject::invokeMethod( this, [this, files = std::move(commandLineFiles), urls = std::move(commandLineUrls)]() mutable { mAddingTorrentsCoroutineScope.launch(addTorrents(std::move(files), std::move(urls))); }, Qt::QueuedConnection ); } const auto* const ipcServer = IpcServer::createInstance(this); QObject::connect( ipcServer, &IpcServer::windowActivationRequested, this, [=, this](const auto& activationToken) { emit showWindow(activationToken); } ); QObject::connect( ipcServer, &IpcServer::torrentsAddingRequested, this, [=, this](const auto& files, const auto& urls, const auto& activationToken) { mAddingTorrentsCoroutineScope.launch(addTorrents(files, urls, activationToken)); } ); QObject::connect(Servers::instance(), &Servers::currentServerChanged, this, [this] { if (Servers::instance()->hasServers()) { mRpc.setConnectionConfiguration(Servers::instance()->currentServer().connectionConfiguration); mRpc.connect(); } else { mRpc.resetConnectionConfiguration(); } }); QObject::connect(&mRpc, &Rpc::aboutToDisconnect, this, [this] { Servers::instance()->saveCurrentServerLastTorrents(&mRpc); }); } void MainWindowViewModel::processDragEnterEvent(QDragEnterEvent* event) { debug().log("MainWindowViewModel: processing QDragEnterEvent"); debug().log("MainWindowViewModel: event: {}", formatDropEvent(event)); const auto dropped = DroppedTorrents(event->mimeData()); if (dropped.isEmpty()) { debug().log("MainWindowViewModel: not accepting QDragEnterEvent"); return; } info().log("MainWindowViewModel: accepting QDragEnterEvent"); if (event->possibleActions().testFlag(Qt::CopyAction)) { event->setDropAction(Qt::CopyAction); event->accept(); } else { event->acceptProposedAction(); } } void MainWindowViewModel::processDropEvent(QDropEvent* event) { debug().log("MainWindowViewModel: processing QDropEvent"); debug().log("MainWindowViewModel: event: {}", formatDropEvent(event)); auto dropped = DroppedTorrents(event->mimeData()); if (dropped.isEmpty()) { warning().log("Dropped torrents are empty"); return; } info().log("MainWindowViewModel: accepting QDropEvent"); event->acceptProposedAction(); mAddingTorrentsCoroutineScope.launch(addTorrents(std::move(dropped.files), std::move(dropped.urls))); } void MainWindowViewModel::pasteShortcutActivated() { debug().log("MainWindowViewModel: pasteShortcutActivated() called"); addTorrentsFromClipboard(); } void MainWindowViewModel::triggeredAddTorrentLinkAction() { debug().log("MainWindowViewModel: triggeredAddTorrentLinkAction() called"); if (Settings::instance()->get_fillTorrentLinkFromClipboard() && addTorrentsFromClipboard(true)) { return; } emit showAddTorrentDialogs({}, {QString{}}, {}); } void MainWindowViewModel::acceptedFileDialog(QStringList files) { debug().log("MainWindowViewModel: acceptedFileDialog() called with: files = {}", files); mAddingTorrentsCoroutineScope.launch(addTorrents(std::move(files), {})); } void MainWindowViewModel::setupNotificationsController(QSystemTrayIcon* trayIcon) { const auto controller = NotificationsController::createInstance(trayIcon, &mRpc, this); QObject::connect( controller, &NotificationsController::notificationClicked, this, [this](const std::optional& windowActivationToken) { emit showWindow(windowActivationToken); } ); } MainWindowViewModel::StartupActionResult MainWindowViewModel::performStartupAction() { if (!Servers::instance()->hasServers()) { return StartupActionResult::ShowAddServerDialog; } mRpc.setConnectionConfiguration(Servers::instance()->currentServer().connectionConfiguration); if (Settings::instance()->get_connectOnStartup()) { mRpc.connect(); } return StartupActionResult::DoNothing; } bool MainWindowViewModel::addTorrentsFromClipboard(bool onlyUrls) { const auto* const mimeData = QGuiApplication::clipboard()->mimeData(); if (!mimeData) { warning().log("MainWindowViewModel: clipboard data is null"); return false; } debug().log("MainWindowViewModel: clipboard data: {}", formatMimeData(mimeData)); if (addTorrentsFromMimeData(mimeData, onlyUrls)) { return true; } debug().log("MainWindowViewModel: ignoring clipboard data"); return false; } bool MainWindowViewModel::addTorrentsFromMimeData(const QMimeData* mimeData, bool onlyUrls) { auto dropped = DroppedTorrents(mimeData); if (dropped.isEmpty()) { return false; } mAddingTorrentsCoroutineScope.launch( addTorrents(onlyUrls ? QStringList{} : std::move(dropped.files), std::move(dropped.urls)) ); return true; } Coroutine>>>> MainWindowViewModel::separateTorrentsThatAlreadyExistForFiles( // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) QStringList& files ) { std::vector> torrentFiles{}; torrentFiles.reserve(static_cast(files.size())); co_await waitAll( files | std::views::transform([&](const QString& filePath) { return parseTorrentFile(filePath, torrentFiles); }) | std::ranges::to() ); std::vector>>> existingTorrents{}; for (auto& [filePath, torrentFile] : torrentFiles) { auto* const torrent = mRpc.torrentByHash(torrentFile.infoHashV1); if (torrent) { existingTorrents.emplace_back(torrent, std::move(torrentFile.trackers)); files.removeOne(filePath); } } co_return existingTorrents; } Coroutine<> MainWindowViewModel::parseTorrentFile( QString filePath, // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters) std::vector>& output ) { try { info().log("Parsing torrent file {}", filePath); auto torrentFile = co_await runOnThreadPool(&tremotesf::parseTorrentFile, filePath); info().log("Parsed {}, result = {}", filePath, torrentFile); output.emplace_back(std::move(filePath), std::move(torrentFile)); } catch (const bencode::Error& e) { warning().logWithException(e, "Failed to parse torrent file {}", filePath); } } void MainWindowViewModel::addTorrentFilesWithoutDialog(const QStringList& files) { if (files.isEmpty()) return; const auto parameters = getAddTorrentParameters(&mRpc); for (const auto& filePath : files) { mRpc.addTorrentFile( filePath, parameters.downloadDirectory, {}, {}, {}, {}, parameters.priority, parameters.startAfterAdding, parameters.deleteTorrentFile ? (parameters.moveTorrentFileToTrash ? Rpc::DeleteFileMode::MoveToTrash : Rpc::DeleteFileMode::Delete) : Rpc::DeleteFileMode::No, {} ); } } std::vector>>> MainWindowViewModel::separateTorrentsThatAlreadyExistForLinks(QStringList& urls) { std::vector>>> existingTorrents{}; const auto toErase = std::ranges::remove_if(urls, [&](const QString& url) { try { info().log("Parsing {} as a magnet link", url); auto magnetLink = parseMagnetLink(QUrl(url)); info().log("Parsed, result = {}", magnetLink); auto* const torrent = mRpc.torrentByHash(magnetLink.infoHashV1); if (torrent) { existingTorrents.emplace_back(torrent, std::move(magnetLink.trackers)); return true; } } catch (const std::runtime_error& e) { warning().logWithException(e, "Failed to parse {} as a magnet link", url); } return false; }); if (!toErase.empty()) { urls.erase(toErase.begin(), toErase.end()); } return existingTorrents; } void MainWindowViewModel::addTorrentLinksWithoutDialog(QStringList urls) { if (urls.isEmpty()) return; auto parameters = getAddTorrentParameters(&mRpc); mRpc.addTorrentLinks( std::move(urls), std::move(parameters.downloadDirectory), parameters.priority, parameters.startAfterAdding, {} ); } Coroutine<> MainWindowViewModel::addTorrents( QStringList files, QStringList urls, std::optional windowActivationToken ) { info().log("MainWindowViewModel: addTorrents() called"); info().log("MainWindowViewModel: files = {}", files); info().log("MainWindowViewModel: urls = {}", urls); const auto* const settings = Settings::instance(); const auto showMainWindowIfNeeded = [&] { if (settings->get_showMainWindowWhenAddingTorrent()) { emit showWindow(windowActivationToken); windowActivationToken.reset(); } }; if (!mRpc.isConnected()) { info().log("Postponing opening torrents until connected to server"); if (mRpc.connectionState() == RpcConnectionState::Connecting) { info().log("We are already connecting, wait a bit before showing message"); co_await waitAny( []() -> Coroutine<> { co_await waitFor(initialDelayedTorrentAddMessageDelay); }(), [](Rpc* rpc) -> Coroutine<> { co_await waitForSignal(rpc, &Rpc::connectedChanged); }(&mRpc) ); } if (!mRpc.isConnected()) { info().log("Showing delayed torrent adding message"); showMainWindowIfNeeded(); emit showDelayedTorrentAddDialog(files + urls, windowActivationToken); windowActivationToken.reset(); co_await waitForSignal(&mRpc, &Rpc::connectedChanged); } } showMainWindowIfNeeded(); // We can parse magnet links immediately so check whether torrents exist event if we show dialogs if (const auto existingTorrents = separateTorrentsThatAlreadyExistForLinks(urls); !existingTorrents.empty()) { emit askForMergingTrackers(existingTorrents, windowActivationToken); windowActivationToken.reset(); } if (settings->get_showAddTorrentDialog()) { emit showAddTorrentDialogs(files, urls, windowActivationToken); } else { addTorrentLinksWithoutDialog(urls); if (const auto existingTorrents = co_await separateTorrentsThatAlreadyExistForFiles(files); !existingTorrents.empty()) { emit askForMergingTrackers(existingTorrents, windowActivationToken); windowActivationToken.reset(); } addTorrentFilesWithoutDialog(files); } } } tremotesf-2.10.1/src/ui/screens/mainwindow/mainwindowviewmodel.h000066400000000000000000000052531523411710200250670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_MAINWINDOWVIEWMODEL_H #define TREMOTESF_MAINWINDOWVIEWMODEL_H #include #include #include "rpc/rpc.h" #include "coroutines/scope.h" class QByteArray; class QDragEnterEvent; class QDropEvent; class QMimeData; class QString; class QSystemTrayIcon; namespace tremotesf { class TorrentMetainfoFile; class MainWindowViewModel final : public QObject { Q_OBJECT public: MainWindowViewModel(QStringList&& commandLineFiles, QStringList&& commandLineUrls, QObject* parent = nullptr); Rpc* rpc() { return &mRpc; }; static void processDragEnterEvent(QDragEnterEvent* event); void processDropEvent(QDropEvent* event); void pasteShortcutActivated(); void triggeredAddTorrentLinkAction(); void acceptedFileDialog(QStringList files); void setupNotificationsController(QSystemTrayIcon* trayIcon); enum class StartupActionResult { ShowAddServerDialog, DoNothing }; StartupActionResult performStartupAction(); private: Rpc mRpc{}; CoroutineScope mAddingTorrentsCoroutineScope{}; bool addTorrentsFromClipboard(bool onlyUrls = false); bool addTorrentsFromMimeData(const QMimeData* mimeData, bool onlyUrls); Coroutine>>>> separateTorrentsThatAlreadyExistForFiles(QStringList& files); Coroutine<> parseTorrentFile(QString filePath, std::vector>& output); void addTorrentFilesWithoutDialog(const QStringList& files); std::vector>>> separateTorrentsThatAlreadyExistForLinks(QStringList& urls); void addTorrentLinksWithoutDialog(QStringList urls); Coroutine<> addTorrents(QStringList files, QStringList urls, std::optional windowActivationToken = {}); signals: void showWindow(const std::optional& windowActivationToken); void showAddTorrentDialogs( const QStringList& files, const QStringList& urls, const std::optional& windowActivationToken ); void askForMergingTrackers( const std::vector>>>& existingTorrents, const std::optional& windowActivationToken ); void showDelayedTorrentAddDialog( const QStringList& torrents, const std::optional& windowActivationToken ); }; } #endif // TREMOTESF_MAINWINDOWVIEWMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/statusfiltersmodel.cpp000066400000000000000000000155211523411710200252660ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "statusfiltersmodel.h" #include #include #include #include "rpc/rpc.h" #include "ui/itemmodels/modelutils.h" #include "desktoputils.h" #include "torrentsproxymodel.h" namespace tremotesf { QVariant StatusFiltersModel::data(const QModelIndex& index, int role) const { using StatusFilter = TorrentsProxyModel::StatusFilter; if (!index.isValid()) { return {}; } const Item& item = mItems.at(static_cast(index.row())); switch (role) { case FilterRole: return QVariant::fromValue(item.filter); case Qt::DecorationRole: { using enum desktoputils::StatusIcon; switch (item.filter) { case StatusFilter::All: { return desktoputils::standardDirIcon(); } case StatusFilter::Active: return desktoputils::statusIcon(Active); case StatusFilter::Downloading: return desktoputils::statusIcon(Downloading); case StatusFilter::Seeding: return desktoputils::statusIcon(Seeding); case StatusFilter::Paused: return desktoputils::statusIcon(Paused); case StatusFilter::Checking: return desktoputils::statusIcon(Checking); case StatusFilter::Errored: return desktoputils::statusIcon(Errored); default: break; } break; } case Qt::DisplayRole: case Qt::ToolTipRole: switch (item.filter) { case StatusFilter::All: //: Filter option of torrents list's status filter. %L1 is total number of torrents return qApp->translate("tremotesf", "All (%L1)").arg(item.torrents); case StatusFilter::Active: //: Filter option of torrents list's status filter. %L1 is a number of torrents with that status return qApp->translate("tremotesf", "Active (%L1)").arg(item.torrents); case StatusFilter::Downloading: //: Filter option of torrents list's status filter. %L1 is a number of torrents with that status return qApp->translate("tremotesf", "Downloading (%L1)").arg(item.torrents); case StatusFilter::Seeding: //: Filter option of torrents list's status filter. %L1 is a number of torrents with that status return qApp->translate("tremotesf", "Seeding (%L1)").arg(item.torrents); case StatusFilter::Paused: //: Filter option of torrents list's status filter. %L1 is a number of torrents with that status return qApp->translate("tremotesf", "Paused (%L1)").arg(item.torrents); case StatusFilter::Checking: //: Filter option of torrents list's status filter. %L1 is a number of torrents with that status return qApp->translate("tremotesf", "Checking (%L1)").arg(item.torrents); case StatusFilter::Errored: //: Filter option of torrents list's status filter. %L1 is a number of torrents with that status return qApp->translate("tremotesf", "Errored (%L1)").arg(item.torrents); default: break; } break; default: break; } return {}; } int StatusFiltersModel::rowCount(const QModelIndex&) const { return static_cast(mItems.size()); } QModelIndex StatusFiltersModel::indexForTorrentsProxyModelFilter() const { if (!torrentsProxyModel()) { return {}; } const auto filter = torrentsProxyModel()->statusFilter(); for (size_t i = 0, max = mItems.size(); i < max; ++i) { const auto& item = mItems[i]; if (item.filter == filter) { return index(static_cast(i)); } } return {}; } void StatusFiltersModel::resetTorrentsProxyModelFilter() const { if (torrentsProxyModel()) { torrentsProxyModel()->setStatusFilter(TorrentsProxyModel::StatusFilter::All); } } class StatusFiltersModelUpdater final : public ModelListUpdater< StatusFiltersModel, StatusFiltersModel::Item, std::map> { public: inline explicit StatusFiltersModelUpdater(StatusFiltersModel& model) : ModelListUpdater(model) {} protected: std::map::iterator findNewItemForItem( std::map& newItems, const StatusFiltersModel::Item& item ) override { return newItems.find(item.filter); } bool updateItem( StatusFiltersModel::Item& item, // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) std::pair&& newItem ) override { const auto& [filter, torrents] = newItem; if (item.torrents != torrents) { item.torrents = torrents; return true; } return false; } StatusFiltersModel::Item createItemFromNewItem( // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) std::pair&& newTracker ) override { return StatusFiltersModel::Item{.filter = newTracker.first, .torrents = newTracker.second}; } }; void StatusFiltersModel::update() { using enum TorrentsProxyModel::StatusFilter; std::map items{ {All, rpc()->torrentsCount()}, {Active, 0}, {Downloading, 0}, {Seeding, 0}, {Paused, 0}, {Checking, 0}, {Errored, 0} }; const auto processFilter = [&](Torrent* torrent, TorrentsProxyModel::StatusFilter filter) { if (TorrentsProxyModel::statusFilterAcceptsTorrent(torrent, filter)) { ++(items.find(filter)->second); } }; for (const auto& torrent : rpc()->torrents()) { processFilter(torrent.get(), Active); processFilter(torrent.get(), Downloading); processFilter(torrent.get(), Seeding); processFilter(torrent.get(), Checking); processFilter(torrent.get(), Paused); processFilter(torrent.get(), Errored); } StatusFiltersModelUpdater updater(*this); updater.update(mItems, std::move(items)); } } tremotesf-2.10.1/src/ui/screens/mainwindow/statusfiltersmodel.h000066400000000000000000000021541523411710200247310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_STATUSFILTERSMODEL_H #define TREMOTESF_STATUSFILTERSMODEL_H #include #include "basetorrentsfilterssettingsmodel.h" #include "torrentsproxymodel.h" namespace tremotesf { class StatusFiltersModel final : public BaseTorrentsFiltersSettingsModel { Q_OBJECT public: static constexpr auto FilterRole = Qt::UserRole; inline explicit StatusFiltersModel(QObject* parent = nullptr) : BaseTorrentsFiltersSettingsModel(parent) {}; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& = {}) const override; QModelIndex indexForTorrentsProxyModelFilter() const override; struct Item { TorrentsProxyModel::StatusFilter filter; int torrents; }; protected: void resetTorrentsProxyModelFilter() const override; private: void update() override; std::vector mItems; }; } #endif // TREMOTESF_STATUSFILTERSMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/torrentsmodel.cpp000066400000000000000000000476611523411710200242440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentsmodel.h" #include #include #include #include #include #include "rpc/pathutils.h" #include "rpc/rpc.h" #include "rpc/serversettings.h" #include "rpc/torrent.h" #include "desktoputils.h" #include "formatutils.h" #include "settings.h" namespace tremotesf { TorrentsModel::TorrentsModel(Rpc* rpc, QObject* parent) : QAbstractTableModel(parent), mRpc(nullptr) { setRpc(rpc); const auto settings = Settings::instance(); mUseRelativeTime = settings->get_displayRelativeTime(); QObject::connect(settings, &Settings::displayRelativeTimeChanged, this, [this, settings] { mUseRelativeTime = settings->get_displayRelativeTime(); }); mDisplayFullDownloadDirectoryPath = settings->get_displayFullDownloadDirectoryPath(); QObject::connect(settings, &Settings::displayFullDownloadDirectoryPathChanged, this, [this, settings] { mDisplayFullDownloadDirectoryPath = settings->get_displayFullDownloadDirectoryPath(); }); } int TorrentsModel::columnCount(const QModelIndex&) const { return QMetaEnum::fromType().keyCount(); } QVariant TorrentsModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } const auto* const torrent = mRpc->torrents().at(static_cast(index.row())).get(); switch (role) { case Qt::DecorationRole: switch (static_cast(index.column())) { case Column::Name: { using namespace desktoputils; using enum desktoputils::StatusIcon; if (torrent->data().error != TorrentData::Error::None) { return statusIcon(Errored); } switch (torrent->data().status) { case TorrentData::Status::Paused: return statusIcon(Paused); case TorrentData::Status::Seeding: if (torrent->data().isSeedingStalled()) { return statusIcon(StalledSeeding); } return statusIcon(Seeding); case TorrentData::Status::Downloading: if (torrent->data().isDownloadingStalled()) { return statusIcon(StalledDownloading); } return statusIcon(Downloading); case TorrentData::Status::QueuedForDownloading: case TorrentData::Status::QueuedForSeeding: return statusIcon(Queued); case TorrentData::Status::Checking: case TorrentData::Status::QueuedForChecking: return statusIcon(Checking); } break; } case Column::Priority: { using namespace desktoputils; switch (torrent->data().bandwidthPriority) { case TorrentData::Priority::High: return priorityIcon(Priority::High); case TorrentData::Priority::Normal: return priorityIcon(Priority::Normal); case TorrentData::Priority::Low: return priorityIcon(Priority::Low); } } default: break; } break; case Qt::DisplayRole: switch (static_cast(index.column())) { case Column::Name: return torrent->data().name; case Column::SizeWhenDone: return formatutils::formatByteSize(torrent->data().sizeWhenDone); case Column::TotalSize: return formatutils::formatByteSize(torrent->data().totalSize); case Column::ProgressBar: case Column::Progress: if (torrent->data().status == TorrentData::Status::Checking) { return formatutils::formatProgress(torrent->data().recheckProgress); } return formatutils::formatProgress(torrent->data().percentDone); case Column::Status: { switch (torrent->data().status) { case TorrentData::Status::Paused: if (torrent->data().hasError()) { //: Torrent status while torrent also has an error. %1 is error string return qApp->translate("tremotesf", "Paused (%1)").arg(torrent->data().errorString); } //: Torrent status return qApp->translate("tremotesf", "Paused"); case TorrentData::Status::Downloading: if (torrent->data().hasError()) { //: Torrent status while torrent also has an error. %1 is error string return qApp->translate("tremotesf", "Downloading (%1)").arg(torrent->data().errorString); } //: Torrent status return qApp->translate("tremotesf", "Downloading", "Torrent status"); case TorrentData::Status::Seeding: if (torrent->data().hasError()) { //: Torrent status while torrent also has an error. %1 is error string return qApp->translate("tremotesf", "Seeding (%1)").arg(torrent->data().errorString); } //: Torrent status return qApp->translate("tremotesf", "Seeding", "Torrent status"); case TorrentData::Status::QueuedForDownloading: case TorrentData::Status::QueuedForSeeding: if (torrent->data().hasError()) { //: Torrent status while torrent also has an error. %1 is error string return qApp->translate("tremotesf", "Queued (%1)").arg(torrent->data().errorString); } //: Torrent status return qApp->translate("tremotesf", "Queued"); case TorrentData::Status::Checking: if (torrent->data().hasError()) { //: Torrent status while torrent also has an error. %1 is error string return qApp->translate("tremotesf", "Checking (%1)").arg(torrent->data().errorString); } //: Torrent status return qApp->translate("tremotesf", "Checking"); case TorrentData::Status::QueuedForChecking: if (torrent->data().hasError()) { //: Torrent status while torrent also has an error. %1 is error string return qApp->translate("tremotesf", "Queued for checking (%1)") .arg(torrent->data().errorString); } //: Torrent status return qApp->translate("tremotesf", "Queued for checking"); } break; } case Column::Priority: switch (torrent->data().bandwidthPriority) { case TorrentData::Priority::High: //: Torrent's loading priority return qApp->translate("tremotesf", "High"); case TorrentData::Priority::Normal: //: Torrent's loading priority return qApp->translate("tremotesf", "Normal"); case TorrentData::Priority::Low: //: Torrent's loading priority return qApp->translate("tremotesf", "Low"); } break; case Column::QueuePosition: return torrent->data().queuePosition; case Column::Seeders: return torrent->data().totalSeedersFromTrackersCount; case Column::Leechers: return torrent->data().totalLeechersFromTrackersCount; case Column::PeersSendingToUs: return torrent->data().peersSendingToUsCount; case Column::PeersGettingFromUs: return torrent->data().peersGettingFromUsCount; case Column::DownloadSpeed: return formatutils::formatByteSpeed(torrent->data().downloadSpeed); case Column::UploadSpeed: return formatutils::formatByteSpeed(torrent->data().uploadSpeed); case Column::Eta: return formatutils::formatEta(torrent->data().eta); case Column::Ratio: return formatutils::formatRatio(torrent->data().ratio); case Column::AddedDate: return formatutils::formatDateTime( torrent->data().addedDate.toLocalTime(), QLocale::ShortFormat, mUseRelativeTime ); case Column::DoneDate: return formatutils::formatDateTime( torrent->data().doneDate.toLocalTime(), QLocale::ShortFormat, mUseRelativeTime ); case Column::DownloadSpeedLimit: if (torrent->data().downloadSpeedLimited) { return formatutils::formatSpeedLimit(torrent->data().downloadSpeedLimit); } break; case Column::UploadSpeedLimit: if (torrent->data().uploadSpeedLimited) { return formatutils::formatSpeedLimit(torrent->data().uploadSpeedLimit); } break; case Column::TotalDownloaded: return formatutils::formatByteSize(torrent->data().totalDownloaded); case Column::TotalUploaded: return formatutils::formatByteSize(torrent->data().totalUploaded); case Column::LeftUntilDone: return formatutils::formatByteSize(torrent->data().leftUntilDone); case Column::DownloadDirectory: if (mDisplayFullDownloadDirectoryPath) { return toNativeSeparators(torrent->data().downloadDirectory, mRpc->serverSettings()->data().pathOs); } return lastPathSegment(torrent->data().downloadDirectory); case Column::CompletedSize: return formatutils::formatByteSize(torrent->data().completedSize); case Column::ActivityDate: return formatutils::formatDateTime( torrent->data().activityDate.toLocalTime(), QLocale::ShortFormat, mUseRelativeTime ); default: break; } break; case Qt::ToolTipRole: switch (static_cast(index.column())) { case Column::Name: case Column::Status: case Column::AddedDate: case Column::DoneDate: case Column::ActivityDate: return data(index, Qt::DisplayRole); case Column::DownloadDirectory: return torrent->data().downloadDirectory; default: break; } break; case static_cast(Role::Sort): switch (static_cast(index.column())) { case Column::SizeWhenDone: return torrent->data().sizeWhenDone; case Column::TotalSize: return torrent->data().totalSize; case Column::ProgressBar: case Column::Progress: if (torrent->data().status == TorrentData::Status::Checking) { return torrent->data().recheckProgress; } return torrent->data().percentDone; case Column::Priority: return static_cast(torrent->data().bandwidthPriority); case Column::Status: return static_cast(torrent->data().status); case Column::DownloadSpeed: return torrent->data().downloadSpeed; case Column::UploadSpeed: return torrent->data().uploadSpeed; case Column::Eta: { const auto eta = torrent->data().eta; if (eta < 0) { return std::numeric_limits::max(); } return eta; } case Column::Ratio: return torrent->data().ratio; case Column::AddedDate: return torrent->data().addedDate; case Column::DoneDate: return torrent->data().doneDate; case Column::DownloadSpeedLimit: if (torrent->data().downloadSpeedLimited) { return torrent->data().downloadSpeedLimit; } return -1; case Column::UploadSpeedLimit: if (torrent->data().uploadSpeedLimited) { return torrent->data().uploadSpeedLimit; } return -1; case Column::TotalDownloaded: return torrent->data().totalDownloaded; case Column::TotalUploaded: return torrent->data().totalUploaded; case Column::LeftUntilDone: return torrent->data().leftUntilDone; case Column::CompletedSize: return torrent->data().completedSize; case Column::ActivityDate: return torrent->data().activityDate; default: return data(index, Qt::DisplayRole); } default: break; } return {}; } QVariant TorrentsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { return {}; } switch (static_cast(section)) { case Column::Name: //: Torrents list column name return qApp->translate("tremotesf", "Name"); case Column::SizeWhenDone: //: Torrents list column name return qApp->translate("tremotesf", "Size"); case Column::TotalSize: //: Torrents list column name return qApp->translate("tremotesf", "Total Size"); case Column::ProgressBar: //: Torrents list column name return qApp->translate("tremotesf", "Progress Bar"); case Column::Progress: //: Torrents list column name return qApp->translate("tremotesf", "Progress"); case Column::Priority: //: Torrents list column name return qApp->translate("tremotesf", "Priority"); case Column::QueuePosition: //: Torrents list column name return qApp->translate("tremotesf", "Queue Position"); case Column::Status: //: Torrents list column name return qApp->translate("tremotesf", "Status"); case Column::Seeders: //: Torrents list column name, number of seeders reported by trackers return qApp->translate("tremotesf", "Seeders"); case Column::Leechers: //: Torrents list column name, number of leechers reported by trackers return qApp->translate("tremotesf", "Leechers"); case Column::PeersSendingToUs: //: Torrents list column name, number of peers that we are downloading from return qApp->translate("tremotesf", "Downloading from peers"); case Column::PeersGettingFromUs: //: Torrents list column name, number of peers that we are uploading to return qApp->translate("tremotesf", "Uploading to peers"); case Column::DownloadSpeed: //: Torrents list column name return qApp->translate("tremotesf", "Down Speed"); case Column::UploadSpeed: //: Torrents list column name return qApp->translate("tremotesf", "Up Speed"); case Column::Eta: //: Torrents list column name return qApp->translate("tremotesf", "ETA"); case Column::Ratio: //: Torrents list column name return qApp->translate("tremotesf", "Ratio"); case Column::AddedDate: //: Torrents list column name, date/time when torrent was added return qApp->translate("tremotesf", "Added on"); case Column::DoneDate: //: Torrents list column name, date/time when torrent was completed return qApp->translate("tremotesf", "Completed on"); case Column::DownloadSpeedLimit: //: Torrents list column name, download speed limit return qApp->translate("tremotesf", "Down Limit"); case Column::UploadSpeedLimit: //: Torrents list column name, upload speed limit return qApp->translate("tremotesf", "Up Limit"); case Column::TotalDownloaded: //: Torrents list column name, downloaded byte size return qApp->translate("tremotesf", "Downloaded"); case Column::TotalUploaded: //: Torrents list column name, uploaded byte size return qApp->translate("tremotesf", "Uploaded"); case Column::LeftUntilDone: //: Torrents list column name, remaining byte size return qApp->translate("tremotesf", "Remaining"); case Column::DownloadDirectory: //: Torrents list column name return qApp->translate("tremotesf", "Download Directory"); case Column::CompletedSize: //: Torrents list column name, completed byte size return qApp->translate("tremotesf", "Completed"); case Column::ActivityDate: //: Torrents list column name return qApp->translate("tremotesf", "Last Activity"); default: return {}; } } int TorrentsModel::rowCount(const QModelIndex&) const { return mRpc->torrentsCount(); } Rpc* TorrentsModel::rpc() const { return mRpc; } void TorrentsModel::setRpc(Rpc* rpc) { if (rpc != mRpc) { if (const auto oldRpc = mRpc.data(); oldRpc) { QObject::disconnect(oldRpc, nullptr, this, nullptr); } mRpc = rpc; if (rpc) { QObject::connect(rpc, &Rpc::onAboutToAddTorrents, this, [=, this](size_t count) { const auto first = mRpc->torrentsCount(); beginInsertRows({}, first, first + static_cast(count) - 1); }); QObject::connect(rpc, &Rpc::onAddedTorrents, this, [=, this] { endInsertRows(); }); QObject::connect(rpc, &Rpc::onAboutToRemoveTorrents, this, [=, this](size_t first, size_t last) { beginRemoveRows({}, static_cast(first), static_cast(last - 1)); }); QObject::connect(rpc, &Rpc::onRemovedTorrents, this, [=, this] { endRemoveRows(); }); QObject::connect(rpc, &Rpc::onChangedTorrents, this, [=, this](size_t first, size_t last) { emit dataChanged( index(static_cast(first), 0), index(static_cast(last - 1), columnCount() - 1) ); }); const auto count = rpc->torrentsCount(); if (count != 0) { beginInsertRows({}, 0, count - 1); endInsertRows(); } } } } Torrent* TorrentsModel::torrentAtIndex(const QModelIndex& index) const { return torrentAtRow(index.row()); } Torrent* TorrentsModel::torrentAtRow(int row) const { return mRpc->torrents()[static_cast(row)].get(); } std::vector TorrentsModel::idsFromIndexes(const QModelIndexList& indexes) const { return indexes | std::views::transform([this](const QModelIndex& index) { return torrentAtIndex(index)->data().id; }) | std::ranges::to(); } } tremotesf-2.10.1/src/ui/screens/mainwindow/torrentsmodel.h000066400000000000000000000037311523411710200236770ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTSMODEL_H #define TREMOTESF_TORRENTSMODEL_H #include #include #include namespace tremotesf { class Torrent; } namespace tremotesf { class Rpc; class TorrentsModel final : public QAbstractTableModel { Q_OBJECT public: enum class Column { Name, SizeWhenDone, TotalSize, ProgressBar, Progress, Status, Priority, QueuePosition, Seeders, Leechers, PeersSendingToUs, PeersGettingFromUs, DownloadSpeed, UploadSpeed, Eta, Ratio, AddedDate, DoneDate, DownloadSpeedLimit, UploadSpeedLimit, TotalDownloaded, TotalUploaded, LeftUntilDone, DownloadDirectory, CompletedSize, ActivityDate }; Q_ENUM(Column) enum class Role { Sort = Qt::UserRole }; explicit TorrentsModel(Rpc* rpc = nullptr, QObject* parent = nullptr); int columnCount(const QModelIndex& = {}) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& = {}) const override; Rpc* rpc() const; void setRpc(Rpc* rpc); Torrent* torrentAtIndex(const QModelIndex& index) const; Torrent* torrentAtRow(int row) const; std::vector idsFromIndexes(const QModelIndexList& indexes) const; private: QPointer mRpc; bool mUseRelativeTime{}; bool mDisplayFullDownloadDirectoryPath{}; }; } #endif // TREMOTESF_TORRENTSMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/torrentsproxymodel.cpp000066400000000000000000000177401523411710200253410ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include "torrentsproxymodel.h" #include "rpc/torrent.h" #include "rpc/tracker.h" #include "settings.h" #include "torrentsmodel.h" namespace tremotesf { class SortFilterProxyModelHelper final { public: template static void changeRowsFilter(TorrentsProxyModel* model, Func&& func) { #if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) model->beginFilterChange(); #endif func(); #if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) model->endFilterChange(QSortFilterProxyModel::Direction::Rows); #else model->invalidateRowsFilter(); #endif } }; TorrentsProxyModel::TorrentsProxyModel(TorrentsModel* sourceModel, QObject* parent) : BaseProxyModel( sourceModel, static_cast(TorrentsModel::Role::Sort), static_cast(TorrentsModel::Column::Name), parent ), mStatusFilterEnabled(Settings::instance()->get_torrentsStatusFilterEnabled()), mStatusFilter(Settings::instance()->get_torrentsStatusFilter()), mLabelFilterEnabled(Settings::instance()->get_torrentsLabelFilterEnabled()), mLabelFilter(Settings::instance()->get_torrentsLabelFilter()), mTrackerFilterEnabled(Settings::instance()->get_torrentsTrackerFilterEnabled()), mTrackerFilter(Settings::instance()->get_torrentsTrackerFilter()), mDownloadDirectoryFilterEnabled(Settings::instance()->get_torrentsDownloadDirectoryFilterEnabled()), mDownloadDirectoryFilter(Settings::instance()->get_torrentsDownloadDirectoryFilter()) {} QString TorrentsProxyModel::searchString() const { return mSearchString; } void TorrentsProxyModel::setSearchString(const QString& string) { if (string != mSearchString) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mSearchString = string; }); } } bool TorrentsProxyModel::isStatusFilterEnabled() const { return mStatusFilterEnabled; } void TorrentsProxyModel::setStatusFilterEnabled(bool enabled) { if (enabled != mStatusFilterEnabled) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mStatusFilterEnabled = enabled; }); Settings::instance()->set_torrentsStatusFilterEnabled(mStatusFilterEnabled); } } TorrentsProxyModel::StatusFilter TorrentsProxyModel::statusFilter() const { return mStatusFilter; } void TorrentsProxyModel::setStatusFilter(TorrentsProxyModel::StatusFilter filter) { if (filter != mStatusFilter) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mStatusFilter = filter; }); emit statusFilterChanged(); Settings::instance()->set_torrentsStatusFilter(mStatusFilter); } } bool TorrentsProxyModel::isLabelFilterEnabled() const { return mLabelFilterEnabled; } void TorrentsProxyModel::setLabelFilterEnabled(bool enabled) { if (enabled != mLabelFilterEnabled) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mLabelFilterEnabled = enabled; }); Settings::instance()->set_torrentsLabelFilterEnabled(mLabelFilterEnabled); } } QString TorrentsProxyModel::labelFilter() const { return mLabelFilter; } void TorrentsProxyModel::setLabelFilter(const QString& filter) { if (filter != mLabelFilter) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mLabelFilter = filter; }); emit labelFilterChanged(); Settings::instance()->set_torrentsLabelFilter(mLabelFilter); } } bool TorrentsProxyModel::isTrackerFilterEnabled() const { return mTrackerFilterEnabled; } void TorrentsProxyModel::setTrackerFilterEnabled(bool enabled) { if (enabled != mTrackerFilterEnabled) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mTrackerFilterEnabled = enabled; }); Settings::instance()->set_torrentsTrackerFilterEnabled(mTrackerFilterEnabled); } } QString TorrentsProxyModel::trackerFilter() const { return mTrackerFilter; } void TorrentsProxyModel::setTrackerFilter(const QString& filter) { if (filter != mTrackerFilter) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mTrackerFilter = filter; }); emit trackerFilterChanged(); Settings::instance()->set_torrentsTrackerFilter(mTrackerFilter); } } bool TorrentsProxyModel::isDownloadDirectoryFilterEnabled() const { return mDownloadDirectoryFilterEnabled; } void TorrentsProxyModel::setDownloadDirectoryFilterEnabled(bool enabled) { if (enabled != mDownloadDirectoryFilterEnabled) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mDownloadDirectoryFilterEnabled = enabled; }); Settings::instance()->set_torrentsDownloadDirectoryFilterEnabled(mDownloadDirectoryFilterEnabled); } } QString TorrentsProxyModel::downloadDirectoryFilter() const { return mDownloadDirectoryFilter; } void TorrentsProxyModel::setDownloadDirectoryFilter(const QString& filter) { if (filter != mDownloadDirectoryFilter) { SortFilterProxyModelHelper::changeRowsFilter(this, [&, this] { mDownloadDirectoryFilter = filter; }); emit downloadDirectoryFilterChanged(); Settings::instance()->set_torrentsDownloadDirectoryFilter(mDownloadDirectoryFilter); } } bool TorrentsProxyModel::statusFilterAcceptsTorrent(const Torrent* torrent, StatusFilter filter) { using enum StatusFilter; switch (filter) { case Active: return ( (torrent->data().status == TorrentData::Status::Downloading && !torrent->data().isDownloadingStalled()) || (torrent->data().status == TorrentData::Status::Seeding && !torrent->data().isSeedingStalled()) ); case Downloading: return ( torrent->data().status == TorrentData::Status::Downloading || torrent->data().status == TorrentData::Status::QueuedForDownloading ); case Seeding: return ( torrent->data().status == TorrentData::Status::Seeding || torrent->data().status == TorrentData::Status::QueuedForSeeding ); case Paused: return torrent->data().status == TorrentData::Status::Paused; case Checking: return ( torrent->data().status == TorrentData::Status::Checking || torrent->data().status == TorrentData::Status::QueuedForChecking ); case Errored: return torrent->data().hasError(); default: return true; } } bool TorrentsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex&) const { const Torrent* torrent = static_cast(sourceModel())->torrentAtRow(sourceRow); if (!mSearchString.isEmpty() && !torrent->data().name.contains(mSearchString, Qt::CaseInsensitive)) { return false; } if (mStatusFilterEnabled && !statusFilterAcceptsTorrent(torrent, mStatusFilter)) { return false; } if (mLabelFilterEnabled && !mLabelFilter.isEmpty()) { if (!std::ranges::contains(torrent->data().labels, mLabelFilter)) { return false; } } if (mTrackerFilterEnabled && !mTrackerFilter.isEmpty()) { if (!std::ranges::contains(torrent->data().trackers, mTrackerFilter, &Tracker::site)) { return false; } } if (mDownloadDirectoryFilterEnabled && !mDownloadDirectoryFilter.isEmpty()) { if (torrent->data().downloadDirectory != mDownloadDirectoryFilter) { return false; } } return true; } } tremotesf-2.10.1/src/ui/screens/mainwindow/torrentsproxymodel.h000066400000000000000000000046261523411710200250050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTSPROXYMODEL_H #define TREMOTESF_TORRENTSPROXYMODEL_H #include "ui/itemmodels/baseproxymodel.h" namespace tremotesf { class Torrent; } namespace tremotesf { class TorrentsModel; class SortFilterProxyModelHelper; class TorrentsProxyModel final : public BaseProxyModel { Q_OBJECT public: enum class StatusFilter { All, Active, Downloading, Seeding, Paused, Checking, Errored }; Q_ENUM(StatusFilter) explicit TorrentsProxyModel(TorrentsModel* sourceModel, QObject* parent = nullptr); ~TorrentsProxyModel() override = default; Q_DISABLE_COPY_MOVE(TorrentsProxyModel) QString searchString() const; void setSearchString(const QString& string); bool isStatusFilterEnabled() const; void setStatusFilterEnabled(bool enabled); StatusFilter statusFilter() const; void setStatusFilter(StatusFilter filter); bool isLabelFilterEnabled() const; void setLabelFilterEnabled(bool enabled); QString labelFilter() const; void setLabelFilter(const QString& filter); bool isTrackerFilterEnabled() const; void setTrackerFilterEnabled(bool enabled); QString trackerFilter() const; void setTrackerFilter(const QString& filter); bool isDownloadDirectoryFilterEnabled() const; void setDownloadDirectoryFilterEnabled(bool enabled); QString downloadDirectoryFilter() const; void setDownloadDirectoryFilter(const QString& filter); static bool statusFilterAcceptsTorrent(const Torrent* torrent, StatusFilter filter); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex&) const override; private: QString mSearchString; bool mStatusFilterEnabled; StatusFilter mStatusFilter; bool mLabelFilterEnabled; QString mLabelFilter; bool mTrackerFilterEnabled; QString mTrackerFilter; bool mDownloadDirectoryFilterEnabled; QString mDownloadDirectoryFilter; friend class SortFilterProxyModelHelper; signals: void statusFilterChanged(); void labelFilterChanged(); void trackerFilterChanged(); void downloadDirectoryFilterChanged(); }; } #endif // TREMOTESF_TORRENTSPROXYMODEL_H tremotesf-2.10.1/src/ui/screens/mainwindow/torrentsview.cpp000066400000000000000000000046741523411710200241130ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentsview.h" #include #include #include "ui/widgets/basetreeview.h" #include "ui/widgets/progressbardelegate.h" #include "downloaddirectorydelegate.h" #include "settings.h" #include "torrentsmodel.h" #include "torrentsproxymodel.h" namespace tremotesf { TorrentsView::TorrentsView(TorrentsProxyModel* model, QWidget* parent) : QTreeView(parent) { setCommonTreeViewProperties(this, true); setContextMenuPolicy(Qt::CustomContextMenu); setItemDelegate(new TooltipWhenElidedDelegate(this)); setItemDelegateForColumn( static_cast(TorrentsModel::Column::ProgressBar), new ProgressBarDelegate(static_cast(TorrentsModel::Role::Sort), this) ); setItemDelegateForColumn( static_cast(TorrentsModel::Column::DownloadDirectory), new DownloadDirectoryDelegate(this) ); setModel(model); setSelectionMode(QAbstractItemView::ExtendedSelection); const auto header = this->header(); if (!header->restoreState(Settings::instance()->get_torrentsViewHeaderState())) { using enum TorrentsModel::Column; const std::set defaultColumns{ Name, TotalSize, ProgressBar, Status, Seeders, Leechers, PeersSendingToUs, PeersGettingFromUs, DownloadSpeed, UploadSpeed, Eta, Ratio, AddedDate, }; for (int i = 0, max = header->count(); i < max; ++i) { if (!defaultColumns.contains(static_cast(i))) { header->hideSection(i); } } header->moveSection( header->visualIndex(static_cast(AddedDate)), header->visualIndex(static_cast(Status)) + 1 ); header->moveSection( header->visualIndex(static_cast(Eta)), header->visualIndex(static_cast(AddedDate)) + 1 ); sortByColumn(static_cast(AddedDate), Qt::DescendingOrder); } } void TorrentsView::saveState() { Settings::instance()->set_torrentsViewHeaderState(header()->saveState()); } } tremotesf-2.10.1/src/ui/screens/mainwindow/torrentsview.h000066400000000000000000000010531523411710200235440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTSVIEW_H #define TREMOTESF_TORRENTSVIEW_H #include namespace tremotesf { class TorrentsProxyModel; class TorrentsView final : public QTreeView { Q_OBJECT public: TorrentsView(TorrentsProxyModel* model, QWidget* parent = nullptr); ~TorrentsView() override = default; Q_DISABLE_COPY_MOVE(TorrentsView) void saveState(); }; } #endif // TREMOTESF_TORRENTSVIEW_H tremotesf-2.10.1/src/ui/screens/serversettings/000077500000000000000000000000001523411710200215345ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/screens/serversettings/serversettingsdialog.cpp000066400000000000000000000727721523411710200265260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "serversettingsdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stdutils.h" #include "rpc/numbervaluerange.h" #include "rpc/rpc.h" #include "rpc/serversettings.h" #include "ui/widgets/remotedirectoryselectionwidget.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { constexpr std::array encryptionModeComboBoxItems{ ServerSettingsData::EncryptionMode::Allowed, ServerSettingsData::EncryptionMode::Preferred, ServerSettingsData::EncryptionMode::Required }; ServerSettingsData::EncryptionMode encryptionModeFromComboBoxItem(int index) { if (index == -1) { return {}; } return encryptionModeComboBoxItems.at(static_cast(index)); } } ServerSettingsDialog::ServerSettingsDialog(const Rpc* rpc, QWidget* parent) : QDialog(parent), mRpc(rpc) { //: Dialog title setWindowTitle(qApp->translate("tremotesf", "Server Options")); setupUi(); resize(sizeHint().expandedTo(QSize(700, 550))); const auto onConnectedChanged = [this] { if (mRpc->isConnected()) { mDisconnectedMessageWidget->animatedHide(); mDisconnectedMessageWidget->setEnabled(true); mDownloadingPageWidget->setEnabled(true); mSeedingPageWidget->setEnabled(true); mQueuePageWidget->setEnabled(true); mSpeedPageWidget->setEnabled(true); mNetworkPageWidget->setEnabled(true); loadSettings(); } else { mDisconnectedMessageWidget->hide(); mDisconnectedMessageWidget->animatedShow(); mDownloadingPageWidget->setEnabled(false); mSeedingPageWidget->setEnabled(false); mQueuePageWidget->setEnabled(false); mSpeedPageWidget->setEnabled(false); mNetworkPageWidget->setEnabled(false); } }; QObject::connect(mRpc, &Rpc::connectedChanged, this, onConnectedChanged); onConnectedChanged(); loadSettings(); } void ServerSettingsDialog::accept() { ServerSettings* settings = mRpc->serverSettings(); settings->setSaveOnSet(false); settings->setDownloadDirectory(mDownloadDirectoryWidget->path()); settings->setStartAddedTorrents(mStartAddedTorrentsCheckBox->isChecked()); //settings->setTrashTorrentFiles(mTrashTorrentFilesCheckBox->isChecked()); settings->setRenameIncompleteFiles(mIncompleteFilesCheckBox->isChecked()); settings->setIncompleteDirectoryEnabled(mIncompleteDirectoryCheckBox->isChecked()); settings->setIncompleteDirectory(mIncompleteDirectoryWidget->path()); settings->setRatioLimited(mRatioLimitCheckBox->isChecked()); settings->setRatioLimit(mRatioLimitSpinBox->value()); settings->setIdleSeedingLimited(mIdleSeedingLimitCheckBox->isChecked()); settings->setIdleSeedingLimit(mIdleSeedingLimitSpinBox->value()); settings->setDownloadQueueEnabled(mMaximumActiveDownloadsCheckBox->isChecked()); settings->setDownloadQueueSize(mMaximumActiveDownloadsSpinBox->value()); settings->setSeedQueueEnabled(mMaximumActiveUploadsCheckBox->isChecked()); settings->setSeedQueueSize(mMaximumActiveUploadsSpinBox->value()); settings->setIdleQueueLimited(mIdleQueueLimitCheckBox->isChecked()); settings->setIdleQueueLimit(mIdleQueueLimitSpinBox->value()); settings->setDownloadSpeedLimited(mDownloadSpeedLimitCheckBox->isChecked()); settings->setDownloadSpeedLimit(mDownloadSpeedLimitSpinBox->value()); settings->setUploadSpeedLimited(mUploadSpeedLimitCheckBox->isChecked()); settings->setUploadSpeedLimit(mUploadSpeedLimitSpinBox->value()); settings->setAlternativeSpeedLimitsEnabled(mEnableAlternativeSpeedLimitsGroupBox->isChecked()); settings->setAlternativeDownloadSpeedLimit(mAlternativeDownloadSpeedLimitSpinBox->value()); settings->setAlternativeUploadSpeedLimit(mAlternativeUploadSpeedLimitSpinBox->value()); settings->setAlternativeSpeedLimitsScheduled(mLimitScheduleGroupBox->isChecked()); settings->setAlternativeSpeedLimitsBeginTime(mLimitScheduleBeginTimeEdit->time()); settings->setAlternativeSpeedLimitsEndTime(mLimitScheduleEndTimeEdit->time()); settings->setAlternativeSpeedLimitsDays( static_cast( mLimitScheduleDaysComboBox->currentData().toInt() ) ); settings->setPeerPort(mPeerPortSpinBox->value()); settings->setRandomPortEnabled(mRandomPortCheckBox->isChecked()); settings->setPortForwardingEnabled(mPortForwardingCheckBox->isChecked()); settings->setEncryptionMode(encryptionModeFromComboBoxItem(mEncryptionComboBox->currentIndex())); settings->setUtpEnabled(mUtpCheckBox->isChecked()); settings->setPexEnabled(mPexCheckBox->isChecked()); settings->setDhtEnabled(mDhtCheckBox->isChecked()); settings->setLpdEnabled(mLpdCheckBox->isChecked()); settings->setMaximumPeersPerTorrent(mTorrentPeerLimitSpinBox->value()); settings->setMaximumPeersGlobally(mGlobalPeerLimitSpinBox->value()); settings->save(); settings->setSaveOnSet(true); QDialog::accept(); } void ServerSettingsDialog::setupUi() { // // Creating layout // auto layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); //: Message that appears when disconnected from server mDisconnectedMessageWidget = new KMessageWidget(qApp->translate("tremotesf", "Disconnected"), this); mDisconnectedMessageWidget->setCloseButtonVisible(false); mDisconnectedMessageWidget->setMessageType(KMessageWidget::Warning); mDisconnectedMessageWidget->setVisible(false); layout->addWidget(mDisconnectedMessageWidget); auto pageWidget = new KPageWidget(this); // Downloading page mDownloadingPageWidget = new QWidget(this); KPageWidgetItem* downloadingPageItem = pageWidget->addPage( mDownloadingPageWidget, //: "Downloading" server setting page qApp->translate("tremotesf", "Downloading", "Noun") ); downloadingPageItem->setIcon(QIcon::fromTheme("folder-download"_L1)); auto downloadingPageLayout = new QFormLayout(mDownloadingPageWidget); downloadingPageLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mDownloadDirectoryWidget = new RemoteDirectorySelectionWidget(this); mDownloadDirectoryWidget->setup({}, mRpc); downloadingPageLayout->addRow(qApp->translate("tremotesf", "Download directory:"), mDownloadDirectoryWidget); //: Check box label mStartAddedTorrentsCheckBox = new QCheckBox(qApp->translate("tremotesf", "Start added torrents"), this); downloadingPageLayout->addRow(mStartAddedTorrentsCheckBox); /*mTrashTorrentFilesCheckBox = new QCheckBox(qApp->translate("tremotesf", "Trash .torrent files"), this); downloadingPageLayout->addRow(mTrashTorrentFilesCheckBox);*/ mIncompleteFilesCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Append \".part\" to names of incomplete files"), this ); downloadingPageLayout->addRow(mIncompleteFilesCheckBox); mIncompleteDirectoryCheckBox = new QCheckBox(qApp->translate("tremotesf", "Directory for incomplete files:"), this); downloadingPageLayout->addRow(mIncompleteDirectoryCheckBox); auto incompleteDirectoryWidgetLayout = new QHBoxLayout(); downloadingPageLayout->addRow(incompleteDirectoryWidgetLayout); mIncompleteDirectoryWidget = new RemoteDirectorySelectionWidget(this); mIncompleteDirectoryWidget->setup({}, mRpc); mIncompleteDirectoryWidget->setEnabled(false); QObject::connect( mIncompleteDirectoryCheckBox, &QCheckBox::toggled, mIncompleteDirectoryWidget, &RemoteDirectorySelectionWidget::setEnabled ); //downloadingPageLayout->addRow(mIncompleteDirectoryCheckBox, mIncompleteDirectoryWidget); incompleteDirectoryWidgetLayout->addSpacing(28); incompleteDirectoryWidgetLayout->addWidget(mIncompleteDirectoryWidget); // Seeding page mSeedingPageWidget = new QWidget(this); KPageWidgetItem* seedingPageItem = pageWidget->addPage( mSeedingPageWidget, //: "Seeding" server setting page qApp->translate("tremotesf", "Seeding", "Noun") ); seedingPageItem->setIcon(QIcon::fromTheme("network-server"_L1)); auto seedingPageLayout = new QGridLayout(mSeedingPageWidget); mRatioLimitCheckBox = new QCheckBox(qApp->translate("tremotesf", "Stop seeding at ratio:"), this); seedingPageLayout->addWidget(mRatioLimitCheckBox, 0, 0, 1, 2, Qt::AlignTop); mRatioLimitSpinBox = new QDoubleSpinBox(this); mRatioLimitSpinBox->setEnabled(false); setSpinBoxLimits(mRatioLimitSpinBox, nonNegativeDecimalsRange); QObject::connect(mRatioLimitCheckBox, &QCheckBox::toggled, mRatioLimitSpinBox, &QSpinBox::setEnabled); seedingPageLayout->addWidget(mRatioLimitSpinBox, 1, 1, Qt::AlignTop); mIdleSeedingLimitCheckBox = new QCheckBox(qApp->translate("tremotesf", "Stop seeding if idle for:"), this); seedingPageLayout->addWidget(mIdleSeedingLimitCheckBox, 2, 0, 1, 2, Qt::AlignTop); mIdleSeedingLimitSpinBox = new QSpinBox(this); mIdleSeedingLimitSpinBox->setEnabled(false); setSpinBoxLimits(mIdleSeedingLimitSpinBox, minutesRange); //: Suffix that is added to input field with number of minuts, e.g. "5 min" mIdleSeedingLimitSpinBox->setSuffix(qApp->translate("tremotesf", " min")); QObject::connect( mIdleSeedingLimitCheckBox, &QCheckBox::toggled, mIdleSeedingLimitSpinBox, &QSpinBox::setEnabled ); seedingPageLayout->addWidget(mIdleSeedingLimitSpinBox, 3, 1, Qt::AlignTop); seedingPageLayout->setRowStretch(3, 1); seedingPageLayout->setColumnStretch(1, 1); seedingPageLayout->setColumnMinimumWidth(0, 28 - seedingPageLayout->spacing()); // Queue page mQueuePageWidget = new QWidget(this); KPageWidgetItem* queuePageItem = pageWidget->addPage( mQueuePageWidget, //: "Queue" server settings page qApp->translate("tremotesf", "Queue") ); queuePageItem->setIcon(QIcon::fromTheme("applications-utilities"_L1)); auto queuePageLayout = new QGridLayout(mQueuePageWidget); mMaximumActiveDownloadsCheckBox = new QCheckBox(qApp->translate("tremotesf", "Maximum active downloads:"), this); queuePageLayout->addWidget(mMaximumActiveDownloadsCheckBox, 0, 0, 1, 2, Qt::AlignTop); mMaximumActiveDownloadsSpinBox = new QSpinBox(this); mMaximumActiveDownloadsSpinBox->setEnabled(false); setSpinBoxLimits(mMaximumActiveDownloadsSpinBox, nonNegativeIntegersRange); QObject::connect( mMaximumActiveDownloadsCheckBox, &QCheckBox::toggled, mMaximumActiveDownloadsSpinBox, &QSpinBox::setEnabled ); queuePageLayout->addWidget(mMaximumActiveDownloadsSpinBox, 1, 1, Qt::AlignTop); mMaximumActiveUploadsCheckBox = new QCheckBox(qApp->translate("tremotesf", "Maximum active uploads:"), this); queuePageLayout->addWidget(mMaximumActiveUploadsCheckBox, 2, 0, 1, 2, Qt::AlignTop); mMaximumActiveUploadsSpinBox = new QSpinBox(this); mMaximumActiveUploadsSpinBox->setEnabled(false); setSpinBoxLimits(mMaximumActiveUploadsSpinBox, nonNegativeIntegersRange); QObject::connect( mMaximumActiveUploadsCheckBox, &QCheckBox::toggled, mMaximumActiveUploadsSpinBox, &QSpinBox::setEnabled ); queuePageLayout->addWidget(mMaximumActiveUploadsSpinBox, 3, 1, Qt::AlignTop); mIdleQueueLimitCheckBox = new QCheckBox(qApp->translate("tremotesf", "Ignore queue position if idle for:"), this); queuePageLayout->addWidget(mIdleQueueLimitCheckBox, 4, 0, 1, 2, Qt::AlignTop); mIdleQueueLimitSpinBox = new QSpinBox(this); mIdleQueueLimitSpinBox->setEnabled(false); setSpinBoxLimits(mIdleQueueLimitSpinBox, minutesRange); //: Suffix that is added to input field with number of minuts, e.g. "5 min" mIdleQueueLimitSpinBox->setSuffix(qApp->translate("tremotesf", " min")); QObject::connect(mIdleQueueLimitCheckBox, &QCheckBox::toggled, mIdleQueueLimitSpinBox, &QSpinBox::setEnabled); queuePageLayout->addWidget(mIdleQueueLimitSpinBox, 5, 1, Qt::AlignTop); queuePageLayout->setRowStretch(5, 1); queuePageLayout->setColumnStretch(1, 1); queuePageLayout->setColumnMinimumWidth(0, 28 - queuePageLayout->spacing()); // Speed page mSpeedPageWidget = new QWidget(this); KPageWidgetItem* speedPageItem = pageWidget->addPage( mSpeedPageWidget, //: "Speed" server settings page qApp->translate("tremotesf", "Speed") ); speedPageItem->setIcon(QIcon::fromTheme("preferences-system-time"_L1)); auto speedPageLayout = new QVBoxLayout(mSpeedPageWidget); //: Speed limits section auto speedLimitsGroupBox = new QGroupBox(qApp->translate("tremotesf", "Limits"), this); auto speedLimitsGroupBoxLayout = new QGridLayout(speedLimitsGroupBox); //: Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes const QString suffix(qApp->translate("tremotesf", " kB/s")); //: Download speed limit input field label mDownloadSpeedLimitCheckBox = new QCheckBox(qApp->translate("tremotesf", "Download:"), this); speedLimitsGroupBoxLayout->addWidget(mDownloadSpeedLimitCheckBox, 0, 0, 1, 2); mDownloadSpeedLimitSpinBox = new QSpinBox(this); mDownloadSpeedLimitSpinBox->setEnabled(false); setSpinBoxLimits(mDownloadSpeedLimitSpinBox, speedLimitKbpsRange); mDownloadSpeedLimitSpinBox->setSuffix(suffix); QObject::connect( mDownloadSpeedLimitCheckBox, &QCheckBox::toggled, mDownloadSpeedLimitSpinBox, &QSpinBox::setEnabled ); speedLimitsGroupBoxLayout->addWidget(mDownloadSpeedLimitSpinBox, 1, 1); //: Upload speed limit input field label mUploadSpeedLimitCheckBox = new QCheckBox(qApp->translate("tremotesf", "Upload:"), this); speedLimitsGroupBoxLayout->addWidget(mUploadSpeedLimitCheckBox, 2, 0, 1, 2); mUploadSpeedLimitSpinBox = new QSpinBox(this); mUploadSpeedLimitSpinBox->setEnabled(false); setSpinBoxLimits(mUploadSpeedLimitSpinBox, speedLimitKbpsRange); mUploadSpeedLimitSpinBox->setSuffix(suffix); QObject::connect( mUploadSpeedLimitCheckBox, &QCheckBox::toggled, mUploadSpeedLimitSpinBox, &QSpinBox::setEnabled ); speedLimitsGroupBoxLayout->addWidget(mUploadSpeedLimitSpinBox, 3, 1); speedLimitsGroupBoxLayout->setColumnMinimumWidth(0, 28 - speedLimitsGroupBoxLayout->spacing()); speedPageLayout->addWidget(speedLimitsGroupBox); //: Alternative speed limits section auto alternativeSpeedLimitsGroupBox = new QGroupBox(qApp->translate("tremotesf", "Alternative Limits"), this); auto alternativeSpeedLimitsGroupBoxLayout = new QVBoxLayout(alternativeSpeedLimitsGroupBox); //: Check box label mEnableAlternativeSpeedLimitsGroupBox = new QGroupBox(qApp->translate("tremotesf", "Enable"), this); mEnableAlternativeSpeedLimitsGroupBox->setCheckable(true); auto enableAlternativeLimitsGroupBoxLayout = new QFormLayout(mEnableAlternativeSpeedLimitsGroupBox); enableAlternativeLimitsGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mAlternativeDownloadSpeedLimitSpinBox = new QSpinBox(this); setSpinBoxLimits(mAlternativeDownloadSpeedLimitSpinBox, speedLimitKbpsRange); mAlternativeDownloadSpeedLimitSpinBox->setSuffix(suffix); enableAlternativeLimitsGroupBoxLayout->addRow( //: Download speed limit input field label qApp->translate("tremotesf", "Download:"), mAlternativeDownloadSpeedLimitSpinBox ); mAlternativeUploadSpeedLimitSpinBox = new QSpinBox(this); setSpinBoxLimits(mAlternativeUploadSpeedLimitSpinBox, speedLimitKbpsRange); mAlternativeUploadSpeedLimitSpinBox->setSuffix(suffix); enableAlternativeLimitsGroupBoxLayout->addRow( //: Upload speed limit input field label qApp->translate("tremotesf", "Upload:"), mAlternativeUploadSpeedLimitSpinBox ); alternativeSpeedLimitsGroupBoxLayout->addWidget(mEnableAlternativeSpeedLimitsGroupBox); //: Title of alternative speed limit scheduling section mLimitScheduleGroupBox = new QGroupBox(qApp->translate("tremotesf", "Scheduled"), this); mLimitScheduleGroupBox->setCheckable(true); auto scheduleGroupBoxLayout = new QFormLayout(mLimitScheduleGroupBox); scheduleGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); auto scheduleTimeLayout = new QHBoxLayout(); scheduleGroupBoxLayout->addRow(scheduleTimeLayout); mLimitScheduleBeginTimeEdit = new QTimeEdit(this); scheduleTimeLayout->addWidget(mLimitScheduleBeginTimeEdit, 1); //: Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" scheduleTimeLayout->addWidget(new QLabel(qApp->translate("tremotesf", "to"))); mLimitScheduleEndTimeEdit = new QTimeEdit(this); scheduleTimeLayout->addWidget(mLimitScheduleEndTimeEdit, 1); mLimitScheduleDaysComboBox = new QComboBox(this); mLimitScheduleDaysComboBox->addItem( qApp->translate("tremotesf", "Every day"), QVariant::fromValue(ServerSettingsData::AlternativeSpeedLimitsDays::All) ); mLimitScheduleDaysComboBox->addItem( qApp->translate("tremotesf", "Weekdays"), QVariant::fromValue(ServerSettingsData::AlternativeSpeedLimitsDays::Weekdays) ); mLimitScheduleDaysComboBox->addItem( qApp->translate("tremotesf", "Weekends"), QVariant::fromValue(ServerSettingsData::AlternativeSpeedLimitsDays::Weekends) ); mLimitScheduleDaysComboBox->insertSeparator(mLimitScheduleDaysComboBox->count()); { auto nextDay = [](Qt::DayOfWeek day) { if (day == Qt::Sunday) { return Qt::Monday; } return static_cast(day + 1); }; auto daysFromQtDay = [](Qt::DayOfWeek day) { switch (day) { case Qt::Monday: return ServerSettingsData::AlternativeSpeedLimitsDays::Monday; case Qt::Tuesday: return ServerSettingsData::AlternativeSpeedLimitsDays::Tuesday; case Qt::Wednesday: return ServerSettingsData::AlternativeSpeedLimitsDays::Wednesday; case Qt::Thursday: return ServerSettingsData::AlternativeSpeedLimitsDays::Thursday; case Qt::Friday: return ServerSettingsData::AlternativeSpeedLimitsDays::Friday; case Qt::Saturday: return ServerSettingsData::AlternativeSpeedLimitsDays::Saturday; case Qt::Sunday: return ServerSettingsData::AlternativeSpeedLimitsDays::Sunday; } return ServerSettingsData::AlternativeSpeedLimitsDays::All; }; const QLocale locale; const Qt::DayOfWeek first = QLocale().firstDayOfWeek(); mLimitScheduleDaysComboBox->addItem(locale.dayName(first), QVariant::fromValue(daysFromQtDay(first))); for (Qt::DayOfWeek day = nextDay(first); day != first; day = nextDay(day)) { mLimitScheduleDaysComboBox->addItem(locale.dayName(day), QVariant::fromValue(daysFromQtDay(day))); } } scheduleGroupBoxLayout->addRow(qApp->translate("tremotesf", "Days:"), mLimitScheduleDaysComboBox); alternativeSpeedLimitsGroupBoxLayout->addWidget(mLimitScheduleGroupBox); auto alternativeSpeedLimitsResizer = new KColumnResizer(this); alternativeSpeedLimitsResizer->addWidgetsFromLayout(enableAlternativeLimitsGroupBoxLayout); alternativeSpeedLimitsResizer->addWidgetsFromLayout(scheduleGroupBoxLayout); speedPageLayout->addWidget(alternativeSpeedLimitsGroupBox); speedPageLayout->addStretch(); // Network page mNetworkPageWidget = new QWidget(this); KPageWidgetItem* networkPageItem = pageWidget->addPage( mNetworkPageWidget, //: "Network" server settings page qApp->translate("tremotesf", "Network") ); networkPageItem->setIcon(QIcon::fromTheme("preferences-system-network"_L1)); auto networkPageLayout = new QVBoxLayout(mNetworkPageWidget); //: Title of settings section related to peer connections auto connectionGroupBox = new QGroupBox(qApp->translate("tremotesf", "Connection"), this); auto connectionGroupBoxLayout = new QFormLayout(connectionGroupBox); connectionGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mPeerPortSpinBox = new QSpinBox(this); setSpinBoxLimits(mPeerPortSpinBox, portsRange); connectionGroupBoxLayout->addRow(qApp->translate("tremotesf", "Peer port:"), mPeerPortSpinBox); //: Check box label mRandomPortCheckBox = new QCheckBox(qApp->translate("tremotesf", "Random port on Transmission start"), this); connectionGroupBoxLayout->addRow(mRandomPortCheckBox); //: Check box label mPortForwardingCheckBox = new QCheckBox(qApp->translate("tremotesf", "Enable port forwarding")); connectionGroupBoxLayout->addRow(mPortForwardingCheckBox); mEncryptionComboBox = new QComboBox(); for (const auto mode : encryptionModeComboBoxItems) { switch (mode) { case ServerSettingsData::EncryptionMode::Allowed: //: Encryption mode (allow/prefer/require) mEncryptionComboBox->addItem(qApp->translate("tremotesf", "Allow")); break; case ServerSettingsData::EncryptionMode::Preferred: //: Encryption mode (allow/prefer/require) mEncryptionComboBox->addItem(qApp->translate("tremotesf", "Prefer")); break; case ServerSettingsData::EncryptionMode::Required: //: Encryption mode (allow/prefer/require) mEncryptionComboBox->addItem(qApp->translate("tremotesf", "Require")); break; } } connectionGroupBoxLayout->addRow(qApp->translate("tremotesf", "Encryption:"), mEncryptionComboBox); //: Check box label mUtpCheckBox = new QCheckBox(qApp->translate("tremotesf", "Enable μTP (Micro Transport Protocol)"), this); connectionGroupBoxLayout->addRow(mUtpCheckBox); //: Check box label mPexCheckBox = new QCheckBox(qApp->translate("tremotesf", "Enable PEX (Peer exchange)"), this); connectionGroupBoxLayout->addRow(mPexCheckBox); //: Check box label mDhtCheckBox = new QCheckBox(qApp->translate("tremotesf", "Enable DHT"), this); connectionGroupBoxLayout->addRow(mDhtCheckBox); //: Check box label mLpdCheckBox = new QCheckBox(qApp->translate("tremotesf", "Enable local peer discovery"), this); connectionGroupBoxLayout->addRow(mLpdCheckBox); networkPageLayout->addWidget(connectionGroupBox); auto peerLimitsGroupBox = new QGroupBox(qApp->translate("tremotesf", "Peer Limits"), this); auto peerLimitsGroupBoxLayout = new QFormLayout(peerLimitsGroupBox); peerLimitsGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); mTorrentPeerLimitSpinBox = new QSpinBox(this); setSpinBoxLimits(mTorrentPeerLimitSpinBox, peersLimitRange); peerLimitsGroupBoxLayout->addRow( qApp->translate("tremotesf", "Maximum peers per torrent:"), mTorrentPeerLimitSpinBox ); mGlobalPeerLimitSpinBox = new QSpinBox(this); setSpinBoxLimits(mGlobalPeerLimitSpinBox, peersLimitRange); peerLimitsGroupBoxLayout->addRow( qApp->translate("tremotesf", "Maximum peers globally:"), mGlobalPeerLimitSpinBox ); networkPageLayout->addWidget(peerLimitsGroupBox); networkPageLayout->addStretch(); auto networkPageResizer = new KColumnResizer(this); networkPageResizer->addWidgetsFromLayout(connectionGroupBoxLayout); networkPageResizer->addWidgetsFromLayout(peerLimitsGroupBoxLayout); layout->addWidget(pageWidget); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &ServerSettingsDialog::accept); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &ServerSettingsDialog::reject); pageWidget->setPageFooter(dialogButtonBox); } void ServerSettingsDialog::loadSettings() { const ServerSettings* settings = mRpc->serverSettings(); mDownloadDirectoryWidget->resetPath(settings->data().downloadDirectory); mStartAddedTorrentsCheckBox->setChecked(settings->data().startAddedTorrents); mIncompleteFilesCheckBox->setChecked(settings->data().renameIncompleteFiles); mIncompleteDirectoryCheckBox->setChecked(settings->data().incompleteDirectoryEnabled); mIncompleteDirectoryWidget->resetPath(settings->data().incompleteDirectory); mRatioLimitCheckBox->setChecked(settings->data().ratioLimited); mRatioLimitSpinBox->setValue(settings->data().ratioLimit); mIdleSeedingLimitCheckBox->setChecked(settings->data().idleSeedingLimited); mIdleSeedingLimitSpinBox->setValue(settings->data().idleSeedingLimit); mMaximumActiveDownloadsCheckBox->setChecked(settings->data().downloadQueueEnabled); mMaximumActiveDownloadsSpinBox->setValue(settings->data().downloadQueueSize); mMaximumActiveUploadsCheckBox->setChecked(settings->data().seedQueueEnabled); mMaximumActiveUploadsSpinBox->setValue(settings->data().seedQueueSize); mIdleQueueLimitCheckBox->setChecked(settings->data().idleQueueLimited); mIdleQueueLimitSpinBox->setValue(settings->data().idleQueueLimit); mDownloadSpeedLimitCheckBox->setChecked(settings->data().downloadSpeedLimited); mDownloadSpeedLimitSpinBox->setValue(settings->data().downloadSpeedLimit); mUploadSpeedLimitCheckBox->setChecked(settings->data().uploadSpeedLimited); mUploadSpeedLimitSpinBox->setValue(settings->data().uploadSpeedLimit); mEnableAlternativeSpeedLimitsGroupBox->setChecked(settings->data().alternativeSpeedLimitsEnabled); mAlternativeDownloadSpeedLimitSpinBox->setValue(settings->data().alternativeDownloadSpeedLimit); mAlternativeUploadSpeedLimitSpinBox->setValue(settings->data().alternativeUploadSpeedLimit); mLimitScheduleGroupBox->setChecked(settings->data().alternativeSpeedLimitsScheduled); mLimitScheduleBeginTimeEdit->setTime(settings->data().alternativeSpeedLimitsBeginTime); mLimitScheduleEndTimeEdit->setTime(settings->data().alternativeSpeedLimitsEndTime); const auto days = settings->data().alternativeSpeedLimitsDays; for (int i = 0, max = mLimitScheduleDaysComboBox->count(); i < max; i++) { if (mLimitScheduleDaysComboBox->itemData(i).value() == days) { mLimitScheduleDaysComboBox->setCurrentIndex(i); break; } } mPeerPortSpinBox->setValue(settings->data().peerPort); mRandomPortCheckBox->setChecked(settings->data().randomPortEnabled); mPortForwardingCheckBox->setChecked(settings->data().portForwardingEnabled); mEncryptionComboBox->setCurrentIndex( // NOLINTNEXTLINE(bugprone-unchecked-optional-access) indexOfCasted(encryptionModeComboBoxItems, settings->data().encryptionMode).value() ); mUtpCheckBox->setChecked(settings->data().utpEnabled); mPexCheckBox->setChecked(settings->data().pexEnabled); mDhtCheckBox->setChecked(settings->data().dhtEnabled); mLpdCheckBox->setChecked(settings->data().lpdEnabled); mTorrentPeerLimitSpinBox->setValue(settings->data().maximumPeersPerTorrent); mGlobalPeerLimitSpinBox->setValue(settings->data().maximumPeersGlobally); } } tremotesf-2.10.1/src/ui/screens/serversettings/serversettingsdialog.h000066400000000000000000000060031523411710200261530ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SERVERSETTINGSDIALOG_H #define TREMOTESF_SERVERSETTINGSDIALOG_H #include class QCheckBox; class QComboBox; class QDoubleSpinBox; class QGroupBox; class QSpinBox; class QTimeEdit; class KMessageWidget; namespace tremotesf { class RemoteDirectorySelectionWidget; class Rpc; class ServerSettingsDialog final : public QDialog { Q_OBJECT public: explicit ServerSettingsDialog(const Rpc* rpc, QWidget* parent = nullptr); void accept() override; private: void setupUi(); void loadSettings(); const Rpc* mRpc; KMessageWidget* mDisconnectedMessageWidget = nullptr; QWidget* mDownloadingPageWidget = nullptr; RemoteDirectorySelectionWidget* mDownloadDirectoryWidget = nullptr; QCheckBox* mStartAddedTorrentsCheckBox = nullptr; QCheckBox* mIncompleteFilesCheckBox = nullptr; QCheckBox* mIncompleteDirectoryCheckBox = nullptr; RemoteDirectorySelectionWidget* mIncompleteDirectoryWidget = nullptr; QWidget* mSeedingPageWidget = nullptr; QCheckBox* mRatioLimitCheckBox = nullptr; QDoubleSpinBox* mRatioLimitSpinBox = nullptr; QCheckBox* mIdleSeedingLimitCheckBox = nullptr; QSpinBox* mIdleSeedingLimitSpinBox = nullptr; QWidget* mQueuePageWidget = nullptr; QCheckBox* mMaximumActiveDownloadsCheckBox = nullptr; QSpinBox* mMaximumActiveDownloadsSpinBox = nullptr; QCheckBox* mMaximumActiveUploadsCheckBox = nullptr; QSpinBox* mMaximumActiveUploadsSpinBox = nullptr; QCheckBox* mIdleQueueLimitCheckBox = nullptr; QSpinBox* mIdleQueueLimitSpinBox = nullptr; QWidget* mSpeedPageWidget = nullptr; QCheckBox* mDownloadSpeedLimitCheckBox = nullptr; QSpinBox* mDownloadSpeedLimitSpinBox = nullptr; QCheckBox* mUploadSpeedLimitCheckBox = nullptr; QSpinBox* mUploadSpeedLimitSpinBox = nullptr; QGroupBox* mEnableAlternativeSpeedLimitsGroupBox = nullptr; QSpinBox* mAlternativeDownloadSpeedLimitSpinBox = nullptr; QSpinBox* mAlternativeUploadSpeedLimitSpinBox = nullptr; QGroupBox* mLimitScheduleGroupBox = nullptr; QTimeEdit* mLimitScheduleBeginTimeEdit = nullptr; QTimeEdit* mLimitScheduleEndTimeEdit = nullptr; QComboBox* mLimitScheduleDaysComboBox = nullptr; QWidget* mNetworkPageWidget = nullptr; QSpinBox* mPeerPortSpinBox = nullptr; QCheckBox* mRandomPortCheckBox = nullptr; QCheckBox* mPortForwardingCheckBox = nullptr; QComboBox* mEncryptionComboBox = nullptr; QCheckBox* mUtpCheckBox = nullptr; QCheckBox* mPexCheckBox = nullptr; QCheckBox* mDhtCheckBox = nullptr; QCheckBox* mLpdCheckBox = nullptr; QSpinBox* mTorrentPeerLimitSpinBox = nullptr; QSpinBox* mGlobalPeerLimitSpinBox = nullptr; }; } #endif // TREMOTESF_SERVERSETTINGSDIALOG_H tremotesf-2.10.1/src/ui/screens/serverstatsdialog.cpp000066400000000000000000000143411523411710200227210ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "serverstatsdialog.h" #include #include #include #include #include #include #include #include #include #include "rpc/serverstats.h" #include "rpc/rpc.h" #include "formatutils.h" namespace tremotesf { ServerStatsDialog::ServerStatsDialog(Rpc* rpc, QWidget* parent) : QDialog(parent) { //: Dialog title setWindowTitle(qApp->translate("tremotesf", "Server Stats")); auto layout = new QVBoxLayout(this); //: Message that appears when disconnected from server auto disconnectedWidget = new KMessageWidget(qApp->translate("tremotesf", "Disconnected")); disconnectedWidget->setCloseButtonVisible(false); disconnectedWidget->setMessageType(KMessageWidget::Warning); disconnectedWidget->hide(); layout->addWidget(disconnectedWidget); //: Server stats section for current Transmission launch auto currentSessionGroupBox = new QGroupBox(qApp->translate("tremotesf", "Current session"), this); auto currentSessionGroupBoxLayout = new QFormLayout(currentSessionGroupBox); currentSessionGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); auto sessionDownloadedLabel = new QLabel(this); //: Downloaded bytes currentSessionGroupBoxLayout->addRow(qApp->translate("tremotesf", "Downloaded:"), sessionDownloadedLabel); auto sessionUploadedLabel = new QLabel(this); //: Uploaded bytes currentSessionGroupBoxLayout->addRow(qApp->translate("tremotesf", "Uploaded:"), sessionUploadedLabel); auto sessionRatioLabel = new QLabel(this); currentSessionGroupBoxLayout->addRow(qApp->translate("tremotesf", "Ratio:"), sessionRatioLabel); auto sessionDurationLabel = new QLabel(this); //: How much time Transmission is running currentSessionGroupBoxLayout->addRow(qApp->translate("tremotesf", "Duration:"), sessionDurationLabel); layout->addWidget(currentSessionGroupBox); //: Server stats section for all Transmission launches (accumulated) auto totalGroupBox = new QGroupBox(qApp->translate("tremotesf", "Total"), this); auto totalGroupBoxLayout = new QFormLayout(totalGroupBox); totalGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); auto totalDownloadedLabel = new QLabel(this); //: Downloaded bytes totalGroupBoxLayout->addRow(qApp->translate("tremotesf", "Downloaded:"), totalDownloadedLabel); auto totalUploadedLabel = new QLabel(this); //: Uploaded bytes totalGroupBoxLayout->addRow(qApp->translate("tremotesf", "Uploaded:"), totalUploadedLabel); auto totalRatioLabel = new QLabel(this); totalGroupBoxLayout->addRow(qApp->translate("tremotesf", "Ratio:"), totalRatioLabel); auto totalDurationLabel = new QLabel(this); //: How much time Transmission is running totalGroupBoxLayout->addRow(qApp->translate("tremotesf", "Duration:"), totalDurationLabel); auto sessionCountLabel = new QLabel(this); //: How many times Transmission was launched totalGroupBoxLayout->addRow(qApp->translate("tremotesf", "Started:"), sessionCountLabel); auto freeSpaceField = new QLabel(this); totalGroupBoxLayout->addRow(qApp->translate("tremotesf", "Free space in download directory:"), freeSpaceField); auto* freeSpaceLabel = qobject_cast(totalGroupBoxLayout->labelForField(freeSpaceField)); freeSpaceLabel->setWordWrap(true); freeSpaceLabel->setAlignment(Qt::AlignTrailing | Qt::AlignVCenter); layout->addWidget(totalGroupBox); layout->addStretch(); auto resizer = new KColumnResizer(this); resizer->addWidgetsFromLayout(currentSessionGroupBoxLayout); resizer->addWidgetsFromLayout(totalGroupBoxLayout); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); dialogButtonBox->button(QDialogButtonBox::Close)->setDefault(true); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &ServerStatsDialog::reject); layout->addWidget(dialogButtonBox); resize(sizeHint().expandedTo(QSize(300, 320))); QObject::connect(rpc, &Rpc::connectedChanged, this, [=] { if (rpc->isConnected()) { disconnectedWidget->animatedHide(); } else { disconnectedWidget->animatedShow(); } currentSessionGroupBox->setEnabled(rpc->isConnected()); totalGroupBox->setEnabled(rpc->isConnected()); sessionCountLabel->setEnabled(rpc->isConnected()); }); auto update = [=] { const SessionStats currentSessionStats(rpc->serverStats()->currentSession()); sessionDownloadedLabel->setText(formatutils::formatByteSize(currentSessionStats.downloaded())); sessionUploadedLabel->setText(formatutils::formatByteSize(currentSessionStats.uploaded())); sessionRatioLabel->setText( formatutils::formatRatio(currentSessionStats.downloaded(), currentSessionStats.uploaded()) ); sessionDurationLabel->setText(formatutils::formatEta(currentSessionStats.duration())); const SessionStats totalStats(rpc->serverStats()->total()); totalDownloadedLabel->setText(formatutils::formatByteSize(totalStats.downloaded())); totalUploadedLabel->setText(formatutils::formatByteSize(totalStats.uploaded())); totalRatioLabel->setText(formatutils::formatRatio(totalStats.downloaded(), totalStats.uploaded())); totalDurationLabel->setText(formatutils::formatEta(totalStats.duration())); //: How many times Transmission was launched sessionCountLabel->setText(qApp->translate("tremotesf", "%Ln times", nullptr, totalStats.sessionCount())); freeSpaceField->setText(formatutils::formatByteSize(rpc->serverStats()->freeSpace())); }; QObject::connect(rpc->serverStats(), &ServerStats::updated, this, update); update(); } } tremotesf-2.10.1/src/ui/screens/serverstatsdialog.h000066400000000000000000000007101523411710200223610ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SERVERSTATSDIALOG_H #define TREMOTESF_SERVERSTATSDIALOG_H #include class QLabel; namespace tremotesf { class Rpc; class ServerStatsDialog final : public QDialog { Q_OBJECT public: explicit ServerStatsDialog(Rpc* rpc, QWidget* parent = nullptr); }; } #endif // TREMOTESF_SERVERSTATSDIALOG_H tremotesf-2.10.1/src/ui/screens/settingsdialog.cpp000066400000000000000000000530771523411710200222050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // SPDX-FileCopyrightText: 2021 LuK1337 // SPDX-FileCopyrightText: 2022 Alex // // SPDX-License-Identifier: GPL-3.0-or-later #include "settingsdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stdutils.h" #include "target_os.h" #include "settings.h" #include "rpc/rpc.h" #include "ui/screens/addtorrent/addtorrentdialog.h" #include "ui/screens/addtorrent/addtorrenthelpers.h" #include "ui/widgets/torrentremotedirectoryselectionwidget.h" #ifdef Q_OS_WIN # include "ui/systemcolorsprovider.h" #endif namespace tremotesf { namespace { #ifdef Q_OS_WIN constexpr std::array darkThemeComboBoxValues{ Settings::DarkThemeMode::FollowSystem, Settings::DarkThemeMode::On, Settings::DarkThemeMode::Off }; #endif constexpr std::array torrentDoubleClickActionComboBoxValues{ Settings::TorrentDoubleClickAction::OpenPropertiesDialog, Settings::TorrentDoubleClickAction::OpenTorrentFile, Settings::TorrentDoubleClickAction::OpenDownloadDirectory }; std::invocable auto createGeneralPage(KPageWidget* pageWidget, Settings* settings) { auto page = new QWidget(); //: Options tab pageWidget->addPage(page, qApp->translate("tremotesf", "General")) ->setIcon(QIcon::fromTheme("preferences-desktop")); auto layout = new QFormLayout(page); layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); #ifdef Q_OS_WIN auto darkThemeComboBox = new QComboBox(pageWidget); //: Dark theme mode darkThemeComboBox->addItem(qApp->translate("tremotesf", "Follow system")); //: Dark theme mode darkThemeComboBox->addItem(qApp->translate("tremotesf", "On")); //: Dark theme mode darkThemeComboBox->addItem(qApp->translate("tremotesf", "Off")); layout->addRow(qApp->translate("tremotesf", "Dark theme"), darkThemeComboBox); auto systemAccentColorCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Use system accent color"), page ); layout->addRow(systemAccentColorCheckBox); darkThemeComboBox->setCurrentIndex( // NOLINTNEXTLINE(bugprone-unchecked-optional-access) indexOfCasted(darkThemeComboBoxValues, settings->get_darkThemeMode()).value() ); systemAccentColorCheckBox->setChecked(settings->get_useSystemAccentColor()); #endif auto showTorrentPropertiesInMainWindowCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Show torrent properties in a panel in the main window"), page ); layout->addRow(showTorrentPropertiesInMainWindowCheckBox); showTorrentPropertiesInMainWindowCheckBox->setChecked(settings->get_showTorrentPropertiesInMainWindow()); auto torrentDoubleClickActionComboBox = new QComboBox(page); layout->addRow( qApp->translate("tremotesf", "What to do when torrent in the list is double clicked:"), torrentDoubleClickActionComboBox ); for (const auto action : torrentDoubleClickActionComboBoxValues) { switch (action) { case Settings::TorrentDoubleClickAction::OpenPropertiesDialog: torrentDoubleClickActionComboBox->addItem(qApp->translate("tremotesf", "Open properties dialog")); break; case Settings::TorrentDoubleClickAction::OpenTorrentFile: torrentDoubleClickActionComboBox->addItem(qApp->translate("tremotesf", "Open torrent's file")); break; case Settings::TorrentDoubleClickAction::OpenDownloadDirectory: torrentDoubleClickActionComboBox->addItem(qApp->translate("tremotesf", "Open download directory")); break; default: break; } } torrentDoubleClickActionComboBox->setCurrentIndex( // NOLINTNEXTLINE(bugprone-unchecked-optional-access) indexOfCasted(torrentDoubleClickActionComboBoxValues, settings->get_torrentDoubleClickAction()) .value() ); auto dialogWarningMessage = new KMessageWidget(page); layout->addRow(dialogWarningMessage); dialogWarningMessage->setMessageType(KMessageWidget::Warning); dialogWarningMessage->setCloseButtonVisible(false); dialogWarningMessage->setText(qApp->translate( "tremotesf", "Properties dialog won't be shown because torrent properties are shown in the main window" )); const auto updateDialogWarningMessage = [=] { if (showTorrentPropertiesInMainWindowCheckBox->isChecked() && torrentDoubleClickActionComboBox->currentIndex() == indexOfCasted( torrentDoubleClickActionComboBoxValues, Settings::TorrentDoubleClickAction::OpenPropertiesDialog )) { dialogWarningMessage->animatedShow(); } else { dialogWarningMessage->animatedHide(); } }; updateDialogWarningMessage(); QObject::connect( showTorrentPropertiesInMainWindowCheckBox, &QCheckBox::toggled, dialogWarningMessage, updateDialogWarningMessage ); QObject::connect( torrentDoubleClickActionComboBox, &QComboBox::currentIndexChanged, dialogWarningMessage, updateDialogWarningMessage ); auto connectOnStartupCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Connect to server on startup"), page ); layout->addRow(connectOnStartupCheckBox); connectOnStartupCheckBox->setChecked(settings->get_connectOnStartup()); auto displayRelativeTimeCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Display relative time"), page ); layout->addRow(displayRelativeTimeCheckBox); displayRelativeTimeCheckBox->setChecked(settings->get_displayRelativeTime()); auto displayFullDownloadDirectoryPathCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Display full path of download directories in sidebar and torrents list"), page ); layout->addRow(displayFullDownloadDirectoryPathCheckBox); displayFullDownloadDirectoryPathCheckBox->setChecked(settings->get_displayFullDownloadDirectoryPath()); return [=] { #ifdef Q_OS_WIN if (const auto index = darkThemeComboBox->currentIndex(); index != -1) { settings->set_darkThemeMode(darkThemeComboBoxValues.at(static_cast(index))); } if (systemAccentColorCheckBox) { settings->set_useSystemAccentColor(systemAccentColorCheckBox->isChecked()); } #endif settings->set_showTorrentPropertiesInMainWindow(showTorrentPropertiesInMainWindowCheckBox->isChecked()); if (const auto index = torrentDoubleClickActionComboBox->currentIndex(); index != -1) { settings->set_torrentDoubleClickAction( torrentDoubleClickActionComboBoxValues.at(static_cast(index)) ); } settings->set_connectOnStartup(connectOnStartupCheckBox->isChecked()); settings->set_displayRelativeTime(displayRelativeTimeCheckBox->isChecked()); settings->set_displayFullDownloadDirectoryPath(displayFullDownloadDirectoryPathCheckBox->isChecked()); }; } std::invocable auto createAddingTorrentsPage(KPageWidget* pageWidget, Settings* settings, Rpc* rpc) { auto page = new QWidget(); //: Options tab pageWidget->addPage(page, qApp->translate("tremotesf", "Adding torrents")) ->setIcon(QIcon::fromTheme("folder-download")); auto layout = new QVBoxLayout(page); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); auto rememberOpenTorrentDirCheckbox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Remember location of last opened torrent file"), page ); layout->addWidget(rememberOpenTorrentDirCheckbox); auto rememberAddTorrentParametersCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Remember parameters of last added torrent"), page ); layout->addWidget(rememberAddTorrentParametersCheckBox); auto addTorrentParametersDisconnectedMessage = new KMessageWidget( //: Server connection status qApp->translate("tremotesf", "Disconnected"), page ); addTorrentParametersDisconnectedMessage->setMessageType(KMessageWidget::Warning); addTorrentParametersDisconnectedMessage->setCloseButtonVisible(false); layout->addWidget(addTorrentParametersDisconnectedMessage); auto addTorrentParametersGroupBox = new QGroupBox(qApp->translate("tremotesf", "Add torrent parameters"), page); auto addTorrentParametersGroupBoxLayout = new QFormLayout(addTorrentParametersGroupBox); addTorrentParametersGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); const auto addTorrentParametersWidgets = AddTorrentDialog::createAddTorrentParametersWidgets(true, addTorrentParametersGroupBoxLayout, rpc); auto addTorrentParametersResetButton = new QPushButton(qApp->translate("tremotesf", "Reset"), page); addTorrentParametersGroupBoxLayout->addRow(addTorrentParametersResetButton); layout->addWidget(addTorrentParametersGroupBox); QCheckBox* showMainWindowWhenAddingTorrentsCheckBox{}; // Disabling this option does not work on macOS since the app is always activated when files are opened, // which causes us to show main window if constexpr (targetOs != TargetOs::UnixMacOS) { showMainWindowWhenAddingTorrentsCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Show main window when adding torrents"), page ); layout->addWidget(showMainWindowWhenAddingTorrentsCheckBox); } auto showDialogWhenAddingTorrentsCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Show dialog when adding torrents"), page ); layout->addWidget(showDialogWhenAddingTorrentsCheckBox); auto fillTorrentLinkFromKeyboardCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Automatically fill link from clipboard when adding torrent link"), page ); layout->addWidget(fillTorrentLinkFromKeyboardCheckBox); auto pasteTipLabel = new QLabel( //: %1 is a key binding, e.g. "Ctrl + C" qApp->translate("tremotesf", "Tip: you can also press %1 in main window to add torrents from clipboard") .arg(QKeySequence(QKeySequence::Paste).toString(QKeySequence::NativeText)) ); layout->addWidget(pasteTipLabel); auto askForMergingTrackersCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Ask for merging trackers when adding existing torrent"), page ); layout->addWidget(askForMergingTrackersCheckBox); auto mergeTrackersCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Merge trackers when adding existing torrent"), page ); layout->addWidget(mergeTrackersCheckBox); QObject::connect( askForMergingTrackersCheckBox, &QCheckBox::toggled, mergeTrackersCheckBox, [mergeTrackersCheckBox](bool checked) { mergeTrackersCheckBox->setEnabled(!checked); } ); layout->addStretch(); rememberOpenTorrentDirCheckbox->setChecked(settings->get_rememberOpenTorrentDir()); rememberAddTorrentParametersCheckBox->setChecked(settings->get_rememberAddTorrentParameters()); addTorrentParametersDisconnectedMessage->setVisible(!rpc->isConnected()); addTorrentParametersGroupBox->setEnabled(rpc->isConnected()); QObject::connect(rpc, &Rpc::connectedChanged, page, [=] { const bool connected = rpc->isConnected(); if (connected) { addTorrentParametersDisconnectedMessage->animatedHide(); } else { addTorrentParametersDisconnectedMessage->animatedShow(); } addTorrentParametersGroupBox->setEnabled(connected); if (connected) { // Update parameters which initial values depend on server state const auto parameters = getAddTorrentParameters(rpc); addTorrentParametersWidgets.downloadDirectoryWidget->resetPath(parameters.downloadDirectory); addTorrentParametersWidgets.startTorrentCheckBox->setChecked(parameters.startAfterAdding); } }); QObject::connect(addTorrentParametersResetButton, &QPushButton::clicked, page, [=] { addTorrentParametersWidgets.reset(rpc); }); if (showMainWindowWhenAddingTorrentsCheckBox) { showMainWindowWhenAddingTorrentsCheckBox->setChecked(settings->get_showMainWindowWhenAddingTorrent()); } showDialogWhenAddingTorrentsCheckBox->setChecked(settings->get_showAddTorrentDialog()); fillTorrentLinkFromKeyboardCheckBox->setChecked(settings->get_fillTorrentLinkFromClipboard()); askForMergingTrackersCheckBox->setChecked(settings->get_askForMergingTrackersWhenAddingExistingTorrent()); mergeTrackersCheckBox->setChecked(settings->get_mergeTrackersWhenAddingExistingTorrent()); mergeTrackersCheckBox->setEnabled(!askForMergingTrackersCheckBox->isChecked()); return [=] { settings->set_rememberOpenTorrentDir(rememberOpenTorrentDirCheckbox->isChecked()); settings->set_rememberAddTorrentParameters(rememberAddTorrentParametersCheckBox->isChecked()); addTorrentParametersWidgets.saveToSettings(); if (showMainWindowWhenAddingTorrentsCheckBox) { settings->set_showMainWindowWhenAddingTorrent(showMainWindowWhenAddingTorrentsCheckBox->isChecked() ); } settings->set_showAddTorrentDialog(showDialogWhenAddingTorrentsCheckBox->isChecked()); settings->set_fillTorrentLinkFromClipboard(fillTorrentLinkFromKeyboardCheckBox->isChecked()); settings->set_askForMergingTrackersWhenAddingExistingTorrent(askForMergingTrackersCheckBox->isChecked() ); settings->set_mergeTrackersWhenAddingExistingTorrent(mergeTrackersCheckBox->isChecked()); }; } std::invocable auto createNotificationsPage(KPageWidget* pageWidget, Settings* settings) { auto page = new QWidget(); //: Options tab pageWidget->addPage(page, qApp->translate("tremotesf", "Notifications")) ->setIcon(QIcon::fromTheme("preferences-desktop-notification")); auto layout = new QVBoxLayout(page); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); auto notificationOnDisconnectingCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Notify when disconnecting from server"), page ); layout->addWidget(notificationOnDisconnectingCheckBox); auto notificationOnAddingTorrentCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Notify on added torrents"), page ); layout->addWidget(notificationOnAddingTorrentCheckBox); auto notificationOfFinishedTorrentsCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Notify on finished torrents"), page ); layout->addWidget(notificationOfFinishedTorrentsCheckBox); auto trayIconCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Show icon in the notification area"), page ); layout->addWidget(trayIconCheckBox); //: Notifications options section auto whenConnectingGroupBox = new QGroupBox(qApp->translate("tremotesf", "When connecting to server"), page); layout->addWidget(whenConnectingGroupBox); auto whenConnectingGroupBoxLayout = new QVBoxLayout(whenConnectingGroupBox); whenConnectingGroupBoxLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); auto addedSinceLastConnectionCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Notify on added torrents since last connection to server"), whenConnectingGroupBox ); whenConnectingGroupBoxLayout->addWidget(addedSinceLastConnectionCheckBox); auto finishedSinceLastConnectionCheckBox = new QCheckBox( //: Check box label qApp->translate("tremotesf", "Notify on finished torrents since last connection to server"), whenConnectingGroupBox ); whenConnectingGroupBoxLayout->addWidget(finishedSinceLastConnectionCheckBox); notificationOnDisconnectingCheckBox->setChecked(settings->get_notificationOnDisconnecting()); notificationOnAddingTorrentCheckBox->setChecked(settings->get_notificationOnAddingTorrent()); notificationOfFinishedTorrentsCheckBox->setChecked(settings->get_notificationOfFinishedTorrents()); trayIconCheckBox->setChecked(settings->get_showTrayIcon()); addedSinceLastConnectionCheckBox->setChecked(settings->get_notificationsOnAddedTorrentsSinceLastConnection() ); finishedSinceLastConnectionCheckBox->setChecked( settings->get_notificationsOnFinishedTorrentsSinceLastConnection() ); return [=] { settings->set_notificationOnDisconnecting(notificationOnDisconnectingCheckBox->isChecked()); settings->set_notificationOnAddingTorrent(notificationOnAddingTorrentCheckBox->isChecked()); settings->set_notificationOfFinishedTorrents(notificationOfFinishedTorrentsCheckBox->isChecked()); settings->set_showTrayIcon(trayIconCheckBox->isChecked()); settings->set_notificationsOnAddedTorrentsSinceLastConnection( addedSinceLastConnectionCheckBox->isChecked() ); settings->set_notificationsOnFinishedTorrentsSinceLastConnection( finishedSinceLastConnectionCheckBox->isChecked() ); }; } } SettingsDialog::SettingsDialog(Rpc* rpc, QWidget* parent) : QDialog(parent) { //: Dialog title setWindowTitle(qApp->translate("tremotesf", "Options")); auto rootLayout = new QVBoxLayout(this); rootLayout->setContentsMargins(0, 0, 0, 0); auto pageWidget = new KPageWidget(this); rootLayout->addWidget(pageWidget); auto settings = Settings::instance(); const auto saveGeneralPage = createGeneralPage(pageWidget, settings); const auto saveAddingTorrentsPage = createAddingTorrentsPage(pageWidget, settings, rpc); const auto saveNotificationsPage = createNotificationsPage(pageWidget, settings); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::accept); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &SettingsDialog::reject); pageWidget->setPageFooter(dialogButtonBox); QObject::connect(this, &SettingsDialog::accepted, this, [=] { saveGeneralPage(); saveAddingTorrentsPage(); saveNotificationsPage(); }); } } tremotesf-2.10.1/src/ui/screens/settingsdialog.h000066400000000000000000000006521523411710200216410ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_SETTINGSDIALOG_H #define TREMOTESF_SETTINGSDIALOG_H #include namespace tremotesf { class Rpc; class SettingsDialog final : public QDialog { Q_OBJECT public: explicit SettingsDialog(Rpc* rpc, QWidget* parent = nullptr); }; } #endif // TREMOTESF_SETTINGSDIALOG_H tremotesf-2.10.1/src/ui/screens/torrentproperties/000077500000000000000000000000001523411710200222575ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/screens/torrentproperties/peersmodel.cpp000066400000000000000000000131121523411710200251200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "peersmodel.h" #include #include #include #include #include "log/log.h" #include "rpc/torrent.h" #include "formatutils.h" #include "stdutils.h" namespace tremotesf { PeersModel::~PeersModel() { if (mTorrent) { mTorrent->setPeersEnabled(false); } } int PeersModel::columnCount(const QModelIndex&) const { return QMetaEnum::fromType().keyCount(); } QVariant PeersModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return {}; } const Peer& peer = mPeers.at(static_cast(index.row())); switch (role) { case Qt::DisplayRole: switch (static_cast(index.column())) { case Column::Address: return peer.address; case Column::DownloadSpeed: return formatutils::formatByteSpeed(peer.downloadSpeed); case Column::UploadSpeed: return formatutils::formatByteSpeed(peer.uploadSpeed); case Column::ProgressBar: case Column::Progress: return formatutils::formatProgress(peer.progress); case Column::Flags: return peer.flags; case Column::Client: return peer.client; default: break; } break; case Qt::ToolTipRole: switch (static_cast(index.column())) { case Column::Address: case Column::Client: return data(index, Qt::DisplayRole); default: break; } break; case SortRole: switch (static_cast(index.column())) { case Column::DownloadSpeed: return peer.downloadSpeed; case Column::UploadSpeed: return peer.uploadSpeed; case Column::ProgressBar: case Column::Progress: return peer.progress; default: return data(index, Qt::DisplayRole); } default: break; } return {}; } QVariant PeersModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { return {}; } switch (static_cast(section)) { case Column::Address: //: Peers list column title return qApp->translate("tremotesf", "Address"); case Column::DownloadSpeed: //: Peers list column title return qApp->translate("tremotesf", "Down Speed"); case Column::UploadSpeed: //: Peers list column title return qApp->translate("tremotesf", "Up Speed"); case Column::ProgressBar: //: Peers list column title return qApp->translate("tremotesf", "Progress Bar"); case Column::Progress: //: Peers list column title return qApp->translate("tremotesf", "Progress"); case Column::Flags: //: Peers list column title return qApp->translate("tremotesf", "Flags"); case Column::Client: //: Peers list column title return qApp->translate("tremotesf", "Client"); default: return {}; } } int PeersModel::rowCount(const QModelIndex&) const { return static_cast(mPeers.size()); } Torrent* PeersModel::torrent() const { return mTorrent; } void PeersModel::setTorrent(Torrent* torrent, bool oldTorrentDestroyed) { if (torrent == mTorrent) { return; } if (mTorrent && !oldTorrentDestroyed) { QObject::disconnect(mTorrent, nullptr, this, nullptr); mTorrent->setPeersEnabled(false); } mTorrent = torrent; beginResetModel(); mPeers.clear(); if (mTorrent) { if (mTorrent->isPeersEnabled()) { warning().log("{} already has enabled peers, this shouldn't happen", *mTorrent); } mTorrent->setPeersEnabled(true); QObject::connect(mTorrent, &Torrent::peersUpdated, this, &PeersModel::update); QObject::connect(mTorrent, &QObject::destroyed, this, [this] { setTorrent(nullptr, true); }); } endResetModel(); } void PeersModel::update( const std::vector>& removedIndexRanges, const std::vector>& changedIndexRanges, int addedCount ) { for (const auto& [first, last] : removedIndexRanges) { beginRemoveRows({}, first, last - 1); mPeers.erase(mPeers.begin() + first, mPeers.begin() + last); endRemoveRows(); } const auto& newPeers = mTorrent->peers(); for (const auto& [first, last] : changedIndexRanges) { std::ranges::copy(slice(newPeers, first, last), mPeers.begin() + first); emit dataChanged(index(0, 0), index(last - 1, columnCount() - 1)); } if (addedCount > 0) { beginInsertRows({}, static_cast(mPeers.size()), static_cast(newPeers.size()) - 1); mPeers.reserve(newPeers.size()); std::ranges::copy( std::views::drop(newPeers, static_cast(mPeers.size())), std::back_insert_iterator(mPeers) ); endInsertRows(); } } } tremotesf-2.10.1/src/ui/screens/torrentproperties/peersmodel.h000066400000000000000000000027361523411710200245770ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_PEERSMODEL_H #define TREMOTESF_PEERSMODEL_H #include #include #include "rpc/peer.h" namespace tremotesf { class Torrent; } namespace tremotesf { class PeersModel final : public QAbstractTableModel { Q_OBJECT public: enum class Column { Address, DownloadSpeed, UploadSpeed, ProgressBar, Progress, Flags, Client }; Q_ENUM(Column) static constexpr auto SortRole = Qt::UserRole; inline explicit PeersModel(QObject* parent = nullptr) : QAbstractTableModel(parent) {} ~PeersModel() override; Q_DISABLE_COPY_MOVE(PeersModel) int columnCount(const QModelIndex& = {}) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex&) const override; Torrent* torrent() const; void setTorrent(Torrent* torrent, bool oldTorrentDestroyed); private: void update( const std::vector>& removedIndexRanges, const std::vector>& changedIndexRanges, int addedCount ); std::vector mPeers{}; Torrent* mTorrent{}; }; } #endif // TREMOTESF_PEERSMODEL_H tremotesf-2.10.1/src/ui/screens/torrentproperties/torrentfilesmodel.cpp000066400000000000000000000156161523411710200265350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentfilesmodel.h" #include #include "coroutines/threadpool.h" #include "log/log.h" #include "rpc/mounteddirectoriesutils.h" #include "rpc/torrent.h" #include "rpc/serversettings.h" #include "rpc/rpc.h" #include "ui/itemmodels/torrentfilestreebuilder.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { std::vector idsFromIndexes(const QList& indexes) { std::vector ids{}; // at least indexes.size(), but may be more ids.reserve(static_cast(indexes.size())); for (const QModelIndex& index : indexes) { auto entry = static_cast(index.internalPointer()); entry->getFileIds(ids); } std::ranges::sort(ids); const auto toErase = std::ranges::unique(ids); ids.erase(toErase.begin(), toErase.end()); return ids; } std::pair, std::vector> doCreateTree(const std::vector& files) { TorrentFilesTreeBuilder builder(files.size()); for (const TorrentFile& file : files) { builder.addFile( file.pathParts(), true, file.size, file.completedSize, file.wanted, TorrentFilesModelEntry::fromFilePriority(file.priority) ); } builder.calculateDirectoriesRecursively(); return {std::move(builder.rootEntry), std::move(builder.files)}; } } TorrentFilesModel::TorrentFilesModel(Rpc* rpc, QObject* parent) : BaseTorrentFilesModel( {Column::Name, Column::Size, Column::ProgressBar, Column::Progress, Column::Priority}, parent ) { setRpc(rpc); } TorrentFilesModel::~TorrentFilesModel() { if (mTorrent) { mTorrent->setFilesEnabled(false); } } Torrent* TorrentFilesModel::torrent() const { return mTorrent; } void TorrentFilesModel::setTorrent(Torrent* torrent, bool oldTorrentDestroyed) { if (torrent == mTorrent) { return; } if (mTorrent && !oldTorrentDestroyed) { QObject::disconnect(mTorrent, nullptr, this, nullptr); mTorrent->setFilesEnabled(false); } mTorrent = torrent; resetTree(); if (mTorrent) { QObject::connect(mTorrent, &Torrent::filesUpdated, this, &TorrentFilesModel::update); QObject::connect(mTorrent, &Torrent::fileRenamed, this, &TorrentFilesModel::fileRenamed); if (mTorrent->isFilesEnabled()) { warning().log("{} already has enabled files, this shouldn't happen", *mTorrent); } mTorrent->setFilesEnabled(true); QObject::connect(mTorrent, &QObject::destroyed, this, [this] { setTorrent(nullptr, true); }); } } Rpc* TorrentFilesModel::rpc() const { return mRpc; } void TorrentFilesModel::setRpc(Rpc* rpc) { mRpc = rpc; } void TorrentFilesModel::setFilesWanted(const QModelIndexList& indexes, bool wanted) { BaseTorrentFilesModel::setFilesWanted(indexes, wanted); mTorrent->setFilesWanted(idsFromIndexes(indexes), wanted); } void TorrentFilesModel::setFilesPriority(const QModelIndexList& indexes, TorrentFilesModelEntry::Priority priority) { BaseTorrentFilesModel::setFilesPriority(indexes, priority); mTorrent->setFilesPriority(idsFromIndexes(indexes), TorrentFilesModelEntry::toFilePriority(priority)); } void TorrentFilesModel::renameFile(const QModelIndex& index, const QString& newName) { mTorrent->renameFile(static_cast(index.internalPointer())->path(), newName); } void TorrentFilesModel::fileRenamed(const QString& path, const QString& newName) { if (!mLoaded || !mRootEntry) { return; } TorrentFilesModelEntry* entry = mRootEntry.get(); const auto parts = path.split('/', Qt::SkipEmptyParts); for (const QString& part : parts) { if (!entry->isDirectory()) return; auto& children = static_cast(entry)->children(); const auto found = std::ranges::find(children, part, &TorrentFilesModelEntry::name); if (found == children.end()) return; entry = &*found; } BaseTorrentFilesModel::fileRenamed(entry, newName); } QString TorrentFilesModel::localFilePath(const QModelIndex& index) const { if (!index.isValid()) { return localTorrentDownloadDirectoryPath(mRpc, mTorrent); } const auto* entry = static_cast(index.internalPointer()); QString path(entry->path()); if (!entry->isDirectory() && entry->progress() < 1 && mRpc->serverSettings()->data().renameIncompleteFiles) { path += ".part"_L1; } return localTorrentDownloadDirectoryPath(mRpc, mTorrent) % '/' % path; } bool TorrentFilesModel::isWanted(const QModelIndex& index) const { if (!index.isValid()) { return true; } return static_cast(index.internalPointer())->wantedState() != TorrentFilesModelEntry::WantedState::Unwanted; } void TorrentFilesModel::update(std::span changed) { if (mLoaded) { updateTree(changed); } else { mCoroutineScope.launch(createTree()); } } Coroutine<> TorrentFilesModel::createTree() { if (mCreatingTree) { co_return; } mCreatingTree = true; beginResetModel(); auto [rootEntry, files] = co_await runOnThreadPool( [](const std::vector& files) { return doCreateTree(files); }, mTorrent->files() ); mRootEntry = std::move(rootEntry); endResetModel(); mFiles = std::move(files); setLoaded(true); mCreatingTree = false; } void TorrentFilesModel::resetTree() { if (mLoaded) { beginResetModel(); mRootEntry.reset(); endResetModel(); mFiles.clear(); setLoaded(false); } } void TorrentFilesModel::updateTree(std::span changed) { const auto& jsons = mTorrent->files(); updateFiles(changed, [&](size_t i, TorrentFilesModelEntry* file) { const auto& json = jsons.at(i); file->update(json.wanted, TorrentFilesModelEntry::fromFilePriority(json.priority), json.completedSize); }); } void TorrentFilesModel::setLoaded(bool loaded) { mLoaded = loaded; } } tremotesf-2.10.1/src/ui/screens/torrentproperties/torrentfilesmodel.h000066400000000000000000000031461523411710200261750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTFILESMODEL_H #define TREMOTESF_TORRENTFILESMODEL_H #include #include "coroutines/scope.h" #include "ui/itemmodels/basetorrentfilesmodel.h" namespace tremotesf { class Rpc; class Torrent; class TorrentFilesModel final : public BaseTorrentFilesModel { Q_OBJECT public: explicit TorrentFilesModel(Rpc* rpc, QObject* parent = nullptr); ~TorrentFilesModel() override; Q_DISABLE_COPY_MOVE(TorrentFilesModel) Torrent* torrent() const; void setTorrent(Torrent* torrent, bool oldTorrentDestroyed); Rpc* rpc() const; void setRpc(Rpc* rpc); void setFilesWanted(const QModelIndexList& indexes, bool wanted) override; void setFilesPriority(const QModelIndexList& indexes, TorrentFilesModelEntry::Priority priority) override; void renameFile(const QModelIndex& index, const QString& newName) override; void fileRenamed(const QString& path, const QString& newName); QString localFilePath(const QModelIndex& index) const; bool isWanted(const QModelIndex& index) const; private: void update(std::span changed); Coroutine<> createTree(); void resetTree(); void updateTree(std::span changed); void setLoaded(bool loaded); Torrent* mTorrent{}; Rpc* mRpc{}; bool mCreatingTree{}; bool mLoaded{}; CoroutineScope mCoroutineScope{}; }; } #endif // TREMOTESF_TORRENTFILESMODEL_H tremotesf-2.10.1/src/ui/screens/torrentproperties/torrentpropertiesdialog.cpp000066400000000000000000000060321523411710200277560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentpropertiesdialog.h" #include #include #include #include #include #include "torrentpropertieswidget.h" #include "settings.h" #include "log/log.h" #include "rpc/rpc.h" #include "rpc/torrent.h" SPECIALIZE_FORMATTER_FOR_QDEBUG(QRect) namespace tremotesf { TorrentPropertiesDialog::TorrentPropertiesDialog(Torrent* torrent, Rpc* rpc, QWidget* parent) : QDialog(parent), mTorrentPropertiesWidget(new TorrentPropertiesWidget(rpc, false, this)) { auto layout = new QVBoxLayout(this); auto messageWidget = new KMessageWidget(this); layout->addWidget(messageWidget); messageWidget->setCloseButtonVisible(false); messageWidget->setMessageType(KMessageWidget::Warning); messageWidget->hide(); mTorrentPropertiesWidget->setTorrent(torrent); layout->addWidget(mTorrentPropertiesWidget); QObject::connect( mTorrentPropertiesWidget, &TorrentPropertiesWidget::hasTorrentChanged, this, [=](bool hasTorrent) { if (hasTorrent) { if (messageWidget->isVisible()) { messageWidget->animatedHide(); } } else { if (rpc->connectionState() == Rpc::ConnectionState::Disconnected) { //: Message that appears when disconnected from server messageWidget->setText(qApp->translate("tremotesf", "Disconnected")); } else { //: Message that appears when torrent is removed messageWidget->setText(qApp->translate("tremotesf", "Torrent Removed")); } messageWidget->animatedShow(); } } ); auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &TorrentPropertiesDialog::reject); layout->addWidget(dialogButtonBox); dialogButtonBox->button(QDialogButtonBox::Close)->setDefault(true); setWindowTitle(torrent->data().name); const QString torrentHash = torrent->data().hashString; QObject::connect(rpc, &Rpc::torrentsUpdated, this, [=, this] { auto torrent = rpc->torrentByHash(torrentHash); mTorrentPropertiesWidget->setTorrent(torrent); if (torrent) { setWindowTitle(torrent->data().name); } }); restoreGeometry(Settings::instance()->get_torrentPropertiesDialogGeometry()); } void TorrentPropertiesDialog::saveState() { debug().log("Saving TorrentPropertiesDialog state, window geometry is {}", geometry()); Settings::instance()->set_torrentPropertiesDialogGeometry(saveGeometry()); mTorrentPropertiesWidget->saveState(); } } tremotesf-2.10.1/src/ui/screens/torrentproperties/torrentpropertiesdialog.h000066400000000000000000000015541523411710200274270ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTPROPERTIESDIALOG_H #define TREMOTESF_TORRENTPROPERTIESDIALOG_H #include #include "ui/savewindowstatedispatcher.h" namespace tremotesf { class Rpc; class Torrent; class TorrentPropertiesWidget; class TorrentPropertiesDialog final : public QDialog { Q_OBJECT public: explicit TorrentPropertiesDialog(Torrent* torrent, Rpc* rpc, QWidget* parent = nullptr); ~TorrentPropertiesDialog() override = default; Q_DISABLE_COPY_MOVE(TorrentPropertiesDialog) private: void saveState(); TorrentPropertiesWidget* mTorrentPropertiesWidget; SaveWindowStateHandler mSaveStateHandler{this, [this] { saveState(); }}; }; } #endif // TREMOTESF_TORRENTPROPERTIESDIALOG_H tremotesf-2.10.1/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp000066400000000000000000000765441523411710200300210ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentpropertieswidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "desktoputils.h" #include "formatutils.h" #include "peersmodel.h" #include "settings.h" #include "stdutils.h" #include "torrentfilesmodel.h" #include "trackersviewwidget.h" #include "rpc/numbervaluerange.h" #include "rpc/pathutils.h" #include "rpc/rpc.h" #include "rpc/serversettings.h" #include "rpc/torrent.h" #include "ui/itemmodels/baseproxymodel.h" #include "ui/itemmodels/stringlistmodel.h" #include "ui/stylehelpers.h" #include "ui/widgets/basetreeview.h" #include "ui/widgets/progressbardelegate.h" #include "ui/widgets/tooltipwhenelideddelegate.h" #include "ui/widgets/torrentfilesview.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { constexpr std::array priorityComboBoxItems{ TorrentData::Priority::High, TorrentData::Priority::Normal, TorrentData::Priority::Low }; constexpr std::array ratioLimitComboBoxItems{ TorrentData::RatioLimitMode::Global, TorrentData::RatioLimitMode::Unlimited, TorrentData::RatioLimitMode::Single }; constexpr std::array idleSeedingLimitComboBoxItems{ TorrentData::IdleSeedingLimitMode::Global, TorrentData::IdleSeedingLimitMode::Unlimited, TorrentData::IdleSeedingLimitMode::Single }; } TorrentPropertiesWidget::TorrentPropertiesWidget(Rpc* rpc, bool horizontalDetails, QWidget* parent) : QTabWidget(parent), mRpc(rpc), mFilesModel(new TorrentFilesModel(mRpc, this)), mFilesView(new TorrentFilesView(mFilesModel, mRpc, this)), mTrackersViewWidget(new TrackersViewWidget(mRpc, this)) { setEnabled(false); setupDetailsTab(horizontalDetails); auto filesTab = new QWidget(this); auto filesTabLayout = new QVBoxLayout(filesTab); filesTabLayout->addWidget(mFilesView); overrideBreezeFramelessScrollAreaHeuristic(mFilesView, true); //: Torrent properties dialog tab addTab(filesTab, qApp->translate("tremotesf", "Files")); //: Torrent properties dialog tab addTab(mTrackersViewWidget, qApp->translate("tremotesf", "Trackers")); setupPeersTab(); setupWebSeedersTab(); setupLimitsTab(); } void TorrentPropertiesWidget::saveState() { Settings::instance()->set_peersViewHeaderState(mPeersView->header()->saveState()); mFilesView->saveState(); mTrackersViewWidget->saveState(); } void TorrentPropertiesWidget::setupDetailsTab(bool horizontal) { auto detailsTab = new QScrollArea(this); detailsTab->setWidgetResizable(true); makeScrollAreaTransparent(detailsTab); if (!horizontal) { detailsTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } //: Torrent's properties dialog tab addTab(detailsTab, qApp->translate("tremotesf", "Details")); auto detailsTabScrollContent = new QWidget(detailsTab); detailsTab->setWidget(detailsTabScrollContent); QBoxLayout* detailsTabLayout{}; if (horizontal) { detailsTabLayout = new QHBoxLayout(detailsTabScrollContent); } else { detailsTabLayout = new QVBoxLayout(detailsTabScrollContent); } //: Torrent's details tab section auto activityGroupBox = new QGroupBox(qApp->translate("tremotesf", "Activity"), this); auto activityGroupBoxLayout = new QFormLayout(activityGroupBox); activityGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); auto completedLabel = new QLabel(this); //: Torrent's completed size activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Completed:"), completedLabel); auto downloadedLabel = new QLabel(this); //: Torrent's downloaded size activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Downloaded:"), downloadedLabel); auto uploadedLabel = new QLabel(this); //: Torrent's uploaded size activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Uploaded:"), uploadedLabel); auto ratioLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Ratio:"), ratioLabel); auto downloadSpeedLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Download speed:"), downloadSpeedLabel); auto uploadSpeedLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Upload speed:"), uploadSpeedLabel); auto etaLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "ETA:"), etaLabel); auto seedersLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Seeders:"), seedersLabel); auto leechersLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Leechers:"), leechersLabel); auto peersSendingToUsLabel = new QLabel(this); activityGroupBoxLayout->addRow( qApp->translate("tremotesf", "Peers we are downloading from:"), peersSendingToUsLabel ); auto webSeedersSendingToUsLabel = new QLabel(this); activityGroupBoxLayout->addRow( qApp->translate("tremotesf", "Web seeders we are downloading from:"), webSeedersSendingToUsLabel ); auto peersGettingFromUsLabel = new QLabel(this); activityGroupBoxLayout->addRow( qApp->translate("tremotesf", "Peers we are uploading to:"), peersGettingFromUsLabel ); auto lastActivityLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Last activity:"), lastActivityLabel); detailsTabLayout->addWidget(activityGroupBox); //: Torrent's details tab section auto infoGroupBox = new QGroupBox(qApp->translate("tremotesf", "Information"), this); auto infoGroupBoxLayout = new QFormLayout(infoGroupBox); infoGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); auto totalSizeLabel = new QLabel(this); infoGroupBoxLayout->addRow(qApp->translate("tremotesf", "Total size:"), totalSizeLabel); auto locationLabel = new QLabel(this); locationLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); //: Torrent's download directory infoGroupBoxLayout->addRow(qApp->translate("tremotesf", "Location:"), locationLabel); auto hashLabel = new QLabel(this); hashLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); //: Torrent's hash string infoGroupBoxLayout->addRow(qApp->translate("tremotesf", "Hash:"), hashLabel); auto creatorLabel = new QLabel(this); //: Program that created torrent file infoGroupBoxLayout->addRow(qApp->translate("tremotesf", "Created by:"), creatorLabel); auto creationDateLabel = new QLabel(this); //: Date/time when torrent was created infoGroupBoxLayout->addRow(qApp->translate("tremotesf", "Created on:"), creationDateLabel); auto commentTextEdit = new QTextBrowser(this); commentTextEdit->setOpenExternalLinks(true); commentTextEdit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); //: Torrent's comment text infoGroupBoxLayout->addRow(qApp->translate("tremotesf", "Comment:"), commentTextEdit); auto labelsModel = new StringListModel({}, QIcon::fromTheme("tag"_L1), this); auto labelsProxyModel = new BaseProxyModel(labelsModel, Qt::DisplayRole, std::nullopt, this); auto labelsView = new QListView(this); labelsView->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); labelsView->setFlow(QListView::LeftToRight); labelsView->setWrapping(true); labelsView->setResizeMode(QListView::Adjust); labelsView->setIconSize(QSize(16, 16)); labelsView->setModel(labelsProxyModel); //: Torrent's labels infoGroupBoxLayout->addRow(qApp->translate("tremotesf", "Labels:"), labelsView); auto labelsLabel = infoGroupBoxLayout->labelForField(labelsView); detailsTabLayout->addWidget(infoGroupBox); if (!horizontal) { auto resizer = new KColumnResizer(this); resizer->addWidgetsFromLayout(activityGroupBoxLayout); resizer->addWidgetsFromLayout(infoGroupBoxLayout); } mUpdateDetailsTab = [=, this] { if (mTorrent) { //: Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents completedLabel->setText(qApp->translate("tremotesf", "%1 of %2 (%3)") .arg( formatutils::formatByteSize(mTorrent->data().completedSize), formatutils::formatByteSize(mTorrent->data().sizeWhenDone), formatutils::formatProgress(mTorrent->data().percentDone) )); downloadedLabel->setText(formatutils::formatByteSize(mTorrent->data().totalDownloaded)); uploadedLabel->setText(formatutils::formatByteSize(mTorrent->data().totalUploaded)); ratioLabel->setText(formatutils::formatRatio(mTorrent->data().ratio)); downloadSpeedLabel->setText(formatutils::formatByteSpeed(mTorrent->data().downloadSpeed)); uploadSpeedLabel->setText(formatutils::formatByteSpeed(mTorrent->data().uploadSpeed)); etaLabel->setText(formatutils::formatEta(mTorrent->data().eta)); const QLocale locale{}; seedersLabel->setText(locale.toString(mTorrent->data().totalSeedersFromTrackersCount)); leechersLabel->setText(locale.toString(mTorrent->data().totalLeechersFromTrackersCount)); peersSendingToUsLabel->setText(locale.toString(mTorrent->data().peersSendingToUsCount)); webSeedersSendingToUsLabel->setText(locale.toString(mTorrent->data().webSeedersSendingToUsCount)); peersGettingFromUsLabel->setText(locale.toString(mTorrent->data().peersGettingFromUsCount)); lastActivityLabel->setText( formatutils::formatDateTime(mTorrent->data().activityDate.toLocalTime(), QLocale::LongFormat) ); totalSizeLabel->setText(formatutils::formatByteSize(mTorrent->data().totalSize)); locationLabel->setText( toNativeSeparators(mTorrent->data().downloadDirectory, mRpc->serverSettings()->data().pathOs) ); hashLabel->setText(mTorrent->data().hashString); creatorLabel->setText(mTorrent->data().creator); creationDateLabel->setText( formatutils::formatDateTime(mTorrent->data().creationDate.toLocalTime(), QLocale::LongFormat) ); if (mTorrent->data().comment != commentTextEdit->toPlainText()) { commentTextEdit->document()->setPlainText(mTorrent->data().comment); desktoputils::findLinksAndAddAnchors(commentTextEdit->document()); } labelsModel->setStringList(mTorrent->data().labels); } else { completedLabel->clear(); downloadedLabel->clear(); uploadedLabel->clear(); ratioLabel->clear(); downloadSpeedLabel->clear(); uploadSpeedLabel->clear(); etaLabel->clear(); seedersLabel->clear(); leechersLabel->clear(); peersSendingToUsLabel->clear(); webSeedersSendingToUsLabel->clear(); peersGettingFromUsLabel->clear(); lastActivityLabel->clear(); totalSizeLabel->clear(); locationLabel->clear(); hashLabel->clear(); creatorLabel->clear(); creationDateLabel->clear(); commentTextEdit->clear(); labelsModel->setStringList({}); } const bool labelsViewVisible = labelsModel->rowCount() > 0; labelsView->setVisible(labelsViewVisible); labelsLabel->setVisible(labelsViewVisible); }; QObject::connect(Settings::instance(), &Settings::displayRelativeTimeChanged, this, mUpdateDetailsTab); } void TorrentPropertiesWidget::setupPeersTab() { mPeersModel = new PeersModel(this); auto peersProxyModel = new BaseProxyModel(mPeersModel, PeersModel::SortRole, static_cast(PeersModel::Column::Address), this); auto peersTab = new QWidget(this); auto peersTabLayout = new QVBoxLayout(peersTab); mPeersView = new QTreeView(this); setCommonTreeViewProperties(mPeersView, true); mPeersView->setItemDelegate(new TooltipWhenElidedDelegate(this)); mPeersView->setItemDelegateForColumn( static_cast(PeersModel::Column::ProgressBar), new ProgressBarDelegate(PeersModel::SortRole, this) ); mPeersView->setModel(peersProxyModel); mPeersView->header()->restoreState(Settings::instance()->get_peersViewHeaderState()); overrideBreezeFramelessScrollAreaHeuristic(mPeersView, true); peersTabLayout->addWidget(mPeersView); //: Torrent's properties dialog tab addTab(peersTab, qApp->translate("tremotesf", "Peers")); } void TorrentPropertiesWidget::setupWebSeedersTab() { //: Web seeders list column title mWebSeedersModel = new StringListModel(qApp->translate("tremotesf", "Web seeder"), {}, this); auto webSeedersProxyModel = new BaseProxyModel(mWebSeedersModel, Qt::DisplayRole, std::nullopt, this); auto webSeedersTab = new QWidget(this); auto webSeedersTabLayout = new QVBoxLayout(webSeedersTab); auto webSeedersView = new QTreeView(this); setCommonTreeViewProperties(webSeedersView, true); webSeedersView->header()->setContextMenuPolicy(Qt::DefaultContextMenu); webSeedersView->setModel(webSeedersProxyModel); overrideBreezeFramelessScrollAreaHeuristic(webSeedersView, true); webSeedersTabLayout->addWidget(webSeedersView); //: Torrent's properties dialog tab addTab(webSeedersTab, qApp->translate("tremotesf", "Web seeders")); } void TorrentPropertiesWidget::setupLimitsTab() { auto limitsTab = new QScrollArea(this); limitsTab->setWidgetResizable(true); limitsTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); makeScrollAreaTransparent(limitsTab); //: Torrent's properties dialog tab addTab(limitsTab, qApp->translate("tremotesf", "Limits")); auto limitsTabScrollContent = new QWidget(limitsTab); limitsTab->setWidget(limitsTabScrollContent); auto limitsTabLayout = new QVBoxLayout(limitsTabScrollContent); // // Speed group box // //: Torrent's limits tab section auto speedGroupBox = new QGroupBox(qApp->translate("tremotesf", "Speed"), this); auto speedGroupBoxLayout = new QFormLayout(speedGroupBox); speedGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); speedGroupBoxLayout->setFormAlignment(Qt::AlignLeft | Qt::AlignVCenter); //: Check box label auto globalLimitsCheckBox = new QCheckBox(qApp->translate("tremotesf", "Honor global limits"), this); speedGroupBoxLayout->addRow(globalLimitsCheckBox); //: Download speed limit input field label auto downloadSpeedCheckBox = new QCheckBox(qApp->translate("tremotesf", "Download:"), this); speedGroupBoxLayout->addRow(downloadSpeedCheckBox); auto downloadSpeedSpinBoxLayout = new QHBoxLayout(); speedGroupBoxLayout->addRow(downloadSpeedSpinBoxLayout); auto downloadSpeedSpinBox = new QSpinBox(this); downloadSpeedSpinBox->setEnabled(false); setSpinBoxLimits(downloadSpeedSpinBox, speedLimitKbpsRange); //: Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes downloadSpeedSpinBox->setSuffix(qApp->translate("tremotesf", " kB/s")); QObject::connect(downloadSpeedCheckBox, &QCheckBox::toggled, downloadSpeedSpinBox, &QSpinBox::setEnabled); downloadSpeedSpinBoxLayout->addSpacing(28); downloadSpeedSpinBoxLayout->addWidget(downloadSpeedSpinBox); //: Upload speed limit input field label auto uploadSpeedCheckBox = new QCheckBox(qApp->translate("tremotesf", "Upload:"), this); speedGroupBoxLayout->addRow(uploadSpeedCheckBox); auto uploadSpeedSpinBoxLayout = new QHBoxLayout(); speedGroupBoxLayout->addRow(uploadSpeedSpinBoxLayout); auto uploadSpeedSpinBox = new QSpinBox(this); uploadSpeedSpinBox->setEnabled(false); setSpinBoxLimits(uploadSpeedSpinBox, speedLimitKbpsRange); //: Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes uploadSpeedSpinBox->setSuffix(qApp->translate("tremotesf", " kB/s")); QObject::connect(uploadSpeedCheckBox, &QCheckBox::toggled, uploadSpeedSpinBox, &QSpinBox::setEnabled); uploadSpeedSpinBoxLayout->addSpacing(28); uploadSpeedSpinBoxLayout->addWidget(uploadSpeedSpinBox); auto priorityComboBox = new QComboBox(this); for (const TorrentData::Priority priority : priorityComboBoxItems) { switch (priority) { case TorrentData::Priority::High: //: Torrent's loading priority priorityComboBox->addItem(qApp->translate("tremotesf", "High")); break; case TorrentData::Priority::Normal: //: Torrent's loading priority priorityComboBox->addItem(qApp->translate("tremotesf", "Normal")); break; case TorrentData::Priority::Low: //: Torrent's loading priority priorityComboBox->addItem(qApp->translate("tremotesf", "Low")); break; } } speedGroupBoxLayout->addRow(qApp->translate("tremotesf", "Torrent priority:"), priorityComboBox); limitsTabLayout->addWidget(speedGroupBox); // // Seeding group box // //: Torrent's limits tab section auto seedingGroupBox = new QGroupBox(qApp->translate("tremotesf", "Seeding", "Options section"), this); auto seedingGroupBoxLayout = new QFormLayout(seedingGroupBox); seedingGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); auto ratioLimitLayout = new QHBoxLayout(); seedingGroupBoxLayout->addRow(qApp->translate("tremotesf", "Ratio limit mode:"), ratioLimitLayout); ratioLimitLayout->setContentsMargins(0, 0, 0, 0); auto ratioLimitComboBox = new QComboBox(this); for (const TorrentData::RatioLimitMode mode : ratioLimitComboBoxItems) { switch (mode) { case TorrentData::RatioLimitMode::Global: //: Seeding ratio limit mode (global settings/stop at ratio/unlimited) ratioLimitComboBox->addItem(qApp->translate("tremotesf", "Use global settings")); break; case TorrentData::RatioLimitMode::Single: //: Seeding ratio limit mode (global settings/stop at ratio/unlimited) ratioLimitComboBox->addItem(qApp->translate("tremotesf", "Stop seeding at ratio:")); break; case TorrentData::RatioLimitMode::Unlimited: //: Seeding ratio limit mode (global settings/stop at ratio/unlimited) ratioLimitComboBox->addItem(qApp->translate("tremotesf", "Seed regardless of ratio")); break; } } ratioLimitLayout->addWidget(ratioLimitComboBox); auto ratioLimitSpinBox = new QDoubleSpinBox(this); setSpinBoxLimits(ratioLimitSpinBox, nonNegativeDecimalsRange); ratioLimitSpinBox->setSingleStep(0.1); ratioLimitSpinBox->setVisible(false); QObject::connect( ratioLimitComboBox, static_cast(&QComboBox::currentIndexChanged), this, [ratioLimitSpinBox](int index) { if (index == indexOfCasted(ratioLimitComboBoxItems, TorrentData::RatioLimitMode::Single)) { ratioLimitSpinBox->show(); } else { ratioLimitSpinBox->hide(); } } ); ratioLimitLayout->addWidget(ratioLimitSpinBox); auto idleSeedingLimitLayout = new QHBoxLayout(); seedingGroupBoxLayout->addRow(qApp->translate("tremotesf", "Idle seeding mode:"), idleSeedingLimitLayout); idleSeedingLimitLayout->setContentsMargins(0, 0, 0, 0); auto idleSeedingLimitComboBox = new QComboBox(this); for (const TorrentData::IdleSeedingLimitMode mode : idleSeedingLimitComboBoxItems) { switch (mode) { case TorrentData::IdleSeedingLimitMode::Global: //: Seeding idle limit mode (global settings/stop if idle for/unlimited) idleSeedingLimitComboBox->addItem(qApp->translate("tremotesf", "Use global settings")); break; case TorrentData::IdleSeedingLimitMode::Single: //: Seeding idle limit mode (global settings/stop if idle for/unlimited) idleSeedingLimitComboBox->addItem(qApp->translate("tremotesf", "Stop seeding if idle for:")); break; case TorrentData::IdleSeedingLimitMode::Unlimited: //: Seeding idle limit mode (global settings/stop if idle for/unlimited) idleSeedingLimitComboBox->addItem(qApp->translate("tremotesf", "Seed regardless of activity")); break; } } idleSeedingLimitLayout->addWidget(idleSeedingLimitComboBox); auto idleSeedingLimitSpinBox = new QSpinBox(this); setSpinBoxLimits(idleSeedingLimitSpinBox, minutesRange); //: Suffix that is added to input field with number of minuts, e.g. "5 min" idleSeedingLimitSpinBox->setSuffix(qApp->translate("tremotesf", " min")); idleSeedingLimitSpinBox->setVisible(false); QObject::connect( idleSeedingLimitComboBox, static_cast(&QComboBox::currentIndexChanged), this, [idleSeedingLimitSpinBox](int index) { if (index == indexOfCasted(idleSeedingLimitComboBoxItems, TorrentData::IdleSeedingLimitMode::Single)) { idleSeedingLimitSpinBox->show(); } else { idleSeedingLimitSpinBox->hide(); } } ); idleSeedingLimitLayout->addWidget(idleSeedingLimitSpinBox); limitsTabLayout->addWidget(seedingGroupBox); // // Peers group box // //: Torrent's limits tab section auto peersGroupBox = new QGroupBox(qApp->translate("tremotesf", "Peers"), this); auto peersGroupBoxLayout = new QFormLayout(peersGroupBox); peersGroupBoxLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); auto peersLimitsSpinBox = new QSpinBox(this); setSpinBoxLimits(peersLimitsSpinBox, peersLimitRange); peersGroupBoxLayout->addRow(qApp->translate("tremotesf", "Maximum peers:"), peersLimitsSpinBox); limitsTabLayout->addWidget(peersGroupBox); limitsTabLayout->addStretch(); auto resizer = new KColumnResizer(this); resizer->addWidgetsFromLayout(speedGroupBoxLayout); resizer->addWidgetsFromLayout(seedingGroupBoxLayout); resizer->addWidgetsFromLayout(peersGroupBoxLayout); QObject::connect(globalLimitsCheckBox, &QCheckBox::toggled, this, [this](bool checked) { if (!mUpdatingLimits && mTorrent) { mTorrent->setHonorSessionLimits(checked); } }); QObject::connect(downloadSpeedCheckBox, &QCheckBox::toggled, this, [this](bool checked) { if (!mUpdatingLimits && mTorrent) { mTorrent->setDownloadSpeedLimited(checked); } }); QObject::connect( downloadSpeedSpinBox, static_cast(&QSpinBox::valueChanged), this, [this](int limit) { if (!mUpdatingLimits && mTorrent) { mTorrent->setDownloadSpeedLimit(limit); } } ); QObject::connect(uploadSpeedCheckBox, &QCheckBox::toggled, this, [this](bool checked) { if (!mUpdatingLimits && mTorrent) { mTorrent->setUploadSpeedLimited(checked); } }); QObject::connect( uploadSpeedSpinBox, static_cast(&QSpinBox::valueChanged), this, [this](int limit) { if (!mUpdatingLimits && mTorrent) { mTorrent->setUploadSpeedLimit(limit); } } ); QObject::connect( priorityComboBox, static_cast(&QComboBox::currentIndexChanged), this, [this](int index) { if (!mUpdatingLimits && mTorrent) { mTorrent->setBandwidthPriority(priorityComboBoxItems.at(static_cast(index))); } } ); QObject::connect( ratioLimitComboBox, static_cast(&QComboBox::currentIndexChanged), this, [this](int index) { if (!mUpdatingLimits && mTorrent) { mTorrent->setRatioLimitMode(ratioLimitComboBoxItems.at(static_cast(index))); } } ); QObject::connect( ratioLimitSpinBox, static_cast(&QDoubleSpinBox::valueChanged), this, [this](double limit) { if (!mUpdatingLimits && mTorrent) { mTorrent->setRatioLimit(limit); } } ); QObject::connect( idleSeedingLimitComboBox, static_cast(&QComboBox::currentIndexChanged), this, [this](int index) { if (!mUpdatingLimits && mTorrent) { mTorrent->setIdleSeedingLimitMode(idleSeedingLimitComboBoxItems.at(static_cast(index))); } } ); QObject::connect( idleSeedingLimitSpinBox, static_cast(&QSpinBox::valueChanged), this, [this](int limit) { if (!mUpdatingLimits && mTorrent) { mTorrent->setIdleSeedingLimit(limit); } } ); QObject::connect( peersLimitsSpinBox, static_cast(&QSpinBox::valueChanged), this, [this](int limit) { if (!mUpdatingLimits && mTorrent) { mTorrent->setPeersLimit(limit); } } ); mUpdateLimitsTab = [=, this] { mUpdatingLimits = true; if (mTorrent) { globalLimitsCheckBox->setChecked(mTorrent->data().honorSessionLimits); downloadSpeedCheckBox->setChecked(mTorrent->data().downloadSpeedLimited); downloadSpeedSpinBox->setValue(mTorrent->data().downloadSpeedLimit); uploadSpeedCheckBox->setChecked(mTorrent->data().uploadSpeedLimited); uploadSpeedSpinBox->setValue(mTorrent->data().uploadSpeedLimit); priorityComboBox->setCurrentIndex( indexOfCasted(priorityComboBoxItems, mTorrent->data().bandwidthPriority).value() ); ratioLimitComboBox->setCurrentIndex( indexOfCasted(ratioLimitComboBoxItems, mTorrent->data().ratioLimitMode).value() ); ratioLimitSpinBox->setValue(mTorrent->data().ratioLimit); idleSeedingLimitComboBox->setCurrentIndex( indexOfCasted(idleSeedingLimitComboBoxItems, mTorrent->data().idleSeedingLimitMode).value() ); idleSeedingLimitSpinBox->setValue(mTorrent->data().idleSeedingLimit); peersLimitsSpinBox->setValue(mTorrent->data().peersLimit); } else { globalLimitsCheckBox->setChecked(false); downloadSpeedCheckBox->setChecked(false); downloadSpeedSpinBox->clear(); uploadSpeedCheckBox->setChecked(false); uploadSpeedSpinBox->clear(); priorityComboBox->setCurrentIndex(0); ratioLimitComboBox->setCurrentIndex(0); ratioLimitSpinBox->clear(); idleSeedingLimitComboBox->setCurrentIndex(0); idleSeedingLimitSpinBox->clear(); peersLimitsSpinBox->clear(); } mUpdatingLimits = false; }; } void TorrentPropertiesWidget::setTorrent(Torrent* torrent) { setTorrent(torrent, false); } void TorrentPropertiesWidget::setTorrent(Torrent* torrent, bool oldTorrentDestroyed) { if (torrent == mTorrent) { return; } if (mTorrent && !oldTorrentDestroyed) { QObject::disconnect(mTorrent, nullptr, this, nullptr); } const bool hadTorrent = mTorrent != nullptr; mTorrent = torrent; const auto update = [this] { mUpdateDetailsTab(); if (mTorrent) { mWebSeedersModel->setStringList(mTorrent->data().webSeeders); } else { mWebSeedersModel->setStringList({}); } mUpdateLimitsTab(); }; update(); if (mTorrent) { QObject::connect(mTorrent, &Torrent::changed, this, update); QObject::connect(mTorrent, &QObject::destroyed, this, [this] { setTorrent(nullptr, true); }); } mFilesModel->setTorrent(mTorrent, oldTorrentDestroyed); mTrackersViewWidget->setTorrent(mTorrent, oldTorrentDestroyed); mPeersModel->setTorrent(mTorrent, oldTorrentDestroyed); const auto onHasTorrentChanged = [this](bool hasTorrent) { setEnabled(hasTorrent); emit hasTorrentChanged(hasTorrent); }; if (hadTorrent && !mTorrent) { onHasTorrentChanged(false); } else if (!hadTorrent && mTorrent) { onHasTorrentChanged(true); } } } tremotesf-2.10.1/src/ui/screens/torrentproperties/torrentpropertieswidget.h000066400000000000000000000030131523411710200274430ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTPROPERTIESWIDGET_H #define TREMOTESF_TORRENTPROPERTIESWIDGET_H #include #include class QTreeView; namespace tremotesf { class PeersModel; class Rpc; class StringListModel; class TorrentFilesModel; class TorrentFilesView; class Torrent; class TrackersViewWidget; class TorrentPropertiesWidget : public QTabWidget { Q_OBJECT public: explicit TorrentPropertiesWidget(Rpc* rpc, bool horizontalDetails, QWidget* parent = nullptr); void setTorrent(Torrent* torrent); bool hasTorrent() const { return mTorrent != nullptr; } void saveState(); private: void setupDetailsTab(bool horizontal); void setupPeersTab(); void setupWebSeedersTab(); void setupLimitsTab(); void setTorrent(Torrent* torrent, bool oldTorrentDestroyed); Torrent* mTorrent{}; Rpc* const mRpc{}; std::function mUpdateDetailsTab; TorrentFilesModel* mFilesModel{}; TorrentFilesView* mFilesView{}; TrackersViewWidget* mTrackersViewWidget{}; QTreeView* mPeersView{}; PeersModel* mPeersModel{}; StringListModel* mWebSeedersModel{}; bool mUpdatingLimits{}; std::function mUpdateLimitsTab; signals: void hasTorrentChanged(bool hasTorrent); }; } #endif // TREMOTESF_TORRENTPROPERTIESWIDGET_H tremotesf-2.10.1/src/ui/screens/torrentproperties/trackersmodel.cpp000066400000000000000000000176261523411710200256360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "trackersmodel.h" #include #include #include #include #include #include "ui/itemmodels/modelutils.h" #include "rpc/torrent.h" #include "rpc/tracker.h" #include "formatutils.h" namespace tremotesf { using std::chrono::seconds; using namespace std::chrono_literals; namespace { QString trackerStatusString(const Tracker& tracker) { switch (tracker.status()) { case Tracker::Status::Inactive: //: Tracker status return qApp->translate("tremotesf", "Inactive"); case Tracker::Status::WaitingForUpdate: //: Tracker status return qApp->translate("tremotesf", "Waiting for update"); case Tracker::Status::QueuedForUpdate: //: Tracker status return qApp->translate("tremotesf", "About to update"); case Tracker::Status::Updating: //: Tracker status return qApp->translate("tremotesf", "Updating"); } return {}; } std::optional nextUpdateEtaFor(const Tracker& tracker) { if (!tracker.nextUpdateTime().isValid()) return std::nullopt; const auto secs = QDateTime::currentDateTimeUtc().secsTo(tracker.nextUpdateTime()); if (secs < 0) return std::nullopt; return seconds(secs); } } struct TrackersModel::TrackerItem { Tracker tracker; std::optional nextUpdateEta{}; TrackerItem(Tracker tracker) : tracker(std::move(tracker)), nextUpdateEta(nextUpdateEtaFor(this->tracker)) {} [[nodiscard]] bool operator==(const TrackerItem& other) const = default; [[nodiscard]] TrackerItem withUpdatedEta() const { TrackerItem updated = *this; updated.nextUpdateEta = nextUpdateEtaFor(tracker); return updated; } }; TrackersModel::TrackersModel(QObject* parent) : QAbstractTableModel(parent), mEtaUpdateTimer(new QTimer(this)) { mEtaUpdateTimer->setInterval(1s); mEtaUpdateTimer->setSingleShot(false); QObject::connect(mEtaUpdateTimer, &QTimer::timeout, this, &TrackersModel::updateEtas); } TrackersModel::~TrackersModel() = default; int TrackersModel::columnCount(const QModelIndex&) const { return QMetaEnum::fromType().keyCount(); } QVariant TrackersModel::data(const QModelIndex& index, int role) const { const auto& tracker = mTrackers.at(static_cast(index.row())); if (role == Qt::DisplayRole) { switch (static_cast(index.column())) { case Column::Announce: return tracker.tracker.announce(); case Column::Status: return trackerStatusString(tracker.tracker); case Column::Error: return tracker.tracker.errorMessage(); case Column::NextUpdate: if (tracker.nextUpdateEta.has_value()) { return formatutils::formatEta(static_cast(tracker.nextUpdateEta->count())); } break; case Column::Peers: return tracker.tracker.peers(); case Column::Seeders: return tracker.tracker.seeders(); case Column::Leechers: return tracker.tracker.leechers(); } } else if (role == SortRole) { if (static_cast(index.column()) == Column::NextUpdate && tracker.nextUpdateEta.has_value()) { return static_cast(tracker.nextUpdateEta->count()); } return data(index, Qt::DisplayRole); } return {}; } QVariant TrackersModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { return {}; } switch (static_cast(section)) { case Column::Announce: //: Trackers list column title return qApp->translate("tremotesf", "Address"); case Column::Status: //: Trackers list column title return qApp->translate("tremotesf", "Status"); case Column::Error: //: Trackers list column title return qApp->translate("tremotesf", "Error"); case Column::NextUpdate: //: Trackers list column title return qApp->translate("tremotesf", "Next Update"); case Column::Peers: //: Trackers list column title return qApp->translate("tremotesf", "Peers"); case Column::Seeders: //: Trackers list column title return qApp->translate("tremotesf", "Seeders"); case Column::Leechers: //: Trackers list column title return qApp->translate("tremotesf", "Leechers"); } return {}; } int TrackersModel::rowCount(const QModelIndex&) const { return static_cast(mTrackers.size()); } Torrent* TrackersModel::torrent() const { return mTorrent; } void TrackersModel::setTorrent(Torrent* torrent, bool oldTorrentDestroyed) { if (torrent == mTorrent) { return; } if (mTorrent && !oldTorrentDestroyed) { QObject::disconnect(mTorrent, nullptr, this, nullptr); } mTorrent = torrent; if (mTorrent) { update(); QObject::connect(mTorrent, &Torrent::updated, this, &TrackersModel::update); QObject::connect(mTorrent, &QObject::destroyed, this, [this] { setTorrent(nullptr, true); }); } else { beginResetModel(); mTrackers.clear(); endResetModel(); } } std::vector TrackersModel::idsFromIndexes(const QModelIndexList& indexes) const { return indexes | std::views::transform([this](const QModelIndex& index) { return mTrackers.at(static_cast(index.row())).tracker.id(); }) | std::ranges::to(); } const Tracker& TrackersModel::trackerAtIndex(const QModelIndex& index) const { return mTrackers.at(static_cast(index.row())).tracker; } class TrackersModelUpdater : public ModelListUpdater> { public: inline explicit TrackersModelUpdater(TrackersModel& model) : ModelListUpdater(model) {} protected: std::vector::iterator findNewItemForItem( std::vector& newItems, const TrackersModel::TrackerItem& item ) override { return std::ranges::find(newItems, item.tracker.id(), [](const auto& tracker) { return tracker.tracker.id(); }); } bool updateItem(TrackersModel::TrackerItem& item, TrackersModel::TrackerItem&& newItem) override { if (newItem != item) { item = std::move(newItem); return true; } return false; } }; void TrackersModel::update() { mEtaUpdateTimer->stop(); TrackersModelUpdater updater(*this); const auto& trackers = mTorrent->data().trackers; updater.update(mTrackers, std::vector(trackers.begin(), trackers.end())); if (std::ranges::any_of(trackers, [](const Tracker& tracker) { return tracker.nextUpdateTime().isValid(); })) { mEtaUpdateTimer->start(); } } void TrackersModel::updateEtas() { TrackersModelUpdater updater(*this); updater.update( mTrackers, mTrackers | std::views::transform(&TrackerItem::withUpdatedEta) | std::ranges::to() ); } } tremotesf-2.10.1/src/ui/screens/torrentproperties/trackersmodel.h000066400000000000000000000033411523411710200252700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TRACKERSMODEL_H #define TREMOTESF_TRACKERSMODEL_H #include #include class QTimer; namespace tremotesf { class Torrent; class Tracker; } namespace tremotesf { class TrackersModel final : public QAbstractTableModel { Q_OBJECT public: enum class Column { Announce, Status, Error, NextUpdate, Peers, Seeders, Leechers }; Q_ENUM(Column) static constexpr auto SortRole = Qt::UserRole; explicit TrackersModel(QObject* parent = nullptr); ~TrackersModel() override; Q_DISABLE_COPY_MOVE(TrackersModel) int columnCount(const QModelIndex& = {}) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& = {}) const override; Torrent* torrent() const; void setTorrent(Torrent* torrent, bool oldTorrentDestroyed); std::vector idsFromIndexes(const QModelIndexList& indexes) const; const Tracker& trackerAtIndex(const QModelIndex& index) const; using QAbstractItemModel::beginInsertRows; using QAbstractItemModel::beginRemoveRows; using QAbstractItemModel::endInsertRows; using QAbstractItemModel::endRemoveRows; struct TrackerItem; private: void update(); void updateEtas(); Torrent* mTorrent{}; std::vector mTrackers; QTimer* mEtaUpdateTimer{}; }; } #endif // TREMOTESF_TRACKERSMODEL_H tremotesf-2.10.1/src/ui/screens/torrentproperties/trackersviewwidget.cpp000066400000000000000000000214211523411710200267000ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "trackersviewwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rpc/torrent.h" #include "rpc/tracker.h" #include "ui/itemmodels/baseproxymodel.h" #include "ui/widgets/basetreeview.h" #include "ui/widgets/textinputdialog.h" #include "rpc/rpc.h" #include "settings.h" #include "trackersmodel.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { class EnterEatingTreeView final : public QTreeView { Q_OBJECT public: explicit EnterEatingTreeView(QWidget* parent = nullptr) : QTreeView(parent) {} protected: void keyPressEvent(QKeyEvent* event) override { QTreeView::keyPressEvent(event); switch (event->key()) { case Qt::Key_Enter: case Qt::Key_Return: event->accept(); default: break; } } }; } TrackersViewWidget::TrackersViewWidget(Rpc* rpc, QWidget* parent) : QWidget(parent), mRpc(rpc), mModel(new TrackersModel(this)), mProxyModel(new BaseProxyModel( mModel, TrackersModel::SortRole, static_cast(TrackersModel::Column::Announce), this )), mTrackersView(new EnterEatingTreeView(this)) { auto layout = new QHBoxLayout(this); setCommonTreeViewProperties(mTrackersView, true); mTrackersView->setContextMenuPolicy(Qt::CustomContextMenu); mTrackersView->setModel(mProxyModel); mTrackersView->setSelectionMode(QAbstractItemView::ExtendedSelection); mTrackersView->header()->restoreState(Settings::instance()->get_trackersViewHeaderState()); QObject::connect(mTrackersView, &EnterEatingTreeView::activated, this, &TrackersViewWidget::showEditDialogs); auto removeAction = new QAction( QIcon::fromTheme("list-remove"_L1), //: Tracker's context menu item qApp->translate("tremotesf", "&Remove"), this ); removeAction->setShortcut(QKeySequence::Delete); mTrackersView->addAction(removeAction); QObject::connect(removeAction, &QAction::triggered, this, &TrackersViewWidget::removeTrackers); QObject::connect(mTrackersView, &EnterEatingTreeView::customContextMenuRequested, this, [=, this](QPoint pos) { if (mTrackersView->indexAt(pos).isValid()) { QMenu contextMenu; const auto* const editAction = contextMenu.addAction( QIcon::fromTheme("document-properties"_L1), //: Tracker's context menu item qApp->translate("tremotesf", "&Edit...") ); QObject::connect(editAction, &QAction::triggered, this, &TrackersViewWidget::showEditDialogs); contextMenu.addAction(removeAction); contextMenu.exec(mTrackersView->viewport()->mapToGlobal(pos)); } }); layout->addWidget(mTrackersView); auto buttonsLayout = new QVBoxLayout(); layout->addLayout(buttonsLayout); auto addTrackersButton = new QPushButton( QIcon::fromTheme("list-add"_L1), //: Button qApp->translate("tremotesf", "Add..."), this ); QObject::connect(addTrackersButton, &QPushButton::clicked, this, &TrackersViewWidget::addTrackers); buttonsLayout->addWidget(addTrackersButton); auto editButton = new QPushButton( QIcon::fromTheme("document-properties"_L1), //: Button qApp->translate("tremotesf", "Edit..."), this ); QObject::connect(editButton, &QPushButton::clicked, this, &TrackersViewWidget::showEditDialogs); editButton->setEnabled(false); buttonsLayout->addWidget(editButton); auto removeButton = new QPushButton( QIcon::fromTheme("list-remove"_L1), //: Button qApp->translate("tremotesf", "Remove"), this ); removeButton->setEnabled(false); QObject::connect(removeButton, &QPushButton::clicked, this, &TrackersViewWidget::removeTrackers); buttonsLayout->addWidget(removeButton); auto reannounceButton = new QPushButton( QIcon::fromTheme("view-refresh"_L1), //: Button qApp->translate("tremotesf", "Reanno&unce"), this ); QObject::connect(reannounceButton, &QPushButton::clicked, this, [=, this] { mRpc->reannounceTorrents(std::array{mTorrent->data().id}); }); buttonsLayout->addWidget(reannounceButton); buttonsLayout->addStretch(); QObject::connect(mTrackersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [=, this] { const bool hasSelection = mTrackersView->selectionModel()->hasSelection(); editButton->setEnabled(hasSelection); removeButton->setEnabled(hasSelection); }); } void TrackersViewWidget::setTorrent(Torrent* torrent, bool oldTorrentDestroyed) { mTorrent = torrent; mModel->setTorrent(torrent, oldTorrentDestroyed); } void TrackersViewWidget::saveState() { Settings::instance()->set_trackersViewHeaderState(mTrackersView->header()->saveState()); } void TrackersViewWidget::addTrackers() { auto dialog = new TextInputDialog( //: Dialog title qApp->translate("tremotesf", "Add Trackers"), qApp->translate("tremotesf", "Trackers announce URLs:"), QString(), //: Dialog confirmation button qApp->translate("tremotesf", "Add"), true, this ); QObject::connect(dialog, &TextInputDialog::accepted, this, [=, this] { auto lines = dialog->text().split('\n', Qt::SkipEmptyParts); mTorrent->addTrackers( lines | std::views::transform([](QString& announceUrl) { return std::set{std::move(announceUrl)}; }) | std::ranges::to() ); }); dialog->show(); } void TrackersViewWidget::showEditDialogs() { const QModelIndexList indexes(mTrackersView->selectionModel()->selectedRows()); for (const QModelIndex& index : indexes) { const Tracker& tracker = mModel->trackerAtIndex(mProxyModel->mapToSource(index)); const int id = tracker.id(); auto dialog = new TextInputDialog( //: Dialog title qApp->translate("tremotesf", "Edit Tracker"), qApp->translate("tremotesf", "Tracker announce URL:"), tracker.announce(), QString(), false, this ); QObject::connect(dialog, &TextInputDialog::accepted, this, [=, this] { mTorrent->setTracker(id, dialog->text()); }); dialog->show(); } } void TrackersViewWidget::removeTrackers() { if (!mTrackersView->selectionModel()->hasSelection()) { return; } QMessageBox dialog(this); dialog.setIcon(QMessageBox::Warning); dialog.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); //: Dialog confirmation button dialog.button(QMessageBox::Ok)->setText(qApp->translate("tremotesf", "Remove")); dialog.setDefaultButton(QMessageBox::Cancel); const auto ids = mModel->idsFromIndexes(mProxyModel->sourceIndexes(mTrackersView->selectionModel()->selectedRows())); if (ids.size() == 1) { //: Dialog title dialog.setWindowTitle(qApp->translate("tremotesf", "Remove Tracker")); dialog.setText(qApp->translate("tremotesf", "Are you sure you want to remove this tracker?")); } else { //: Dialog title dialog.setWindowTitle(qApp->translate("tremotesf", "Remove Trackers")); // Don't put static_cast in qApp->translate() - lupdate doesn't like it const auto count = static_cast(ids.size()); //: %Ln is number of trackers selected for deletion dialog.setText( qApp->translate("tremotesf", "Are you sure you want to remove %Ln selected trackers?", nullptr, count) ); } if (dialog.exec() == QMessageBox::Ok) { mTorrent->removeTrackers(ids); } } } #include "trackersviewwidget.moc" tremotesf-2.10.1/src/ui/screens/torrentproperties/trackersviewwidget.h000066400000000000000000000017221523411710200263470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TRACKERSVIEWWIDGET_H #define TRACKERSVIEWWIDGET_H #include class QTreeView; namespace tremotesf { class Torrent; } namespace tremotesf { class BaseProxyModel; class Rpc; class TrackersModel; class TrackersViewWidget final : public QWidget { Q_OBJECT public: TrackersViewWidget(Rpc* rpc, QWidget* parent = nullptr); ~TrackersViewWidget() override = default; Q_DISABLE_COPY_MOVE(TrackersViewWidget) void setTorrent(Torrent* torrent, bool oldTorrentDestroyed); void saveState(); private: void addTrackers(); void showEditDialogs(); void removeTrackers(); Torrent* mTorrent{}; Rpc* mRpc{}; TrackersModel* mModel{}; BaseProxyModel* mProxyModel{}; QTreeView* mTrackersView{}; }; } #endif // TRACKERSVIEWWIDGET_H tremotesf-2.10.1/src/ui/stylehelpers.cpp000066400000000000000000000034071523411710200202360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "stylehelpers.h" #include #include #include #include "target_os.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { const QStyle* baseStyle(const QStyle* style) { while (style) { if (const auto proxyStyle = qobject_cast(style); proxyStyle) { style = proxyStyle->baseStyle(); if (style == proxyStyle) { break; } } else { break; } } return style; } } std::optional determineStyle(const QStyle* style) { style = baseStyle(style); const auto name = style->name(); if constexpr (targetOs == TargetOs::UnixMacOS) { if (name.compare("macos"_L1, Qt::CaseInsensitive) == 0) { return KnownStyle::macOS; } } if (name.compare("breeze"_L1, Qt::CaseInsensitive) == 0) { return KnownStyle::Breeze; } return std::nullopt; } std::optional determineStyle() { return determineStyle(QApplication::style()); } void overrideBreezeFramelessScrollAreaHeuristic(QAbstractScrollArea* widget, bool drawFrame) { widget->setProperty("_breeze_force_frame", drawFrame); } void makeScrollAreaTransparent(QAbstractScrollArea* widget) { widget->setFrameShape(QFrame::NoFrame); QPalette palette{}; palette.setColor(widget->viewport()->backgroundRole(), QColor(Qt::transparent)); widget->setPalette(palette); } } tremotesf-2.10.1/src/ui/stylehelpers.h000066400000000000000000000011471523411710200177020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_STYLEHELPERS_H #define TREMOTESF_STYLEHELPERS_H #include class QAbstractScrollArea; class QStyle; namespace tremotesf { enum class KnownStyle { Breeze, macOS }; std::optional determineStyle(const QStyle* style); std::optional determineStyle(); void overrideBreezeFramelessScrollAreaHeuristic(QAbstractScrollArea* widget, bool drawFrame); void makeScrollAreaTransparent(QAbstractScrollArea* widget); } #endif // TREMOTESF_STYLEHELPERS_H tremotesf-2.10.1/src/ui/systemcolorsprovider.h000066400000000000000000000036031523411710200214770ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef SYSTEMCOLORSPROVIDER_H #define SYSTEMCOLORSPROVIDER_H #include #include #include "log/formatters.h" namespace tremotesf { class SystemColorsProvider : public QObject { Q_OBJECT public: static SystemColorsProvider* createInstance(QObject* parent = nullptr); virtual bool isDarkThemeEnabled() const { return false; }; struct AccentColors { QColor accentColor{}; QColor accentColorLight1{}; QColor accentColorDark1{}; QColor accentColorDark2{}; [[nodiscard]] bool isValid() const { return accentColor.isValid() && accentColorLight1.isValid() && accentColorDark1.isValid() && accentColorDark2.isValid(); } [[nodiscard]] bool operator==(const AccentColors&) const = default; }; virtual AccentColors accentColors() const { return {}; }; protected: explicit SystemColorsProvider(QObject* parent = nullptr) : QObject{parent} {} signals: void darkThemeEnabledChanged(); void accentColorsChanged(); }; } template<> struct fmt::formatter : tremotesf::SimpleFormatter { format_context::iterator format(const tremotesf::SystemColorsProvider::AccentColors& colors, format_context& ctx) const { return fmt::format_to( ctx.out(), "AccentColors(accentColor={}, accentColorLight1={}, accentColorDark1={}, accentColorDark2={})", colors.accentColor.name(), colors.accentColorLight1.name(), colors.accentColorDark1.name(), colors.accentColorDark2.name() ); } }; #endif // SYSTEMCOLORSPROVIDER_H tremotesf-2.10.1/src/ui/systemcolorsprovider_windows.cpp000066400000000000000000000063561523411710200236140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "systemcolorsprovider.h" #include #include #include #include "log/log.h" #include "windowshelpers.h" using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::ViewManagement; namespace tremotesf { namespace { QColor qColor(Color color) { return {color.R, color.G, color.B, color.A}; } } namespace { class SystemColorsProviderWindows final : public SystemColorsProvider { Q_OBJECT public: explicit SystemColorsProviderWindows(QObject* parent = nullptr) : SystemColorsProvider(parent) { info().log("System dark theme enabled = {}", mDarkThemeEnabled); info().log("System accent colors = {}", mAccentColors); revoker = settings.ColorValuesChanged(winrt::auto_revoke, [this](auto&&...) { QMetaObject::invokeMethod(this, [this] { if (const bool newDarkThemeEnabled = isDarkThemeEnabledImpl(); newDarkThemeEnabled != mDarkThemeEnabled) { info().log("System dark theme state changed to {}", newDarkThemeEnabled); mDarkThemeEnabled = newDarkThemeEnabled; emit darkThemeEnabledChanged(); } if (const auto newAccentColors = accentColorsImpl(); newAccentColors != mAccentColors) { info().log("System accent colors changed to {}", newAccentColors); mAccentColors = newAccentColors; emit accentColorsChanged(); } }); }); } bool isDarkThemeEnabled() const override { return mDarkThemeEnabled; }; AccentColors accentColors() const override { return mAccentColors; }; private: bool isDarkThemeEnabledImpl() { // Apparently this is the way to do it according to Microsoft const auto foreground = settings.GetColorValue(UIColorType::Foreground); return (((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128)); } AccentColors accentColorsImpl() { return { .accentColor = qColor(settings.GetColorValue(UIColorType::Accent)), .accentColorLight1 = qColor(settings.GetColorValue(UIColorType::AccentLight1)), .accentColorDark1 = qColor(settings.GetColorValue(UIColorType::AccentDark1)), .accentColorDark2 = qColor(settings.GetColorValue(UIColorType::AccentDark2)), }; } UISettings settings{}; UISettings::ColorValuesChanged_revoker revoker{}; bool mDarkThemeEnabled{isDarkThemeEnabledImpl()}; AccentColors mAccentColors{accentColorsImpl()}; }; } SystemColorsProvider* SystemColorsProvider::createInstance(QObject* parent) { return new SystemColorsProviderWindows(parent); } } #include "systemcolorsprovider_windows.moc" tremotesf-2.10.1/src/ui/widgets/000077500000000000000000000000001523411710200164515ustar00rootroot00000000000000tremotesf-2.10.1/src/ui/widgets/basetreeview.cpp000066400000000000000000000033261523411710200216460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "basetreeview.h" #include #include #include #include namespace tremotesf { void setCommonTreeViewProperties(QTreeView* view, bool isFlatList) { view->setAllColumnsShowFocus(true); view->setSortingEnabled(true); view->setUniformRowHeights(true); if (isFlatList) { view->setRootIsDecorated(false); view->header()->setFirstSectionMovable(true); } const auto header = view->header(); header->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(header, &QHeaderView::customContextMenuRequested, view, [=](QPoint pos) { const auto model = view->model(); if (!model) { return; } const auto menu = new QMenu(view); menu->setAttribute(Qt::WA_DeleteOnClose); for (int i = 0, max = model->columnCount(); i < max; ++i) { const auto action = menu->addAction(model->headerData(i, Qt::Horizontal).toString()); action->setCheckable(true); action->setChecked(!view->isColumnHidden(i)); } QObject::connect(menu, &QMenu::triggered, view, [=](QAction* action) { const auto column = static_cast(menu->actions().indexOf(action)); if (view->isColumnHidden(column)) { view->showColumn(column); } else { view->hideColumn(column); } }); menu->popup(header->viewport()->mapToGlobal(pos)); }); } } tremotesf-2.10.1/src/ui/widgets/basetreeview.h000066400000000000000000000004751523411710200213150ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_BASETREEVIEW_H #define TREMOTESF_BASETREEVIEW_H class QTreeView; namespace tremotesf { void setCommonTreeViewProperties(QTreeView* view, bool isFlatList); } #endif // TREMOTESF_BASETREEVIEW_H tremotesf-2.10.1/src/ui/widgets/editlabelswidget.cpp000066400000000000000000000130421523411710200224710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include #include #include "editlabelswidget.h" #include "rpc/rpc.h" #include "rpc/torrent.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { const QIcon& tagIcon() { static const auto icon = QIcon::fromTheme("tag"_L1); return icon; } } EditLabelsWidget::EditLabelsWidget(const std::vector& enabledLabels, Rpc* rpc, QWidget* parent) : QWidget{parent}, mRpc{rpc} { auto layout = new QGridLayout(this); layout->setContentsMargins(QMargins{}); mLabelsList = new QListWidget(this); layout->addWidget(mLabelsList, 0, 0, 1, 2); mLabelsList->setSelectionMode(QAbstractItemView::ExtendedSelection); mLabelsList->setIconSize(QSize(16, 16)); const auto addLabel = [=, this](const QString& label) { mLabelsList->addItem(label); const auto item = mLabelsList->item(mLabelsList->count() - 1); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren); item->setIcon(tagIcon()); }; for (const auto& label : enabledLabels) { addLabel(label); } mLabelsList->sortItems(); const auto updateListVisibility = [this] { mLabelsList->setVisible(mLabelsList->count() > 0); }; updateListVisibility(); QObject::connect(mLabelsList->model(), &QAbstractItemModel::rowsInserted, this, updateListVisibility); QObject::connect(mLabelsList->model(), &QAbstractItemModel::rowsRemoved, this, updateListVisibility); const auto removeAction = new QAction(QIcon::fromTheme("list-remove"_L1), qApp->translate("tremotesf", "&Remove"), mLabelsList); mLabelsList->addAction(removeAction); removeAction->setShortcut(QKeySequence::Delete); QObject::connect(removeAction, &QAction::triggered, this, [this] { qDeleteAll(mLabelsList->selectedItems()); }); mLabelsList->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(mLabelsList, &QWidget::customContextMenuRequested, this, [=, this](QPoint pos) { if (mLabelsList->selectionModel()->hasSelection() && mLabelsList->indexAt(pos).isValid()) { const auto menu = new QMenu(this); menu->addAction(removeAction); menu->popup(mLabelsList->viewport()->mapToGlobal(pos)); } }); mComboBox = new QComboBox(this); layout->addWidget(mComboBox, 1, 0); mComboBox->setEditable(true); mComboBox->setInsertPolicy(QComboBox::NoInsert); mComboBox->lineEdit()->setPlaceholderText(qApp->translate("tremotesf", "New label...")); updateComboBoxLabels(); QObject::connect(rpc, &Rpc::connectedChanged, this, &EditLabelsWidget::updateComboBoxLabels); const auto addButton = new QPushButton(QIcon::fromTheme("list-add"_L1), qApp->translate("tremotesf", "Add"), this); layout->addWidget(addButton, 1, 1); addButton->setEnabled(false); const auto updateButtonEnabledState = [=, this] { addButton->setEnabled(!mComboBox->currentText().trimmed().isEmpty()); }; QObject::connect(mComboBox, &QComboBox::currentTextChanged, this, updateButtonEnabledState); layout->setColumnStretch(0, 1); const auto addLabelFromComboBox = [=, this] { const auto text = mComboBox->currentText().trimmed(); if (!text.isEmpty() && mLabelsList->findItems(text, Qt::MatchExactly).isEmpty()) { addLabel(text); return true; } return false; }; QObject::connect(mComboBox, &QComboBox::activated, this, [=, this] { addLabelFromComboBox(); mComboBox->setCurrentIndex(-1); }); QObject::connect(mComboBox->lineEdit(), &QLineEdit::returnPressed, this, [=, this] { if (addLabelFromComboBox()) { mComboBox->clearEditText(); } }); QObject::connect(addButton, &QPushButton::clicked, this, [=, this] { if (addLabelFromComboBox()) { mComboBox->clearEditText(); } }); } void EditLabelsWidget::setFocusOnComboBox() { mComboBox->setFocus(); } bool EditLabelsWidget::comboBoxHasFocus() const { return mComboBox->hasFocus(); } std::vector EditLabelsWidget::enabledLabels() const { return std::views::iota(0, mLabelsList->count()) | std::views::transform([this](int i) { return mLabelsList->item(i)->text(); }) | std::ranges::to(); } void EditLabelsWidget::updateComboBoxLabels() { mComboBox->clear(); if (!mRpc->isConnected()) { return; } const auto allLabels = mRpc->torrents() | std::views::transform([](const auto& t) { return t->data().labels; }) | std::views::join | std::views::common // For some reason GCC 14 requies views::common here | std::ranges::to(); for (const auto& label : allLabels) { mComboBox->addItem(tagIcon(), label); } mComboBox->setCurrentIndex(-1); } } // namespace tremotesf tremotesf-2.10.1/src/ui/widgets/editlabelswidget.h000066400000000000000000000014261523411710200221410ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_EDITLABELSWIDGET_H #define TREMOTESF_EDITLABELSWIDGET_H #include class QComboBox; class QListWidget; namespace tremotesf { class Rpc; class EditLabelsWidget : public QWidget { Q_OBJECT public: explicit EditLabelsWidget(const std::vector& enabledLabels, Rpc* rpc, QWidget* parent); void setFocusOnComboBox(); bool comboBoxHasFocus() const; std::vector enabledLabels() const; private: void updateComboBoxLabels(); Rpc* mRpc; QListWidget* mLabelsList{}; QComboBox* mComboBox{}; }; } // namespace tremotesf #endif // TREMOTESF_EDITLABELSWIDGET_H tremotesf-2.10.1/src/ui/widgets/listplaceholder.cpp000066400000000000000000000023741523411710200223410ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "listplaceholder.h" #include #include #include namespace tremotesf { QLabel* createListPlaceholderLabel(const QString& text) { auto* const label = new QLabel(text); label->setForegroundRole(QPalette::PlaceholderText); label->setTextInteractionFlags(Qt::NoTextInteraction); return label; } void addListPlaceholderLabelToViewportAndManageVisibility(QAbstractItemView* itemView, QLabel* placeholderLabel) { auto* const layout = new QVBoxLayout(itemView->viewport()); layout->addWidget(placeholderLabel); layout->setAlignment(placeholderLabel, Qt::AlignCenter); auto* const model = itemView->model(); const auto updateVisibility = [=] { placeholderLabel->setVisible(model->rowCount() == 0); }; updateVisibility(); QObject::connect(model, &QAbstractItemModel::rowsInserted, placeholderLabel, updateVisibility); QObject::connect(model, &QAbstractItemModel::rowsRemoved, placeholderLabel, updateVisibility); QObject::connect(model, &QAbstractItemModel::modelReset, placeholderLabel, updateVisibility); } } tremotesf-2.10.1/src/ui/widgets/listplaceholder.h000066400000000000000000000007401523411710200220010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_LISTPLACEHOLDER_H #define TREMOTESF_LISTPLACEHOLDER_H #include class QAbstractItemView; class QLabel; namespace tremotesf { QLabel* createListPlaceholderLabel(const QString& text = {}); void addListPlaceholderLabelToViewportAndManageVisibility(QAbstractItemView* itemView, QLabel* placeholderLabel); } #endif // TREMOTESF_LISTPLACEHOLDER_H tremotesf-2.10.1/src/ui/widgets/progressbardelegate.cpp000066400000000000000000000050011523411710200231750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "progressbardelegate.h" #include "ui/stylehelpers.h" #include "target_os.h" using namespace Qt::StringLiterals; namespace tremotesf { namespace { [[maybe_unused]] const QStyle* fusionStyle() { static const auto* const style = [] { const auto s = QStyleFactory::create("fusion"_L1); if (!s) { throw std::runtime_error("Failed to create Fusion style"); } s->setParent(qApp); return s; }(); return style; } } void ProgressBarDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const auto* style = opt.widget ? opt.widget->style() : QApplication::style(); if constexpr (targetOs == TargetOs::UnixMacOS) { if (determineStyle(style) == KnownStyle::macOS) { style = fusionStyle(); } } // Draw background style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); const int horizontalMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin); const int verticalMargin = style->pixelMetric(QStyle::PM_FocusFrameVMargin); QStyleOptionProgressBar progressBar{}; progressBar.rect = opt.rect.marginsRemoved(QMargins(horizontalMargin, verticalMargin, horizontalMargin, verticalMargin)); progressBar.minimum = 0; progressBar.maximum = 100; const auto progress = index.data(mProgressRole).toDouble(); progressBar.progress = static_cast(progress * 100); if (progressBar.progress < 0) { progressBar.progress = 0; } else if (progressBar.progress > 100) { progressBar.progress = 100; } progressBar.state = opt.state | QStyle::State_Horizontal; progressBar.text = opt.text; progressBar.textVisible = true; progressBar.palette = opt.palette; // Sometimes this is out of sync if (opt.widget && opt.widget->isActiveWindow()) { progressBar.palette.setCurrentColorGroup(QPalette::Active); } style->drawControl(QStyle::CE_ProgressBar, &progressBar, painter, opt.widget); } } tremotesf-2.10.1/src/ui/widgets/progressbardelegate.h000066400000000000000000000012721523411710200226500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_PROGRESSBARDELEGATE_H #define TREMOTESF_PROGRESSBARDELEGATE_H #include namespace tremotesf { class ProgressBarDelegate final : public QStyledItemDelegate { Q_OBJECT public: explicit ProgressBarDelegate(int progressRole, QObject* parent = nullptr) : QStyledItemDelegate(parent), mProgressRole(progressRole) {}; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: int mProgressRole; }; } #endif // TREMOTESF_PROGRESSBARDELEGATE_H tremotesf-2.10.1/src/ui/widgets/remotedirectoryselectionwidget.cpp000066400000000000000000000156531523411710200255210ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "remotedirectoryselectionwidget.h" #include #include #include #include #include #include #include #include "target_os.h" #include "rpc/rpc.h" #include "rpc/servers.h" #include "rpc/serversettings.h" #include "ui/stylehelpers.h" using namespace Qt::StringLiterals; namespace tremotesf { RemoteDirectorySelectionWidgetViewModel::RemoteDirectorySelectionWidgetViewModel( QString path, const Rpc* rpc, QObject* parent ) : QObject(parent), mRpc(rpc), mPath(std::move(path)), mMode( rpc->isLocal() ? Mode::Local : (Servers::instance()->currentServerHasMountedDirectories() ? Mode::RemoteMounted : Mode::Remote) ) {} QString RemoteDirectorySelectionWidgetViewModel::fileDialogDirectory() { if (mMode == Mode::RemoteMounted) { return Servers::instance()->fromRemoteToLocalDirectory(mPath, mRpc->serverSettings()); } return mPath; } void RemoteDirectorySelectionWidgetViewModel::updatePathProgrammatically(QString path) { auto displayPath = toNativeSeparators(path); updatePathProgrammatically(std::move(path), std::move(displayPath)); } void RemoteDirectorySelectionWidgetViewModel::updatePathProgrammatically(QString path, QString displayPath) { if (updatePathImpl(std::move(path), std::move(displayPath))) { emit pathChanged(false); } } void RemoteDirectorySelectionWidgetViewModel::onPathEditedByUser(const QString& text) { auto displayPath = text.trimmed(); auto path = normalizePath(displayPath); if (updatePathImpl(std::move(path), std::move(displayPath))) { emit pathChanged(true); } } void RemoteDirectorySelectionWidgetViewModel::onFileDialogAccepted(QString path) { if (mMode != Mode::RemoteMounted) { updatePathProgrammatically(std::move(path)); return; } auto remoteDirectory = Servers::instance()->fromLocalToRemoteDirectory(path, mRpc->serverSettings()); if (remoteDirectory.isEmpty()) { emit showMountedDirectoryError(); } else { updatePathProgrammatically(std::move(remoteDirectory)); } } QString RemoteDirectorySelectionWidgetViewModel::normalizePath(const QString& path) const { return tremotesf::normalizePath(path, mRpc->serverSettings()->data().pathOs); } QString RemoteDirectorySelectionWidgetViewModel::toNativeSeparators(const QString& path) const { return tremotesf::toNativeSeparators(path, mRpc->serverSettings()->data().pathOs); } bool RemoteDirectorySelectionWidgetViewModel::updatePathImpl(QString path, QString displayPath) { if (path != mPath) { mPath = std::move(path); mDisplayPath = std::move(displayPath); return true; } return false; } RemoteDirectorySelectionWidget::RemoteDirectorySelectionWidget(QWidget* parent) : QWidget(parent) {} void RemoteDirectorySelectionWidget::setup(QString path, const Rpc* rpc) { auto layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); mTextField = createTextField(); layout->addWidget(mTextField, 1); mSelectDirectoryButton = new QPushButton(QIcon::fromTheme("document-open"_L1), QString(), this); layout->addWidget(mSelectDirectoryButton); mSelectDirectoryButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); if constexpr (targetOs == TargetOs::UnixMacOS) { if (determineStyle() == KnownStyle::macOS) { // Button becomes ugly if we don't set these specific values mSelectDirectoryButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); mSelectDirectoryButton->setMaximumSize(32, 32); } } mViewModel = createViewModel(std::move(path), rpc); mSelectDirectoryButton->setEnabled(mViewModel->enableFileDialog()); if (mViewModel->enableFileDialog()) { QObject::connect( mSelectDirectoryButton, &QPushButton::clicked, this, &RemoteDirectorySelectionWidget::showFileDialog ); } const auto lineEdit = lineEditFromTextField(); const auto updateLineEdit = [=, this] { lineEdit->setText(mViewModel->displayPath()); }; updateLineEdit(); QObject::connect( mViewModel, &RemoteDirectorySelectionWidgetViewModel::pathChanged, this, [=](bool textFieldWasEdited) { if (!textFieldWasEdited) { updateLineEdit(); } } ); QObject::connect( mViewModel, &RemoteDirectorySelectionWidgetViewModel::pathChanged, this, &RemoteDirectorySelectionWidget::pathChanged ); QObject::connect(lineEdit, &QLineEdit::textEdited, this, [=, this](const auto& text) { mViewModel->onPathEditedByUser(text); }); QObject::connect( mViewModel, &RemoteDirectorySelectionWidgetViewModel::showMountedDirectoryError, this, [=, this] { QMessageBox::warning( this, //: Dialog title qApp->translate("tremotesf", "Error"), qApp->translate("tremotesf", "Selected directory should be inside mounted directory") ); } ); } RemoteDirectorySelectionWidgetViewModel* RemoteDirectorySelectionWidget::createViewModel(QString path, const Rpc* rpc) { return new RemoteDirectorySelectionWidgetViewModel(std::move(path), rpc, this); } QWidget* RemoteDirectorySelectionWidget::createTextField() { return new QLineEdit(this); } QLineEdit* RemoteDirectorySelectionWidget::lineEditFromTextField() { return qobject_cast(mTextField); } void RemoteDirectorySelectionWidget::showFileDialog() { auto dialog = new QFileDialog( this, //: Directory chooser dialog title qApp->translate("tremotesf", "Select Directory"), mViewModel->fileDialogDirectory() ); dialog->setFileMode(QFileDialog::Directory); dialog->setOptions(QFileDialog::ShowDirsOnly); dialog->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(dialog, &QFileDialog::accepted, this, [=, this] { mViewModel->onFileDialogAccepted(dialog->selectedFiles().constFirst()); }); if constexpr (targetOs == TargetOs::Windows) { dialog->open(); } else { dialog->show(); } } } tremotesf-2.10.1/src/ui/widgets/remotedirectoryselectionwidget.h000066400000000000000000000046661523411710200251700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_FILESELECTIONWIDGET_H #define TREMOTESF_FILESELECTIONWIDGET_H #include class QLineEdit; class QPushButton; namespace tremotesf { class Rpc; class RemoteDirectorySelectionWidgetViewModel : public QObject { Q_OBJECT public: explicit RemoteDirectorySelectionWidgetViewModel(QString path, const Rpc* rpc, QObject* parent = nullptr); [[nodiscard]] QString path() const { return mPath; }; [[nodiscard]] QString displayPath() const { return mDisplayPath; }; [[nodiscard]] virtual bool enableFileDialog() const { return mMode != Mode::Remote; } [[nodiscard]] virtual QString fileDialogDirectory(); void updatePathProgrammatically(QString path); void updatePathProgrammatically(QString path, QString displayPath); void onPathEditedByUser(const QString& text); void onFileDialogAccepted(QString path); protected: [[nodiscard]] QString normalizePath(const QString& path) const; [[nodiscard]] QString toNativeSeparators(const QString& path) const; const Rpc* mRpc{}; QString mPath{}; QString mDisplayPath{toNativeSeparators(mPath)}; enum class Mode { Local, RemoteMounted, Remote }; Mode mMode{}; private: [[nodiscard]] bool updatePathImpl(QString path, QString displayPath); signals: void pathChanged(bool textFieldWasEdited); void showMountedDirectoryError(); }; class RemoteDirectorySelectionWidget : public QWidget { Q_OBJECT public: explicit RemoteDirectorySelectionWidget(QWidget* parent = nullptr); virtual void setup(QString path, const Rpc* rpc); [[nodiscard]] QString path() const { return mViewModel->path(); } virtual void resetPath(QString path) { mViewModel->updatePathProgrammatically(std::move(path)); } protected: virtual QWidget* createTextField(); virtual QLineEdit* lineEditFromTextField(); virtual RemoteDirectorySelectionWidgetViewModel* createViewModel(QString path, const Rpc* rpc); RemoteDirectorySelectionWidgetViewModel* mViewModel{}; QWidget* mTextField{}; QPushButton* mSelectDirectoryButton{}; private: void showFileDialog(); signals: void pathChanged(); }; } #endif // TREMOTESF_FILESELECTIONWIDGET_H tremotesf-2.10.1/src/ui/widgets/textinputdialog.cpp000066400000000000000000000047251523411710200224110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "textinputdialog.h" #include #include #include #include #include #include namespace tremotesf { TextInputDialog::TextInputDialog( const QString& title, const QString& labelText, const QString& text, const QString& okButtonText, bool multiline, QWidget* parent ) : QDialog(parent) { setWindowTitle(title); auto layout = new QVBoxLayout(this); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); auto label = new QLabel(labelText, this); label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); layout->addWidget(label); if (multiline) { mPlainTextEdit = new QPlainTextEdit(text, this); layout->addWidget(mPlainTextEdit); } else { mLineEdit = new QLineEdit(text, this); layout->addWidget(mLineEdit); } auto dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); if (!okButtonText.isEmpty()) { dialogButtonBox->button(QDialogButtonBox::Ok)->setText(okButtonText); } QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &TextInputDialog::accept); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &TextInputDialog::reject); if (text.isEmpty()) { dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } const auto onTextChanged = [dialogButtonBox](const QString& text) { if (text.isEmpty()) { dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } else { dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } }; if (multiline) { QObject::connect(mPlainTextEdit, &QPlainTextEdit::textChanged, this, [=, this] { onTextChanged(mPlainTextEdit->toPlainText()); }); } else { QObject::connect(mLineEdit, &QLineEdit::textChanged, this, onTextChanged); } layout->addWidget(dialogButtonBox); resize(sizeHint().expandedTo(QSize(720, 0))); } QString TextInputDialog::text() const { return mLineEdit ? mLineEdit->text() : mPlainTextEdit->toPlainText(); } } tremotesf-2.10.1/src/ui/widgets/textinputdialog.h000066400000000000000000000013501523411710200220450ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TEXTINPUTDIALOG_H #define TREMOTESF_TEXTINPUTDIALOG_H #include class QLineEdit; class QPlainTextEdit; namespace tremotesf { class TextInputDialog final : public QDialog { Q_OBJECT public: explicit TextInputDialog( const QString& title, const QString& labelText, const QString& text, const QString& okButtonText, bool multiline, QWidget* parent = nullptr ); QString text() const; private: QLineEdit* mLineEdit = nullptr; QPlainTextEdit* mPlainTextEdit = nullptr; }; } #endif tremotesf-2.10.1/src/ui/widgets/tooltipwhenelideddelegate.cpp000066400000000000000000000045001523411710200243720ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include "tooltipwhenelideddelegate.h" namespace tremotesf { namespace { bool isTextElided(const QString& text, const QStyleOptionViewItem& option) { const QFontMetrics metrics(option.font); const int textWidth = metrics.horizontalAdvance(text); const auto style = option.widget ? option.widget->style() : qApp->style(); QRect textRect(style->subElementRect(QStyle::SE_ItemViewItemText, &option, option.widget)); const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, option.widget) + 1; textRect.adjust(textMargin, 0, -textMargin, 0); return textWidth > textRect.width(); } } bool TooltipWhenElidedDelegate::helpEvent( QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index ) { if (event->type() != QEvent::ToolTip) { return QStyledItemDelegate::helpEvent(event, view, option, index); } if (!index.isValid()) { event->ignore(); return false; } const auto tooltip = displayText(index.data(Qt::ToolTipRole), QLocale{}); if (tooltip.isEmpty()) { event->ignore(); return false; } if (QToolTip::isVisible() && QToolTip::text() == tooltip) { event->accept(); return true; } QStyleOptionViewItem opt(option); initStyleOption(&opt, index); if (!mAlwaysShowTooltip) { // Get real item rect const QRect intersected(opt.rect.intersected(view->viewport()->rect())); opt.rect.setLeft(intersected.left()); opt.rect.setRight(intersected.right()); // Show tooltip only if display text is elided if (!isTextElided(displayText(index.data(Qt::DisplayRole), opt.locale), opt)) { event->ignore(); return false; } } QToolTip::showText(event->globalPos(), tooltip, view->viewport(), opt.rect); event->accept(); return true; } } tremotesf-2.10.1/src/ui/widgets/tooltipwhenelideddelegate.h000066400000000000000000000013231523411710200240370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TOOLTIPWHENELIDEDDELEGATE_H #define TREMOTESF_TOOLTIPWHENELIDEDDELEGATE_H #include namespace tremotesf { class TooltipWhenElidedDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit TooltipWhenElidedDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} bool helpEvent( QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index ) override; protected: bool mAlwaysShowTooltip{false}; }; } #endif // TREMOTESF_TOOLTIPWHENELIDEDDELEGATE_H tremotesf-2.10.1/src/ui/widgets/torrentfilesview.cpp000066400000000000000000000274141523411710200226000ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "torrentfilesview.h" #include #include #include #include #include #include #include "rpc/mounteddirectoriesutils.h" #include "rpc/serversettings.h" #include "basetreeview.h" #include "desktoputils.h" #include "filemanagerlauncher.h" #include "settings.h" #include "rpc/rpc.h" #include "ui/itemmodels/torrentfilesmodelentry.h" #include "ui/itemmodels/torrentfilesproxymodel.h" #include "ui/screens/addtorrent/localtorrentfilesmodel.h" #include "ui/screens/torrentproperties/torrentfilesmodel.h" #include "progressbardelegate.h" #include "textinputdialog.h" #include "tooltipwhenelideddelegate.h" using namespace Qt::StringLiterals; namespace tremotesf { TorrentFilesView::TorrentFilesView(LocalTorrentFilesModel* model, Rpc* rpc, QWidget* parent) : QTreeView(parent), mLocalFile(true), mModel(model), mProxyModel(new TorrentFilesProxyModel( mModel, LocalTorrentFilesModel::SortRole, model->columnNumber(BaseTorrentFilesModel::Column::Name), this )), mRpc(rpc) { init(); setItemDelegate(new TooltipWhenElidedDelegate(this)); if (!header()->restoreState(Settings::instance()->get_localTorrentFilesViewHeaderState())) { sortByColumn(model->columnNumber(BaseTorrentFilesModel::Column::Name), Qt::AscendingOrder); } } TorrentFilesView::TorrentFilesView(TorrentFilesModel* model, Rpc* rpc, QWidget* parent) : QTreeView(parent), mLocalFile(false), mModel(model), mProxyModel(new TorrentFilesProxyModel( mModel, TorrentFilesModel::SortRole, model->columnNumber(BaseTorrentFilesModel::Column::Name), this )), mRpc(rpc) { init(); setItemDelegate(new TooltipWhenElidedDelegate(this)); setItemDelegateForColumn( model->columnNumber(BaseTorrentFilesModel::Column::ProgressBar), new ProgressBarDelegate(TorrentFilesModel::SortRole, this) ); if (!header()->restoreState(Settings::instance()->get_torrentFilesViewHeaderState())) { sortByColumn(model->columnNumber(BaseTorrentFilesModel::Column::Name), Qt::AscendingOrder); } QObject::connect(this, &TorrentFilesView::activated, this, [=, this](const auto& index) { const QModelIndex sourceIndex(mProxyModel->mapToSource(index)); auto entry = static_cast(mProxyModel->mapToSource(index).internalPointer()); if (!entry->isDirectory() && isServerLocalOrTorrentIsMounted(mRpc, static_cast(mModel)->torrent()) && entry->wantedState() != TorrentFilesModelEntry::WantedState::Unwanted) { desktoputils::openFile( static_cast(mModel)->localFilePath(sourceIndex), nativeParentWidget() ); } }); } void TorrentFilesView::showFileRenameDialog( const QString& fileName, QWidget* parent, const std::function& onAccepted ) { auto dialog = new TextInputDialog( //: Dialog title qApp->translate("tremotesf", "Rename"), qApp->translate("tremotesf", "File name:"), fileName, //: Dialog confirmation button qApp->translate("tremotesf", "Rename"), false, parent ); dialog->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(dialog, &QDialog::accepted, parent, [=] { onAccepted(dialog->text()); }); dialog->show(); } void TorrentFilesView::saveState() { if (mLocalFile) { Settings::instance()->set_localTorrentFilesViewHeaderState(header()->saveState()); } else { Settings::instance()->set_torrentFilesViewHeaderState(header()->saveState()); } } void TorrentFilesView::init() { setCommonTreeViewProperties(this, false); setContextMenuPolicy(Qt::CustomContextMenu); setModel(mProxyModel); setSelectionMode(QAbstractItemView::ExtendedSelection); QObject::connect(mModel, &BaseTorrentFilesModel::modelReset, this, &TorrentFilesView::onModelReset); QObject::connect(this, &TorrentFilesView::customContextMenuRequested, this, &TorrentFilesView::showContextMenu); onModelReset(); } void TorrentFilesView::onModelReset() { if (mModel->rowCount() > 0) { if (mModel->rowCount(mModel->index(0, 0)) == 0) { setRootIsDecorated(false); } else { setRootIsDecorated(true); expand(mProxyModel->index(0, 0)); } } } void TorrentFilesView::showContextMenu(QPoint pos) { if (!indexAt(pos).isValid()) { return; } const QModelIndexList sourceIndexes(mProxyModel->sourceIndexes(selectionModel()->selectedRows())); QMenu contextMenu; if (!mLocalFile) { bool show = true; for (const QModelIndex& index : sourceIndexes) { if (static_cast(index.internalPointer())->wantedState() == TorrentFilesModelEntry::WantedState::Unwanted) { show = false; break; } } if (show) { const bool localOrMounted = isServerLocalOrTorrentIsMounted(mRpc, static_cast(mModel)->torrent()); QAction* openAction = contextMenu.addAction( QIcon::fromTheme("document-open"_L1), //: Context menu item qApp->translate("tremotesf", "&Open") ); openAction->setEnabled(localOrMounted); QObject::connect(openAction, &QAction::triggered, this, [sourceIndexes, this] { for (const QModelIndex& index : sourceIndexes) { desktoputils::openFile( static_cast(mModel)->localFilePath(index), nativeParentWidget() ); } }); QAction* openDownloadDirectoryAction = contextMenu.addAction( QIcon::fromTheme("go-jump"_L1), //: Context menu item qApp->translate("tremotesf", "Open &Download Directory") ); openDownloadDirectoryAction->setEnabled(localOrMounted); QObject::connect(openDownloadDirectoryAction, &QAction::triggered, this, [sourceIndexes, this] { std::vector files{}; files.reserve(static_cast(sourceIndexes.size())); for (const QModelIndex& index : sourceIndexes) { files.push_back(static_cast(mModel)->localFilePath(index)); } launchFileManagerAndSelectFiles(files, nativeParentWidget()); }); } } contextMenu.addSeparator(); QAction* downloadAction = contextMenu.addAction( QIcon::fromTheme("download"_L1), //: Context menu item to select file for downloading qApp->translate("tremotesf", "&Download") ); QObject::connect(downloadAction, &QAction::triggered, this, [=, this] { mModel->setFilesWanted(sourceIndexes, true); }); QAction* notDownloadAction = contextMenu.addAction( QIcon::fromTheme("dialog-cancel"_L1), //: Context menu item to unselect file for downloading qApp->translate("tremotesf", "&Not Download") ); QObject::connect(notDownloadAction, &QAction::triggered, this, [=, this] { mModel->setFilesWanted(sourceIndexes, false); }); contextMenu.addSeparator(); QMenu* priorityMenu = contextMenu.addMenu(qApp->translate("tremotesf", "&Priority")); QActionGroup priorityGroup(this); priorityGroup.setExclusive(true); //: File loading priority QAction* highPriorityAction = priorityGroup.addAction(qApp->translate("tremotesf", "&High")); highPriorityAction->setCheckable(true); QObject::connect(highPriorityAction, &QAction::triggered, this, [=, this](bool checked) { if (checked) { mModel->setFilesPriority(sourceIndexes, TorrentFilesModelEntry::Priority::High); } }); //: File loading priority QAction* normalPriorityAction = priorityGroup.addAction(qApp->translate("tremotesf", "&Normal")); normalPriorityAction->setCheckable(true); QObject::connect(normalPriorityAction, &QAction::triggered, this, [=, this](bool checked) { if (checked) { mModel->setFilesPriority(sourceIndexes, TorrentFilesModelEntry::Priority::Normal); } }); //: File loading priority QAction* lowPriorityAction = priorityGroup.addAction(qApp->translate("tremotesf", "&Low")); lowPriorityAction->setCheckable(true); QObject::connect(lowPriorityAction, &QAction::triggered, this, [=, this](bool checked) { if (checked) { mModel->setFilesPriority(sourceIndexes, TorrentFilesModelEntry::Priority::Low); } }); //: File loading priority QAction* mixedPriorityAction = priorityGroup.addAction(qApp->translate("tremotesf", "Mixed")); mixedPriorityAction->setCheckable(true); mixedPriorityAction->setChecked(true); mixedPriorityAction->setVisible(false); priorityMenu->addActions(priorityGroup.actions()); if (sourceIndexes.size() == 1) { auto entry = static_cast(sourceIndexes.first().internalPointer()); if (entry->wantedState() == TorrentFilesModelEntry::WantedState::Wanted) { downloadAction->setEnabled(false); } else if (entry->wantedState() == TorrentFilesModelEntry::WantedState::Unwanted) { notDownloadAction->setEnabled(false); } switch (entry->priority()) { case TorrentFilesModelEntry::Priority::Low: lowPriorityAction->setChecked(true); break; case TorrentFilesModelEntry::Priority::Normal: normalPriorityAction->setChecked(true); break; case TorrentFilesModelEntry::Priority::High: highPriorityAction->setChecked(true); break; case TorrentFilesModelEntry::Priority::Mixed: mixedPriorityAction->setVisible(true); } } if (mRpc->serverSettings()->data().canRenameFiles()) { contextMenu.addSeparator(); QAction* renameAction = contextMenu.addAction( QIcon::fromTheme("edit-rename"_L1), //: Context menu item qApp->translate("tremotesf", "&Rename") ); renameAction->setEnabled(sourceIndexes.size() == 1); QObject::connect(renameAction, &QAction::triggered, this, [=, this] { const QModelIndex& index = sourceIndexes.first(); auto entry = static_cast(index.internalPointer()); showFileRenameDialog(entry->name(), this, [=, this](const auto& newName) { mModel->renameFile(index, newName); }); }); } contextMenu.exec(viewport()->mapToGlobal(pos)); } } tremotesf-2.10.1/src/ui/widgets/torrentfilesview.h000066400000000000000000000023301523411710200222330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTFILESVIEW_H #define TREMOTESF_TORRENTFILESVIEW_H #include #include namespace tremotesf { class BaseTorrentFilesModel; class LocalTorrentFilesModel; class Rpc; class TorrentFilesModel; class TorrentFilesProxyModel; class TorrentFilesView final : public QTreeView { Q_OBJECT public: explicit TorrentFilesView(LocalTorrentFilesModel* model, Rpc* rpc, QWidget* parent = nullptr); explicit TorrentFilesView(TorrentFilesModel* model, Rpc* rpc, QWidget* parent = nullptr); ~TorrentFilesView() override = default; Q_DISABLE_COPY_MOVE(TorrentFilesView) static void showFileRenameDialog( const QString& fileName, QWidget* parent, const std::function& onAccepted ); void saveState(); private: void init(); void onModelReset(); void showContextMenu(QPoint pos); bool mLocalFile; BaseTorrentFilesModel* mModel; TorrentFilesProxyModel* mProxyModel; Rpc* mRpc; }; } #endif // TREMOTESF_TORRENTFILESVIEW_H tremotesf-2.10.1/src/ui/widgets/torrentremotedirectoryselectionwidget.cpp000066400000000000000000000147601523411710200271350ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include "torrentremotedirectoryselectionwidget.h" #include "rpc/rpc.h" #include "rpc/servers.h" #include "rpc/serversettings.h" namespace tremotesf { void TorrentDownloadDirectoryDirectorySelectionWidgetViewModel::saveDirectories( std::vector comboBoxItems ) { if (mPath.isEmpty()) { return; } auto paths = comboBoxItems | std::views::transform(&ComboBoxItem::path) | std::views::as_rvalue | std::ranges::to(); if (!paths.contains(mPath)) { paths.push_back(mPath); } auto servers = Servers::instance(); servers->setCurrentServerLastDownloadDirectories(paths); servers->setCurrentServerLastDownloadDirectory(mPath); } std::vector TorrentDownloadDirectoryDirectorySelectionWidgetViewModel::createInitialComboBoxItems() const { QStringList directories = Servers::instance()->currentServerLastDownloadDirectories(mRpc->serverSettings()); directories.reserve(directories.size() + static_cast(mRpc->torrents().size()) + 2); for (const auto& torrent : mRpc->torrents()) { directories.push_back(torrent->data().downloadDirectory); } if (!mPath.isEmpty()) { directories.push_back(mPath); } directories.push_back(mRpc->serverSettings()->data().downloadDirectory); directories.removeDuplicates(); QCollator collator{}; collator.setCaseSensitivity(Qt::CaseInsensitive); collator.setNumericMode(true); // QStringList is not compatibly with std::ranges::sort in Qt 5 std::ranges::sort(directories, [&collator](const QString& first, const QString& second) { return collator.compare(first, second) < 0; }); auto ret = directories | std::views::transform([=, this](QString& dir) { QString display = toNativeSeparators(dir); return TorrentDownloadDirectoryDirectorySelectionWidgetViewModel::ComboBoxItem{ .path = std::move(dir), .displayPath = std::move(display) }; }) | std::ranges::to(); return ret; } void TorrentDownloadDirectoryDirectorySelectionWidgetViewModel::updateInitialComboBoxItems() { auto items = createInitialComboBoxItems(); if (items != mInitialComboBoxItems) { mInitialComboBoxItems = std::move(items); emit initialComboBoxItemsChanged(); } } QLineEdit* TorrentDownloadDirectoryDirectorySelectionWidget::lineEditFromTextField() { return qobject_cast(mTextField)->lineEdit(); } QWidget* TorrentDownloadDirectoryDirectorySelectionWidget::createTextField() { const auto comboBox = new QComboBox(); comboBox->setEditable(true); comboBox->setInsertPolicy(QComboBox::NoInsert); comboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); new ComboBoxDeleteKeyEventFilter(comboBox); return comboBox; } void TorrentDownloadDirectoryDirectorySelectionWidget::setup(QString path, const Rpc* rpc) { RemoteDirectorySelectionWidget::setup(std::move(path), rpc); const auto viewModel = qobject_cast(mViewModel); const auto comboBox = qobject_cast(mTextField); const auto updateItems = [=] { const auto items = viewModel->initialComboBoxItems(); comboBox->clear(); for (const auto& [itemPath, itemDisplayPath] : items) { comboBox->addItem(itemDisplayPath, itemPath); } comboBox->lineEdit()->setText(viewModel->displayPath()); }; updateItems(); QObject::connect( viewModel, &TorrentDownloadDirectoryDirectorySelectionWidgetViewModel::initialComboBoxItemsChanged, this, updateItems ); QObject::connect(comboBox, &QComboBox::activated, this, [=](int index) { if (index != -1) { viewModel->updatePathProgrammatically(comboBox->itemData(index).toString(), comboBox->itemText(index)); } }); } void TorrentDownloadDirectoryDirectorySelectionWidget::resetPath(QString path) { RemoteDirectorySelectionWidget::resetPath(std::move(path)); qobject_cast(mViewModel) ->updateInitialComboBoxItems(); } void TorrentDownloadDirectoryDirectorySelectionWidget::saveDirectories() { auto comboBox = qobject_cast(mTextField); auto comboBoxItems = std::views::iota(0, comboBox->count()) | std::views::transform([comboBox](int index) { return TorrentDownloadDirectoryDirectorySelectionWidgetViewModel::ComboBoxItem{ .path = comboBox->itemData(index).toString(), .displayPath = comboBox->itemText(index) }; }) | std::ranges::to(); qobject_cast(mViewModel) ->saveDirectories(std::move(comboBoxItems)); } ComboBoxDeleteKeyEventFilter::ComboBoxDeleteKeyEventFilter(QComboBox* comboBox) : QObject(comboBox) { comboBox->view()->installEventFilter(this); } bool ComboBoxDeleteKeyEventFilter::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::KeyPress && static_cast(event)->matches(QKeySequence::Delete)) { const auto index = qobject_cast(watched)->currentIndex(); if (index.isValid()) { qobject_cast(parent())->removeItem(index.row()); return true; } } return QObject::eventFilter(watched, event); } } tremotesf-2.10.1/src/ui/widgets/torrentremotedirectoryselectionwidget.h000066400000000000000000000043241523411710200265750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_TORRENTREMOTEDIRECTORYSELECTIONWIDGET_H #define TREMOTESF_TORRENTREMOTEDIRECTORYSELECTIONWIDGET_H #include "remotedirectoryselectionwidget.h" class QComboBox; namespace tremotesf { class Rpc; class TorrentDownloadDirectoryDirectorySelectionWidgetViewModel final : public RemoteDirectorySelectionWidgetViewModel { Q_OBJECT public: using RemoteDirectorySelectionWidgetViewModel::RemoteDirectorySelectionWidgetViewModel; struct ComboBoxItem { QString path{}; QString displayPath{}; [[nodiscard]] bool operator==(const ComboBoxItem&) const = default; }; [[nodiscard]] std::vector initialComboBoxItems() const { return mInitialComboBoxItems; }; void updateInitialComboBoxItems(); void saveDirectories(std::vector comboBoxItems); private: [[nodiscard]] std::vector createInitialComboBoxItems() const; std::vector mInitialComboBoxItems{createInitialComboBoxItems()}; signals: void initialComboBoxItemsChanged(); }; class TorrentDownloadDirectoryDirectorySelectionWidget final : public RemoteDirectorySelectionWidget { Q_OBJECT public: using RemoteDirectorySelectionWidget::RemoteDirectorySelectionWidget; void setup(QString path, const Rpc* rpc) override; void resetPath(QString path) override; void saveDirectories(); protected: QWidget* createTextField() override; QLineEdit* lineEditFromTextField() override; RemoteDirectorySelectionWidgetViewModel* createViewModel(QString path, const Rpc* rpc) override { return new TorrentDownloadDirectoryDirectorySelectionWidgetViewModel(std::move(path), rpc, this); } }; class ComboBoxDeleteKeyEventFilter final : public QObject { Q_OBJECT public: explicit ComboBoxDeleteKeyEventFilter(QComboBox* comboBox); protected: bool eventFilter(QObject* watched, QEvent* event) override; }; } #endif // TREMOTESF_TORRENTREMOTEDIRECTORYSELECTIONWIDGET_H tremotesf-2.10.1/src/unixhelpers.cpp000066400000000000000000000007201523411710200174370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "unixhelpers.h" #include #include #include namespace tremotesf::impl { void throwWithErrno(std::string_view functionName) { const auto baseError = std::system_error(errno, std::system_category()); throw std::system_error(baseError.code(), fmt::format("{} failed with", functionName)); } } tremotesf-2.10.1/src/unixhelpers.h000066400000000000000000000017011523411710200171040ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_UNIXHELPERS_H #define TREMOTESF_UNIXHELPERS_H #include #include namespace tremotesf { namespace impl { void throwWithErrno(std::string_view functionName); } /** * @brief checkPosixError * @param result Return value of POSIX function that returns -1 on failure and sets errno * @param functionName Name of function that returned result * @returns result * @throws std::system_error */ template T checkPosixError(T result, std::string_view functionName) { if (result == T{-1}) { impl::throwWithErrno(functionName); } return result; } #ifdef TREMOTESF_UNIX_FREEDESKTOP inline constexpr auto xdgActivationTokenEnvVariable = "XDG_ACTIVATION_TOKEN"; #endif } #endif // TREMOTESF_UNIXHELPERS_H tremotesf-2.10.1/src/windowshelpers.cpp000066400000000000000000000051471523411710200201560ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #include "windowshelpers.h" #include #include #include #include #include #include "log/log.h" namespace tremotesf { namespace { /** * @brief std::error_category for Win32 errors (returned by GetLastError()) * Returns UTF-8 strings * We need custom implementation instead of std::system_category() * because GCC < 12 doesn't support Windows errors at all, * and MSVC and GCC >= 12 use FormatMessageA function which may not return UTF-8 */ class Win32Category final : public std::error_category { public: const char* name() const noexcept override { return "Win32Category"; } std::string message(int code) const override { const wchar_t* wstr{}; const auto size = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, static_cast(code), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast(&wstr), 0, nullptr ); if (size == 0) return "Unknown error"; return QString::fromWCharArray(wstr, static_cast(size)).trimmed().toStdString(); } static const Win32Category& instance() { static const Win32Category category{}; return category; } }; } void checkWin32Bool(int win32BoolResult, std::string_view functionName) { if (win32BoolResult != FALSE) return; // Don't use winrt::check_bool because it doesn't preserve Win32 error code // (it is converted to HRESULT) const auto error = GetLastError(); throw std::system_error( static_cast(error), Win32Category::instance(), fmt::format("{} failed with", functionName) ); } void checkHResult(int32_t hresult, std::string_view functionName) { if (hresult == S_OK) return; winrt::hstring message = winrt::hresult_error(hresult).message(); throw winrt::hresult_error( hresult, QString::fromStdString(fmt::format("{} failed with: {}", functionName, std::move(message))).toStdWString() ); } } tremotesf-2.10.1/src/windowshelpers.h000066400000000000000000000023021523411710200176110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2015-2025 Alexey Rochev // // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TREMOTESF_WINDOWSHELPERS_H #define TREMOTESF_WINDOWSHELPERS_H #include #include #include #include namespace tremotesf { /** * @brief checkWin32Bool * @param win32BoolResult BOOL returned from Win32 function * @param functionName Name of function that returned BOOL * @throws std::system_error */ void checkWin32Bool(int win32BoolResult, std::string_view functionName); /** * @brief checkHResult * @param hresult HRESULT returned from COM function * @param functionName Name of function that returned HRESULT * @throws winrt::hresult_error */ void checkHResult(int32_t hresult, std::string_view functionName); [[nodiscard]] inline const wchar_t* getCWString(const QString& string) { using utf16Type = std::remove_pointer_t; static_assert(sizeof(utf16Type) == sizeof(wchar_t)); return reinterpret_cast(string.utf16()); } inline const wchar_t* getCWString(QString&&) = delete; } #endif // TREMOTESF_WINDOWSHELPERS_H tremotesf-2.10.1/translations/000077500000000000000000000000001523411710200163205ustar00rootroot00000000000000tremotesf-2.10.1/translations/CMakeLists.txt000066400000000000000000000030201523411710200210530ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 find_package(Qt6 ${TREMOTESF_MINIMUM_QT_VERSION} REQUIRED COMPONENTS LinguistTools) qt_add_lupdate( TS_FILES source.ts SOURCE_TARGETS tremotesf_objects tremotesf ) qt_add_lrelease( TS_FILES de.ts en.ts es.ts es_ES.ts fr.ts it_IT.ts nl_BE.ts nl.ts pl.ts ru.ts tr.ts zh_CN.ts QM_FILES_OUTPUT_VARIABLE qm_files ) qt_add_resources( tremotesf_objects "translations" PREFIX "/translations" FILES ${qm_files} BASE "${CMAKE_CURRENT_BINARY_DIR}" ) if (WIN32 OR APPLE) message(STATUS "Building for Windows or macOS, deploying Qt translations") if (DEFINED VCPKG_TARGET_TRIPLET) # vcpkg set(relative_qt_translations_dir "translations/Qt6") else () # MSYS2 set(relative_qt_translations_dir "share/qt6/translations") endif () if (DEFINED QT_HOST_PATH) find_file(qt_translations_dir "${relative_qt_translations_dir}" PATHS "${QT_HOST_PATH}" REQUIRED) else () find_file(qt_translations_dir "${relative_qt_translations_dir}" REQUIRED) endif () message(STATUS "Deploying Qt translations from ${qt_translations_dir}") install(DIRECTORY "${qt_translations_dir}/" DESTINATION "${TREMOTESF_EXTERNAL_RESOURCES_PATH}/qt-translations" FILES_MATCHING PATTERN "qt_*.qm" PATTERN "qtbase_*.qm" PATTERN "qtmultimedia_*.qm" PATTERN "qt_help*" EXCLUDE) endif () tremotesf-2.10.1/translations/de.ts000066400000000000000000003461021523411710200172660ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Über Source code: Translations: Maintainer Betreuer Contributor Mitwirkender Authors "About" dialog's "Authors" tab title Autoren Translators "About" dialog's "Translators" tab title Übersetzer License "About" dialog's "License" tab title Lizenz Add Torrent File Dialog title Torrent-Datei öffnen Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Freier Speicherplatz: %1 Error getting free space Fehler beim Berechnen von freiem Speicher Files Torrent properties dialog tab Dateien High Torrent's file loading priority ---------- Torrent's loading priority Hoch Normal Torrent's file loading priority ---------- Torrent's loading priority Normal Low Torrent's file loading priority ---------- Torrent's loading priority Niedrig Start downloading after adding Nach dem Hinzufügen mit dem Download starten Add Torrent Link Dialog title Torrent-Link öffnen No servers Keine Server Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Getrennt Downloading Noun "Downloading" server setting page Herunterladen Start added torrents Check box label Hinzugefügte Torrents starten Append ".part" to names of incomplete files Check box label Zu nicht fertigen Dateinamen ".part" hinzufügen Rename Dialog title ---------- Dialog confirmation button Umbenennen Select Directory Directory chooser dialog title Verzeichnis wählen Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Status Directories Title of torrents download directory filters list Verzeichnisse Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Tracker Priority Column title in torrent's file list ---------- Torrents list column name Priorität Mixed Torrent's file loading priority ---------- File loading priority Gemischt No torrents Torrents list placeholder Keine Torrents Remove Button ---------- Dialog confirmation button Entfernen Set Location Dialog title for changing torrent's download directory Ziel wählen Error adding torrent Fehler beim hinzufügen von Torrent This torrent is already added Dieser Torrent wurde bereits hinzugefügt Torrent added Notification title Torrent hinzugefügt Torrent finished Notification title Torrent fertig Network "Network" server settings page Netzwerk Connection Title of settings section related to peer connections Verbindung Random port on Transmission start Check box label Zufälliger Port beim Start von Transmission Enable port forwarding Check box label Port-Forwarding aktivieren Allow Encryption mode (allow/prefer/require) Erlauben Prefer Encryption mode (allow/prefer/require) Bevorzugen Require Encryption mode (allow/prefer/require) Voraussetzen Enable DHT Check box label DHT aktivieren Peer Limits Peer-Limits Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Peers Queue "Queue" server settings page Warteschlange Also delete the files on the hard disk Check box label Auch die Dateien auf dem Datenträger löschen Seeding Noun "Seeding" server setting page Seeden Overwrite Dialog's confirmation button Überschreiben Server already exists Server existiert bereits Add Server Dialog title Server hinzufügen Name Column title in torrent's file list ---------- Torrents list column name Name Address Peers list column title ---------- Trackers list column title Adresse Default Default proxy option Standard HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Datei laden von... Use client certificate authentication Check box label Client-Zertifikat-Authentifizierung nutzen Authentication Check box label Authentifizierung Auto reconnect on error Check box label Bei Fehler automatisch neu verbinden Mounted directories Gemountete Verzeichnisse Local directory Column title in the list of mounted directories Lokale Verzeichnisse Remote directory Column title in the list of mounted directories Remote-Verzichnisse Add Button ---------- Dialog confirmation button Hinzufügen Speed "Speed" server settings page ---------- Torrent's limits tab section Geschwindigkeit Connection Settings Servers list placeholder Dialog title Verbindungseinstellungen Edit... Button Bearbeiten... Add Server... Button Server hinzufügen... Add... Button Hinzufügen... Connect to server on startup Check box label Beim Starten zu Server verbinden Adding torrents Options tab Torrents hinzufügen Add torrent parameters Torrent-Parameter hinzufügen Reset Zurücksetzen Show main window when adding torrents Check box label Beim Hinzufügen von Torrents das Hauptfenster anzeigen Show dialog when adding torrents Check box label Dialog beim Hinzufügen von Torrents anzeigen Automatically fill link from clipboard when adding torrent link Check box label Link automatisch aus Zwischenablage einfügen Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Tip: im Hauptfenster %1 drücken, um Torrents aus der Zwischenablage hinzuzufügen Ask for merging trackers when adding existing torrent Check box label Nach dem Zusammenführen von Trackern fragen, wenn vorhandene Torrents hinzugefügt werden. Merge trackers when adding existing torrent Check box label Beim Hinzufügen vorhandener Torrents Tracker zusammenführen Open properties dialog Eigenschaftendialog öffnen Open torrent's file Es werden Torrent-Dateien geöffnet Open download directory Downloadverzeichnis öffnen What to do when torrent in the list is double clicked: Was tun, wenn in der Liste auf ein Torrent doppelt geklickt wird: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Benachrichtigungen Notify when disconnecting from server Check box label Bei Abmelden von Server benachrichtigen Notify on added torrents Check box label Beim Hinzufügen von Torrents benachrichtigen Notify on finished torrents Check box label Beim Fertigstellen von Torrents benachrichtigen When connecting to server Notifications options section Beim Verbinden zum Server Notify on added torrents since last connection to server Check box label Benachrichtigung über hinzugefügte Torrents seit der letzten Verbindung zum Server Notify on finished torrents since last connection to server Check box label Benachrichtigung über fertiggestellte Torrents seit der letzten Verbindung zum Server Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Fortschritt ETA Torrents list column name Restzeit Server Stats Dialog title Serverdaten Current session Server stats section for current Transmission launch Aktuelle Sitzung Ratio Torrents list column name Verhältnis Total Server stats section for all Transmission launches (accumulated) Gesamt %Ln times How many times Transmission was launched Einmal%Ln mal Size Column title in torrent's file list ---------- Torrents list column name Größe Limits Speed limits section ---------- Torrent's properties dialog tab Begrenzungen Alternative Limits Alternative speed limits section Alternative Begrenzungen Enable Check box label aktivieren Scheduled Title of alternative speed limit scheduling section Geplant to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" bis Every day Jeden Tag Weekdays Wochentage Weekends Wochenenden %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 von %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Überprüfen (%L1) Honor global limits Check box label Globale Beschränkungen befolgen Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Globale Einstellungen nutzen Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seeden unabhängig von der Ratio Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Bei diesem Verhältnis aufhören zu Seeden: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Seeden unabhängig von der Aktivität Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Aufhören zu seeden, wenn im Leerlauf für: Activity Torrent's details tab section Aktivität Completed Torrents list column name, completed byte size Fertiggestellt Downloaded Torrents list column name, downloaded byte size Heruntergeladen Paused (%1) Torrent status while torrent also has an error. %1 is error string Angehalten (%1) Paused Torrent status Angehalten Downloading (%1) Torrent status while torrent also has an error. %1 is error string Herunterladen (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string Seeden (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string In Warteschlange (%1) Queued Torrent status In Warteschlange Checking (%1) Torrent status while torrent also has an error. %1 is error string Überprüfen (%1) Checking Torrent status Überprüfen Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string Zur Prüfung eingereiht (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Hochladen zu Peers Uploaded Torrents list column name, uploaded byte size Hochgeladen Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Seeder Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Leecher Information Torrent's details tab section Information Torrent Removed Message that appears when torrent is removed Torrent entfernt Edit Tracker Dialog title Tracker bearbeiten Torrent file: Input field's label Torrent-Datei: Torrent link: Input field's label Torrent-Link: Download directory: Input field's label Downloadverzeichnis: Torrent priority: Combo box label Priorität: Delete .torrent file .torrent-Datei löschen Move .torrent file to trash .torrent-Datei in Papierkorb verschieben Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed Laden &Connect Button / menu item to connect to server &Verbinden &Disconnect Button / menu item to disconnect from server Verbindung &trennen &Add Torrent File... Menu item &Torrent-Datei hinzufügen... Add Torrent &Link... Menu item Torrent &Link hinzufügen... P&ause Torrent's context menu item P&ause &Delete Torrent's context menu item Lös&chen Open &Download Directory Context menu item &Downloadverzeichnis öffnen Delete with files Mit Dateien löschen Delete Löschen Delete Torrent Dialog title Torrent löschen Are you sure you want to delete this torrent? Soll der Torrent wirklich gelöscht werden? Delete Torrents Dialog title Torrents löschen Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion Soll dieser Torrent wirklich gelöscht werden?Sollen %Ln Torrents wirklich gelöscht werden? No torrents matching filters Torrents list placeholder Keine Torrents passen zu dem Filter &Quit Menu item &Beenden &Torrent Menu bar item &Torrent Error adding torrent «%1» Fehler beim hinzufügen von Torrent «%1» &Properties Torrent's context menu item &Eigenschaften &Show Tremotesf Tremote&sf anzeigen &Hide Tremotesf Tremotesf a&usblenden &Start Torrent's context menu item &Start Start &Now Torrent's context menu item &Jetzt starten Copy &Magnet Link Torrent's context menu item &Magnet-Link kopieren &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item Entfe&rnen Set &Location Torrent's context menu item Verzeichnis fest&legen Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item &Öffnen Op&en Download Directory Torrent's context menu item Downloadverzeichnis öffn&en &Check Local Data Torrent's context menu item Lokale Daten &prüfen Reanno&unce Torrent's context menu item ---------- Button Reanno&unce &Queue Torrent's context menu item &Warteschlange Move To &Top Torrent's context menu item Nach O&ben verschieben Move &Up Torrent's context menu item Nach &Oben Move &Down Torrent's context menu item Nach &Unten Move To &Bottom Torrent's context menu item Nach Unten verschie&ben &Connection Settings Verbindun&gseinstellungen &Server Options &Serveroptionen Server S&tats Server-S&tatistiken Select Files File chooser dialog title Dateien wählen Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Torrent-Dateien (*.torrent) Error Dialog title ---------- Trackers list column title Fehler This file/directory does not exist Datei/Verzeichnis existiert nicht &File Menu bar item &Datei &Close Window Fenster s&chließen &Edit Menu bar item &Bearbeiten Select &All &Alle auswählen &Invert Selection Auswahl &umkehren &View Menu bar item Ans&icht &Toolbar Symbolleis&te &Sidebar &Seitenleiste St&atusbar St&atusleiste Torrent properties &panel &Lock Toolbar Symbo&lleiste sperren T&ools Menu bar item Werk&zeuge &Options &Optionen S&hutdown Server Server &herunterfahren Shutdown Server Dialog title Server herunterfahren Are you sure you want to shutdown remote Transmission instance? Sind Sie sicher, dass Sie die Remote-Instanz von Transmission herunterfahren möchten? Shutdown Dialog confirmation button Herunterfahren &Help Menu bar item &Hilfe &About Menu item opening "About" dialog Ü&ber Icon Only Toolbar mode Nur Symbole Text Only Toolbar mode Nur Text Text Beside Icon Toolbar mode Text neben Symbolen Text Under Icon Toolbar mode Text unter Symbolen Follow System Style Toolbar mode System-Design nutzen Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Tremotesf anzeigen Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Aktiv (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Herunterladen (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Seeden (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Angehalten (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Fehler (%L1) Search... Search field placeholder Suche... &Select... Context menu item to open directory chooser &Auswählen... Overwrite Server Dialog title Server überschreiben Name: Name: Address: Adresse: Port: Port: API path: API-Pfad: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Keine Proxy type: Proxy-Typ: Username: Benutzername: Password: Passwort: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Aktualisierungsintervall: Timeout: Timeout: Auto reconnect interval: Neuverbindungs-Intervall: &Edit... Server's context menu item ---------- Tracker's context menu item &Bearbeiten... Server Options Dialog title Servereinstellungen Directory for incomplete files: Verzeichnis für unvollständige Dateien: min Suffix that is added to input field with number of minuts, e.g. "5 min" Min Maximum active downloads: Maximale Anzahl an aktiven Downloads: Maximum active uploads: Maximale Anzahl an aktiven Uploads: Ignore queue position if idle for: Warteschlangenposition ignorieren, wenn inaktiv für: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label Herunterladen: Upload: Upload speed limit input field label Hochladen: Days: Tage: Peer port: Peer-Port: Encryption: Verschlüsselung: Enable μTP (Micro Transport Protocol) Check box label μTP (Micro Transport Protocol) aktivieren Enable PEX (Peer exchange) Check box label PEX (Peer exchange) aktivieren Enable local peer discovery Check box label Local Peer Discovery aktivieren Maximum peers per torrent: Maximale Peers per Torrent: Maximum peers globally: Glabales Maximum von Peers: Options Dialog title Einstellungen Follow system Dark theme mode Systemdesign nutzen On Dark theme mode Ein Off Dark theme mode Aus Dark theme Dunkles Design Use system accent color Check box label System-Akzentfarben nutzen Remember location of last opened torrent file Check box label Speicherort der zuletzt geöffneten Torrent-Datei merken Remember parameters of last added torrent Check box label Parameter des zuletzt hinzugefügten Torrents merken Show icon in the notification area Check box label Symbol im Benachrichtigungsbereich anzeigen &Not Download Context menu item to unselect file for downloading &Nicht herunterladen &Priority Torrent's context menu item &Priorität &Download Context menu item to select file for downloading &Herunterladen &High File loading priority H&och &Normal File loading priority No&rmal &Low File loading priority N&iedrig &Rename Torrent's context menu item ---------- Context menu item &Umbenennen File name: Dateiname: Details Torrent's properties dialog tab Details Completed: Torrent's completed size Fertiggestellt: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Heruntergeladen: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Hochgeladen: Ratio: Verhältnis: Duration: How much time Transmission is running Dauer: Started: How many times Transmission was launched Gestartet: Free space in download directory: Freier Speicher in Downloadverzeichnis: Download speed: Downloadgeschwindigkeit: Upload speed: Uploadgeschwindigkeit: ETA: Restzeit: Seeders: Seeder: Leechers: Leecher: Peers we are downloading from: Peers von denen heruntergeladen wird: Web seeders we are downloading from: Web-Seeder von denen heruntergeladen wird: Peers we are uploading to: Peers zu denen hochgeladen wird: Last activity: Letzte Aktivität: Total size: Gesamtgröße: Location: Torrent's download directory Ort: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Erstellt von: Created on: Date/time when torrent was created Erstellt am: Comment: Torrent's comment text Kommentar: Labels: Torrent's labels Web seeder Web seeders list column title Web-Seeder Web seeders Torrent's properties dialog tab Web-Seeder Seeding Options section Torrent's limits tab section Seeden Ratio limit mode: Ratio-Limit-Modus: Idle seeding mode: Leerlauf-Seed-Modus: Maximum peers: Maximale Peers: Add Trackers Dialog title Tracker hinzufügen Trackers announce URLs: Tracker-Announce URLs: Tracker announce URL: Tracker-Announce URL: Remove Tracker Dialog title Tracker entfernen Are you sure you want to remove this tracker? Soll dieser Tracker wirklich entfernt werden? Remove Trackers Dialog title Tracker entfernen Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Soll dieser Tracker wirklich entfernt werden?Sollen %Ln Tracker wirklich entfernt werden? Down Speed Torrents list column name ---------- Peers list column title Herunterladgeschwindigkeit Up Speed Torrents list column name ---------- Peers list column title Uploadgeschwindigkeit Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Fortschrittsbalken Flags Peers list column title Flags Client Peers list column title Client Timed out Server connection status Timeout Connection error Server connection status Verbindungsfehler Authentication error Server connection status Fehler bei der Authentifizierung Parse error Server connection status Fehler beim Parsen Server is too new Server connection status Server ist zu neu Server is too old Server connection status Server ist zu alt Connecting... Server connection status Verbinden... Connected Server connection status Verbunden Downloading Torrent status Torrent status Herunterladen Seeding Torrent status Torrent status Seeden Queued for checking Torrent status Zur Überprüfung eingereiht Merge trackers? Dialog title Tracker zusammenführen? Torrent «%1» is already added, merge trackers? Torrent «%1» bereits hinzugefügt, Tracker zusammenführen? Merge Zusammenführen Do not ask again Nicht noch einmal fragen Merged trackers Dialog title Tracker zusammengeführt Merged trackers for torrent «%1» Tracker für Torrent „%1“ zusammengeführt Torrent already exists Dialog title Torrent existiert bereits Torrent «%1» already exists Torrent «%1» existiert bereits Error reading torrent file Fehler beim Lesen von Torrent-Datei Error parsing torrent file Fehler beim Parsen von Torrent-Datei Total Size Torrents list column name Gesamtgröße Queue Position Torrents list column name Warteschlange Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added Hinzugefügt am Completed on Torrents list column name, date/time when torrent was completed Fertiggestellt am Down Limit Torrents list column name, download speed limit Limit für Herunterladen Up Limit Torrents list column name, upload speed limit Limit für Hochladen Remaining Torrents list column name, remaining byte size Verbleibend Download Directory Torrents list column name Downloadverzeichnis Last Activity Torrents list column name Letzte Aktivität Inactive Tracker status Inaktiv Waiting for update Tracker status Auf Aktualisierung warten About to update Tracker status Wird gleich aktualisiert Updating Tracker status Aktualisieren Next Update Trackers list column title Nächste Aktualisierung %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 T %L2 Std %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 Std %L2 min %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 min %L2 sek %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 sek Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Fehler beim Öffnen von %1 Move files from current directory Check box label Dateien aus aktuellem Verzeichnis verschieben Selected directory should be inside mounted directory Gewähltes Verzeichnis soll in eingehängtem Verzeichnis sein All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents Alle (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Dieses Verzeichnis existiert nicht Edit Labels New label... tremotesf-2.10.1/translations/en.ts000066400000000000000000003444741523411710200173120ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title About Source code: Source code: Translations: Translations: Maintainer Maintainer Contributor Contributor Authors "About" dialog's "Authors" tab title Authors Translators "About" dialog's "Translators" tab title Translators License "About" dialog's "License" tab title License Add Torrent File Dialog title Add Torrent File Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Free space: %1 Error getting free space Error getting free space Files Torrent properties dialog tab Files High Torrent's file loading priority ---------- Torrent's loading priority High Normal Torrent's file loading priority ---------- Torrent's loading priority Normal Low Torrent's file loading priority ---------- Torrent's loading priority Low Start downloading after adding Start downloading after adding Add Torrent Link Dialog title Add Torrent Link No servers No servers Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Disconnected Downloading Noun "Downloading" server setting page Downloading Start added torrents Check box label Start added torrents Append ".part" to names of incomplete files Check box label Append ".part" to names of incomplete files Rename Dialog title ---------- Dialog confirmation button Rename Select Directory Directory chooser dialog title Select Directory Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Status Directories Title of torrents download directory filters list Directories Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Trackers Priority Column title in torrent's file list ---------- Torrents list column name Priority Mixed Torrent's file loading priority ---------- File loading priority Mixed No torrents Torrents list placeholder No torrents Remove Button ---------- Dialog confirmation button Remove Set Location Dialog title for changing torrent's download directory Set Location Error adding torrent Error adding torrent This torrent is already added This torrent is already added Torrent added Notification title Torrent added Torrent finished Notification title Torrent finished Network "Network" server settings page Network Connection Title of settings section related to peer connections Connection Random port on Transmission start Check box label Random port on Transmission start Enable port forwarding Check box label Enable port forwarding Allow Encryption mode (allow/prefer/require) Allow Prefer Encryption mode (allow/prefer/require) Prefer Require Encryption mode (allow/prefer/require) Require Enable DHT Check box label Enable DHT Peer Limits Peer Limits Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Peers Queue "Queue" server settings page Queue Also delete the files on the hard disk Check box label Also delete the files on the hard disk Seeding Noun "Seeding" server setting page Seeding Overwrite Dialog's confirmation button Overwrite Server already exists Server already exists Add Server Dialog title Add Server Name Column title in torrent's file list ---------- Torrents list column name Name Address Peers list column title ---------- Trackers list column title Address Default Default proxy option Default HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Load from file... Use client certificate authentication Check box label Use client certificate authentication Authentication Check box label Authentication Auto reconnect on error Check box label Auto reconnect on error Mounted directories Mounted directories Local directory Column title in the list of mounted directories Local directory Remote directory Column title in the list of mounted directories Remote directory Add Button ---------- Dialog confirmation button Add Speed "Speed" server settings page ---------- Torrent's limits tab section Speed Connection Settings Servers list placeholder Dialog title Connection Settings Edit... Button Edit... Add Server... Button Add Server... Add... Button Add... Connect to server on startup Check box label Connect to server on startup Adding torrents Options tab Adding torrents Add torrent parameters Add torrent parameters Reset Reset Show main window when adding torrents Check box label Show main window when adding torrents Show dialog when adding torrents Check box label Show dialog when adding torrents Automatically fill link from clipboard when adding torrent link Check box label Automatically fill link from clipboard when adding torrent link Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Tip: you can also press %1 in main window to add torrents from clipboard Ask for merging trackers when adding existing torrent Check box label Ask for merging trackers when adding existing torrent Merge trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Open properties dialog Open properties dialog Open torrent's file Open torrent's file Open download directory Open download directory What to do when torrent in the list is double clicked: What to do when torrent in the list is double clicked: General Options tab General Show torrent properties in a panel in the main window Check box label Show torrent properties in a panel in the main window Properties dialog won't be shown because torrent properties are shown in the main window Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display relative time Display full path of download directories in sidebar and torrents list Check box label Display full path of download directories in sidebar and torrents list Notifications Options tab Notifications Notify when disconnecting from server Check box label Notify when disconnecting from server Notify on added torrents Check box label Notify on added torrents Notify on finished torrents Check box label Notify on finished torrents When connecting to server Notifications options section When connecting to server Notify on added torrents since last connection to server Check box label Notify on added torrents since last connection to server Notify on finished torrents since last connection to server Check box label Notify on finished torrents since last connection to server Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Progress ETA Torrents list column name ETA Server Stats Dialog title Server Stats Current session Server stats section for current Transmission launch Current session Ratio Torrents list column name Ratio Total Server stats section for all Transmission launches (accumulated) Total %Ln times How many times Transmission was launched %Ln time%Ln times Size Column title in torrent's file list ---------- Torrents list column name Size Limits Speed limits section ---------- Torrent's properties dialog tab Limits Alternative Limits Alternative speed limits section Alternative Limits Enable Check box label Enable Scheduled Title of alternative speed limit scheduling section Scheduled to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" to Every day Every day Weekdays Weekdays Weekends Weekends %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 of %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Checking (%L1) Honor global limits Check box label Honor global limits Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Use global settings Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seed regardless of ratio Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Stop seeding at ratio: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Seed regardless of activity Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Stop seeding if idle for: Activity Torrent's details tab section Activity Completed Torrents list column name, completed byte size Completed Downloaded Torrents list column name, downloaded byte size Downloaded Paused (%1) Torrent status while torrent also has an error. %1 is error string Paused (%1) Paused Torrent status Paused Downloading (%1) Torrent status while torrent also has an error. %1 is error string Downloading (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string Seeding (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string Queued (%1) Queued Torrent status Queued Checking (%1) Torrent status while torrent also has an error. %1 is error string Checking (%1) Checking Torrent status Checking Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string Queued for checking (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Uploading to peers Uploaded Torrents list column name, uploaded byte size Uploaded Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Seeders Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Leechers Information Torrent's details tab section Information Torrent Removed Message that appears when torrent is removed Torrent Removed Edit Tracker Dialog title Edit Tracker Torrent file: Input field's label Torrent file: Torrent link: Input field's label Torrent link: Download directory: Input field's label Download directory: Torrent priority: Combo box label Torrent priority: Delete .torrent file Delete .torrent file Move .torrent file to trash Move .torrent file to trash Labels Title of torrents label filters list Labels Loading Placeholder shown when torrent file is being read/parsed Loading &Connect Button / menu item to connect to server &Connect &Disconnect Button / menu item to disconnect from server &Disconnect &Add Torrent File... Menu item &Add Torrent File... Add Torrent &Link... Menu item Add Torrent &Link... P&ause Torrent's context menu item P&ause &Delete Torrent's context menu item &Delete Open &Download Directory Context menu item Open &Download Directory Delete with files Delete with files Delete Delete Delete Torrent Dialog title Delete Torrent Are you sure you want to delete this torrent? Are you sure you want to delete this torrent? Delete Torrents Dialog title Delete Torrents Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion Are you sure you want to delete %Ln selected torrent?Are you sure you want to delete %Ln selected torrents? No torrents matching filters Torrents list placeholder No torrents matching filters &Quit Menu item &Quit &Torrent Menu bar item &Torrent Error adding torrent «%1» Error adding torrent «%1» &Properties Torrent's context menu item &Properties &Show Tremotesf &Show Tremotesf &Hide Tremotesf &Hide Tremotesf &Start Torrent's context menu item &Start Start &Now Torrent's context menu item Start &Now Copy &Magnet Link Torrent's context menu item Copy &Magnet Link &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Remove Set &Location Torrent's context menu item Set &Location Edi&t Labels Torrent's context menu item Edi&t Labels &Open Torrent's context menu item ---------- Context menu item &Open Op&en Download Directory Torrent's context menu item Op&en Download Directory &Check Local Data Torrent's context menu item &Check Local Data Reanno&unce Torrent's context menu item ---------- Button Reanno&unce &Queue Torrent's context menu item &Queue Move To &Top Torrent's context menu item Move To &Top Move &Up Torrent's context menu item Move &Up Move &Down Torrent's context menu item Move &Down Move To &Bottom Torrent's context menu item Move To &Bottom &Connection Settings &Connection Settings &Server Options &Server Options Server S&tats Server S&tats Select Files File chooser dialog title Select Files Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Torrent Files (*.torrent) Error Dialog title ---------- Trackers list column title Error This file/directory does not exist This file/directory does not exist &File Menu bar item &File &Close Window &Close Window &Edit Menu bar item &Edit Select &All Select &All &Invert Selection &Invert Selection &View Menu bar item &View &Toolbar &Toolbar &Sidebar &Sidebar St&atusbar St&atusbar Torrent properties &panel Torrent properties &panel &Lock Toolbar &Lock Toolbar T&ools Menu bar item T&ools &Options &Options S&hutdown Server S&hutdown Server Shutdown Server Dialog title Shutdown Server Are you sure you want to shutdown remote Transmission instance? Are you sure you want to shutdown remote Transmission instance? Shutdown Dialog confirmation button Shutdown &Help Menu bar item &Help &About Menu item opening "About" dialog &About Icon Only Toolbar mode Icon Only Text Only Toolbar mode Text Only Text Beside Icon Toolbar mode Text Beside Icon Text Under Icon Toolbar mode Text Under Icon Follow System Style Toolbar mode Follow System Style Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Torrents will be added after connection to server Show Tremotesf Button on notification Show Tremotesf Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Active (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Downloading (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Seeding (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Paused (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Errored (%L1) Search... Search field placeholder Search... &Select... Context menu item to open directory chooser &Select... Overwrite Server Dialog title Overwrite Server Name: Name: Address: Address: Port: Port: API path: API path: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option None Proxy type: Proxy type: Username: Username: Password: Password: No Server does not use custom certificates No Self-signed certificate Self-signed certificate Custom CA root certificate Custom CA root certificate Server uses custom certificates: Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Update interval: Timeout: Timeout: Auto reconnect interval: Auto reconnect interval: &Edit... Server's context menu item ---------- Tracker's context menu item &Edit... Server Options Dialog title Server Options Directory for incomplete files: Directory for incomplete files: min Suffix that is added to input field with number of minuts, e.g. "5 min" min Maximum active downloads: Maximum active downloads: Maximum active uploads: Maximum active uploads: Ignore queue position if idle for: Ignore queue position if idle for: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label Download: Upload: Upload speed limit input field label Upload: Days: Days: Peer port: Peer port: Encryption: Encryption: Enable μTP (Micro Transport Protocol) Check box label Enable μTP (Micro Transport Protocol) Enable PEX (Peer exchange) Check box label Enable PEX (Peer exchange) Enable local peer discovery Check box label Enable local peer discovery Maximum peers per torrent: Maximum peers per torrent: Maximum peers globally: Maximum peers globally: Options Dialog title Options Follow system Dark theme mode Follow system On Dark theme mode On Off Dark theme mode Off Dark theme Dark theme Use system accent color Check box label Use system accent color Remember location of last opened torrent file Check box label Remember location of last opened torrent file Remember parameters of last added torrent Check box label Remember parameters of last added torrent Show icon in the notification area Check box label Show icon in the notification area &Not Download Context menu item to unselect file for downloading &Not Download &Priority Torrent's context menu item &Priority &Download Context menu item to select file for downloading &Download &High File loading priority &High &Normal File loading priority &Normal &Low File loading priority &Low &Rename Torrent's context menu item ---------- Context menu item &Rename File name: File name: Details Torrent's properties dialog tab Details Completed: Torrent's completed size Completed: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Downloaded: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Uploaded: Ratio: Ratio: Duration: How much time Transmission is running Duration: Started: How many times Transmission was launched Started: Free space in download directory: Free space in download directory: Download speed: Download speed: Upload speed: Upload speed: ETA: ETA: Seeders: Seeders: Leechers: Leechers: Peers we are downloading from: Peers we are downloading from: Web seeders we are downloading from: Web seeders we are downloading from: Peers we are uploading to: Peers we are uploading to: Last activity: Last activity: Total size: Total size: Location: Torrent's download directory Location: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Created by: Created on: Date/time when torrent was created Created on: Comment: Torrent's comment text Comment: Labels: Torrent's labels Labels: Web seeder Web seeders list column title Web seeder Web seeders Torrent's properties dialog tab Web seeders Seeding Options section Torrent's limits tab section Seeding Ratio limit mode: Ratio limit mode: Idle seeding mode: Idle seeding mode: Maximum peers: Maximum peers: Add Trackers Dialog title Add Trackers Trackers announce URLs: Trackers announce URLs: Tracker announce URL: Tracker announce URL: Remove Tracker Dialog title Remove Tracker Are you sure you want to remove this tracker? Are you sure you want to remove this tracker? Remove Trackers Dialog title Remove Trackers Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Are you sure you want to remove %Ln selected tracker?Are you sure you want to remove %Ln selected trackers? Down Speed Torrents list column name ---------- Peers list column title Down Speed Up Speed Torrents list column name ---------- Peers list column title Up Speed Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Progress Bar Flags Peers list column title Flags Client Peers list column title Client Timed out Server connection status Timed out Connection error Server connection status Connection error Authentication error Server connection status Authentication error Parse error Server connection status Parse error Server is too new Server connection status Server is too new Server is too old Server connection status Server is too old Connecting... Server connection status Connecting... Connected Server connection status Connected Downloading Torrent status Torrent status Downloading Seeding Torrent status Torrent status Seeding Queued for checking Torrent status Queued for checking Merge trackers? Dialog title Merge trackers? Torrent «%1» is already added, merge trackers? Torrent «%1» is already added, merge trackers? Merge Merge Do not ask again Do not ask again Merged trackers Dialog title Merged trackers Merged trackers for torrent «%1» Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent already exists Torrent «%1» already exists Torrent «%1» already exists Error reading torrent file Error reading torrent file Error parsing torrent file Error parsing torrent file Total Size Torrents list column name Total Size Queue Position Torrents list column name Queue Position Downloading from peers Torrents list column name, number of peers that we are downloading from Downloading from peers Added on Torrents list column name, date/time when torrent was added Added on Completed on Torrents list column name, date/time when torrent was completed Completed on Down Limit Torrents list column name, download speed limit Down Limit Up Limit Torrents list column name, upload speed limit Up Limit Remaining Torrents list column name, remaining byte size Remaining Download Directory Torrents list column name Download Directory Last Activity Torrents list column name Last Activity Inactive Tracker status Inactive Waiting for update Tracker status Waiting for update About to update Tracker status About to update Updating Tracker status Updating Next Update Trackers list column title Next Update %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 d %L2 h %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 h %L2 m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 m %L2 s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 s Today Today Yesterday Yesterday Two days ago Two days ago %1 at %2 Relative date & time %1 at %2 Just now Relative time Just now %n minute(s) ago @item:intext %1 is a whole number %n minute ago%n minutes ago %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Error opening %1 Move files from current directory Check box label Move files from current directory Selected directory should be inside mounted directory Selected directory should be inside mounted directory All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents All (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist This directory does not exist Edit Labels Edit Labels New label... New label... tremotesf-2.10.1/translations/es.ts000066400000000000000000003415411523411710200173070ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Acerca de Source code: Translations: Maintainer Contributor Authors "About" dialog's "Authors" tab title Autores Translators "About" dialog's "Translators" tab title Traductores License "About" dialog's "License" tab title Licencia Add Torrent File Dialog title Agregar archivo torrente Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Espacio libre: %1 Error getting free space Error al obtener espacio libre Files Torrent properties dialog tab Archivos High Torrent's file loading priority ---------- Torrent's loading priority Alta Normal Torrent's file loading priority ---------- Torrent's loading priority Normal Low Torrent's file loading priority ---------- Torrent's loading priority Bajo Start downloading after adding Empezar a bajar después de agregar Add Torrent Link Dialog title Agregar enlace torrente No servers No hay servidores Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Desconectado Downloading Noun "Downloading" server setting page Bajando Start added torrents Check box label Empezar torrentes agregados Append ".part" to names of incomplete files Check box label Adjuntar " .part" a nombres de archivos incompletos> Rename Dialog title ---------- Dialog confirmation button Renombrar Select Directory Directory chooser dialog title Seleccionar directorio Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Estado Directories Title of torrents download directory filters list Directorios Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Rastreadores Priority Column title in torrent's file list ---------- Torrents list column name Prioridad Mixed Torrent's file loading priority ---------- File loading priority Mezclado No torrents Torrents list placeholder No hay torrentes Remove Button ---------- Dialog confirmation button Quitar Set Location Dialog title for changing torrent's download directory Establecer ubicación Error adding torrent Error al añadir torrente This torrent is already added Este torrente ya esta añadido Torrent added Notification title Torrente agregado Torrent finished Notification title Torrente finalizado Network "Network" server settings page Red Connection Title of settings section related to peer connections Conexión Random port on Transmission start Check box label Puerto aleatorio al inicio de transmisión Enable port forwarding Check box label Habilitar reenvío de puerto Allow Encryption mode (allow/prefer/require) Permitir Prefer Encryption mode (allow/prefer/require) Preferir Require Encryption mode (allow/prefer/require) Requerir Enable DHT Check box label Habilitar DHT Peer Limits Límite de pares Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Pares Queue "Queue" server settings page Cola Also delete the files on the hard disk Check box label También quitar los archivos en el disco duro Seeding Noun "Seeding" server setting page Semillando Overwrite Dialog's confirmation button Sobrescribir Server already exists El servidor ya existe Add Server Dialog title Agregar servidor Name Column title in torrent's file list ---------- Torrents list column name Nombre Address Peers list column title ---------- Trackers list column title Dirección Default Default proxy option Predeterminado HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Use client certificate authentication Check box label Usar autenticación de certificado de cliente Authentication Check box label Autenticación Auto reconnect on error Check box label Mounted directories Directorios montados Local directory Column title in the list of mounted directories Directorio local Remote directory Column title in the list of mounted directories Directorio remoto Add Button ---------- Dialog confirmation button Agregar Speed "Speed" server settings page ---------- Torrent's limits tab section Velocidad Connection Settings Servers list placeholder Dialog title Edit... Button Editar Add Server... Button Add... Button Agregar Connect to server on startup Check box label Conéctar al servidor al inicio Adding torrents Options tab Add torrent parameters Reset Show main window when adding torrents Check box label Show dialog when adding torrents Check box label Automatically fill link from clipboard when adding torrent link Check box label Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog Open torrent's file Open download directory What to do when torrent in the list is double clicked: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Notificaciones Notify when disconnecting from server Check box label Notificar al desconectar del servidor Notify on added torrents Check box label Notificar al agregar torrentes Notify on finished torrents Check box label Notificar al finalizar torrentes When connecting to server Notifications options section Cuando esta conectandose al servidor Notify on added torrents since last connection to server Check box label Notificar sobre los torrents añadidos desde la última conexión al servidor Notify on finished torrents since last connection to server Check box label Notificar sobre torrentes finalizados desde la última conexión al servidor Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Progreso ETA Torrents list column name ETA Server Stats Dialog title Estadísticas de servidor Current session Server stats section for current Transmission launch sesión actual Ratio Torrents list column name Proporción Total Server stats section for all Transmission launches (accumulated) Total %Ln times How many times Transmission was launched %Ln horas%Ln horas%Ln horas Size Column title in torrent's file list ---------- Torrents list column name Tamaño Limits Speed limits section ---------- Torrent's properties dialog tab Limites Alternative Limits Alternative speed limits section Límites alternativos Enable Check box label Habilitar Scheduled Title of alternative speed limit scheduling section Fechado to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" Para Every day Cada dia Weekdays Fin de semana Weekends Fin de semanas %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 de %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status comprobando (%L1) Honor global limits Check box label Honrar los límites globales Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Usar configuraciones globa Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Semilla independientemente de la proporción Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Parar de semillar a proporción: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Semilla independientemente de la actividad Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Para de semillar si está inactivo para: Activity Torrent's details tab section Actividad Completed Torrents list column name, completed byte size Completado Downloaded Torrents list column name, downloaded byte size Bajado Paused (%1) Torrent status while torrent also has an error. %1 is error string Paused Torrent status Downloading (%1) Torrent status while torrent also has an error. %1 is error string Seeding (%1) Torrent status while torrent also has an error. %1 is error string Queued (%1) Torrent status while torrent also has an error. %1 is error string Queued Torrent status Checking (%1) Torrent status while torrent also has an error. %1 is error string Checking Torrent status Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string Downloading to peers Torrents list column name, number of peers that we are downloading from Uploading to peers Torrents list column name, number of peers that we are uploading to Uploaded Torrents list column name, uploaded byte size Subido Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Semilleros Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Sanguijuelos Information Torrent's details tab section Información Torrent Removed Message that appears when torrent is removed Torrente removido Edit Tracker Dialog title Editar rastreador Torrent file: Input field's label Archivo torrente: Torrent link: Input field's label Enlace torrente: Download directory: Input field's label Directorio de bajado: Torrent priority: Combo box label Prioridad torrente: Delete .torrent file Move .torrent file to trash Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed &Connect Button / menu item to connect to server &Conectar &Disconnect Button / menu item to disconnect from server &Desconectar &Add Torrent File... Menu item &Agregar archivo torrente Add Torrent &Link... Menu item Agregar enlace &torrente P&ause Torrent's context menu item P&ausa &Delete Torrent's context menu item Open &Download Directory Context menu item Delete with files Delete Delete Torrent Dialog title Are you sure you want to delete this torrent? Delete Torrents Dialog title Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion No torrents matching filters Torrents list placeholder &Quit Menu item &Salir &Torrent Menu bar item &Torrente Error adding torrent «%1» &Properties Torrent's context menu item &Propiedades &Show Tremotesf &Hide Tremotesf &Start Torrent's context menu item &Iniciar Start &Now Torrent's context menu item Iniciar &ahora Copy &Magnet Link Torrent's context menu item &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Quitar Set &Location Torrent's context menu item Establecer &Ubicación Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item &Abrir Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item &Verificar datos locales Reanno&unce Torrent's context menu item ---------- Button reani&mar &Queue Torrent's context menu item &Cola Move To &Top Torrent's context menu item Mover a &tope Move &Up Torrent's context menu item Mover &arriba Move &Down Torrent's context menu item Mover &abajo Move To &Bottom Torrent's context menu item Mover al &fondo &Connection Settings &Server Options &Opciones de servidor Server S&tats estadísticas de s&ervidor Select Files File chooser dialog title Seleccionar archivos Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Archivos torrente (*.torrent) Error Dialog title ---------- Trackers list column title Error This file/directory does not exist &File Menu bar item &Archivos &Close Window &Edit Menu bar item &Editar Select &All Elegir &Todo &Invert Selection &Invertir selección &View Menu bar item &Vista &Toolbar &BarraDeHerramientas &Sidebar &BarraLateral St&atusbar &BarraDeEstado Torrent properties &panel &Lock Toolbar T&ools Menu bar item &Herramientas &Options &Opciones S&hutdown Server Shutdown Server Dialog title Are you sure you want to shutdown remote Transmission instance? Shutdown Dialog confirmation button &Help Menu bar item &Ayuda &About Menu item opening "About" dialog &Acerca de Icon Only Toolbar mode Solo icono Text Only Toolbar mode Solo texto Text Beside Icon Toolbar mode Texto al lado del icono Text Under Icon Toolbar mode Texto abajo del icono Follow System Style Toolbar mode Seguir estilo del sistema Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Activo (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Bajando (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Semillero (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Pausado (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Errado (%L1) Search... Search field placeholder Buscar &Select... Context menu item to open directory chooser &Seleccionar Overwrite Server Dialog title Sobrescribir servidor Name: Nombre: Address: Dirección: Port: Puerto: API path: Ruta de API: Proxy HTTP HTTP proxy option None None proxy option Proxy type: Username: Nombre de usuario: Password: Contraseña: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Intervalo de actualización: Timeout: Tiempo agotado: Auto reconnect interval: &Edit... Server's context menu item ---------- Tracker's context menu item &Editar Server Options Dialog title Opciones de servidor Directory for incomplete files: Directorio de archivos incompletos: min Suffix that is added to input field with number of minuts, e.g. "5 min" Minutos Maximum active downloads: Máximos bajados activos: Maximum active uploads: Máximos subidos activos: Ignore queue position if idle for: Ignorar posición de cola si está inactivo para: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes Download: Download speed limit input field label Bajar: Upload: Upload speed limit input field label Subir: Days: Dias: Peer port: Puerto de par: Encryption: Cifrado: Enable μTP (Micro Transport Protocol) Check box label Enable PEX (Peer exchange) Check box label Enable local peer discovery Check box label Maximum peers per torrent: Pares máximos por torrente: Maximum peers globally: Máximos pares globalmente: Options Dialog title Opciones Follow system Dark theme mode On Dark theme mode Off Dark theme mode Dark theme Use system accent color Check box label Remember location of last opened torrent file Check box label Remember parameters of last added torrent Check box label Show icon in the notification area Check box label Mostrar icono en el área de notificación &Not Download Context menu item to unselect file for downloading &No hay bajados &Priority Torrent's context menu item &Prioridad &Download Context menu item to select file for downloading &High File loading priority &Alto &Normal File loading priority &Normal &Low File loading priority &Bajar &Rename Torrent's context menu item ---------- Context menu item &Renombrar File name: Nombre de archivo: Details Torrent's properties dialog tab Detalles Completed: Torrent's completed size Completado: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Bajado: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Subido: Ratio: Proporción: Duration: How much time Transmission is running Duración Started: How many times Transmission was launched Iniciado: Free space in download directory: Download speed: Velocidad a bajar: Upload speed: Velocidad a subir: ETA: HEA: Seeders: Semilleros: Leechers: Sanguijuelos: Peers we are downloading from: Web seeders we are downloading from: Peers we are uploading to: Last activity: Última actividad: Total size: Tamaño total: Location: Torrent's download directory ubicación: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Creado por: Created on: Date/time when torrent was created Creado en: Comment: Torrent's comment text Comentar: Labels: Torrent's labels Web seeder Web seeders list column title Web seeders Torrent's properties dialog tab Seeding Options section Torrent's limits tab section Ratio limit mode: Modo límite de proporción: Idle seeding mode: Modo semillero inactivo: Maximum peers: Pares máximos: Add Trackers Dialog title Agregar rastreadores Trackers announce URLs: Rastreadores anuncian URLs: Tracker announce URL: Rastreador anuncia URL: Remove Tracker Dialog title Quitar rastreador Are you sure you want to remove this tracker? Estas seguro que quieres quitar este rastreador? Remove Trackers Dialog title Quitar rastreadores Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Down Speed Torrents list column name ---------- Peers list column title Velocidad baja Up Speed Torrents list column name ---------- Peers list column title Velocidad alta Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Barra de progreso Flags Peers list column title Banderas Client Peers list column title Cliente Timed out Server connection status Tiempo agotado Connection error Server connection status Error de conexión Authentication error Server connection status Error de autenticación Parse error Server connection status Error de análisis Server is too new Server connection status El servidor es demasiado nuevo Server is too old Server connection status El servidor es demasiado viejo Connecting... Server connection status Conectando... Connected Server connection status Conectado Downloading Torrent status Torrent status Bajando Seeding Torrent status Torrent status Estado de torrente Queued for checking Torrent status En cola para verificar Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file Error al leer el archivo torrent Error parsing torrent file Error al analizar archivo torrent Total Size Torrents list column name Tamaño total Queue Position Torrents list column name Posición en cola Added on Torrents list column name, date/time when torrent was added Agregado en Completed on Torrents list column name, date/time when torrent was completed Completado en Down Limit Torrents list column name, download speed limit Límite bajo Up Limit Torrents list column name, upload speed limit Límite arriba Remaining Torrents list column name, remaining byte size Restante Download Directory Torrents list column name Directorio de bajado Last Activity Torrents list column name Última actividad Inactive Tracker status Waiting for update Tracker status About to update Tracker status Updating Tracker status Next Update Trackers list column title Próxima actualización %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second >%L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 d %L2 h %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 h %L2 m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 m %L2 s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 s Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Error al abrir %1 Move files from current directory Check box label Mover archivos desde el directorio actual Selected directory should be inside mounted directory El directorio seleccionado debe estar dentro del directorio montado All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Edit Labels New label... tremotesf-2.10.1/translations/es_ES.ts000066400000000000000000003451611523411710200177000ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Acerca de Source code: Translations: Maintainer Encargado del mantenimiento Contributor Colaborador Authors "About" dialog's "Authors" tab title Autores Translators "About" dialog's "Translators" tab title Traductores License "About" dialog's "License" tab title Licencia Add Torrent File Dialog title Añadir archivo torrent Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Espacio libre: %1 Error getting free space Error al obtener el espacio libre Files Torrent properties dialog tab Archivos High Torrent's file loading priority ---------- Torrent's loading priority Alta Normal Torrent's file loading priority ---------- Torrent's loading priority Normal Low Torrent's file loading priority ---------- Torrent's loading priority Baja Start downloading after adding Iniciar descarga después de añadir Add Torrent Link Dialog title Añadir enlace torrent No servers No hay servidores Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Desconectado Downloading Noun "Downloading" server setting page Descargas Start added torrents Check box label Iniciar torrents añadidos Append ".part" to names of incomplete files Check box label Añadir ".part" a los nombres de archivos incompletos Rename Dialog title ---------- Dialog confirmation button Renombrar Select Directory Directory chooser dialog title Seleccionar directorio Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Estado Directories Title of torrents download directory filters list Directorios Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Rastreadores Priority Column title in torrent's file list ---------- Torrents list column name Prioridad Mixed Torrent's file loading priority ---------- File loading priority Mezclado No torrents Torrents list placeholder No hay torrents Remove Button ---------- Dialog confirmation button Eliminar Set Location Dialog title for changing torrent's download directory Establecer ubicación Error adding torrent Error al añadir torrent This torrent is already added Este torrent ya se ha añadido Torrent added Notification title Torrent añadido Torrent finished Notification title Torrent completado Network "Network" server settings page Red Connection Title of settings section related to peer connections Conexión Random port on Transmission start Check box label Puerto aleatorio al iniciar Transmission Enable port forwarding Check box label Habilitar el reenvío de puertos Allow Encryption mode (allow/prefer/require) Permitir Prefer Encryption mode (allow/prefer/require) Preferir Require Encryption mode (allow/prefer/require) Exigir Enable DHT Check box label Habilitar DHT Peer Limits Limite de pares conectados Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Pares Queue "Queue" server settings page Cola Also delete the files on the hard disk Check box label Borrar también los archivos del disco duro Seeding Noun "Seeding" server setting page Compartir Overwrite Dialog's confirmation button Sobrescribir Server already exists El servidor ya existe Add Server Dialog title Añadir servidor Name Column title in torrent's file list ---------- Torrents list column name Nombre Address Peers list column title ---------- Trackers list column title Dirección Default Default proxy option Por defecto HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Cargar desde archivo... Use client certificate authentication Check box label Usar autenticación de certificado de cliente Authentication Check box label Autenticación Auto reconnect on error Check box label Volver a conectar automáticamente en caso de error Mounted directories Directorios montados Local directory Column title in the list of mounted directories Directorio local Remote directory Column title in the list of mounted directories Directorio remoto Add Button ---------- Dialog confirmation button Añadir Speed "Speed" server settings page ---------- Torrent's limits tab section Velocidad Connection Settings Servers list placeholder Dialog title Ajustes de conexión Edit... Button Editar... Add Server... Button Añadir servidor... Add... Button Añadir... Connect to server on startup Check box label Conectarse al servidor al iniciar Adding torrents Options tab Añadiendo torrents Add torrent parameters Reset Show main window when adding torrents Check box label Show dialog when adding torrents Check box label Automatically fill link from clipboard when adding torrent link Check box label Cumplimentar automáticamente del portapapeles al añadir enlace torrent Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Sugerencia: también puedes presionar %1 en la ventana principal para añadir torrents desde el portapapeles Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog Open torrent's file Open download directory What to do when torrent in the list is double clicked: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Notificaciones Notify when disconnecting from server Check box label Avisar cuando se desconecta del servidor Notify on added torrents Check box label Informar sobre los torrents añadidos Notify on finished torrents Check box label Informar sobre los torrents completados When connecting to server Notifications options section Al conectarse al servidor Notify on added torrents since last connection to server Check box label Informar sobre los torrents añadidos desde la última conexión al servidor Notify on finished torrents since last connection to server Check box label Informar sobre los torrents completados desde la última conexión al servidor Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Progreso ETA Torrents list column name Tiempo restante Server Stats Dialog title Estadísticas del servidor Current session Server stats section for current Transmission launch Sesión actual Ratio Torrents list column name Ratio Total Server stats section for all Transmission launches (accumulated) Total %Ln times How many times Transmission was launched %Ln vez%Ln veces%Ln veces Size Column title in torrent's file list ---------- Torrents list column name Tamaño Limits Speed limits section ---------- Torrent's properties dialog tab Límites Alternative Limits Alternative speed limits section Límites alternativos Enable Check box label Habilitar Scheduled Title of alternative speed limit scheduling section Programado to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" a Every day Todos los días Weekdays Entre semana Weekends Fines de semana %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 de %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Comprobando (%L1) Honor global limits Check box label Respetar límites globales Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Usar ajustes globales Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Enviar independientemente de la ratio Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Dejar de enviar en la ratio: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Enviar independientemente de la actividad Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Dejar de enviar si está inactivo durante: Activity Torrent's details tab section Actividad Completed Torrents list column name, completed byte size Completado Downloaded Torrents list column name, downloaded byte size Descargado Paused (%1) Torrent status while torrent also has an error. %1 is error string En pausa (%1) Paused Torrent status En pausa Downloading (%1) Torrent status while torrent also has an error. %1 is error string Descargando (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string Enviando (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string En cola (%1) Queued Torrent status En cola Checking (%1) Torrent status while torrent also has an error. %1 is error string Comprobando (%1) Checking Torrent status Comprobando Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string En cola para comprobar (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Subiendo a los pares Uploaded Torrents list column name, uploaded byte size Subido Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Semillas Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Sanguijuelas Information Torrent's details tab section Información Torrent Removed Message that appears when torrent is removed Torrent eliminado Edit Tracker Dialog title Editar rastreador Torrent file: Input field's label Archivo torrent: Torrent link: Input field's label Enlace torrent: Download directory: Input field's label Directorio de descarga: Torrent priority: Combo box label Prioridad del torrent: Delete .torrent file Borrar archivo .torrent Move .torrent file to trash Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed Cargando &Connect Button / menu item to connect to server &Conectar &Disconnect Button / menu item to disconnect from server &Desconectar &Add Torrent File... Menu item &Añadir archivo torrent... Add Torrent &Link... Menu item Añadir &enlace torrent... P&ause Torrent's context menu item P&ausar &Delete Torrent's context menu item &Borrar Open &Download Directory Context menu item Delete with files Borrar con archivos Delete Borrar Delete Torrent Dialog title Borrar torrent Are you sure you want to delete this torrent? ¿Estás seguro de que quieres borrar este torrent? Delete Torrents Dialog title Borrar torrents Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion ¿Estás seguro de que quieres borrar %Ln torrent seleccionado?¿Estás seguro de que quieres borrar %Ln torrents seleccionados?¿Estás seguro de que quieres borrar %Ln torrents seleccionados? No torrents matching filters Torrents list placeholder No hay torrents que coincidan con los filtros &Quit Menu item &Salir &Torrent Menu bar item &Torrent Error adding torrent «%1» &Properties Torrent's context menu item &Propiedades &Show Tremotesf &Hide Tremotesf &Start Torrent's context menu item &Iniciar Start &Now Torrent's context menu item Iniciar &ahora Copy &Magnet Link Torrent's context menu item Copiar &enlace magnet &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Eliminar Set &Location Torrent's context menu item Establecer &ubicación Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item &Abrir Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item &Comprobar datos locales Reanno&unce Torrent's context menu item ---------- Button Volver a an&unciar &Queue Torrent's context menu item &Cola Move To &Top Torrent's context menu item Mover al &principio Move &Up Torrent's context menu item &Subir Move &Down Torrent's context menu item &Bajar Move To &Bottom Torrent's context menu item Mover al &final &Connection Settings Ajustes de &conexión &Server Options Opciones del &servidor Server S&tats Es&tadísticas del servidor Select Files File chooser dialog title Seleccionar archivos Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Archivos torrent (*.torrent) Error Dialog title ---------- Trackers list column title Error This file/directory does not exist &File Menu bar item &Archivo &Close Window &Edit Menu bar item &Editar Select &All Seleccionar &todo &Invert Selection &Invertir selección &View Menu bar item &Vista &Toolbar &Barra de herramientas &Sidebar &Barra lateral St&atusbar Barra de est&ado Torrent properties &panel &Lock Toolbar &Bloquear barra de herramientas T&ools Menu bar item H&erramientas &Options &Opciones S&hutdown Server A&pagar servidor Shutdown Server Dialog title Apagar servidor Are you sure you want to shutdown remote Transmission instance? ¿Estás seguro de que quieres apagar la instancia remota de Transmission? Shutdown Dialog confirmation button Apagar &Help Menu bar item &Ayuda &About Menu item opening "About" dialog &Acerca de Icon Only Toolbar mode Sólo icono Text Only Toolbar mode Sólo texto Text Beside Icon Toolbar mode Texto junto al icono Text Under Icon Toolbar mode Texto bajo el icono Follow System Style Toolbar mode Seguir el estilo del sistema Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Mostrar Tremotesf Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Activos (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Descargando (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Enviando (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status En pausa (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Con errores (%L1) Search... Search field placeholder Buscar... &Select... Context menu item to open directory chooser &Seleccionar... Overwrite Server Dialog title Sobrescribir servidor Name: Nombre: Address: Dirección: Port: Puerto: API path: Ruta de la API: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Proxy type: Tipo de proxy: Username: Nombre de usuario: Password: Contraseña: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Intervalo de actualización: Timeout: Tiempo de espera: Auto reconnect interval: Intervalo de reconexión automática: &Edit... Server's context menu item ---------- Tracker's context menu item &Editar... Server Options Dialog title Opciones del servidor Directory for incomplete files: Directorio para archivos incompletos: min Suffix that is added to input field with number of minuts, e.g. "5 min" min Maximum active downloads: Máximo de descargas activas: Maximum active uploads: Máximo de subidas activas: Ignore queue position if idle for: Ignorar la posición en la cola si está inactivo durante: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label Descargar: Upload: Upload speed limit input field label Subir: Days: Días: Peer port: Puerto del par: Encryption: Cifrado: Enable μTP (Micro Transport Protocol) Check box label Habilitar μTP (Micro Transport Protocol) Enable PEX (Peer exchange) Check box label Habilitar PEX (Intercambio de pares) Enable local peer discovery Check box label Habilitar búsqueda local de pares Maximum peers per torrent: Máximo de pares por torrent: Maximum peers globally: Máximo global de pares: Options Dialog title Opciones Follow system Dark theme mode Seguir sistema On Dark theme mode Encendido Off Dark theme mode Apagado Dark theme Tema oscuro Use system accent color Check box label Usar color principal del sistema Remember location of last opened torrent file Check box label Recordar ubicación del último archivo torrent abierto Remember parameters of last added torrent Check box label Recordar parámetros del último torrent añadido Show icon in the notification area Check box label Mostrar icono en el área de notificación &Not Download Context menu item to unselect file for downloading &No descargar &Priority Torrent's context menu item &Prioridad &Download Context menu item to select file for downloading &Descargar &High File loading priority &Alta &Normal File loading priority &Normal &Low File loading priority &Baja &Rename Torrent's context menu item ---------- Context menu item &Renombrar File name: Nombre de archivo: Details Torrent's properties dialog tab Detalles Completed: Torrent's completed size Completado: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Descargado: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Subido: Ratio: Ratio: Duration: How much time Transmission is running Duración: Started: How many times Transmission was launched Iniciado: Free space in download directory: Espacio libre en el directorio de descarga: Download speed: Velocidad de descarga: Upload speed: Velocidad de subida: ETA: Tiempo restante: Seeders: Semillas: Leechers: Sanguijuelas: Peers we are downloading from: Pares desde los que se está descargando: Web seeders we are downloading from: Semillas web desde los que se está descargando: Peers we are uploading to: Pares a los que se está subiendo: Last activity: Última actividad: Total size: Tamaño total: Location: Torrent's download directory Ubicación: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Creado por: Created on: Date/time when torrent was created Creado: Comment: Torrent's comment text Comentario: Labels: Torrent's labels Web seeder Web seeders list column title Semilla web Web seeders Torrent's properties dialog tab Semillas web Seeding Options section Torrent's limits tab section Enviando Ratio limit mode: Modo límite por ratio: Idle seeding mode: Modo de envío inactivo: Maximum peers: Máximo de pares: Add Trackers Dialog title Añadir rastreadores Trackers announce URLs: URLs de anuncio de los rastreadores: Tracker announce URL: URL de anuncio del rastreador: Remove Tracker Dialog title Eliminar rastreador Are you sure you want to remove this tracker? ¿Estás seguro de que quieres eliminar este rastreador? Remove Trackers Dialog title Eliminar rastradores Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion ¿Estás seguro de que quieres eliminar %Ln rastreador seleccionado?¿Estás seguro de que quieres eliminar %Ln rastreadores seleccionados?¿Estás seguro de que quieres eliminar %Ln rastreadores seleccionados? Down Speed Torrents list column name ---------- Peers list column title Vel. descarga Up Speed Torrents list column name ---------- Peers list column title Vel. subida Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Barra de progreso Flags Peers list column title Indicadores Client Peers list column title Cliente Timed out Server connection status Tiempo de espera Connection error Server connection status Error de conexión Authentication error Server connection status Error de autenticación Parse error Server connection status Error de análisis Server is too new Server connection status El servidor es demasiado nuevo Server is too old Server connection status El servidor es demasiado antiguo Connecting... Server connection status Conectando... Connected Server connection status Conectado Downloading Torrent status Torrent status Descargando Seeding Torrent status Torrent status Enviando Queued for checking Torrent status En cola para comprobar Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file Error al leer archivo torrent Error parsing torrent file Error al analizar archivo torrent Total Size Torrents list column name Tamaño total Queue Position Torrents list column name Posición en la cola Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added Añadido el Completed on Torrents list column name, date/time when torrent was completed Completado el Down Limit Torrents list column name, download speed limit Límite inferior Up Limit Torrents list column name, upload speed limit Límite superior Remaining Torrents list column name, remaining byte size Restante Download Directory Torrents list column name Directorio de descargas Last Activity Torrents list column name Última actividad Inactive Tracker status Inactivo Waiting for update Tracker status Esperando actualización About to update Tracker status A punto de actualizar Updating Tracker status Actualizando Next Update Trackers list column title Próxima actualización %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 d %L2 h %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 h %L2 m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 m %L2 s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 s Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Error al abrir %1 Move files from current directory Check box label Mover archivos desde el directorio actual Selected directory should be inside mounted directory El directorio seleccionado debe estar dentro del directorio montado All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents Todo (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Edit Labels New label... tremotesf-2.10.1/translations/fr.ts000066400000000000000000003461651523411710200173160ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title À propos Source code: Translations: Maintainer Mainteneur Contributor Contributeur Authors "About" dialog's "Authors" tab title Auteurs Translators "About" dialog's "Translators" tab title Traducteurs License "About" dialog's "License" tab title License Add Torrent File Dialog title Ajouter un fichier torrent Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Espace libre : %1 Error getting free space Erreur lors de l'obtention de l'espace libre Files Torrent properties dialog tab Fichiers High Torrent's file loading priority ---------- Torrent's loading priority Haute Normal Torrent's file loading priority ---------- Torrent's loading priority Normale Low Torrent's file loading priority ---------- Torrent's loading priority Basse Start downloading after adding Commencer à télécharger après l'ajout Add Torrent Link Dialog title Ajouter un lien torrent No servers Aucun serveur Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Déconnecté Downloading Noun "Downloading" server setting page Téléchargement Start added torrents Check box label Lancer les torrents ajoutés Append ".part" to names of incomplete files Check box label Ajouter ".part" aux noms de fichiers incomplets Rename Dialog title ---------- Dialog confirmation button Renommer Select Directory Directory chooser dialog title Sélectionner un répertoire Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Statut Directories Title of torrents download directory filters list Répertoires Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Trackers Priority Column title in torrent's file list ---------- Torrents list column name Priorité Mixed Torrent's file loading priority ---------- File loading priority Mixé No torrents Torrents list placeholder Aucun torrent Remove Button ---------- Dialog confirmation button Supprimer Set Location Dialog title for changing torrent's download directory Définir l'emplacement Error adding torrent Erreur en ajoutant le torrent This torrent is already added Ce torrent est déjà ajouté Torrent added Notification title Torrent ajouté Torrent finished Notification title Torrents terminés Network "Network" server settings page Réseau Connection Title of settings section related to peer connections Connexion Random port on Transmission start Check box label Port aléatoire au lancement de Transmission Enable port forwarding Check box label Activer le transfert de port Allow Encryption mode (allow/prefer/require) Permettre Prefer Encryption mode (allow/prefer/require) Préférer Require Encryption mode (allow/prefer/require) Exiger Enable DHT Check box label Activer DHT Peer Limits Limites de pairs Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Pairs Queue "Queue" server settings page Queue Also delete the files on the hard disk Check box label Supprimer également les fichiers sur le disque dur Seeding Noun "Seeding" server setting page Partage Overwrite Dialog's confirmation button Écraser Server already exists Le serveur existe déjà Add Server Dialog title Ajouter un serveur Name Column title in torrent's file list ---------- Torrents list column name Nom Address Peers list column title ---------- Trackers list column title Adresse Default Default proxy option Par défaut HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Charger à partir du fichier... Use client certificate authentication Check box label Utiliser l'authentification par certificat client Authentication Check box label Authentification Auto reconnect on error Check box label Reconnexion automatique en cas d'erreur Mounted directories Répertoires montés Local directory Column title in the list of mounted directories Répertoire local Remote directory Column title in the list of mounted directories Répertoire distant Add Button ---------- Dialog confirmation button Ajouter Speed "Speed" server settings page ---------- Torrent's limits tab section Vitesse Connection Settings Servers list placeholder Dialog title Paramètres de connexion Edit... Button Éditer... Add Server... Button Ajouter un serveur... Add... Button Ajouter... Connect to server on startup Check box label Se connecter au serveur au démarrage Adding torrents Options tab Ajouter des torrents Add torrent parameters Reset Show main window when adding torrents Check box label Show dialog when adding torrents Check box label Automatically fill link from clipboard when adding torrent link Check box label Remplir automatiquement le lien depuis le presse-papiers lors de l'ajout d'un lien torrent Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Astuce : vous pouvez également appuyer sur %1 dans la fenêtre principale pour ajouter des torrents à partir du presse-papiers Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog Open torrent's file Open download directory What to do when torrent in the list is double clicked: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Notifications Notify when disconnecting from server Check box label Notifier lors de la déconnexion du serveur Notify on added torrents Check box label Notifier lors de l'ajout de torrents Notify on finished torrents Check box label Notifier les torrents terminés When connecting to server Notifications options section Lors de la connexion au serveur Notify on added torrents since last connection to server Check box label Notifier les torrents ajoutés depuis la dernière connexion au serveur Notify on finished torrents since last connection to server Check box label Notifier les torrents finis depuis la dernière connexion au serveur Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Progression ETA Torrents list column name ETA Server Stats Dialog title Stats du serveur Current session Server stats section for current Transmission launch Session courante Ratio Torrents list column name Ratio Total Server stats section for all Transmission launches (accumulated) Total %Ln times How many times Transmission was launched %Ln fois%Ln fois%Ln fois Size Column title in torrent's file list ---------- Torrents list column name Taille Limits Speed limits section ---------- Torrent's properties dialog tab Limites Alternative Limits Alternative speed limits section Limites alternatives Enable Check box label Activer Scheduled Title of alternative speed limit scheduling section Programmé to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" vers Every day Quotidien Weekdays Hebdomadaire Weekends Weekends %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 de %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Vérification (%L1) Honor global limits Check box label Respecter les limites globales Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Utiliser les paramètres globaux Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Partager quel que soit le ratio Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Arrêter de partager au ratio : Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Partager quelle que soit l'activité Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Arrêter le partage si inactif pendant : Activity Torrent's details tab section Activité Completed Torrents list column name, completed byte size Terminé Downloaded Torrents list column name, downloaded byte size Téléchargé Paused (%1) Torrent status while torrent also has an error. %1 is error string En pause (%1) Paused Torrent status En pause Downloading (%1) Torrent status while torrent also has an error. %1 is error string En téléchargement (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string En partage (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string En file d'attente (%1) Queued Torrent status En file d'attente Checking (%1) Torrent status while torrent also has an error. %1 is error string Vérification (%1) Checking Torrent status Vérification Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string En file d'attente pour vérification (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Envoi vers des pairs Uploaded Torrents list column name, uploaded byte size Téléversé Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Seeders Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Leechers Information Torrent's details tab section Information Torrent Removed Message that appears when torrent is removed Torrent supprimé Edit Tracker Dialog title Éditer le tracker Torrent file: Input field's label Fichier torrent : Torrent link: Input field's label Lien torrent : Download directory: Input field's label Répertoire de téléchargement : Torrent priority: Combo box label Priorité du torrent : Delete .torrent file Supprimer le fichier .torrent Move .torrent file to trash Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed Chargement &Connect Button / menu item to connect to server &Connexion &Disconnect Button / menu item to disconnect from server &Déconnexion &Add Torrent File... Menu item &Ajouter un fichier torrent... Add Torrent &Link... Menu item Ajouter un &lien torrent P&ause Torrent's context menu item P&ause &Delete Torrent's context menu item &Supprimer Open &Download Directory Context menu item Delete with files Supprimer avec les fichiers Delete Supprimer Delete Torrent Dialog title Supprimer le torrent Are you sure you want to delete this torrent? Êtes-vous sûr de vouloir supprimer ce torrent ? Delete Torrents Dialog title Supprimer les torrents Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion Êtes-vous sûr de vouloir supprimer le torrent sélectionné ?Êtes-vous sûr de vouloir supprimer les %Ln torrents sélectionnés ?Êtes-vous sûr de vouloir supprimer les %Ln torrents sélectionnés ? No torrents matching filters Torrents list placeholder Aucun torrent ne correspond aux filtres &Quit Menu item &Quitter &Torrent Menu bar item &Torrent Error adding torrent «%1» &Properties Torrent's context menu item &Propriétés &Show Tremotesf &Hide Tremotesf &Start Torrent's context menu item &Démarrer Start &Now Torrent's context menu item Démarrer &maintenant Copy &Magnet Link Torrent's context menu item Copier &le lien magnet &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Supprimer Set &Location Torrent's context menu item Définir &l'emplacement Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item &Ouvrir Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item &Vérifier les données locales Reanno&unce Torrent's context menu item ---------- Button Ré-annoncer &Queue Torrent's context menu item &Queue Move To &Top Torrent's context menu item Déplacer en hau&t Move &Up Torrent's context menu item Déplacer vers le ha&ut Move &Down Torrent's context menu item &Déplacer vers le bas Move To &Bottom Torrent's context menu item Déplacer en &bas &Connection Settings &Paramètres de connexion &Server Options Options du &serveur Server S&tats Serveur et s&tats Select Files File chooser dialog title Sélectionner des fichiers Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Fichiers torrent (*.torrent) Error Dialog title ---------- Trackers list column title Erreur This file/directory does not exist &File Menu bar item &Fichier &Close Window &Edit Menu bar item &Éditer Select &All &Tout sélectionner &Invert Selection &Inverser la sélection &View Menu bar item &Vue &Toolbar Barre d'ou&tils &Sidebar &Barre latérale St&atusbar Barre de st&atut Torrent properties &panel &Lock Toolbar &Verrouiller la barre d'outils T&ools Menu bar item &Outils &Options &Options S&hutdown Server &Arrêter le serveur Shutdown Server Dialog title Arrêter le serveur Are you sure you want to shutdown remote Transmission instance? Voulez-vous vraiment arrêter l'instance distante de transmission ? Shutdown Dialog confirmation button Arrêt &Help Menu bar item &Aide &About Menu item opening "About" dialog &À propos Icon Only Toolbar mode Icône seulement Text Only Toolbar mode Texte seulement Text Beside Icon Toolbar mode Texte à côté de l'icône Text Under Icon Toolbar mode Texte sous l'icône Follow System Style Toolbar mode Suivre le style du système Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Afficher Tremotesf Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Actifs (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status En téléchargement (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status En partage (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status En pause (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Erreur (%L1) Search... Search field placeholder Chercher... &Select... Context menu item to open directory chooser &Sélectionner... Overwrite Server Dialog title Écraser le serveur Name: Nom : Address: Adresse : Port: Port : API path: Chemin vers l'API : Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Proxy type: Type de proxy : Username: Nom d'utilisateur : Password: Mot de passe : No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Intervalle de mise à jour : Timeout: Délai d'attente : Auto reconnect interval: Intervalle de reconnexion automatique : &Edit... Server's context menu item ---------- Tracker's context menu item &Éditer... Server Options Dialog title Options du serveur Directory for incomplete files: Répertoire pour les fichiers incomplets : min Suffix that is added to input field with number of minuts, e.g. "5 min" min Maximum active downloads: Limite de téléchargements actifs : Maximum active uploads: Limite de téléversements actifs : Ignore queue position if idle for: Ignorer la position de la file d'attente si inactif pendant : kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes ko/s Download: Download speed limit input field label Télécharger : Upload: Upload speed limit input field label Téléverser : Days: Jours : Peer port: Port peer : Encryption: Cryptage : Enable μTP (Micro Transport Protocol) Check box label Activer μTP (Micro Transport Protocol) Enable PEX (Peer exchange) Check box label Activer PEX (Peer exchange) Enable local peer discovery Check box label Activer la découverte des pairs locaux Maximum peers per torrent: Limite de pairs par torrent : Maximum peers globally: Limite globale de pairs : Options Dialog title Options Follow system Dark theme mode Suivre le système On Dark theme mode Activer Off Dark theme mode Désactiver Dark theme Thème sombre Use system accent color Check box label Utiliser la couleur d'accentuation du système Remember location of last opened torrent file Check box label Se souvenir de l'emplacement du dernier fichier torrent ouvert Remember parameters of last added torrent Check box label Se souvenir des paramètres du dernier torrent ajouté Show icon in the notification area Check box label Afficher l'icône dans la zone de notification &Not Download Context menu item to unselect file for downloading &Ne pas télécharger &Priority Torrent's context menu item &Priorité &Download Context menu item to select file for downloading &Télécharger &High File loading priority &Haute &Normal File loading priority &Normale &Low File loading priority &Basse &Rename Torrent's context menu item ---------- Context menu item &Renommer File name: Nom de fichier : Details Torrent's properties dialog tab Détails Completed: Torrent's completed size Terminé : Downloaded: Downloaded bytes ---------- Torrent's downloaded size Téléchargé : Uploaded: Uploaded bytes ---------- Torrent's uploaded size Téléversé : Ratio: Ratio : Duration: How much time Transmission is running Durée : Started: How many times Transmission was launched Commencé : Free space in download directory: Espace libre dans le répertoire de téléchargement : Download speed: Vitesse de téléchargement : Upload speed: Vitesse de téléversement : ETA: ETA : Seeders: Seeders : Leechers: Leechers : Peers we are downloading from: Pairs à partir desquels nous téléchargeons : Web seeders we are downloading from: Web seeders à partir desquels nous téléchargeons : Peers we are uploading to: Pairs vers lesquels nous envoyons : Last activity: Dernière activité : Total size: Taille totale : Location: Torrent's download directory Emplacement : Hash: Torrent's hash string Hashage : Created by: Program that created torrent file Créé par : Created on: Date/time when torrent was created Créé le : Comment: Torrent's comment text Commentaire : Labels: Torrent's labels Web seeder Web seeders list column title Seeder web Web seeders Torrent's properties dialog tab Seeders web Seeding Options section Torrent's limits tab section En partage Ratio limit mode: Mode de limite de ratio : Idle seeding mode: Mode de seed inactif : Maximum peers: Limite de pairs : Add Trackers Dialog title Ajouter des traqueurs Trackers announce URLs: URLs d'annonce des trackers : Tracker announce URL: URL d'annonce du tracker : Remove Tracker Dialog title Supprimer un tracker Are you sure you want to remove this tracker? Êtes-vous sûr de vouloir supprimer ce tracker ? Remove Trackers Dialog title Supprimer les Trackers Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Êtes-vous sûr de vouloir supprimer le tracker sélectionné ?Êtes-vous sûr de vouloir supprimer les %Ln trackers sélectionnés ?Êtes-vous sûr de vouloir supprimer les %Ln trackers sélectionnés ? Down Speed Torrents list column name ---------- Peers list column title Vitesse descendante Up Speed Torrents list column name ---------- Peers list column title Vitesse montante Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Barre de progression Flags Peers list column title Drapeaux Client Peers list column title Client Timed out Server connection status Délai expiré Connection error Server connection status Erreur de connexion Authentication error Server connection status Erreur d'authentification Parse error Server connection status Erreur d'analyse Server is too new Server connection status Le serveur est trop récent Server is too old Server connection status Le serveur est trop vieux Connecting... Server connection status Connexion... Connected Server connection status Connecté Downloading Torrent status Torrent status En téléchargement Seeding Torrent status Torrent status Seeding Queued for checking Torrent status En file d'attente pour vérification Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file Erreur en lisant le fichier torrent Error parsing torrent file Erreur en analysant le fichier torrent Total Size Torrents list column name Taille totale Queue Position Torrents list column name Position dans la file d'attente Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added Ajouté le Completed on Torrents list column name, date/time when torrent was completed Terminé le Down Limit Torrents list column name, download speed limit Limite descendante Up Limit Torrents list column name, upload speed limit Limite montante Remaining Torrents list column name, remaining byte size Restant Download Directory Torrents list column name Répertoire de téléchargement Last Activity Torrents list column name Dernière activité Inactive Tracker status Inactif Waiting for update Tracker status En attente de mise à jour About to update Tracker status Mise à jour imminente Updating Tracker status Mise à jour Next Update Trackers list column title Prochaine mise à jour %L1 B Size suffix in bytes %L1 o %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 Kio %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 Mio %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 Gio %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 Tio %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 Pio %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 Eio %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 Zio %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 Yio %L1 B/s Download speed suffix in bytes per second %L1 o/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 Kio/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 Mio/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 Gio/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 Tio/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 Pio/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 Eio/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 Zio/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 Yio/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 j %L2 h %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 h %L2 m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 m %L2 s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 s Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Erreur en ouvrant %1 Move files from current directory Check box label Déplacer les fichiers du répertoire actuel Selected directory should be inside mounted directory Le répertoire sélectionné doit être à l'intérieur du répertoire monté All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents Tous (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Edit Labels New label... tremotesf-2.10.1/translations/it_IT.ts000066400000000000000000003447701523411710200177170ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Informazioni Source code: Translations: Maintainer Manutentori Contributor Contributore Authors "About" dialog's "Authors" tab title Autori Translators "About" dialog's "Translators" tab title Traduttori License "About" dialog's "License" tab title Licenza Add Torrent File Dialog title Aggiungi file torrent Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Spazio libero: %1 Error getting free space Errore nel calcolo dello spazio libero Files Torrent properties dialog tab File High Torrent's file loading priority ---------- Torrent's loading priority Alta Normal Torrent's file loading priority ---------- Torrent's loading priority Normale Low Torrent's file loading priority ---------- Torrent's loading priority Bassa Start downloading after adding Comincia il download dopo l'aggiunta Add Torrent Link Dialog title Aggiungi torrent tramite collegamento No servers Nessun server Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Disconnesso Downloading Noun "Downloading" server setting page In scaricamento Start added torrents Check box label Inzia i torrent aggiunti Append ".part" to names of incomplete files Check box label Aggiungi ".part" al nome dei file incompleti Rename Dialog title ---------- Dialog confirmation button Rinomina Select Directory Directory chooser dialog title Scegli la cartella Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Stato Directories Title of torrents download directory filters list Cartelle Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Server traccia Priority Column title in torrent's file list ---------- Torrents list column name Priorità Mixed Torrent's file loading priority ---------- File loading priority Misto No torrents Torrents list placeholder Nessun torrent Remove Button ---------- Dialog confirmation button Rimuovi Set Location Dialog title for changing torrent's download directory Scegli la destinazione Error adding torrent Errore nell'aggiunta del torrent This torrent is already added Questo torrent è già stato aggiunto Torrent added Notification title Torrent aggiunto Torrent finished Notification title Torrent completato Network "Network" server settings page Rete Connection Title of settings section related to peer connections Connessione Random port on Transmission start Check box label Porta casuale all'apertura di Transmission Enable port forwarding Check box label Abilita l'apertura delle porte Allow Encryption mode (allow/prefer/require) Permetti Prefer Encryption mode (allow/prefer/require) Preferisci Require Encryption mode (allow/prefer/require) Richiesta Enable DHT Check box label Abilita DHT Peer Limits Limiti del peer Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Peer Queue "Queue" server settings page Coda Also delete the files on the hard disk Check box label Elimina anche i file nel disco Seeding Noun "Seeding" server setting page In seeding Overwrite Dialog's confirmation button Sovrascrivi Server already exists Il server già esiste Add Server Dialog title Aggiungi server Name Column title in torrent's file list ---------- Torrents list column name Nome Address Peers list column title ---------- Trackers list column title Indirizzo Default Default proxy option Predefinito HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Carica da file... Use client certificate authentication Check box label Usa l'autenticazione tramite certificato client Authentication Check box label Autenticazione Auto reconnect on error Check box label Riconnessione automatica in caso di errore Mounted directories Cartelle montate Local directory Column title in the list of mounted directories Cartella locale Remote directory Column title in the list of mounted directories Cartella remota Add Button ---------- Dialog confirmation button Aggiungi Speed "Speed" server settings page ---------- Torrent's limits tab section Velocità Connection Settings Servers list placeholder Dialog title Impostazioni di connessione Edit... Button Modifica... Add Server... Button Aggiungi server... Add... Button Aggiungi Connect to server on startup Check box label Connetti al server all'avvio Adding torrents Options tab Aggiungi torrent Add torrent parameters Reset Show main window when adding torrents Check box label Show dialog when adding torrents Check box label Automatically fill link from clipboard when adding torrent link Check box label Riempi automaticamente il collegamento dagli appunti quando aggiungi un collegamento torrent Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Suggerimento: puoi anche premere %1 nella finestra principale per aggiungere torrent dagli appunti Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog Open torrent's file Open download directory What to do when torrent in the list is double clicked: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Notifiche Notify when disconnecting from server Check box label Notifica quando disconnesso dal server Notify on added torrents Check box label Notifica all'aggiunta dei torrent Notify on finished torrents Check box label Notifica alla fine del torrent When connecting to server Notifications options section Quando connette al server Notify on added torrents since last connection to server Check box label Notifica all'aggiunta dei torrent dall'ultima connessione al serverr Notify on finished torrents since last connection to server Check box label Notifica alla fine dei torrent dall'ultima connessione al server Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Progresso ETA Torrents list column name ETA Server Stats Dialog title Statistiche del server Current session Server stats section for current Transmission launch Sessione corrente Ratio Torrents list column name Rapporto Total Server stats section for all Transmission launches (accumulated) Totale %Ln times How many times Transmission was launched %Ln volta%Ln volte%Ln volte Size Column title in torrent's file list ---------- Torrents list column name Dimensione Limits Speed limits section ---------- Torrent's properties dialog tab Limiti Alternative Limits Alternative speed limits section Limiti alternativi Enable Check box label Abilita Scheduled Title of alternative speed limit scheduling section Schedula to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" a Every day Ogni giorno Weekdays Giorni della settimana (lunedì - venerdì) Weekends Fine settimana (sabato - domenica) %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 di %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Controllo (%L1) Honor global limits Check box label Segui i limiti globali Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Usa impostazioni globali Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Continua il seed ignorando il rapporto Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Ferma il seed al rapporto: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Continua il seed ignorando l'attività Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Ferma il seed se inattivo per: Activity Torrent's details tab section Attività Completed Torrents list column name, completed byte size Completati Downloaded Torrents list column name, downloaded byte size Scaricato Paused (%1) Torrent status while torrent also has an error. %1 is error string In pausa (%1) Paused Torrent status In pausa Downloading (%1) Torrent status while torrent also has an error. %1 is error string Download (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string In seeding (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string In coda (%1) Queued Torrent status In coda Checking (%1) Torrent status while torrent also has an error. %1 is error string Controllo in corso (%1) Checking Torrent status Controllo in corso Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string In coda per il controllo (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Caricamento su peer Uploaded Torrents list column name, uploaded byte size Caricato Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Seed Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Leech Information Torrent's details tab section Informazioni Torrent Removed Message that appears when torrent is removed Torrent rimosso Edit Tracker Dialog title Modifica server traccia Torrent file: Input field's label File torrent: Torrent link: Input field's label Collegamento del torrent: Download directory: Input field's label Cartella di Download Torrent priority: Combo box label Priorità del torrent: Delete .torrent file Elimina file .torrent Move .torrent file to trash Sposta il file .torrent nel cestino Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed Caricamento in corso &Connect Button / menu item to connect to server &Connetti &Disconnect Button / menu item to disconnect from server &Disconnetti &Add Torrent File... Menu item &Aggiungi file torrent Add Torrent &Link... Menu item Aggiungi &link torrent P&ause Torrent's context menu item P&ausa &Delete Torrent's context menu item &Elimina Open &Download Directory Context menu item Delete with files Elimina i file Delete Elimina Delete Torrent Dialog title Elimina Torrent Are you sure you want to delete this torrent? Sicuri di voler eliminare questo torrent? Delete Torrents Dialog title Elimina torrent Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion Sei sicuro di voler eliminare %Ln torrent selezionato?Sei sicuro di voler eliminare %Ln torrent selezionati?Sei sicuro di voler eliminare %Ln torrent selezionati? No torrents matching filters Torrents list placeholder Nessun torrent corrispondente ai filtri &Quit Menu item &Esci &Torrent Menu bar item &Torrent Error adding torrent «%1» &Properties Torrent's context menu item &Proprietà &Show Tremotesf &Hide Tremotesf &Start Torrent's context menu item &Inizia Start &Now Torrent's context menu item Inizia &Ora Copy &Magnet Link Torrent's context menu item Copia collegamento &Magnet &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Rimuovi Set &Location Torrent's context menu item Sceg&li la destinazione Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item A&pri Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item &Controlla i dati scaricati Reanno&unce Torrent's context menu item ---------- Button Riann&uncia &Queue Torrent's context menu item Co&da Move To &Top Torrent's context menu item Sposta all'inizio Move &Up Torrent's context menu item Sposta su Move &Down Torrent's context menu item Sposta giù Move To &Bottom Torrent's context menu item Sposta alla fine &Connection Settings &Impostazioni di connessione &Server Options Opzioni &Server Server S&tats S&tatistiche del server Select Files File chooser dialog title Scegli i file Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged File Torrent (*.torrent) Error Dialog title ---------- Trackers list column title Errore This file/directory does not exist &File Menu bar item &File &Close Window &Edit Menu bar item &Modifica Select &All Scegli Tutto &Invert Selection &Inverti Selezione &View Menu bar item &Vista &Toolbar &Barra degli strumenti &Sidebar Bar&ra Laterale St&atusbar Barra di stato Torrent properties &panel &Lock Toolbar Blocca la barra degli strumenti T&ools Menu bar item S&trumenti &Options &Opzioni S&hutdown Server S&pegnimento del server Shutdown Server Dialog title Spegnimento del server Are you sure you want to shutdown remote Transmission instance? Sei sicuro di voler spegnere l'istanza di trasmissione remota? Shutdown Dialog confirmation button Spegnimento &Help Menu bar item Aiuto &About Menu item opening "About" dialog Informazioni... Icon Only Toolbar mode Solo Icone Text Only Toolbar mode Solo Testo Text Beside Icon Toolbar mode Testo a fianco dell'icona Text Under Icon Toolbar mode Testo sotto l'icona Follow System Style Toolbar mode Utilizza il tema di sistema Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Mostra Tremotesf Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Attivi (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status In scaricamento (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status In seeding (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status In pausa (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status In errore (%L1) Search... Search field placeholder Cerca... &Select... Context menu item to open directory chooser Scegli... Overwrite Server Dialog title Sovrascrivi server Name: Nome: Address: Indirizzo: Port: Porta: API path: Percorso API: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Proxy type: Tipo di proxy: Username: Nome utente: Password: Password: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Intervallo di aggiornamento: Timeout: Timeout: Auto reconnect interval: Intervallo di riconnessione automatica: &Edit... Server's context menu item ---------- Tracker's context menu item &Modifica... Server Options Dialog title Opzioni Server Directory for incomplete files: Cartella dei file incompleti: min Suffix that is added to input field with number of minuts, e.g. "5 min" minuti Maximum active downloads: N° massimo di download attivi: Maximum active uploads: N° massimo di upload attivi: Ignore queue position if idle for: Ignora la coda se è fermo per: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label Scaricato: Upload: Upload speed limit input field label Caricato: Days: Giorni: Peer port: Porta del peer: Encryption: Crittografia: Enable μTP (Micro Transport Protocol) Check box label Abilita μTP (Micro Transport Protocol) Enable PEX (Peer exchange) Check box label Abilita PEX (scambio peer) Enable local peer discovery Check box label Abilita l'individuazione peer locale Maximum peers per torrent: Peer massimi per torrent: Maximum peers globally: Peer massimi globali: Options Dialog title Opzioni Follow system Dark theme mode Segui sistema On Dark theme mode Acceso Off Dark theme mode Spento Dark theme Tema scuro Use system accent color Check box label Usa il colore principale del sistema Remember location of last opened torrent file Check box label Ricorda il percorso dell'ultimo file torrent aperto Remember parameters of last added torrent Check box label Ricorda i parametri dell'ultimo file torrent aggiunto Show icon in the notification area Check box label Mostra l'icona nell'area di notifica &Not Download Context menu item to unselect file for downloading Non Scaricato &Priority Torrent's context menu item Priorità &Download Context menu item to select file for downloading &Download &High File loading priority Alta &Normal File loading priority Normale &Low File loading priority Bassa &Rename Torrent's context menu item ---------- Context menu item Rinomina File name: Nome del file: Details Torrent's properties dialog tab Dettagli Completed: Torrent's completed size Completati: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Scaricati: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Caricati: Ratio: Rapporto: Duration: How much time Transmission is running Durata: Started: How many times Transmission was launched Iniziato il: Free space in download directory: Spazio libero nella directory di download: Download speed: Velocità di scaricamento: Upload speed: Velocità di caricamento: ETA: Tempo di attesa stimato: Seeders: Seeders: Leechers: Leechers: Peers we are downloading from: Peer da cui stiamo scaricando: Web seeders we are downloading from: Web seeder che stiamo scaricando da: Peers we are uploading to: Peer su cui stiamo caricando: Last activity: Ultima attività: Total size: Dimensione totale: Location: Torrent's download directory Posizione: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Creato da: Created on: Date/time when torrent was created Creato il: Comment: Torrent's comment text Commento: Labels: Torrent's labels Web seeder Web seeders list column title Seeder web Web seeders Torrent's properties dialog tab Seeder web Seeding Options section Torrent's limits tab section In seeding Ratio limit mode: Modalità limite rapporto: Idle seeding mode: Modalità seeding quando inutilizzato: Maximum peers: Peer massimi: Add Trackers Dialog title Aggiungi server traccia Trackers announce URLs: URL dei server traccia: Tracker announce URL: URL del server traccia: Remove Tracker Dialog title Rimuovi server traccia Are you sure you want to remove this tracker? Sicuro di voler rimuovere questo server traccia? Remove Trackers Dialog title Rimuovi server traccia Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Sei sicuro di voler rimuovere %Ln tracker selezionato?Sei sicuro di voler rimuovere %Ln tracker selezionati?Sei sicuro di voler rimuovere %Ln tracker selezionati? Down Speed Torrents list column name ---------- Peers list column title Velocità di scaricamento Up Speed Torrents list column name ---------- Peers list column title Velocità di caricamento Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Barra di progressione Flags Peers list column title Opzioni Client Peers list column title Client Timed out Server connection status Tempo esaurito Connection error Server connection status Errore di connessione Authentication error Server connection status Errore di autenticazione Parse error Server connection status Errore di lettura Server is too new Server connection status Il server è troppo recente Server is too old Server connection status Il server è troppo vecchio Connecting... Server connection status Connessione in corso... Connected Server connection status Connesso Downloading Torrent status Torrent status In scaricamento Seeding Torrent status Torrent status In seeding Queued for checking Torrent status In coda per il controllo Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file Errore nella lettura del file torrent Error parsing torrent file Errore nella lettura del file torrent Total Size Torrents list column name Dimensione Totale Queue Position Torrents list column name Posizione in coda Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added Aggiunto il Completed on Torrents list column name, date/time when torrent was completed Completato il Down Limit Torrents list column name, download speed limit Limite di scaricamento Up Limit Torrents list column name, upload speed limit Limite di caricamento Remaining Torrents list column name, remaining byte size Rimanente Download Directory Torrents list column name Cartella di Download Last Activity Torrents list column name Ultima attività Inactive Tracker status Inattivo Waiting for update Tracker status In attesa di aggiornamento About to update Tracker status In procinto di aggiornare Updating Tracker status In aggiornamento Next Update Trackers list column title Prossimo Aggiornamento %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 g %L2 o %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 o %L2 m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 m %L2 s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 s Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Errore nell'apertura di %1 Move files from current directory Check box label Sposta i file dalla cartella corrente Selected directory should be inside mounted directory La cartella selezionata dovrebbe essere all'interno della cartella montata All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents Tutto (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Edit Labels New label... tremotesf-2.10.1/translations/nl.ts000066400000000000000000003411641523411710200173120ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Over Source code: Translations: Maintainer Contributor Authors "About" dialog's "Authors" tab title Auteurs Translators "About" dialog's "Translators" tab title Vertalers License "About" dialog's "License" tab title Licentie Add Torrent File Dialog title Torrentbestand toevoegen Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Vrije ruimte: %1 Error getting free space Fout bij verkrijgen van vrije ruimte Files Torrent properties dialog tab Bestanden High Torrent's file loading priority ---------- Torrent's loading priority Hoog Normal Torrent's file loading priority ---------- Torrent's loading priority Normaal Low Torrent's file loading priority ---------- Torrent's loading priority Laag Start downloading after adding Downloaden starten na toevoegen Add Torrent Link Dialog title Torrentverwijzing toevoegen No servers Geen servers Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Verbinding verbroken Downloading Noun "Downloading" server setting page Downloaden Start added torrents Check box label Toegevoegde torrents starten Append ".part" to names of incomplete files Check box label ".part" toevoegen aan naam van onvolledige bestanden Rename Dialog title ---------- Dialog confirmation button Hernoemen Select Directory Directory chooser dialog title Selecteer map Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Status Directories Title of torrents download directory filters list Mappen Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Trackers Priority Column title in torrent's file list ---------- Torrents list column name Prioriteit Mixed Torrent's file loading priority ---------- File loading priority Gemengd No torrents Torrents list placeholder Geen torrents Remove Button ---------- Dialog confirmation button Verwijderen Set Location Dialog title for changing torrent's download directory Locatie instellen Error adding torrent Fout bij toevoegen van torrent This torrent is already added Deze torrent is al toegevoegd Torrent added Notification title Torrent toegevoegd Torrent finished Notification title Torrent voltooid Network "Network" server settings page Netwerk Connection Title of settings section related to peer connections Verbinding Random port on Transmission start Check box label Willekeurige poort bij opstarten van Transmission Enable port forwarding Check box label Poort-doorsturen inschakelen Allow Encryption mode (allow/prefer/require) Toestaan Prefer Encryption mode (allow/prefer/require) Verkiezen Require Encryption mode (allow/prefer/require) Vereisen Enable DHT Check box label DHT inschakelen Peer Limits Peerlimieten Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Peers Queue "Queue" server settings page Wachtrij Also delete the files on the hard disk Check box label Bestanden op harde schijf ook verwijderen Seeding Noun "Seeding" server setting page Seeden Overwrite Dialog's confirmation button Overschrijven Server already exists Server bestaat al Add Server Dialog title Server toevoegen Name Column title in torrent's file list ---------- Torrents list column name Naam Address Peers list column title ---------- Trackers list column title Adres Default Default proxy option Standaard HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Use client certificate authentication Check box label Cliëntcertificaatauthenticatie gebruiken Authentication Check box label Authenticatie Auto reconnect on error Check box label Mounted directories Aangekoppelde mappen Local directory Column title in the list of mounted directories Lokale map Remote directory Column title in the list of mounted directories Externe map Add Button ---------- Dialog confirmation button Toevoegen Speed "Speed" server settings page ---------- Torrent's limits tab section Snelheid Connection Settings Servers list placeholder Dialog title Edit... Button Bewerken... Add Server... Button Add... Button Toevoegen... Connect to server on startup Check box label Verbinden met server bij opstarten Adding torrents Options tab Add torrent parameters Reset Show main window when adding torrents Check box label Show dialog when adding torrents Check box label Automatically fill link from clipboard when adding torrent link Check box label Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog Open torrent's file Open download directory What to do when torrent in the list is double clicked: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Meldingen Notify when disconnecting from server Check box label Melden bij verbreken van verbinding met server Notify on added torrents Check box label Melden bij toegevoegde torrents Notify on finished torrents Check box label Melden bij voltooide torrents When connecting to server Notifications options section Bij verbinden met server Notify on added torrents since last connection to server Check box label Melden bij toegevoegde torrents sinds laatste verbinding met server Notify on finished torrents since last connection to server Check box label Melden bij voltooide torrents sinds laatste verbinding met server Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Voortgang ETA Torrents list column name Resterende tijd Server Stats Dialog title Serverstatistieken Current session Server stats section for current Transmission launch Huidige sessie Ratio Torrents list column name Verhouding Total Server stats section for all Transmission launches (accumulated) Totaal %Ln times How many times Transmission was launched %Ln keer%Ln keer Size Column title in torrent's file list ---------- Torrents list column name Grootte Limits Speed limits section ---------- Torrent's properties dialog tab Limieten Alternative Limits Alternative speed limits section Alternatieve limieten Enable Check box label Inschakelen Scheduled Title of alternative speed limit scheduling section Gepland to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" tot Every day Elke dag Weekdays Weekdagen Weekends Week-ends %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 van %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Controleren (%L1) Honor global limits Check box label Algemene limieten aanhouden Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Algemene instellingen gebruiken Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seeden ongeacht verhouding Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seeden stoppen bij verhouding: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Seeden ongeacht activiteit Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Seeden stoppen indien niet-actief voor: Activity Torrent's details tab section Activiteit Completed Torrents list column name, completed byte size Voltooid Downloaded Torrents list column name, downloaded byte size Gedownload Paused (%1) Torrent status while torrent also has an error. %1 is error string Paused Torrent status Downloading (%1) Torrent status while torrent also has an error. %1 is error string Seeding (%1) Torrent status while torrent also has an error. %1 is error string Queued (%1) Torrent status while torrent also has an error. %1 is error string Queued Torrent status Checking (%1) Torrent status while torrent also has an error. %1 is error string Checking Torrent status Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string Downloading to peers Torrents list column name, number of peers that we are downloading from Uploading to peers Torrents list column name, number of peers that we are uploading to Uploaded Torrents list column name, uploaded byte size Geüpload Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Seeders Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Leechers Information Torrent's details tab section Informatie Torrent Removed Message that appears when torrent is removed Torrent verwijderd Edit Tracker Dialog title Tracker bewerken Torrent file: Input field's label Torrentbestand: Torrent link: Input field's label Torrentverwijzing: Download directory: Input field's label Downloadmap: Torrent priority: Combo box label Torrentprioriteit: Delete .torrent file Move .torrent file to trash Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed &Connect Button / menu item to connect to server &Verbinden &Disconnect Button / menu item to disconnect from server Verbin&ding verbreken &Add Torrent File... Menu item Torrentbest&and toevoegen... Add Torrent &Link... Menu item Torrentver&wijzing toevoegen... P&ause Torrent's context menu item P&auzeren &Delete Torrent's context menu item Open &Download Directory Context menu item Delete with files Delete Delete Torrent Dialog title Are you sure you want to delete this torrent? Delete Torrents Dialog title Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion No torrents matching filters Torrents list placeholder &Quit Menu item Af&sluiten &Torrent Menu bar item &Torrent Error adding torrent «%1» &Properties Torrent's context menu item &Eigenschappen &Show Tremotesf &Hide Tremotesf &Start Torrent's context menu item &Starten Start &Now Torrent's context menu item &Nu starten Copy &Magnet Link Torrent's context menu item &Magnetlink kopiëren &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item Ve&rwijderen Set &Location Torrent's context menu item &Locatie instellen Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item &Openen Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item Lokale gegevens &controleren Reanno&unce Torrent's context menu item ---------- Button Opnie&uw aankondigen &Queue Torrent's context menu item &Wachtrij Move To &Top Torrent's context menu item Verplaatsen naar &boven Move &Up Torrent's context menu item &Omhoog verplaatsen Move &Down Torrent's context menu item &Omlaag verplaatsen Move To &Bottom Torrent's context menu item Verplaatsen naar &beneden &Connection Settings &Server Options &Serveropties Server S&tats Servers&tatistieken Select Files File chooser dialog title Selecteer bestanden Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Torrentbestanden (*.torrent) Error Dialog title ---------- Trackers list column title Fout This file/directory does not exist &File Menu bar item &Bestand &Close Window &Edit Menu bar item B&ewerken Select &All &Alles selecteren &Invert Selection Select&ie omkeren &View Menu bar item Weerga&ve &Toolbar &Werkbalk &Sidebar &Zijbalk St&atusbar St&atusbalk Torrent properties &panel &Lock Toolbar T&ools Menu bar item &Gereedschap &Options &Opties S&hutdown Server Shutdown Server Dialog title Are you sure you want to shutdown remote Transmission instance? Shutdown Dialog confirmation button &Help Menu bar item &Hulp &About Menu item opening "About" dialog &Info Icon Only Toolbar mode Enkel pictogram Text Only Toolbar mode Enkel tekst Text Beside Icon Toolbar mode Tekst naast pictogram Text Under Icon Toolbar mode Tekst onder pictogram Follow System Style Toolbar mode Systeemstijl volgen Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Tremotesf tonen Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Actief (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Downloaden (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Seeden (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Gepauzeerd (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Fouten (%L1) Search... Search field placeholder Zoeken... &Select... Context menu item to open directory chooser &Selecteren… Overwrite Server Dialog title Server overschrijven Name: Naam: Address: Adres: Port: Poort: API path: API-pad: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Proxy type: Proxytype: Username: Gebruikersnaam: Password: Wachtwoord: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Update-interval: Timeout: Time-out: Auto reconnect interval: &Edit... Server's context menu item ---------- Tracker's context menu item B&ewerken... Server Options Dialog title Serveropties Directory for incomplete files: Map voor onvolledige bestanden: min Suffix that is added to input field with number of minuts, e.g. "5 min" min Maximum active downloads: Maximaal aantal actieve downloads: Maximum active uploads: Maximaal aantal actieve uploads: Ignore queue position if idle for: Wachtrijpositie negeren indien niet-actief voor: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label Download: Upload: Upload speed limit input field label Upload: Days: Dagen: Peer port: Peerpoort: Encryption: Versleuteling: Enable μTP (Micro Transport Protocol) Check box label Enable PEX (Peer exchange) Check box label Enable local peer discovery Check box label Maximum peers per torrent: Maximaal aantal peers per torrent: Maximum peers globally: Algemeen maximaal aantal peers: Options Dialog title Opties Follow system Dark theme mode On Dark theme mode Off Dark theme mode Dark theme Use system accent color Check box label Remember location of last opened torrent file Check box label Remember parameters of last added torrent Check box label Show icon in the notification area Check box label Pictogram tonen in meldingsgebied &Not Download Context menu item to unselect file for downloading &Niet downloaden &Priority Torrent's context menu item &Prioriteit &Download Context menu item to select file for downloading &High File loading priority &Hoog &Normal File loading priority &Normaal &Low File loading priority &Laag &Rename Torrent's context menu item ---------- Context menu item He&rnoemen File name: Bestandsnaam: Details Torrent's properties dialog tab Details Completed: Torrent's completed size Voltooid: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Gedownload: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Geüpload: Ratio: Verhouding: Duration: How much time Transmission is running Duur: Started: How many times Transmission was launched Gestart: Free space in download directory: Download speed: Downloadsnelheid: Upload speed: Uploadsnelheid: ETA: Resterende tijd: Seeders: Seeders: Leechers: Leechers: Peers we are downloading from: Web seeders we are downloading from: Peers we are uploading to: Last activity: Laatste activiteit: Total size: Totale grootte: Location: Torrent's download directory Locatie: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Aangemaakt door: Created on: Date/time when torrent was created Aangemaakt op: Comment: Torrent's comment text Commentaar: Labels: Torrent's labels Web seeder Web seeders list column title Web seeders Torrent's properties dialog tab Seeding Options section Torrent's limits tab section Ratio limit mode: Verhoudingslimietmodus: Idle seeding mode: Modus niet-actief seeden: Maximum peers: Maximaal aantal peers: Add Trackers Dialog title Trackers toevoegen Trackers announce URLs: Trackeraankondigings-URL’s: Tracker announce URL: Tracker-aankondigings-URL: Remove Tracker Dialog title Tracker verwijderen Are you sure you want to remove this tracker? Weet je zeker dat je deze tracker wil verwijderen? Remove Trackers Dialog title Trackers verwijderen Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Down Speed Torrents list column name ---------- Peers list column title Downloadsnelheid Up Speed Torrents list column name ---------- Peers list column title Uploadsnelheid Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Voortgangsbalk Flags Peers list column title Vlaggen Client Peers list column title Cliënt Timed out Server connection status Time-out Connection error Server connection status Verbindingsfout Authentication error Server connection status Authenticatiefout Parse error Server connection status Verwerkingsfout Server is too new Server connection status Server is te recent Server is too old Server connection status Server is te oud Connecting... Server connection status Verbinden... Connected Server connection status Verbonden Downloading Torrent status Torrent status Downloaden Seeding Torrent status Torrent status Seeden Queued for checking Torrent status In wachtrij voor controle Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file Fout bij lezen van torrentbestand Error parsing torrent file Fout bij verwerken van torrentbestand Total Size Torrents list column name Totale grootte Queue Position Torrents list column name Positie in wachtrij Added on Torrents list column name, date/time when torrent was added Toegevoegd op Completed on Torrents list column name, date/time when torrent was completed Voltooid op Down Limit Torrents list column name, download speed limit Downloadlimiet Up Limit Torrents list column name, upload speed limit Uploadlimiet Remaining Torrents list column name, remaining byte size Resterend Download Directory Torrents list column name Downloadmap Last Activity Torrents list column name Laatste activiteit Inactive Tracker status Waiting for update Tracker status About to update Tracker status Updating Tracker status Next Update Trackers list column title Volgende update %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1d, %L2u %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1u, %L2m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1m, %L2s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1s Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Fout bij openen van %1 Move files from current directory Check box label Bestanden verplaatsen uit huidige map Selected directory should be inside mounted directory Geselecteerde map moet in aangekoppelde map zijn All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Edit Labels New label... tremotesf-2.10.1/translations/nl_BE.ts000066400000000000000000003411551523411710200176600ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Over Source code: Translations: Maintainer Contributor Authors "About" dialog's "Authors" tab title Auteurs Translators "About" dialog's "Translators" tab title Vertalers License "About" dialog's "License" tab title Licentie Add Torrent File Dialog title Torrentbestand toevoegen Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Vrije ruimte: %1 Error getting free space Fout bij verkrijgen van vrije ruimte Files Torrent properties dialog tab Bestanden High Torrent's file loading priority ---------- Torrent's loading priority Hoog Normal Torrent's file loading priority ---------- Torrent's loading priority Normaal Low Torrent's file loading priority ---------- Torrent's loading priority Laag Start downloading after adding Downloaden starten na toevoegen Add Torrent Link Dialog title Torrentkoppeling toevoegen No servers Geen servers Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Verbinding verbroken Downloading Noun "Downloading" server setting page Downloaden Start added torrents Check box label Toegevoegde torrents starten Append ".part" to names of incomplete files Check box label ‘.part’ toevoegen aan naam van onvolledige bestanden Rename Dialog title ---------- Dialog confirmation button Hernoemen Select Directory Directory chooser dialog title Selecteert een map Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Status Directories Title of torrents download directory filters list Mappen Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Trackers Priority Column title in torrent's file list ---------- Torrents list column name Prioriteit Mixed Torrent's file loading priority ---------- File loading priority Gemengd No torrents Torrents list placeholder Geen torrents Remove Button ---------- Dialog confirmation button Verwijderen Set Location Dialog title for changing torrent's download directory Locatie instellen Error adding torrent Fout bij toevoegen van torrent This torrent is already added Deze torrent is al toegevoegd Torrent added Notification title Torrent toegevoegd Torrent finished Notification title Torrent voltooid Network "Network" server settings page Netwerk Connection Title of settings section related to peer connections Verbinding Random port on Transmission start Check box label Willekeurige poort bij opstarten van Transmission Enable port forwarding Check box label Poort-doorsturen inschakelen Allow Encryption mode (allow/prefer/require) Toelaten Prefer Encryption mode (allow/prefer/require) Verkiezen Require Encryption mode (allow/prefer/require) Vereisen Enable DHT Check box label DHT inschakelen Peer Limits Peerlimieten Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Peers Queue "Queue" server settings page Wachtrij Also delete the files on the hard disk Check box label Bestanden op de harde schijf ook verwijderen Seeding Noun "Seeding" server setting page Seeden Overwrite Dialog's confirmation button Overschrijven Server already exists Server bestaat al Add Server Dialog title Server toevoegen Name Column title in torrent's file list ---------- Torrents list column name Naam Address Peers list column title ---------- Trackers list column title Adres Default Default proxy option Standaard HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Use client certificate authentication Check box label Cliëntcertificaatauthenticatie gebruiken Authentication Check box label Authenticatie Auto reconnect on error Check box label Mounted directories Aangekoppelde mappen Local directory Column title in the list of mounted directories Lokale map Remote directory Column title in the list of mounted directories Externe map Add Button ---------- Dialog confirmation button Toevoegen Speed "Speed" server settings page ---------- Torrent's limits tab section Snelheid Connection Settings Servers list placeholder Dialog title Edit... Button Bewerken... Add Server... Button Add... Button Toevoegen... Connect to server on startup Check box label Verbinden met server bij opstarten Adding torrents Options tab Add torrent parameters Reset Show main window when adding torrents Check box label Show dialog when adding torrents Check box label Automatically fill link from clipboard when adding torrent link Check box label Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog Open torrent's file Open download directory What to do when torrent in the list is double clicked: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Meldingen Notify when disconnecting from server Check box label Melden bij verbreken van verbinding met server Notify on added torrents Check box label Melden bij toegevoegde torrents Notify on finished torrents Check box label Melden bij voltooide torrents When connecting to server Notifications options section Bij verbinden met server Notify on added torrents since last connection to server Check box label Melden bij toegevoegde torrents sinds laatste verbinding met server Notify on finished torrents since last connection to server Check box label Melden bij voltooide torrents sinds laatste verbinding met server Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Voortgang ETA Torrents list column name Resterende tijd Server Stats Dialog title Serverstatistieken Current session Server stats section for current Transmission launch Huidige sessie Ratio Torrents list column name Verhouding Total Server stats section for all Transmission launches (accumulated) Totaal %Ln times How many times Transmission was launched %Ln keer%Ln keer Size Column title in torrent's file list ---------- Torrents list column name Grootte Limits Speed limits section ---------- Torrent's properties dialog tab Limieten Alternative Limits Alternative speed limits section Alternatieve limieten Enable Check box label Inschakelen Scheduled Title of alternative speed limit scheduling section Gepland to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" tot Every day Elke dag Weekdays Weekdagen Weekends Week-ends %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 van %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Controleren (%L1) Honor global limits Check box label Algemene limieten aanhouden Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Algemene instellingen gebruiken Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seeden ongeacht verhouding Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seeden stoppen bij verhouding: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Seeden ongeacht activiteit Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Seeden stoppen indien inactief voor: Activity Torrent's details tab section Activiteit Completed Torrents list column name, completed byte size Voltooid Downloaded Torrents list column name, downloaded byte size Gedownload Paused (%1) Torrent status while torrent also has an error. %1 is error string Paused Torrent status Downloading (%1) Torrent status while torrent also has an error. %1 is error string Seeding (%1) Torrent status while torrent also has an error. %1 is error string Queued (%1) Torrent status while torrent also has an error. %1 is error string Queued Torrent status Checking (%1) Torrent status while torrent also has an error. %1 is error string Checking Torrent status Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string Downloading to peers Torrents list column name, number of peers that we are downloading from Uploading to peers Torrents list column name, number of peers that we are uploading to Uploaded Torrents list column name, uploaded byte size Geüpload Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Seeders Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Leechers Information Torrent's details tab section Informatie Torrent Removed Message that appears when torrent is removed Torrent verwijderd Edit Tracker Dialog title Tracker bewerken Torrent file: Input field's label Torrentbestand: Torrent link: Input field's label Torrentverwijzing: Download directory: Input field's label Downloadmap: Torrent priority: Combo box label Torrentprioriteit: Delete .torrent file Move .torrent file to trash Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed &Connect Button / menu item to connect to server &Verbinden &Disconnect Button / menu item to disconnect from server Verbin&ding verbreken &Add Torrent File... Menu item Torrentbest&and toevoegen... Add Torrent &Link... Menu item Torrentkoppe&ling toevoegen... P&ause Torrent's context menu item P&auzeren &Delete Torrent's context menu item Open &Download Directory Context menu item Delete with files Delete Delete Torrent Dialog title Are you sure you want to delete this torrent? Delete Torrents Dialog title Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion No torrents matching filters Torrents list placeholder &Quit Menu item Af&sluiten &Torrent Menu bar item &Torrent Error adding torrent «%1» &Properties Torrent's context menu item &Eigenschappen &Show Tremotesf &Hide Tremotesf &Start Torrent's context menu item &Starten Start &Now Torrent's context menu item &Nu starten Copy &Magnet Link Torrent's context menu item &Magnetlink kopiëren &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item Ve&rwijderen Set &Location Torrent's context menu item &Locatie instellen Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item &Openen Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item Lokale gegevens &controleren Reanno&unce Torrent's context menu item ---------- Button Ter&ug aankondigen &Queue Torrent's context menu item &Wachtrij Move To &Top Torrent's context menu item Verplaatsen naar &boven Move &Up Torrent's context menu item &Omhoog verplaatsen Move &Down Torrent's context menu item &Omlaag verplaatsen Move To &Bottom Torrent's context menu item Verplaatsen naar &beneden &Connection Settings &Server Options &Serveropties Server S&tats Servers&tatistieken Select Files File chooser dialog title Selecteert bestanden Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Torrentbestanden (*.torrent) Error Dialog title ---------- Trackers list column title Fout This file/directory does not exist &File Menu bar item &Bestand &Close Window &Edit Menu bar item B&ewerken Select &All &Alles selecteren &Invert Selection Select&ie omkeren &View Menu bar item Weerga&ve &Toolbar &Werkbalk &Sidebar &Zijbalk St&atusbar St&atusbalk Torrent properties &panel &Lock Toolbar T&ools Menu bar item &Gereedschap &Options &Opties S&hutdown Server Shutdown Server Dialog title Are you sure you want to shutdown remote Transmission instance? Shutdown Dialog confirmation button &Help Menu bar item &Hulp &About Menu item opening "About" dialog &Info Icon Only Toolbar mode Enkel pictogram Text Only Toolbar mode Enkel tekst Text Beside Icon Toolbar mode Tekst naast pictogram Text Under Icon Toolbar mode Tekst onder pictogram Follow System Style Toolbar mode Systeemstijl volgen Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Tremotesf tonen Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Actief (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Downloaden (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Seeden (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Gepauzeerd (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Fouten (%L1) Search... Search field placeholder Zoeken... &Select... Context menu item to open directory chooser &Selecteren… Overwrite Server Dialog title Server overschrijven Name: Naam: Address: Adres: Port: Poort: API path: API-pad: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Proxy type: Proxytype Username: Gebruikersnaam: Password: Paswoord: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Update-interval: Timeout: Time-out: Auto reconnect interval: &Edit... Server's context menu item ---------- Tracker's context menu item B&ewerken... Server Options Dialog title Serveropties Directory for incomplete files: Map voor onvolledige bestanden: min Suffix that is added to input field with number of minuts, e.g. "5 min" min Maximum active downloads: Maximaal aantal actieve downloads: Maximum active uploads: Maximaal aantal actieve uploads: Ignore queue position if idle for: Wachtrijpositie negeren indien inactief voor: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label Download: Upload: Upload speed limit input field label Upload: Days: Dagen: Peer port: Peerpoort: Encryption: Versleuteling: Enable μTP (Micro Transport Protocol) Check box label Enable PEX (Peer exchange) Check box label Enable local peer discovery Check box label Maximum peers per torrent: Maximaal aantal peers per torrent: Maximum peers globally: Algemeen maximaal aantal peers: Options Dialog title Opties Follow system Dark theme mode On Dark theme mode Off Dark theme mode Dark theme Use system accent color Check box label Remember location of last opened torrent file Check box label Remember parameters of last added torrent Check box label Show icon in the notification area Check box label Pictogram tonen in meldingsgebied &Not Download Context menu item to unselect file for downloading &Niet downloaden &Priority Torrent's context menu item &Prioriteit &Download Context menu item to select file for downloading &High File loading priority &Hoog &Normal File loading priority &Normaal &Low File loading priority &Laag &Rename Torrent's context menu item ---------- Context menu item He&rnoemen File name: Bestandsnaam: Details Torrent's properties dialog tab Details Completed: Torrent's completed size Voltooid: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Gedownload: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Geüpload: Ratio: Verhouding: Duration: How much time Transmission is running Duur: Started: How many times Transmission was launched Gestart: Free space in download directory: Download speed: Downloadsnelheid: Upload speed: Uploadsnelheid: ETA: Resterende tijd: Seeders: Seeders: Leechers: Leechers: Peers we are downloading from: Web seeders we are downloading from: Peers we are uploading to: Last activity: Laatste activiteit: Total size: Totale grootte: Location: Torrent's download directory Locatie: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Aangemaakt door: Created on: Date/time when torrent was created Aangemaakt op: Comment: Torrent's comment text Commentaar: Labels: Torrent's labels Web seeder Web seeders list column title Web seeders Torrent's properties dialog tab Seeding Options section Torrent's limits tab section Ratio limit mode: Verhoudingslimietmodus: Idle seeding mode: Modus inactief seeden: Maximum peers: Maximaal aantal peers: Add Trackers Dialog title Trackers toevoegen Trackers announce URLs: Trackeraankondigings-URL’s: Tracker announce URL: Tracker-aankondigings-URL: Remove Tracker Dialog title Tracker verwijderen Are you sure you want to remove this tracker? Zijt ge zeker dat ge dezen tracker wilt verwijderen? Remove Trackers Dialog title Trackers verwijderen Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Down Speed Torrents list column name ---------- Peers list column title Downloadsnelheid Up Speed Torrents list column name ---------- Peers list column title Uploadsnelheid Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Voortgangsbalk Flags Peers list column title Vlaggen Client Peers list column title Cliënt Timed out Server connection status Time-out Connection error Server connection status Verbindingsfout Authentication error Server connection status Authenticatiefout Parse error Server connection status Verwerkingsfout Server is too new Server connection status Server is te recent Server is too old Server connection status Server is te oud Connecting... Server connection status Verbinden... Connected Server connection status Verbonden Downloading Torrent status Torrent status Downloaden Seeding Torrent status Torrent status Seeden Queued for checking Torrent status In wachtrij voor controle Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file Fout bij lezen van torrentbestand Error parsing torrent file Fout bij verwerken van torrentbestand Total Size Torrents list column name Totale grootte Queue Position Torrents list column name Positie in wachtrij Added on Torrents list column name, date/time when torrent was added Toegevoegd op Completed on Torrents list column name, date/time when torrent was completed Voltooid op Down Limit Torrents list column name, download speed limit Downloadlimiet Up Limit Torrents list column name, upload speed limit Uploadlimiet Remaining Torrents list column name, remaining byte size Resterend Download Directory Torrents list column name Downloadmap Last Activity Torrents list column name Laatste activiteit Inactive Tracker status Waiting for update Tracker status About to update Tracker status Updating Tracker status Next Update Trackers list column title Volgenden update %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1d, %L2u %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1u, %L2m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1m, %L2s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1s Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Fout bij openen van %1 Move files from current directory Check box label Bestanden verplaatsen uit huidige map Selected directory should be inside mounted directory Geselecteerde map moet in aangekoppelde map zijn All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Edit Labels New label... tremotesf-2.10.1/translations/pl.ts000066400000000000000000003477121523411710200173210ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title O aplikacji Source code: Translations: Maintainer Opiekun projektu Contributor Kontrybutor Authors "About" dialog's "Authors" tab title Autorzy Translators "About" dialog's "Translators" tab title Tłumaczenie License "About" dialog's "License" tab title Licencja Add Torrent File Dialog title Dodaj plik torrent Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Wolne miejsce: %1 Error getting free space Błąd podczas uzyskiwania wolnego miejsca Files Torrent properties dialog tab Pliki High Torrent's file loading priority ---------- Torrent's loading priority Wysoki Normal Torrent's file loading priority ---------- Torrent's loading priority Normalny Low Torrent's file loading priority ---------- Torrent's loading priority Niski Start downloading after adding Rozpocznij pobieranie po dodaniu Add Torrent Link Dialog title Dodaj torrent linkiem No servers Brak serwerów Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Odłączony Downloading Noun "Downloading" server setting page Pobieranie Start added torrents Check box label Uruchom dodane torrenty Append ".part" to names of incomplete files Check box label Dołącz ".part" do nazw niekompletnych plików Rename Dialog title ---------- Dialog confirmation button Zmiana nazwy Select Directory Directory chooser dialog title Wybierz katalog Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Stan Directories Title of torrents download directory filters list Katalogi Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Trackery Priority Column title in torrent's file list ---------- Torrents list column name Priorytet Mixed Torrent's file loading priority ---------- File loading priority Mieszany No torrents Torrents list placeholder Brak torrentów Remove Button ---------- Dialog confirmation button Usuń Set Location Dialog title for changing torrent's download directory Ustaw lokalizację Error adding torrent Błąd podczas dodawania torrenta This torrent is already added Ten torrent jest już dodany Torrent added Notification title Torrent został dodany Torrent finished Notification title Torrent został ukończony Network "Network" server settings page Sieć Connection Title of settings section related to peer connections Połączenia Random port on Transmission start Check box label Losowy port po uruchomieniu Transmission Enable port forwarding Check box label Włącz przekierowanie portu Allow Encryption mode (allow/prefer/require) Pozwól Prefer Encryption mode (allow/prefer/require) Preferuj Require Encryption mode (allow/prefer/require) Wymagaj Enable DHT Check box label Włącz DHT Peer Limits Limity połączeń z peerami Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Peerów Queue "Queue" server settings page Kolejka Also delete the files on the hard disk Check box label Usuń także pliki z dysku twardego Seeding Noun "Seeding" server setting page Seedowanie Overwrite Dialog's confirmation button Nadpisz Server already exists Serwer już istnieje Add Server Dialog title Dodaj serwer Name Column title in torrent's file list ---------- Torrents list column name Nazwa Address Peers list column title ---------- Trackers list column title Adres Default Default proxy option Domyślny HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Załaduj z pliku... Use client certificate authentication Check box label Użyj uwierzytelnienia certyfikatu klienta Authentication Check box label Uwierzytelnienie Auto reconnect on error Check box label Połącz ponownie w przypadku błędu Mounted directories Zamontowane katalogi Local directory Column title in the list of mounted directories Katalog lokalny Remote directory Column title in the list of mounted directories Katalog zdalny Add Button ---------- Dialog confirmation button Dodaj Speed "Speed" server settings page ---------- Torrent's limits tab section Prędkość Connection Settings Servers list placeholder Dialog title Konfiguracja połączeń Edit... Button Edytuj... Add Server... Button Dodaj serwer... Add... Button Dodawanie... Connect to server on startup Check box label Połącz z serwerem po uruchomieniu Adding torrents Options tab Dodawanie torrentów Add torrent parameters Parametry dodawanego torrenta Reset Zresetuj Show main window when adding torrents Check box label Pokaż główne okno podczas dodawania torrentów Show dialog when adding torrents Check box label Pokaż okno dialogowe podczas dodawania torrentów. Automatically fill link from clipboard when adding torrent link Check box label Automatycznie wypełnij link ze schowka podczas dodawania linku do torrenta Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Wskazówka: możesz także nacisnąć %1 w głównym oknie, żeby dodać torrenty ze schowka Ask for merging trackers when adding existing torrent Check box label Pytaj o scalenie trackerów przy dodawaniu istniejącego torrenta Merge trackers when adding existing torrent Check box label Scal trackery przy dodawaniu istniejącego torrenta Open properties dialog Otwórz okno właściwości Open torrent's file Otwórz plik torrenta Open download directory Otwórz katalog docelowy What to do when torrent in the list is double clicked: Co zrobić, kiedy na liście torrent zostanie kliknięty dwukrotnie: General Options tab Ogólne Show torrent properties in a panel in the main window Check box label Pokazuj właściwości torrentów w panelu w głównym oknie Properties dialog won't be shown because torrent properties are shown in the main window Okno właściwości nie otworzy się, ponieważ właściwości torrenta są wyświetlane w głównym oknie Display relative time Check box label Pokazuj czas względny Display full path of download directories in sidebar and torrents list Check box label Wyświetlaj pełne ścieżki katalogów w pasku bocznym i liście torrentów Notifications Options tab Powiadomienia Notify when disconnecting from server Check box label Powiadom o odłączeniu od serwera Notify on added torrents Check box label Powiadom o dodanych torrentach Notify on finished torrents Check box label Powiadom o ukończonych torrentach When connecting to server Notifications options section Podczas łączenia się z serwerem Notify on added torrents since last connection to server Check box label Powiadamiaj o dodanych torrentach od ostatniego połączenia z serwerem Notify on finished torrents since last connection to server Check box label Powiadamiaj o ukończonych torrentach od ostatniego połączenia z serwerem Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Postęp ETA Torrents list column name ETA Server Stats Dialog title Statystyki serwera Current session Server stats section for current Transmission launch Obecna sesja Ratio Torrents list column name Ratio Total Server stats section for all Transmission launches (accumulated) �ącznie %Ln times How many times Transmission was launched %Ln raz%Ln razy%Ln razy%Ln razy Size Column title in torrent's file list ---------- Torrents list column name Rozmiar Limits Speed limits section ---------- Torrent's properties dialog tab Limity Alternative Limits Alternative speed limits section Alternatywne limity Enable Check box label Włącz Scheduled Title of alternative speed limit scheduling section Zgodnie z harmonogramem to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" do Every day Codziennie Weekdays Dni powszednie Weekends Weekendy %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 z %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Sprawdzanie (%L1) Honor global limits Check box label Przestrzegaj globalnych limitów Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Użyj ustawień globalnych Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seeduj niezależnie od ratio Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Zakończ seedowanie po osiągnięciu ratio: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Seeduj niezależnie od aktywności Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Zakończ seedowanie w przypadku bezczynności przez: Activity Torrent's details tab section Działania Completed Torrents list column name, completed byte size Zakończone Downloaded Torrents list column name, downloaded byte size Pobrano Paused (%1) Torrent status while torrent also has an error. %1 is error string Wstrzymano (%1) Paused Torrent status Wstrzymano Downloading (%1) Torrent status while torrent also has an error. %1 is error string Pobieranie (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string Seedowanie (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string W kolejce (%1) Queued Torrent status W kolejce Checking (%1) Torrent status while torrent also has an error. %1 is error string Sprawdzanie (%1) Checking Torrent status Sprawdzanie Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string W kolejce do sprawdzenia (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Wysyłanie do peerów Uploaded Torrents list column name, uploaded byte size Przesłano Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Seedów Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Leechów Information Torrent's details tab section Informacje Torrent Removed Message that appears when torrent is removed Torrent został usunięty Edit Tracker Dialog title Edytowanie trackera Torrent file: Input field's label Plik torrent: Torrent link: Input field's label Link do torrenta: Download directory: Input field's label Katalog docelowy: Torrent priority: Combo box label Priorytet torrenta: Delete .torrent file Usuń plik .torrent Move .torrent file to trash Przenieś plik .torrent do kosza Labels Title of torrents label filters list Etykiety Loading Placeholder shown when torrent file is being read/parsed Wczytywanie &Connect Button / menu item to connect to server &Połącz &Disconnect Button / menu item to disconnect from server &Rozłącz &Add Torrent File... Menu item &Dodaj plik torrent... Add Torrent &Link... Menu item Dodaj torrent &linkiem P&ause Torrent's context menu item Z&atrzymaj &Delete Torrent's context menu item &Usuń Open &Download Directory Context menu item Otwórz katalog &docelowy Delete with files Usuń z plikami Delete Usuń Delete Torrent Dialog title Usuń Torrent Are you sure you want to delete this torrent? Czy na pewno chcesz usunąć ten torrent? Delete Torrents Dialog title Usuń Torrenty Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion Jesteś pewien, że chcesz usunąć zaznaczony torrent?Jesteś pewien, że chcesz usunąć %Ln zaznaczone torrenty?Jesteś pewien, że chcesz usunąć %Ln zaznaczonych torrentów?Jesteś pewien, że chcesz usunąć %Ln zaznaczonych torrentów? No torrents matching filters Torrents list placeholder Brak torrentów odpowiadających kryteriom wyszukiwania &Quit Menu item &Wyjdź &Torrent Menu bar item &Torrent Error adding torrent «%1» Wystąpił błąd przy dodawaniu torrenta «%1» &Properties Torrent's context menu item &Właściwości &Show Tremotesf &Pokaż Tremotesf &Hide Tremotesf &Ukryj Tremotesf &Start Torrent's context menu item &Uruchom Start &Now Torrent's context menu item Uruchom &Teraz Copy &Magnet Link Torrent's context menu item Skopiuj link &magnet &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Usuń Set &Location Torrent's context menu item Ustaw &Lokalizację Edi&t Labels Torrent's context menu item Zmień e&tykiety &Open Torrent's context menu item ---------- Context menu item &Otwórz Op&en Download Directory Torrent's context menu item Ot&wórz katalog docelowy &Check Local Data Torrent's context menu item &Sprawdź dane lokalne Reanno&unce Torrent's context menu item ---------- Button Rozgłoś ponownie &Queue Torrent's context menu item &Kolejka Move To &Top Torrent's context menu item Przenieś na &początek Move &Up Torrent's context menu item Przenieś &wyżej Move &Down Torrent's context menu item Przenieś &niżej Move To &Bottom Torrent's context menu item Przenieś na &koniec &Connection Settings &Konfiguracja połączeń &Server Options &Opcje serwera Server S&tats S&tatystyki serwera Select Files File chooser dialog title Wybierz pliki Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Pliki torrent (*.torrent) Error Dialog title ---------- Trackers list column title Błąd This file/directory does not exist Ten plik/katalog nie istnieje &File Menu bar item &Plik &Close Window &Zamknij okno &Edit Menu bar item &Edycja Select &All Zaznacz &Wszystko &Invert Selection &Odwróć zaznaczenie &View Menu bar item &Widok &Toolbar &Pasek narzędzi &Sidebar &Pasek boczny St&atusbar Pasek st&anu Torrent properties &panel &Panel właściwości torrentów &Lock Toolbar &Zablokuj pasek narzędzi T&ools Menu bar item &Narzędzia &Options &Opcje S&hutdown Server Z&amknij serwer Shutdown Server Dialog title Zamykanie serwera Are you sure you want to shutdown remote Transmission instance? Czy na pewno chcesz zamknąć zdalną instancję Transmission? Shutdown Dialog confirmation button Zamknij &Help Menu bar item &Pomoc &About Menu item opening "About" dialog &O programie Icon Only Toolbar mode Tylko ikony Text Only Toolbar mode Tylko tekst Text Beside Icon Toolbar mode Tekst obok ikon Text Under Icon Toolbar mode Tekst pod ikonami Follow System Style Toolbar mode Dopasuj do stylu systemu Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Torrenty zostaną dodane po nawiązaniu połączenia z serwerem Show Tremotesf Button on notification Pokaż Tremotesf Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Aktywne (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Pobierane (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Seedowane (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Zatrzymane (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Błąd (%L1) Search... Search field placeholder Wyszukaj... &Select... Context menu item to open directory chooser &Wybieranie... Overwrite Server Dialog title Zastępowanie serwera Name: Nazwa: Address: Adres: Port: Port: API path: Ścieżka API: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Brak Proxy type: Typ proxy: Username: Nazwa Użytkownika: Password: Hasło: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" s Update interval: Interwał aktualizacji: Timeout: Limit czasu: Auto reconnect interval: Interwał pomiędzy ponownym połączeniem: &Edit... Server's context menu item ---------- Tracker's context menu item &Edycja... Server Options Dialog title Opcje serwera Directory for incomplete files: Katalog niekompletnych plików: min Suffix that is added to input field with number of minuts, e.g. "5 min" min Maximum active downloads: Maksymalna liczba aktywnych pozycji pobieranych: Maximum active uploads: Maksymalna liczba aktywnych pozycji wysyłanych: Ignore queue position if idle for: Zignoruj ​​pozycję w kolejce, jeśli jest bezczynna przez: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label Pobieranie: Upload: Upload speed limit input field label Wysyłanie: Days: Dni: Peer port: Peer port: Encryption: Szyfrowanie: Enable μTP (Micro Transport Protocol) Check box label Włącz μTP (Micro Transport Protocol) Enable PEX (Peer exchange) Check box label Włącz PEX (Peer exchange) Enable local peer discovery Check box label Włącz protokół lokalnego odnajdywania peerów (Local Peer Discovery) Maximum peers per torrent: Maksymalna ilość połączonych peerów na torrent: Maximum peers globally: Maksymalna ilość połączonych peerów globalnie: Options Dialog title Opcje Follow system Dark theme mode Dopasuj do systemu On Dark theme mode Włączony Off Dark theme mode Wyłączony Dark theme Ciemny motyw Use system accent color Check box label Użyj systemowego koloru wiodącego Remember location of last opened torrent file Check box label Zapamiętuje lokalizację ostatnio otwartego pliku torrent Remember parameters of last added torrent Check box label Zapamiętaj parametry z ostatniego dodanego torrenta Show icon in the notification area Check box label Pokaż ikonę w obszarze powiadomień &Not Download Context menu item to unselect file for downloading &Nie pobieraj &Priority Torrent's context menu item &Priorytet &Download Context menu item to select file for downloading &Pobierz &High File loading priority &Wysoki &Normal File loading priority &Normalny &Low File loading priority &Niski &Rename Torrent's context menu item ---------- Context menu item &Zmień nazwę File name: Nazwa pliku: Details Torrent's properties dialog tab Szczegóły Completed: Torrent's completed size Zakończono: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Pobrano: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Przesłano: Ratio: Ratio: Duration: How much time Transmission is running Czas działania: Started: How many times Transmission was launched Uruchomiono: Free space in download directory: Wolnego miejsca w katalogu docelowym: Download speed: Prędkość pobierania: Upload speed: Prędkość wysyłania: ETA: ETA: Seeders: Seedów: Leechers: Leechów: Peers we are downloading from: Połączonych peerów od których pobieramy: Web seeders we are downloading from: Web seedów od których pobieramy: Peers we are uploading to: Połączonych peerów do których wysyłamy: Last activity: Ostatnia aktywność: Total size: Całkowity rozmiar: Location: Torrent's download directory Lokalizacja: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Stworzone przez: Created on: Date/time when torrent was created Utworzono: Comment: Torrent's comment text Komentarz: Labels: Torrent's labels Etykiety: Web seeder Web seeders list column title Web seed Web seeders Torrent's properties dialog tab Web seedy Seeding Options section Torrent's limits tab section Seedowanie Ratio limit mode: Tryb limitu ratio: Idle seeding mode: Tryb bezczynnego seedowania: Maximum peers: Maksymalna ilość połączonych peerów: Add Trackers Dialog title Dodaj trackery Trackers announce URLs: Adresy URL announce trackerów: Tracker announce URL: Adres URL announce trackera: Remove Tracker Dialog title Usuwanie trackera Are you sure you want to remove this tracker? Jesteś pewien, że chcesz usunąć ten tracker? Remove Trackers Dialog title Usuwanie trackerów Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Jesteś pewien, że chcesz usunąć zaznaczony tracker?Jesteś pewien, że chcesz usunąć %Ln zaznaczone trackery?Jesteś pewien, że chcesz usunąć %Ln zaznaczonych trackerów?Jesteś pewien, że chcesz usunąć %Ln zaznaczonych trackerów? Down Speed Torrents list column name ---------- Peers list column title Prędkość pobierania Up Speed Torrents list column name ---------- Peers list column title Prędkość wysyłania Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Pasek postępu Flags Peers list column title Flagi Client Peers list column title Klient Timed out Server connection status Przekroczono limit czasu Connection error Server connection status Błąd połączenia Authentication error Server connection status Błąd uwierzytelniania Parse error Server connection status Błąd przetwarzania Server is too new Server connection status Serwer jest zbyt nowy Server is too old Server connection status Serwer jest za stary Connecting... Server connection status Trwa łączenie... Connected Server connection status Połączony Downloading Torrent status Torrent status Pobieranie Seeding Torrent status Torrent status Seedowanie Queued for checking Torrent status W kolejce do sprawdzenia Merge trackers? Dialog title Czy scalić trackery? Torrent «%1» is already added, merge trackers? Torrent «%1» był już dodany, czy scalić trackery? Merge Scal Do not ask again Nie pytaj ponownie Merged trackers Dialog title Scalono trackery Merged trackers for torrent «%1» Scalono trackery dla torrenta «%1» Torrent already exists Dialog title Ten torrent już istnieje Torrent «%1» already exists Torrent «%1» już istnieje Error reading torrent file Błąd odczytu pliku torrent Error parsing torrent file Błąd podczas analizowania pliku torrent Total Size Torrents list column name Całkowity rozmiar Queue Position Torrents list column name Pozycja w kolejce Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added Dodano Completed on Torrents list column name, date/time when torrent was completed Ukończono Down Limit Torrents list column name, download speed limit Limit prędkości pobierania Up Limit Torrents list column name, upload speed limit Limit prędkości wysyłania Remaining Torrents list column name, remaining byte size Pozostało Download Directory Torrents list column name Katalog docelowy Last Activity Torrents list column name Ostatnia aktywność Inactive Tracker status Bezczynny Waiting for update Tracker status Oczekiwanie na aktualizację About to update Tracker status Niebawem aktualizacja Updating Tracker status W trakcie aktualizacji Next Update Trackers list column title Następna aktualizacja %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 d %L2 godz. %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 godz. %L2 m %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 m %L2 s %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 s Today Dzisiaj Yesterday Wczoraj Two days ago Dwa dni temu %1 at %2 Relative date & time %1 o %2 Just now Relative time Przed chwilą %n minute(s) ago @item:intext %1 is a whole number %n minutę temu%n minuty temu%n minut temu%n minut(y) temu %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Błąd otwierania %1 Move files from current directory Check box label Przenieś pliki z bieżącego katalogu Selected directory should be inside mounted directory Wybrany katalog powinien znajdować się w katalogu zamontowanym All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents Wszystkie (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Ten katalog nie istnieje Edit Labels Zmień Etykiety New label... Nowa etykieta... tremotesf-2.10.1/translations/ru.ts000066400000000000000000003644641523411710200173370ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title О программе Source code: И�ходный код: Translations: Переводы: Maintainer Сопровождающий Contributor Уча�тник Authors "About" dialog's "Authors" tab title �вторы Translators "About" dialog's "Translators" tab title Переводчики License "About" dialog's "License" tab title Лицензи� Add Torrent File Dialog title Добавить торрент-файл Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Свободно: %1 Error getting free space Ошибка определени� �вободного ме�та Files Torrent properties dialog tab Файлы High Torrent's file loading priority ---------- Torrent's loading priority Вы�окий Normal Torrent's file loading priority ---------- Torrent's loading priority �ормальный Low Torrent's file loading priority ---------- Torrent's loading priority �изкий Start downloading after adding �ачать загрузку по�ле добавлени� Add Torrent Link Dialog title Добавить ��ылку на торрент No servers �ет �ерверов Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Отключено Downloading Noun "Downloading" server setting page Загрузка Start added torrents Check box label Запу�кать добавленные торренты Append ".part" to names of incomplete files Check box label Добавл�ть ".part" к именам незавершённых файлов Rename Dialog title ---------- Dialog confirmation button Переименовать Select Directory Directory chooser dialog title Выбрать каталог Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Стату� Directories Title of torrents download directory filters list Каталоги Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Трекеры Priority Column title in torrent's file list ---------- Torrents list column name Приоритет Mixed Torrent's file loading priority ---------- File loading priority Смешанный No torrents Torrents list placeholder �ет торрентов Remove Button ---------- Dialog confirmation button Удалить Set Location Dialog title for changing torrent's download directory Переме�тить Error adding torrent Ошибка добавлени� торрента This torrent is already added Этот торрент уже добавлен Torrent added Notification title Торрент добавлен Torrent finished Notification title Торрент завершен Network "Network" server settings page Сеть Connection Title of settings section related to peer connections Соединение Random port on Transmission start Check box label Случайный порт при запу�ке Transmission Enable port forwarding Check box label И�пользовать перенаправление портов Allow Encryption mode (allow/prefer/require) Разрешить Prefer Encryption mode (allow/prefer/require) Предпочитать Require Encryption mode (allow/prefer/require) Требовать Enable DHT Check box label И�пользовать DHT Peer Limits Ограничени� пиров Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Пиры Queue "Queue" server settings page Очередь Also delete the files on the hard disk Check box label Также удалить файлы на же�тком ди�ке Seeding Noun "Seeding" server setting page Раздача Overwrite Dialog's confirmation button Перезапи�ать Server already exists Сервер уже �уще�твует Add Server Dialog title Добавить �ервер Name Column title in torrent's file list ---------- Torrents list column name �азвание Address Peers list column title ---------- Trackers list column title �дре� Default Default proxy option По умолчанию HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Загрузить из файла... Use client certificate authentication Check box label �утентификаци� � помощью клиент�кого �ертификата Authentication Check box label �утентификаци� Auto reconnect on error Check box label �втоматиче�кое переподключение при разрыве �оединени� Mounted directories Подключенные каталоги Local directory Column title in the list of mounted directories Локальный каталог Remote directory Column title in the list of mounted directories Удаленный каталог Add Button ---------- Dialog confirmation button Добавить Speed "Speed" server settings page ---------- Torrent's limits tab section Скоро�ть Connection Settings Servers list placeholder Dialog title �а�тройки подключени� Edit... Button Редактировать... Add Server... Button Добавить �ервер... Add... Button Добавить... Connect to server on startup Check box label Подключать�� к �ерверу при запу�ке Adding torrents Options tab Добавление торрентов Add torrent parameters Параметры добавлени� торрентов Reset Сбро�ить Show main window when adding torrents Check box label Показывать главное окно при добавлении торрентов Show dialog when adding torrents Check box label Показывать диалог добавлени� торрента Automatically fill link from clipboard when adding torrent link Check box label �втоматиче�ки копировать ��ылку из буфера обмена при добавлении ��ылки на торрент Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Совет: также можно нажать %1 в главном окне чтобы добавить торрент из буфера обмена Ask for merging trackers when adding existing torrent Check box label Спрашивать об объединении трекеров при добавлении �уще�твующего торрента Merge trackers when adding existing torrent Check box label Объедин�ть трекеры при добавлении �уще�твующего торрента Open properties dialog Показать �вой�тва торрента Open torrent's file Открыть файл торрента Open download directory Открыть каталог загрузки What to do when torrent in the list is double clicked: Что делать при нажатии на торрент в �пи�ке: General Options tab Общие Show torrent properties in a panel in the main window Check box label Показывать �вой�тва торрента в панели главного окна Properties dialog won't be shown because torrent properties are shown in the main window Диалог �вой�тв торрента не будет показан т.к. �вой�тва торрента отображают�� в главном окне Display relative time Check box label Отображать отно�ительное врем� Display full path of download directories in sidebar and torrents list Check box label Отображать полный путь каталогов загрузки в боковой панели и �пи�ке торрентов Notifications Options tab Уведомлени� Notify when disconnecting from server Check box label Уведомл�ть при отключении от �ервера Notify on added torrents Check box label Уведомл�ть при добавлении торрентов Notify on finished torrents Check box label Уведомл�ть при завершении торрентов When connecting to server Notifications options section При подключении к �ерверу Notify on added torrents since last connection to server Check box label Уведомл�ть о добавленных торрентах �о времени по�леднего подключени� к �ерверу Notify on finished torrents since last connection to server Check box label Уведомл�ть о завершенных торрентах �о времени по�леднего подключени� к �ерверу Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Прогре�� ETA Torrents list column name ETA Server Stats Dialog title Стати�тика �ервера Current session Server stats section for current Transmission launch Текуща� �е��и� Ratio Torrents list column name Рейтинг Total Server stats section for all Transmission launches (accumulated) В�его %Ln times How many times Transmission was launched %Ln раз%Ln раза%Ln раз%Ln раз Size Column title in torrent's file list ---------- Torrents list column name Размер Limits Speed limits section ---------- Torrent's properties dialog tab Ограничени� Alternative Limits Alternative speed limits section �льтернативные ограничени� Enable Check box label Включить Scheduled Title of alternative speed limit scheduling section По ра�пи�анию to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" до Every day Каждый день Weekdays По рабочим дн�м Weekends По выходным %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 из %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Провер�ющие�� (%L1) Honor global limits Check box label Учитывать глобальные ограничени� Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) И�пользовать глобальные на�тройки Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Раздавать незави�имо от рейтинга Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Прекратить раздачу при рейтинге: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Раздавать незави�имо от активно�ти Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Прекратить раздачу при про�тое: Activity Torrent's details tab section �ктивно�ть Completed Torrents list column name, completed byte size Завершено Downloaded Torrents list column name, downloaded byte size Загружено Paused (%1) Torrent status while torrent also has an error. %1 is error string Прио�тановлен (%1) Paused Torrent status Прио�тановлен Downloading (%1) Torrent status while torrent also has an error. %1 is error string Загружает�� (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string Раздает�� (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string В очереди (%1) Queued Torrent status В очереди Checking (%1) Torrent status while torrent also has an error. %1 is error string Провер�ет�� (%1) Checking Torrent status Провер�ет�� Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string В очереди на проверку (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Отдает�� пирам Uploaded Torrents list column name, uploaded byte size Отдано Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Сиды Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Личи Information Torrent's details tab section Информаци� Torrent Removed Message that appears when torrent is removed Торрент удален Edit Tracker Dialog title Редактировать трекер Torrent file: Input field's label Торрент-файл: Torrent link: Input field's label С�ылка на торрент: Download directory: Input field's label Каталог загрузки: Torrent priority: Combo box label Приоритет торрента: Delete .torrent file Удалить .torrent файл Move .torrent file to trash Переме�тить .torrent файл в корзину Labels Title of torrents label filters list Метки Loading Placeholder shown when torrent file is being read/parsed Загрузка &Connect Button / menu item to connect to server &Подключить�� &Disconnect Button / menu item to disconnect from server &Отключить�� &Add Torrent File... Menu item &Добавить торрент-файл... Add Torrent &Link... Menu item Добавить &��ылку на торрент... P&ause Torrent's context menu item &Прио�тановить &Delete Torrent's context menu item &Удалить Open &Download Directory Context menu item Открыть &каталог загрузки Delete with files Удалить вме�те � файлами Delete Удалить Delete Torrent Dialog title Удалить торрент Are you sure you want to delete this torrent? Вы уверены, что хотите удалить �тот торрент? Delete Torrents Dialog title Удалить торренты Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion Вы уверены, что хотите удалить %Ln выбранный торрент?Вы уверены, что хотите удалить %Ln выбранных торрента?Вы уверены, что хотите удалить %Ln выбранных торрентов?Вы уверены, что хотите удалить %Ln выбранных торрентов? No torrents matching filters Torrents list placeholder �ет торрентов �оответ�твующих фильтрам &Quit Menu item &Выход &Torrent Menu bar item &Торрент Error adding torrent «%1» Ошибка добавлени� торрента «%1» &Properties Torrent's context menu item &Свой�тва &Show Tremotesf &Показать Tremotesf &Hide Tremotesf &Скрыть Tremotesf &Start Torrent's context menu item &Запу�тить Start &Now Torrent's context menu item Запу�&тить �ейча� Copy &Magnet Link Torrent's context menu item &Копировать magnet-��ылку &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Удалить Set &Location Torrent's context menu item П&ереме�тить Edi&t Labels Torrent's context menu item Редактировать &метки &Open Torrent's context menu item ---------- Context menu item &Открыть Op&en Download Directory Torrent's context menu item Открыть &каталог загрузки &Check Local Data Torrent's context menu item Проверить &локальные данные Reanno&unce Torrent's context menu item ---------- Button Пере&анон�ировать &Queue Torrent's context menu item О&чередь Move To &Top Torrent's context menu item Сдвинуть в &начало Move &Up Torrent's context menu item Сдвинуть &вверх Move &Down Torrent's context menu item Сдвинуть вн&из Move To &Bottom Torrent's context menu item Сдвинуть в &конец &Connection Settings �а�тройки &подключени� &Server Options �&а�тройки �ервера Server S&tats С&тати�тика �ервера Select Files File chooser dialog title Выбрать файлы Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Торрент-файлы (*.torrent) Error Dialog title ---------- Trackers list column title Ошибка This file/directory does not exist Этого файла/каталога не �уще�твует &File Menu bar item &Файл &Close Window &Закрыть окно &Edit Menu bar item &Правка Select &All &Выбрать в�е &Invert Selection &Инвертировать выделение &View Menu bar item &Вид &Toolbar &Панель ин�трументов &Sidebar &Бокова� панель St&atusbar Панель &�тату�а Torrent properties &panel Панель �вой�тв &торрента &Lock Toolbar &Закрепить панель ин�трументов T&ools Menu bar item &Ин�трументы &Options &�а�тройки S&hutdown Server &Завершить �ервер Shutdown Server Dialog title Завершить �ервер Are you sure you want to shutdown remote Transmission instance? Вы уверены что хотите завершить удаленный �ервер Transmission? Shutdown Dialog confirmation button Завершить &Help Menu bar item &Справка &About Menu item opening "About" dialog &О программе Icon Only Toolbar mode Только значки Text Only Toolbar mode Только тек�т Text Beside Icon Toolbar mode Тек�т р�дом зо значком Text Under Icon Toolbar mode Тек�т под значком Follow System Style Toolbar mode И�пользовать �тиль �и�темы Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Торренты будут добавлены по�ле подключени� к �ерверу Show Tremotesf Button on notification Показать Tremotesf Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status �ктивные (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Загрузки (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Раздачи (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Прио�тановленные (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status С ошибкой (%L1) Search... Search field placeholder Пои�к... &Select... Context menu item to open directory chooser &Выбрать Overwrite Server Dialog title Перезапи�ать �ервер Name: �азвание: Address: �дре�: Port: Порт: API path: Путь API: Proxy Прок�и HTTP HTTP proxy option HTTP None None proxy option Без прок�и Proxy type: Тип прок�и: Username: Им� пользовател�: Password: Пароль: No Server does not use custom certificates �ет Self-signed certificate Само подпи�анный �ертификат Custom CA root certificate �е доверенный корневой CA �ертификат Server uses custom certificates: Сервер и�пользует не доверенный �ертификат: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass В �лучае е�ли �ервер и�пользует конечный �ертификат � неправильно на�троенным доменным именем он тоже должен быть указан Server's leaf certificate in PEM format: Конечный �ертификат �ервера в формате PEM: Server's CA root certificate in PEM format: Корневой CA �ертификат �ервера в формате PEM: Client certificate with private key in PEM format: Сертификат клиента � приватным ключом в формате PEM: s Suffix that is added to input field with number of seconds, e.g. "30 s" � Update interval: Интервал обновлени�: Timeout: Таймаут: Auto reconnect interval: Интервал автоматиче�кого переподключени�: &Edit... Server's context menu item ---------- Tracker's context menu item &Редактировать... Server Options Dialog title �а�тройки �ервера Directory for incomplete files: Каталог дл� незавершенных файлов: min Suffix that is added to input field with number of minuts, e.g. "5 min" мин Maximum active downloads: Мак�имальное количе�тво активных загрузок: Maximum active uploads: Мак�имальное количе�тво активных раздач: Ignore queue position if idle for: Игнорировать позицию в очереди при про�тое: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes кБ/� Download: Download speed limit input field label Загрузка: Upload: Upload speed limit input field label Отдача: Days: Дни: Peer port: Порт дл� вход�щих �оединений: Encryption: Шифрование: Enable μTP (Micro Transport Protocol) Check box label И�пользовать μTP (Micro Transport Protocol) Enable PEX (Peer exchange) Check box label И�пользовать PEX (Peer exchange) Enable local peer discovery Check box label И�пользовать обнаружение пиров в локальной �ети Maximum peers per torrent: Мак�имальное количе�тво пиров на торрент: Maximum peers globally: Общее мак�имальное количе�тво пиров: Options Dialog title �а�тройки Follow system Dark theme mode Как в �и�теме On Dark theme mode Включено Off Dark theme mode Отключено Dark theme Темна� тема Use system accent color Check box label И�пользовать цвета �и�темы Remember location of last opened torrent file Check box label Запоминать ра�положение по�леднего открытого торрент-файла Remember parameters of last added torrent Check box label Запоминать параметры по�леднего добавленного торрента Show icon in the notification area Check box label Показывать значок в обла�ти уведомлений &Not Download Context menu item to unselect file for downloading &�е загружать &Priority Torrent's context menu item &Приоритет &Download Context menu item to select file for downloading &Загружать &High File loading priority &Вы�окий &Normal File loading priority &�ормальный &Low File loading priority &�изкий &Rename Torrent's context menu item ---------- Context menu item П&ереименовать File name: Им� файла: Details Torrent's properties dialog tab Сведени� Completed: Torrent's completed size Завершено: Downloaded: Downloaded bytes ---------- Torrent's downloaded size Загружено: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Отдано: Ratio: Рейтинг: Duration: How much time Transmission is running Продолжительно�ть: Started: How many times Transmission was launched Запущен: Free space in download directory: Свободное ме�то в каталоге загрузки: Download speed: Скоро�ть загрузки: Upload speed: Скоро�ть отдачи: ETA: ETA: Seeders: Сиды: Leechers: Личи: Peers we are downloading from: Пиры � которых мы загружаем: Web seeders we are downloading from: Веб-�иды � которых мы загружаем: Peers we are uploading to: Пиры которым мы отдаем: Last activity: По�ледн�� активно�ть: Total size: Общий размер: Location: Torrent's download directory Ра�положение: Hash: Torrent's hash string Х�ш: Created by: Program that created torrent file Создан в: Created on: Date/time when torrent was created Дата �оздани�: Comment: Torrent's comment text Комментарий: Labels: Torrent's labels Метки: Web seeder Web seeders list column title Веб-�ид Web seeders Torrent's properties dialog tab Веб-�иды Seeding Options section Torrent's limits tab section Раздача Ratio limit mode: Ограничение рейтинга: Idle seeding mode: Раздача при про�тое: Maximum peers: Мак�имальное количе�тво пиров: Add Trackers Dialog title Добавить трекеры Trackers announce URLs: �дре�а объ�влений трекеров: Tracker announce URL: �дре� объ�влений трекера: Remove Tracker Dialog title Удалить трекер Are you sure you want to remove this tracker? Вы уверены, что хотите удалить �тот трекер? Remove Trackers Dialog title Удалить трекеры Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Вы уверены, что хотите удалить %Ln выбранный трекер?Вы уверены, что хотите удалить %Ln выбранных трекера?Вы уверены, что хотите удалить %Ln выбранных трекеров?Вы уверены, что хотите удалить %Ln выбранных трекеров? Down Speed Torrents list column name ---------- Peers list column title Загрузка Up Speed Torrents list column name ---------- Peers list column title Отдача Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Индикатор прогре��а Flags Peers list column title Флаги Client Peers list column title Клиент Timed out Server connection status Врем� �оединени� и�текло Connection error Server connection status Ошибка �оединени� Authentication error Server connection status Ошибка аутентификации Parse error Server connection status Ошибка разбора ответа от �ервера Server is too new Server connection status Сервер �лишком новый Server is too old Server connection status Сервер �лишком �тарый Connecting... Server connection status Подключение... Connected Server connection status Подключено Downloading Torrent status Torrent status Загружает�� Seeding Torrent status Torrent status Раздает�� Queued for checking Torrent status В очереди на проверку Merge trackers? Dialog title Объединить трекеры? Torrent «%1» is already added, merge trackers? Торрент «%1» уже добавлен, объединить трекеры? Merge Объединить Do not ask again Больше не �прашивать Merged trackers Dialog title Трекеры объединены Merged trackers for torrent «%1» Объединены трекеры дл� торрента «%1» Torrent already exists Dialog title Торрент уже добавлен Torrent «%1» already exists Торрент «%1» уже добавлен Error reading torrent file Ошибка чтени� торрент-файла Error parsing torrent file Ошибка разбора торрент-файла Total Size Torrents list column name Общий размер Queue Position Torrents list column name Позици� в очереди Downloading from peers Torrents list column name, number of peers that we are downloading from Загружает�� � пиров Added on Torrents list column name, date/time when torrent was added Добавлен Completed on Torrents list column name, date/time when torrent was completed Завершен Down Limit Torrents list column name, download speed limit Огр. загрузки Up Limit Torrents list column name, upload speed limit Огр. отдачи Remaining Torrents list column name, remaining byte size О�тало�ь Download Directory Torrents list column name Каталог загрузки Last Activity Torrents list column name По�ледн�� активно�ть Inactive Tracker status �еактивен Waiting for update Tracker status В ожидании обновлени� About to update Tracker status В очереди на обновление Updating Tracker status Обновл�ет�� Next Update Trackers list column title Следующее обновление %L1 B Size suffix in bytes %L1 Б %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 КиБ %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 МиБ %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 ГиБ %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 ТиБ %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 ПиБ %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 ЭиБ %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ЗиБ %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 ЙиБ %L1 B/s Download speed suffix in bytes per second %L1 Б/� %L1 KiB/s Download speed suffix in kibibytes per second %L1 КиБ/� %L1 MiB/s Download speed suffix in mebibytes per second %L1 МиБ/� %L1 GiB/s Download speed suffix in gibibytes per second %L1 ГиБ/� %L1 TiB/s Download speed suffix in tebibytes per second %L1 ТиБ/� %L1 PiB/s Download speed suffix in pebibytes per second %L1 ПиБ/� %L1 EiB/s Download speed suffix in exbibytes per second %L1 ЭиБ/� %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ЗиБ/� %L1 YiB/s Download speed suffix in yobibytes per second %L1 ЙиБ/� %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 д %L2 ч %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 ч %L2 м %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 м %L2 � %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 � Today Сегодн� Yesterday Вчера Two days ago Позавчера %1 at %2 Relative date & time %1 в %2 Just now Relative time Только что %n minute(s) ago @item:intext %1 is a whole number %n минуту назад%n минуты назад%n минут назад%n минут назад %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Ошибка открыти� %1 Move files from current directory Check box label Переме�тить файлы из текущего каталога Selected directory should be inside mounted directory Выбранный каталог должен быть внутри подключенного каталога All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents В�е (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Этого каталога не �уще�твует Edit Labels Редактировать метки New label... �ова� метка... tremotesf-2.10.1/translations/source.ts000066400000000000000000003465031523411710200202030ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Source code: Translations: Maintainer Contributor Authors "About" dialog's "Authors" tab title Translators "About" dialog's "Translators" tab title License "About" dialog's "License" tab title Add Torrent File Dialog title Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Error getting free space Files Torrent properties dialog tab High Torrent's file loading priority ---------- Torrent's loading priority Normal Torrent's file loading priority ---------- Torrent's loading priority Low Torrent's file loading priority ---------- Torrent's loading priority Start downloading after adding Add Torrent Link Dialog title No servers Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Downloading Noun "Downloading" server setting page Start added torrents Check box label Append ".part" to names of incomplete files Check box label Rename Dialog title ---------- Dialog confirmation button Select Directory Directory chooser dialog title Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Directories Title of torrents download directory filters list Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Priority Column title in torrent's file list ---------- Torrents list column name Mixed Torrent's file loading priority ---------- File loading priority No torrents Torrents list placeholder Remove Button ---------- Dialog confirmation button Set Location Dialog title for changing torrent's download directory Error adding torrent This torrent is already added Torrent added Notification title Torrent finished Notification title Network "Network" server settings page Connection Title of settings section related to peer connections Random port on Transmission start Check box label Enable port forwarding Check box label Allow Encryption mode (allow/prefer/require) Prefer Encryption mode (allow/prefer/require) Require Encryption mode (allow/prefer/require) Enable DHT Check box label Peer Limits Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Queue "Queue" server settings page Also delete the files on the hard disk Check box label Seeding Noun "Seeding" server setting page Overwrite Dialog's confirmation button Server already exists Add Server Dialog title Name Column title in torrent's file list ---------- Torrents list column name Address Peers list column title ---------- Trackers list column title Default Default proxy option HTTPS SOCKS5 SOCKS5 proxy option Load from file... Button Use client certificate authentication Check box label Authentication Check box label Auto reconnect on error Check box label Mounted directories Local directory Column title in the list of mounted directories Remote directory Column title in the list of mounted directories Add Button ---------- Dialog confirmation button Speed "Speed" server settings page ---------- Torrent's limits tab section Connection Settings Servers list placeholder Dialog title Edit... Button Add Server... Button Add... Button Connect to server on startup Check box label Adding torrents Options tab Add torrent parameters Reset Show main window when adding torrents Check box label Show dialog when adding torrents Check box label Automatically fill link from clipboard when adding torrent link Check box label Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog Open torrent's file Open download directory What to do when torrent in the list is double clicked: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab Notify when disconnecting from server Check box label Notify on added torrents Check box label Notify on finished torrents Check box label When connecting to server Notifications options section Notify on added torrents since last connection to server Check box label Notify on finished torrents since last connection to server Check box label Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title ETA Torrents list column name Server Stats Dialog title Current session Server stats section for current Transmission launch Ratio Torrents list column name Total Server stats section for all Transmission launches (accumulated) %Ln times How many times Transmission was launched Size Column title in torrent's file list ---------- Torrents list column name Limits Speed limits section ---------- Torrent's properties dialog tab Alternative Limits Alternative speed limits section Enable Check box label Scheduled Title of alternative speed limit scheduling section to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" Every day Weekdays Weekends %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Honor global limits Check box label Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Activity Torrent's details tab section Completed Torrents list column name, completed byte size Downloaded Torrents list column name, downloaded byte size Paused (%1) Torrent status while torrent also has an error. %1 is error string Paused Torrent status Downloading (%1) Torrent status while torrent also has an error. %1 is error string Seeding (%1) Torrent status while torrent also has an error. %1 is error string Queued (%1) Torrent status while torrent also has an error. %1 is error string Queued Torrent status Checking (%1) Torrent status while torrent also has an error. %1 is error string Checking Torrent status Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string Uploading to peers Torrents list column name, number of peers that we are uploading to Uploaded Torrents list column name, uploaded byte size Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title Information Torrent's details tab section Torrent Removed Message that appears when torrent is removed Edit Tracker Dialog title Torrent file: Input field's label Torrent link: Input field's label Download directory: Input field's label Torrent priority: Combo box label Delete .torrent file Move .torrent file to trash Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed &Connect Button / menu item to connect to server &Disconnect Button / menu item to disconnect from server &Add Torrent File... Menu item Add Torrent &Link... Menu item P&ause Torrent's context menu item &Delete Torrent's context menu item Open &Download Directory Context menu item Delete with files Delete Delete Torrent Dialog title Are you sure you want to delete this torrent? Delete Torrents Dialog title Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion No torrents matching filters Torrents list placeholder &Quit Menu item &Torrent Menu bar item Error adding torrent «%1» &Properties Torrent's context menu item &Show Tremotesf &Hide Tremotesf &Start Torrent's context menu item Start &Now Torrent's context menu item Copy &Magnet Link Torrent's context menu item &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item Set &Location Torrent's context menu item Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item Reanno&unce Torrent's context menu item ---------- Button &Queue Torrent's context menu item Move To &Top Torrent's context menu item Move &Up Torrent's context menu item Move &Down Torrent's context menu item Move To &Bottom Torrent's context menu item &Connection Settings &Server Options Server S&tats Select Files File chooser dialog title Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Error Dialog title ---------- Trackers list column title This file/directory does not exist &File Menu bar item &Close Window &Edit Menu bar item Select &All &Invert Selection &View Menu bar item &Toolbar &Sidebar St&atusbar Torrent properties &panel &Lock Toolbar T&ools Menu bar item &Options S&hutdown Server Shutdown Server Dialog title Are you sure you want to shutdown remote Transmission instance? Shutdown Dialog confirmation button &Help Menu bar item &About Menu item opening "About" dialog Icon Only Toolbar mode Text Only Toolbar mode Text Beside Icon Toolbar mode Text Under Icon Toolbar mode Follow System Style Toolbar mode Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Search... Search field placeholder &Select... Context menu item to open directory chooser Overwrite Server Dialog title Name: Address: Port: API path: Proxy HTTP HTTP proxy option None None proxy option Proxy type: Username: Password: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" Update interval: Timeout: Auto reconnect interval: &Edit... Server's context menu item ---------- Tracker's context menu item Server Options Dialog title Directory for incomplete files: min Suffix that is added to input field with number of minuts, e.g. "5 min" Maximum active downloads: Maximum active uploads: Ignore queue position if idle for: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes Download: Download speed limit input field label Upload: Upload speed limit input field label Days: Peer port: Encryption: Enable μTP (Micro Transport Protocol) Check box label Enable PEX (Peer exchange) Check box label Enable local peer discovery Check box label Maximum peers per torrent: Maximum peers globally: Options Dialog title Follow system Dark theme mode On Dark theme mode Off Dark theme mode Dark theme Use system accent color Check box label Remember location of last opened torrent file Check box label Remember parameters of last added torrent Check box label Show icon in the notification area Check box label &Not Download Context menu item to unselect file for downloading &Priority Torrent's context menu item &Download Context menu item to select file for downloading &High File loading priority &Normal File loading priority &Low File loading priority &Rename Torrent's context menu item ---------- Context menu item File name: Details Torrent's properties dialog tab Completed: Torrent's completed size Downloaded: Downloaded bytes ---------- Torrent's downloaded size Uploaded: Uploaded bytes ---------- Torrent's uploaded size Ratio: Duration: How much time Transmission is running Started: How many times Transmission was launched Free space in download directory: Download speed: Upload speed: ETA: Seeders: Leechers: Peers we are downloading from: Web seeders we are downloading from: Peers we are uploading to: Last activity: Total size: Location: Torrent's download directory Hash: Torrent's hash string Created by: Program that created torrent file Created on: Date/time when torrent was created Comment: Torrent's comment text Labels: Torrent's labels Web seeder Web seeders list column title Web seeders Torrent's properties dialog tab Seeding Options section Torrent's limits tab section Ratio limit mode: Idle seeding mode: Maximum peers: Add Trackers Dialog title Trackers announce URLs: Tracker announce URL: Remove Tracker Dialog title Are you sure you want to remove this tracker? Remove Trackers Dialog title Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Down Speed Torrents list column name ---------- Peers list column title Up Speed Torrents list column name ---------- Peers list column title Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title Flags Peers list column title Client Peers list column title Timed out Server connection status Connection error Server connection status Authentication error Server connection status Parse error Server connection status Server is too new Server connection status Server is too old Server connection status Connecting... Server connection status Connected Server connection status Downloading Torrent status Torrent status Seeding Torrent status Torrent status Queued for checking Torrent status Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file Error parsing torrent file Total Size Torrents list column name Queue Position Torrents list column name Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added Completed on Torrents list column name, date/time when torrent was completed Down Limit Torrents list column name, download speed limit Up Limit Torrents list column name, upload speed limit Remaining Torrents list column name, remaining byte size Download Directory Torrents list column name Last Activity Torrents list column name Inactive Tracker status Waiting for update Tracker status About to update Tracker status Updating Tracker status Next Update Trackers list column title %L1 B Size suffix in bytes %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 B/s Download speed suffix in bytes per second %L1 KiB/s Download speed suffix in kibibytes per second %L1 MiB/s Download speed suffix in mebibytes per second %L1 GiB/s Download speed suffix in gibibytes per second %L1 TiB/s Download speed suffix in tebibytes per second %L1 PiB/s Download speed suffix in pebibytes per second %L1 EiB/s Download speed suffix in exbibytes per second %L1 ZiB/s Download speed suffix in zebibytes per second %L1 YiB/s Download speed suffix in yobibytes per second %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 s Remaining time string. %L1 is seconds, "10 s" Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path Move files from current directory Check box label Selected directory should be inside mounted directory All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label This directory does not exist Edit Labels New label... tremotesf-2.10.1/translations/tr.ts000066400000000000000000003467361523411710200173400ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title Hakkında Source code: Kaynak kodu: Translations: Çeviriler: Maintainer Geliştiren Contributor Katkıda bulunan Authors "About" dialog's "Authors" tab title Yazarlar Translators "About" dialog's "Translators" tab title Çevirmenler License "About" dialog's "License" tab title Lisans Add Torrent File Dialog title Torrent Dosyası Ekle Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB Boş alan: %1 Error getting free space Boş alan alınırken hata oluştu Files Torrent properties dialog tab Dosyalar High Torrent's file loading priority ---------- Torrent's loading priority Yüksek Normal Torrent's file loading priority ---------- Torrent's loading priority Normal Low Torrent's file loading priority ---------- Torrent's loading priority Düşük Start downloading after adding Ekledikten sonra indirmeye başla Add Torrent Link Dialog title Torrent Bağlantısı Ekle No servers Sunucu yok Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server Bağlantı kesildi Downloading Noun "Downloading" server setting page İndiriliyor Start added torrents Check box label Eklenen torrentleri başlat Append ".part" to names of incomplete files Check box label Tamamlanmamış dosyaların adlarına ".part" ekle Rename Dialog title ---------- Dialog confirmation button Yeniden Adlandır Select Directory Directory chooser dialog title Dizin Seçiniz Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title Durum Directories Title of torrents download directory filters list Dizinler Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab İzleyiciler Priority Column title in torrent's file list ---------- Torrents list column name Öncelik Mixed Torrent's file loading priority ---------- File loading priority Karışık No torrents Torrents list placeholder Torrent yok Remove Button ---------- Dialog confirmation button Kaldır Set Location Dialog title for changing torrent's download directory Konum Ayarla Error adding torrent Torrent eklerken hata oluştu This torrent is already added Bu torrent zaten eklendi Torrent added Notification title Torrent eklendi Torrent finished Notification title Torrent bitti Network "Network" server settings page Ağ Connection Title of settings section related to peer connections Bağlantı Random port on Transmission start Check box label Transmission başlangıcında rastgele bağlantı noktası Enable port forwarding Check box label Bağlantı noktası yönlendirmeyi etkinleştir Allow Encryption mode (allow/prefer/require) İzin ver Prefer Encryption mode (allow/prefer/require) Tercih et Require Encryption mode (allow/prefer/require) Gerekli Enable DHT Check box label DHT'yi Etkinleştir Peer Limits Eş Limitleri Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title Eşler Queue "Queue" server settings page Kuyruk Also delete the files on the hard disk Check box label Sabit diskteki dosyaları da silin Seeding Noun "Seeding" server setting page Gönderiliyor Overwrite Dialog's confirmation button Üzerine Yaz Server already exists Sunucu zaten mevcut Add Server Dialog title Sunucu Ekle Name Column title in torrent's file list ---------- Torrents list column name Ad Address Peers list column title ---------- Trackers list column title Adres Default Default proxy option Varsayılan HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button Dosyadan yükle... Use client certificate authentication Check box label İstemci sertifikası kimlik doğrulamasını kullan Authentication Check box label Kimlik Doğrula Auto reconnect on error Check box label Hata durumunda otomatik yeniden bağlan Mounted directories Bağlanmış dizinler Local directory Column title in the list of mounted directories Yerel dizin Remote directory Column title in the list of mounted directories Uzak dizin Add Button ---------- Dialog confirmation button Ekle Speed "Speed" server settings page ---------- Torrent's limits tab section Hız Connection Settings Servers list placeholder Dialog title Bağlantı Ayarları Edit... Button Düzenle... Add Server... Button Sunucu Ekle... Add... Button Ekle... Connect to server on startup Check box label Başlangıçta sunucuya bağlan Adding torrents Options tab Torrent ekleme Add torrent parameters Torrent parametreleri ekle Reset Sıfırla Show main window when adding torrents Check box label Torrent eklerken ana pencereyi göster Show dialog when adding torrents Check box label Torrent eklerken iletişim kutusunu göster Automatically fill link from clipboard when adding torrent link Check box label Torrent bağlantısı eklerken bağlantıyı panodan otomatik olarak doldur Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" İpucu: Torrentleri panodan eklemek için ana pencerede %1 basabilirsiniz Ask for merging trackers when adding existing torrent Check box label Mevcut torrent eklerken izleyicileri birleştirmeyi iste Merge trackers when adding existing torrent Check box label Mevcut torrent eklenirken izleyicileri birleştir Open properties dialog Özellikler iletişim kutusunu aç Open torrent's file Torrent dosyasını aç Open download directory İndirme dizinini aç What to do when torrent in the list is double clicked: Listedeki torrent çift tıklandığında ne yapılmalı: General Options tab Genel Show torrent properties in a panel in the main window Check box label Torrent özelliklerini ana penceredeki panelde göster Properties dialog won't be shown because torrent properties are shown in the main window Torrent özellikleri ana pencerede gösterildiğinden özellikler iletişim kutusu gösterilmez Display relative time Check box label Bağıl zamanı göster Display full path of download directories in sidebar and torrents list Check box label Kenar çubuğunda ve torrentler listesinde indirme dizinlerinin tam yolunu göster Notifications Options tab Bildirimler Notify when disconnecting from server Check box label Sunucu ile bağlantı kesildiğinde bildir Notify on added torrents Check box label Torrentler eklendiğinde bildir Notify on finished torrents Check box label Torrentler bittiğinde bildir When connecting to server Notifications options section Sunucuya bağlanırken Notify on added torrents since last connection to server Check box label Sunucuya son bağlantıdan bu yana eklenen torrentler hakkında bildir Notify on finished torrents since last connection to server Check box label Sunucuya son bağlantıdan bu yana tamamlanan torrentler hakkında bildir Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title İlerleme ETA Torrents list column name ETA Server Stats Dialog title Sunucu İstatistikleri Current session Server stats section for current Transmission launch Güncel oturum Ratio Torrents list column name Oran Total Server stats section for all Transmission launches (accumulated) Toplam %Ln times How many times Transmission was launched %Ln kez%Ln kez Size Column title in torrent's file list ---------- Torrents list column name Boyut Limits Speed limits section ---------- Torrent's properties dialog tab Limitler Alternative Limits Alternative speed limits section Alternatif Limitler Enable Check box label Etkinleştir Scheduled Title of alternative speed limit scheduling section Planlanmış to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" için Every day Her gün Weekdays Hafta içi Weekends Hafta Sonları %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 of %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Kontrol (%L1) Honor global limits Check box label Küresel sınırlara uy Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) Genel ayarları kullan Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) Oran ne olursa olsun gönder Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) Orana göre göndermeyi durdur: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) Aktiviteden bağımsız olarak gönder Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) Boşta kalırsa göndermeyi durdur: Activity Torrent's details tab section Aktivite Completed Torrents list column name, completed byte size Tamamlandı Downloaded Torrents list column name, downloaded byte size İndirildi Paused (%1) Torrent status while torrent also has an error. %1 is error string Duraklatıldı (%1) Paused Torrent status Duraklatıldı Downloading (%1) Torrent status while torrent also has an error. %1 is error string İndiriliyor (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string Gönderiliyor (%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string Kuyrukta (%1) Queued Torrent status Kuyrukta Checking (%1) Torrent status while torrent also has an error. %1 is error string Kontrol ediliyor (%1) Checking Torrent status Kontrol Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string Kontrol için kuyruğa alındı (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to Eşlere yükleniyor Uploaded Torrents list column name, uploaded byte size Yüklendi Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title Göndericiler Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title İndirenler Information Torrent's details tab section Bilgi Torrent Removed Message that appears when torrent is removed Torrent Kaldırıldı Edit Tracker Dialog title İzleyiciyi Düzenle Torrent file: Input field's label Torrent dosyası: Torrent link: Input field's label Torrent bağlantısı: Download directory: Input field's label İndirme Dizini: Torrent priority: Combo box label Torrent önceliği: Delete .torrent file .torrent dosyasını sil Move .torrent file to trash .torrent dosyasını çöp kutusuna taşı Labels Title of torrents label filters list Etiketler Loading Placeholder shown when torrent file is being read/parsed Yükleniyor &Connect Button / menu item to connect to server &Bağlan &Disconnect Button / menu item to disconnect from server &Bağlantıyı Kes &Add Torrent File... Menu item &Torrent Dosyası Ekle... Add Torrent &Link... Menu item Torrent &Bağlantı Ekle... P&ause Torrent's context menu item &Durdur &Delete Torrent's context menu item &Sil Open &Download Directory Context menu item Aç &Dizine İndir Delete with files Dosyalarla sil Delete Sil Delete Torrent Dialog title Torrent'i Sil Are you sure you want to delete this torrent? Bu torrenti silmek istediğinizden emin misiniz? Delete Torrents Dialog title Torrentleri Sil Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion Are you sure you want to delete %Ln selected torrents?Seçili %Ln torrentleri silmek istediğinizden emin misiniz? No torrents matching filters Torrents list placeholder Filtrelerle eşleşen torrent yok &Quit Menu item &Çık &Torrent Menu bar item &Torrent Error adding torrent «%1» Torrent «%1» eklenirken hata oluştu &Properties Torrent's context menu item &Özellikler &Show Tremotesf &Tremotesf'i Göster &Hide Tremotesf &Tremotesf'i Gizle &Start Torrent's context menu item &Başlat Start &Now Torrent's context menu item &Şimdi Başlat Copy &Magnet Link Torrent's context menu item &Magnet Bağlantısını Kopyala &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &Kaldır Set &Location Torrent's context menu item &Konum Ayarla Edi&t Labels Torrent's context menu item Etiketleri Düzen&le &Open Torrent's context menu item ---------- Context menu item &Aç Op&en Download Directory Torrent's context menu item Aç &Dizine İndir &Check Local Data Torrent's context menu item &Yerel Verileri Kontrol Et Reanno&unce Torrent's context menu item ---------- Button Yeniden &duyur &Queue Torrent's context menu item &Kuyruk Move To &Top Torrent's context menu item Taşı &Üste Move &Up Torrent's context menu item Taşı &Yukarı Move &Down Torrent's context menu item Taşı &Aşağı Move To &Bottom Torrent's context menu item Taşı &Alta  &Connection Settings &Bağlantı Ayarları &Server Options &Sunucu Seçenekleri Server S&tats Sunucu İ&statistikleri Select Files File chooser dialog title Dosyaları Seç Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Torrent Dosyaları (*.torrent) Error Dialog title ---------- Trackers list column title Hata This file/directory does not exist Bu dosya/dizin mevcut değil &File Menu bar item &Dosya &Close Window &Pencereyi Kapat &Edit Menu bar item &Düzenle Select &All &Tümünü Seç &Invert Selection &Seçimi Ters Çevir &View Menu bar item &Görünüm &Toolbar &Araç Çubuğu &Sidebar &Kenar Çubuğu St&atusbar Du&rum çubuğu Torrent properties &panel Torrent özellikleri &Lock Toolbar Araç Çubuğunu &Kilitle T&ools Menu bar item A&raçlar &Options &Seçenekler S&hutdown Server Sunucuyu K&apat Shutdown Server Dialog title Sunucuyu Kapat Are you sure you want to shutdown remote Transmission instance? Transmissionu kapatmak istediğinizden emin misiniz? Shutdown Dialog confirmation button Kapat &Help Menu bar item &Yardım &About Menu item opening "About" dialog &Hakkında Icon Only Toolbar mode Yalnızca Simge Text Only Toolbar mode Sadece Metin Text Beside Icon Toolbar mode Simge Yanında Metin Text Under Icon Toolbar mode Simge Altında Metin Follow System Style Toolbar mode Sistem Stilini Takip Et Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Torrentler sunucuya bağlandıktan sonra eklenecektir Show Tremotesf Button on notification Tremotesf'i Göster Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Aktif (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status İndiriliyor (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Gönderiliyor (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Duraklatıldı (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status Hatalı (%L1) Search... Search field placeholder Ara... &Select... Context menu item to open directory chooser &Seç... Overwrite Server Dialog title Sunucunun Üzerine Yaz Name: İsim: Address: Adres: Port: Port: API path: API yolu: Proxy Proxy HTTP HTTP proxy option HTTP None None proxy option Hiçbiri Proxy type: Proxy türü: Username: Kullanıcı adı: Password: Şifre: No Server does not use custom certificates Hayır Self-signed certificate Kendinden imzalı sertifika Custom CA root certificate Özel CA kök sertifikası Server uses custom certificates: Sunucu özel sertifikalar kullanır: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Sunucunun yaprak sertifikasında doğru ana bilgisayar adı yoksa, sertifika doğrulamasının geçmesi için bunu da sağlamanız gerekir. Server's leaf certificate in PEM format: PEM formatında sunucu yaprak sertifikası: Server's CA root certificate in PEM format: PEM formatında sunucunun CA kök sertifikası: Client certificate with private key in PEM format: PEM formatında özel anahtara sahip istemci sertifikası: s Suffix that is added to input field with number of seconds, e.g. "30 s" sn Update interval: Güncelleme aralığı: Timeout: Zaman aşımı: Auto reconnect interval: Otomatik yeniden bağlanma aralığı: &Edit... Server's context menu item ---------- Tracker's context menu item &Düzenle... Server Options Dialog title Sunucu Seçenekleri Directory for incomplete files: Tamamlanmamış dosyalar için dizin: min Suffix that is added to input field with number of minuts, e.g. "5 min" dk Maximum active downloads: Maksimum aktif indirme: Maximum active uploads: Maksimum aktif yükleme: Ignore queue position if idle for: Boşta ise kuyruk konumunu yoksay: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/sn Download: Download speed limit input field label İndirme: Upload: Upload speed limit input field label Yükleme: Days: Günler: Peer port: Eş bağlantı noktası: Encryption: Şifreleme: Enable μTP (Micro Transport Protocol) Check box label μTP'yi (Mikro Aktarım Protokolü) etkinleştirin Enable PEX (Peer exchange) Check box label PEX'i (Eş değişimi) etkinleştirin Enable local peer discovery Check box label Yerel eş bulmayı etkinleştir Maximum peers per torrent: Torrent başına maksimum eşler: Maximum peers globally: Küresel maksimum eşler: Options Dialog title Seçenekler Follow system Dark theme mode Sistemi takip et On Dark theme mode Açık Off Dark theme mode Kapalı Dark theme Koyu tema Use system accent color Check box label Sistem vurgu rengini kullanın Remember location of last opened torrent file Check box label Son açılan torrent dosyasının konumunu hatırla Remember parameters of last added torrent Check box label Son eklenen torrentin parametrelerini hatırla Show icon in the notification area Check box label Bildirim alanında simge göster &Not Download Context menu item to unselect file for downloading &İndirme &Priority Torrent's context menu item &Öncelik &Download Context menu item to select file for downloading &İndir &High File loading priority &Yüksek &Normal File loading priority &Normal &Low File loading priority &Düşük &Rename Torrent's context menu item ---------- Context menu item &Yeniden Adlandır File name: Dosya adı: Details Torrent's properties dialog tab Detaylar Completed: Torrent's completed size Tamamlandı: Downloaded: Downloaded bytes ---------- Torrent's downloaded size İndirildi: Uploaded: Uploaded bytes ---------- Torrent's uploaded size Yüklendi: Ratio: Oran: Duration: How much time Transmission is running Süre: Started: How many times Transmission was launched Başladı: Free space in download directory: İndirme dizininde boş alan: Download speed: İndirme hızı: Upload speed: Yükleme hızı: ETA: ETA: Seeders: Göndericiler: Leechers: İndirenler: Peers we are downloading from: İndirdiğimiz eşler: Web seeders we are downloading from: İndirdiğimiz web göndericileri: Peers we are uploading to: Yüklediğimiz eşler: Last activity: Son etkinlik: Total size: Toplam Boyut: Location: Torrent's download directory Konum: Hash: Torrent's hash string Hash: Created by: Program that created torrent file Tarafından oluşturuldu: Created on: Date/time when torrent was created Oluşturuldu: Comment: Torrent's comment text Yorum: Labels: Torrent's labels Etiketler: Web seeder Web seeders list column title Web gönderici Web seeders Torrent's properties dialog tab Web göndericileri Seeding Options section Torrent's limits tab section Gönderiliyor Ratio limit mode: Oran sınırı modu: Idle seeding mode: Boşta gönderici modu: Maximum peers: Maksimum eşler: Add Trackers Dialog title İzleyici Ekle Trackers announce URLs: İzleyiciler URL'leri duyur: Tracker announce URL: İzleyici URL'yi duyur: Remove Tracker Dialog title İzleyiciyi Kaldır Are you sure you want to remove this tracker? Bu izleyiciyi kaldırmak istediğinizden emin misiniz? Remove Trackers Dialog title İzleyicileri Kaldır Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion Are you sure you want to remove %Ln selected trackers?Seçili %Ln izleyicileri kaldırmak istediğinizden emin misiniz? Down Speed Torrents list column name ---------- Peers list column title İndirme Hızı Up Speed Torrents list column name ---------- Peers list column title Yükleme Hızı Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title İlerleme Çubuğu Flags Peers list column title Bayraklar Client Peers list column title İstemci Timed out Server connection status Zaman aşımına uğradı Connection error Server connection status Bağlantı hatası Authentication error Server connection status Kimlik doğrulama hatası Parse error Server connection status Ayrıştırma hatası Server is too new Server connection status Sunucu çok yeni Server is too old Server connection status Sunucu çok eski Connecting... Server connection status Bağlanıyor... Connected Server connection status Bağlı Downloading Torrent status Torrent status İndiriliyor Seeding Torrent status Torrent status Gönderiliyor Queued for checking Torrent status Kontrol için sıraya alındı Merge trackers? Dialog title İzleyicileri birleştirelim mi? Torrent «%1» is already added, merge trackers? Torrent «%1» zaten eklendi, izleyicileri birleştirelim mi? Merge Birleştir Do not ask again Bir daha sorma Merged trackers Dialog title İzleyiciler birleştirildi Merged trackers for torrent «%1» Torrent «%1» için izleyiciler birleştirildi Torrent already exists Dialog title Torrent zaten mevcut Torrent «%1» already exists Torrent «%1» zaten mevcut Error reading torrent file Torrent dosyası okunurken hata oluştu Error parsing torrent file Torrent dosyası ayrıştırılırken hata oluştu Total Size Torrents list column name Toplam Boyut Queue Position Torrents list column name Kuyruk Pozisyonu Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added Eklendi Completed on Torrents list column name, date/time when torrent was completed Tamamlandı Down Limit Torrents list column name, download speed limit İndirme Limiti Up Limit Torrents list column name, upload speed limit Yükleme Limiti Remaining Torrents list column name, remaining byte size Kalan Download Directory Torrents list column name İndirme Dizini Last Activity Torrents list column name Son Etkinlik Inactive Tracker status Aktif değil Waiting for update Tracker status Güncelleme için bekleniyor About to update Tracker status Güncellenmek üzere Updating Tracker status Güncelleniyor Next Update Trackers list column title Sonraki Güncelleme %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YB %L1 B/s Download speed suffix in bytes per second %L1 B/sn %L1 KiB/s Download speed suffix in kibibytes per second %L1 KB/sn %L1 MiB/s Download speed suffix in mebibytes per second %L1 MB/sn %L1 GiB/s Download speed suffix in gibibytes per second %L1 GB/sn %L1 TiB/s Download speed suffix in tebibytes per second %L1 TB/sn %L1 PiB/s Download speed suffix in pebibytes per second %L1 PB/sn %L1 EiB/s Download speed suffix in exbibytes per second %L1 EB/sn %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZB/sn %L1 YiB/s Download speed suffix in yobibytes per second %L1 YB/sn %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 g %L2 s %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 s %L2 dk %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 dk %L2 sn %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 sn Today Bugün Yesterday Dün Two days ago İki gün önce %1 at %2 Relative date & time %2'de %1 Just now Relative time Şimdi %n minute(s) ago @item:intext %1 is a whole number %n dakika(lar) önce%n dakika(lar) önce %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path %1 açılırken hata oluştu Move files from current directory Check box label Dosyaları geçerli dizinden taşı Selected directory should be inside mounted directory Seçilen dizin bağlı dizinin içinde olmalıdır All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents Tümü (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist Bu dizin mevcut değil Edit Labels Etiketleri Düzenle New label... Yeni etiket... tremotesf-2.10.1/translations/zh_CN.ts000066400000000000000000003417601523411710200177040ustar00rootroot00000000000000 tremotesf About "About" dialog title ---------- "About" dialog's "About" tab title 关于 Source code: Translations: Maintainer 维护者 Contributor 贡献者 Authors "About" dialog's "Authors" tab title 作者 Translators "About" dialog's "Translators" tab title 翻译 License "About" dialog's "License" tab title 许� Add Torrent File Dialog title 添加Torrent Free space: %1 %1 is a amount of free space in a directory, e.g. 1 GiB �用空间: %1 Error getting free space 获��用空间时出错 Files Torrent properties dialog tab 文件 High Torrent's file loading priority ---------- Torrent's loading priority 高 Normal Torrent's file loading priority ---------- Torrent's loading priority 中 Low Torrent's file loading priority ---------- Torrent's loading priority 低 Start downloading after adding 添加�开始下载 Add Torrent Link Dialog title 添加Torrent链接 No servers 没有�务器 Disconnected Server connection status ---------- Notification title when disconnected from server ---------- Message that appears when disconnected from server 断开 Downloading Noun "Downloading" server setting page 下载 Start added torrents Check box label 自动开始新添加的�� Append ".part" to names of incomplete files Check box label 在未完�的文件��加上“.part��缀 Rename Dialog title ---------- Dialog confirmation button �命� Select Directory Directory chooser dialog title 选择目录 Status Title of torrents status filters list ---------- Torrents list column name ---------- Trackers list column title 状� Directories Title of torrents download directory filters list 数�目录 Trackers Title of torrents tracker filters list ---------- Torrent properties dialog tab Trackers Priority Column title in torrent's file list ---------- Torrents list column name 优先级 Mixed Torrent's file loading priority ---------- File loading priority 混� No torrents Torrents list placeholder 没有torrents Remove Button ---------- Dialog confirmation button 删除 Set Location Dialog title for changing torrent's download directory 设置数��置 Error adding torrent 添加Torrent时出错 This torrent is already added 这个torrent已�添加了 Torrent added Notification title 添加Torrent Torrent finished Notification title Torrent完� Network "Network" server settings page 网络 Connection Title of settings section related to peer connections 连接 Random port on Transmission start Check box label �用�机端� Enable port forwarding Check box label �用端�转� Allow Encryption mode (allow/prefer/require) 关闭加密 Prefer Encryption mode (allow/prefer/require) 开�加密 Require Encryption mode (allow/prefer/require) 强制加密 Enable DHT Check box label �用 DHT Peer Limits 连接�制 Peers Torrent's properties dialog tab ---------- Torrent's limits tab section ---------- Trackers list column title 连接 Queue "Queue" server settings page 传输队列 Also delete the files on the hard disk Check box label �时删除硬盘上的文件 Seeding Noun "Seeding" server setting page �� Overwrite Dialog's confirmation button 覆盖 Server already exists �务器已存在 Add Server Dialog title 添加�务器 Name Column title in torrent's file list ---------- Torrents list column name �称 Address Peers list column title ---------- Trackers list column title 地� Default Default proxy option 默认 HTTPS HTTPS SOCKS5 SOCKS5 proxy option SOCKS5 Load from file... Button 从文件加载.. Use client certificate authentication Check box label 使用客户端�书认� Authentication Check box label 验� Auto reconnect on error Check box label 出错时自动�新连接 Mounted directories 安装目录 Local directory Column title in the list of mounted directories 本地目录 Remote directory Column title in the list of mounted directories 远程目录 Add Button ---------- Dialog confirmation button 添加 Speed "Speed" server settings page ---------- Torrent's limits tab section 速度 Connection Settings Servers list placeholder Dialog title 连接设置 Edit... Button 编辑… Add Server... Button 添加�务器... Add... Button 添加… Connect to server on startup Check box label �动时连接到�务器 Adding torrents Options tab 添加�� Add torrent parameters 添加���数 Reset �置 Show main window when adding torrents Check box label 在添加��时显示主窗� Show dialog when adding torrents Check box label 在添加��时显示对�框 Automatically fill link from clipboard when adding torrent link Check box label 自动填充从剪贴�添加链接 Tip: you can also press %1 in main window to add torrents from clipboard %1 is a key binding, e.g. "Ctrl + C" �示:你也�以在主窗�按 %1 从剪贴�中添加�� Ask for merging trackers when adding existing torrent Check box label Merge trackers when adding existing torrent Check box label Open properties dialog 打开属性对�框 Open torrent's file 打开 Torrent 的文件 Open download directory 打开下载目录 What to do when torrent in the list is double clicked: �击列表中的��时该怎么�: General Options tab Show torrent properties in a panel in the main window Check box label Properties dialog won't be shown because torrent properties are shown in the main window Display relative time Check box label Display full path of download directories in sidebar and torrents list Check box label Notifications Options tab 通知 Notify when disconnecting from server Check box label 与�务器断开连接时通知 Notify on added torrents Check box label 通知添加的Torrents Notify on finished torrents Check box label 通知已完�的Torrents When connecting to server Notifications options section 连接到�务器时 Notify on added torrents since last connection to server Check box label 通知自上次连接到�务器�添加的�� Notify on finished torrents since last connection to server Check box label 通知自上次连接到�务器完�的�� Progress Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title 进度 ETA Torrents list column name 预计剩余时间 Server Stats Dialog title �务器统计 Current session Server stats section for current Transmission launch 当�会� Ratio Torrents list column name 分享率 Total Server stats section for all Transmission launches (accumulated) 累计 %Ln times How many times Transmission was launched %Ln 次 Size Column title in torrent's file list ---------- Torrents list column name 大� Limits Speed limits section ---------- Torrent's properties dialog tab �制 Alternative Limits Alternative speed limits section 备选速度�制 Enable Check box label �用 Scheduled Title of alternative speed limit scheduling section 定时计划 to Separates time range input fields. E.g. "to" inside "1:00 AM to 5:00 AM" 到 Every day �一天 Weekdays 工作日 Weekends 周末 %1 of %2 (%3) Torrent's completion size, e.g. 100 MiB of 200 MiB (50%). %1 is completed size, %2 is size, %3 is progress in percents %1 / %2 (%3) Checking (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status 校验中 (%L1) Honor global limits Check box label 以全局设置为准 Use global settings Seeding ratio limit mode (global settings/stop at ratio/unlimited) ---------- Seeding idle limit mode (global settings/stop if idle for/unlimited) 使用全局设置 Seed regardless of ratio Seeding ratio limit mode (global settings/stop at ratio/unlimited) ��与分享率无关 Stop seeding at ratio: Seeding ratio limit mode (global settings/stop at ratio/unlimited) �止��的比率: Seed regardless of activity Seeding idle limit mode (global settings/stop if idle for/unlimited) ��与活动无关 Stop seeding if idle for: Seeding idle limit mode (global settings/stop if idle for/unlimited) 无活动自动�止: Activity Torrent's details tab section 活动 Completed Torrents list column name, completed byte size 已完� Downloaded Torrents list column name, downloaded byte size 已下载 Paused (%1) Torrent status while torrent also has an error. %1 is error string 暂� (%1) Paused Torrent status 暂� Downloading (%1) Torrent status while torrent also has an error. %1 is error string 下载中 (%1) Seeding (%1) Torrent status while torrent also has an error. %1 is error string ��(%1) Queued (%1) Torrent status while torrent also has an error. %1 is error string 队列中(%1) Queued Torrent status 队列 Checking (%1) Torrent status while torrent also has an error. %1 is error string 校验(%1) Checking Torrent status 校验 Queued for checking (%1) Torrent status while torrent also has an error. %1 is error string 排队检查 (%1) Uploading to peers Torrents list column name, number of peers that we are uploading to 正在上传 Uploaded Torrents list column name, uploaded byte size 已上传 Seeders Torrents list column name, number of seeders reported by trackers ---------- Trackers list column title �� Leechers Torrents list column name, number of leechers reported by trackers ---------- Trackers list column title 连接 Information Torrent's details tab section 信� Torrent Removed Message that appears when torrent is removed 移除Torrent Edit Tracker Dialog title 编辑Tracker Torrent file: Input field's label torrent文件: Torrent link: Input field's label Torrent链接: Download directory: Input field's label 下载目录: Torrent priority: Combo box label Torrent优先级: Delete .torrent file 删除. ��文件 Move .torrent file to trash 将��文件移动到垃圾桶 Labels Title of torrents label filters list Loading Placeholder shown when torrent file is being read/parsed 正在加载 &Connect Button / menu item to connect to server &连接 &Disconnect Button / menu item to disconnect from server &断开 &Add Torrent File... Menu item &添加Torrent文件… Add Torrent &Link... Menu item 添加Torrent&链接… P&ause Torrent's context menu item &暂� &Delete Torrent's context menu item &删除 Open &Download Directory Context menu item 打开 &下载目录 Delete with files �时删除文件 Delete 删除 Delete Torrent Dialog title 删除�� Are you sure you want to delete this torrent? 你确定�删除这个���? Delete Torrents Dialog title 删除多个�� Are you sure you want to delete %Ln selected torrents? %Ln is a number of torrents selected for deletion 确实�删除 %Ln 选定的���? No torrents matching filters Torrents list placeholder 没有与过滤匹�的�� &Quit Menu item &退出 &Torrent Menu bar item &�� Error adding torrent «%1» &Properties Torrent's context menu item &属性 &Show Tremotesf &显示 Tremotesf &Hide Tremotesf &�� Tremotesf &Start Torrent's context menu item &开始 Start &Now Torrent's context menu item 强制&开始 Copy &Magnet Link Torrent's context menu item �制 &�力链接 &Remove Server's context menu item ---------- Context menu item ---------- Tracker's context menu item &删除 Set &Location Torrent's context menu item 设置&�存�置 Edi&t Labels Torrent's context menu item &Open Torrent's context menu item ---------- Context menu item &打开 Op&en Download Directory Torrent's context menu item &Check Local Data Torrent's context menu item &校验本地数� Reanno&unce Torrent's context menu item ---------- Button 刷新&Trackers &Queue Torrent's context menu item &队列 Move To &Top Torrent's context menu item 移到&最� Move &Up Torrent's context menu item 上&移 Move &Down Torrent's context menu item 下&移 Move To &Bottom Torrent's context menu item 移到&最� &Connection Settings &连接设置 &Server Options &�务器选项 Server S&tats 统计&数� Select Files File chooser dialog title 选择文件 Torrent Files (*.torrent) Torrent file type. Parentheses and text within them must remain unchanged Torrent 文件 (*.torrent) Error Dialog title ---------- Trackers list column title 错误 This file/directory does not exist 这个文件/目录�存在 &File Menu bar item &文件 &Close Window &关闭窗� &Edit Menu bar item &编辑 Select &All 选择&全部 &Invert Selection &�选 &View Menu bar item &视图 &Toolbar &工具� &Sidebar &侧边� St&atusbar 状�&� Torrent properties &panel &Lock Toolbar &�定工具� T&ools Menu bar item 工&具 &Options &选项 S&hutdown Server &关闭�务器 Shutdown Server Dialog title 关闭�务器 Are you sure you want to shutdown remote Transmission instance? 您确定�关闭Transmission�? Shutdown Dialog confirmation button 关闭 &Help Menu bar item &帮助 &About Menu item opening "About" dialog &关于 Icon Only Toolbar mode 仅图标 Text Only Toolbar mode 仅文本 Text Beside Icon Toolbar mode 图标�的文本 Text Under Icon Toolbar mode 图标下的文本 Follow System Style Toolbar mode �循系统样� Torrents will be added after connection to server Message shown when user attempts to add torrent while disconnect from server. Show Tremotesf Button on notification 显示Tremotesf Active (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status 活动 (%L1) Downloading (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status 下载中 (%L1) Seeding (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status ��中 (%L1) Paused (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status 暂� (%L1) Errored (%L1) Filter option of torrents list's status filter. %L1 is a number of torrents with that status 错误(%L1) Search... Search field placeholder �索… &Select... Context menu item to open directory chooser &选择… Overwrite Server Dialog title 覆盖�务器 Name: �称: Address: 地�: Port: 端�: API path: API路径: Proxy 代� HTTP HTTP proxy option HTTP None None proxy option Proxy type: 代�类型: Username: 用户�: Password: 密�: No Server does not use custom certificates Self-signed certificate Custom CA root certificate Server uses custom certificates: If server's leaf certificate does not have correct host name, you need to provide it too for certificate validation to pass Server's leaf certificate in PEM format: Server's CA root certificate in PEM format: Client certificate with private key in PEM format: s Suffix that is added to input field with number of seconds, e.g. "30 s" 秒 Update interval: 刷新间隔: Timeout: 超时: Auto reconnect interval: 自动�连间隔: &Edit... Server's context menu item ---------- Tracker's context menu item &编辑… Server Options Dialog title �务器选项 Directory for incomplete files: 未完�文件目录: min Suffix that is added to input field with number of minuts, e.g. "5 min" 分 Maximum active downloads: 最大�时下载: Maximum active uploads: 最大活动上传: Ignore queue position if idle for: 如果空闲时间超过以下时间,则忽略队列�置: kB/s Suffix that is added to input field with download/upload speed limit, e.g. "5000 kB/s". 'k' prefix means SI prefix, i.e kB = 1000 bytes kB/s Download: Download speed limit input field label 下载: Upload: Upload speed limit input field label 上传: Days: 天: Peer port: 端�: Encryption: 加密: Enable μTP (Micro Transport Protocol) Check box label �用 μTP (Micro Transport Protocol) Enable PEX (Peer exchange) Check box label �用 PEX (用户交�) Enable local peer discovery Check box label �用本地对等�现 Maximum peers per torrent: ��最大连接数 Maximum peers globally: 全局最大连接数: Options Dialog title 选项 Follow system Dark theme mode �考系统 On Dark theme mode 开 Off Dark theme mode 关 Dark theme 黑暗主题 Use system accent color Check box label 使用系统主题色 Remember location of last opened torrent file Check box label 记�上次打开��文件的�置 Remember parameters of last added torrent Check box label 记�上次添加torrent的�数 Show icon in the notification area Check box label 在通知区域显示图标 &Not Download Context menu item to unselect file for downloading &�下载 &Priority Torrent's context menu item &优先级 &Download Context menu item to select file for downloading &下载 &High File loading priority &高 &Normal File loading priority &中 &Low File loading priority &低 &Rename Torrent's context menu item ---------- Context menu item &�命� File name: 文件�: Details Torrent's properties dialog tab 详细信� Completed: Torrent's completed size 完�: Downloaded: Downloaded bytes ---------- Torrent's downloaded size 下载: Uploaded: Uploaded bytes ---------- Torrent's uploaded size 上传: Ratio: 分享率: Duration: How much time Transmission is running �续时间: Started: How many times Transmission was launched �动: Free space in download directory: 下载目录�用空间: Download speed: 下载速度: Upload speed: 上传速度: ETA: 预计剩余时间: Seeders: ��数: Leechers: 下载数: Peers we are downloading from: 从以下网站下载: Web seeders we are downloading from: 从以下网站下载: Peers we are uploading to: 正在上传到: Last activity: 最�活动: Total size: 总大� Location: Torrent's download directory �置: Hash: Torrent's hash string 哈希值: Created by: Program that created torrent file 创建者: Created on: Date/time when torrent was created 创建日期: Comment: Torrent's comment text 注释: Labels: Torrent's labels Web seeder Web seeders list column title 网络播�机 Web seeders Torrent's properties dialog tab 网络播�机 Seeding Options section Torrent's limits tab section �� Ratio limit mode: 分享率�制模�: Idle seeding mode: 活动��模� Maximum peers: 最大连接数: Add Trackers Dialog title 添加 Trackers Trackers announce URLs: Trackers 宣告链接: Tracker announce URL: Trackers 宣告链接: Remove Tracker Dialog title 移除Tracker Are you sure you want to remove this tracker? 是�确实�删除此Tracker? Remove Trackers Dialog title 移除Trackers Are you sure you want to remove %Ln selected trackers? %Ln is number of trackers selected for deletion 确实�删除 %Ln 选定 trackers? Down Speed Torrents list column name ---------- Peers list column title 下载速度 Up Speed Torrents list column name ---------- Peers list column title 上传速度 Progress Bar Column title in torrent's file list ---------- Torrents list column name ---------- Peers list column title 进度� Flags Peers list column title 标志 Client Peers list column title 客户端 Timed out Server connection status 超时 Connection error Server connection status 连接错误 Authentication error Server connection status 身份验�错误 Parse error Server connection status 解�错误 Server is too new Server connection status �务器太新 Server is too old Server connection status �务器太旧 Connecting... Server connection status 连接中... Connected Server connection status 已连接 Downloading Torrent status Torrent status 下载中 Seeding Torrent status Torrent status ��中 Queued for checking Torrent status 排队等待校验 Merge trackers? Dialog title Torrent «%1» is already added, merge trackers? Merge Do not ask again Merged trackers Dialog title Merged trackers for torrent «%1» Torrent already exists Dialog title Torrent «%1» already exists Error reading torrent file 读�Torrent文件时出错 Error parsing torrent file 分�Torrent文件时出错 Total Size Torrents list column name 总大� Queue Position Torrents list column name 队列�置 Downloading from peers Torrents list column name, number of peers that we are downloading from Added on Torrents list column name, date/time when torrent was added 添加时间 Completed on Torrents list column name, date/time when torrent was completed 完�时间 Down Limit Torrents list column name, download speed limit 下载�制 Up Limit Torrents list column name, upload speed limit 上传�制 Remaining Torrents list column name, remaining byte size 剩余大� Download Directory Torrents list column name 下载目录 Last Activity Torrents list column name 最�活动 Inactive Tracker status �活动 Waiting for update Tracker status 等待更新 About to update Tracker status 准备更新 Updating Tracker status 更新中 Next Update Trackers list column title 下一次更新 %L1 B Size suffix in bytes %L1 B %L1 KiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in kibibytes %L1 KiB %L1 MiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in mebibytes %L1 MiB %L1 GiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in gibibytes %L1 GiB %L1 TiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in tebibytes %L1 TiB %L1 PiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in pebibytes %L1 PiB %L1 EiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in exbibytes %L1 EiB %L1 ZiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in zebibytes %L1 ZiB %L1 YiB IEC 80000 binary prefixes, i.e. KiB = 1024 bytes Size suffix in yobibytes %L1 YiB %L1 B/s Download speed suffix in bytes per second %L1 B/s %L1 KiB/s Download speed suffix in kibibytes per second %L1 KiB/s %L1 MiB/s Download speed suffix in mebibytes per second %L1 MiB/s %L1 GiB/s Download speed suffix in gibibytes per second %L1 GiB/s %L1 TiB/s Download speed suffix in tebibytes per second %L1 TiB/s %L1 PiB/s Download speed suffix in pebibytes per second %L1 PiB/s %L1 EiB/s Download speed suffix in exbibytes per second %L1 EiB/s %L1 ZiB/s Download speed suffix in zebibytes per second %L1 ZiB/s %L1 YiB/s Download speed suffix in yobibytes per second %L1 YiB/s %L1% Progress in percents. %L1 must remain unchanged, % after it is a percent character %L1% %L1 d %L2 h Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" %L1 天 %L2 �时 %L1 h %L2 m Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" %L1 �时 %L2 分 %L1 m %L2 s Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" %L1 分 %L2 秒 %L1 s Remaining time string. %L1 is seconds, "10 s" %L1 秒 Today Yesterday Two days ago %1 at %2 Relative date & time Just now Relative time %n minute(s) ago @item:intext %1 is a whole number %n minutes ago %n minute ago Error opening %1 File opening error, %1 is a file path ---------- Directory opening error, %1 is a file path 打开时出错 %1 Move files from current directory Check box label 从当�目录移动文件 Selected directory should be inside mounted directory 所选目录已在已挂载的目录中 All (%L1) Filter option of torrents list's tracker filter. %L1 is total number of torrents ---------- Filter option of torrents list's download directory filter. %L1 is total number of torrents ---------- Filter option of torrents list's label filter. %L1 is total number of torrents ---------- Filter option of torrents list's status filter. %L1 is total number of torrents 所有Torrent (%L1) %1 (%L2) Filter option of torrents list's tracker filter. %1 is tracker domain name, %L2 is number of torrents with that tracker ---------- Filter option of torrents list's label filter. %1 is label, %L2 is number of torrents with that label %1 (%L2) This directory does not exist 此目录�存在 Edit Labels New label... tremotesf-2.10.1/vcpkg-configuration.json000066400000000000000000000010571523411710200204540ustar00rootroot00000000000000{ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg-configuration.schema.json", "default-registry": { "kind": "builtin", "baseline": "c8f45a069ad0266bc70e703c1252cb51506ddcaf" }, "registries": [ { "kind": "git", "repository": "https://github.com/equeim/vcpkg-registry.git", "baseline": "3e51e21c82a7a8582a9606e4e0d8acf9d671a330", "packages": [ "qttools", "shared-mime-info" ] } ] } tremotesf-2.10.1/vcpkg-overlay-triplets/000077500000000000000000000000001523411710200202345ustar00rootroot00000000000000tremotesf-2.10.1/vcpkg-overlay-triplets/arm64-osx-release.cmake000066400000000000000000000006771523411710200244260ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 include("${VCPKG_ROOT_DIR}/triplets/community/arm64-osx-release.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../cmake/MacOSDeploymentTarget.cmake") set(VCPKG_OSX_DEPLOYMENT_TARGET "${TREMOTESF_MACOS_DEPLOYMENT_TARGET}") set(VCPKG_C_FLAGS "-g -ftrivial-auto-var-init=pattern -fstack-protector-strong -D_FORTIFY_SOURCE=3") set(VCPKG_CXX_FLAGS "${VCPKG_C_FLAGS}") tremotesf-2.10.1/vcpkg-overlay-triplets/arm64-windows-static.cmake000066400000000000000000000007561523411710200251540ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 include("${VCPKG_ROOT_DIR}/triplets/community/arm64-windows-static.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../cmake/WindowsMinimumVersion.cmake") # /await:strict - use C++20 coroutines ABI when building C++17 dependencies set(flags "/await:strict /DWINVER=${TREMOTESF_WINDOWS_WINVER_MACRO} /D_WIN32_WINNT=${TREMOTESF_WINDOWS_WINVER_MACRO}") set(VCPKG_CXX_FLAGS "${flags}") set(VCPKG_C_FLAGS "${flags}") tremotesf-2.10.1/vcpkg-overlay-triplets/x64-osx-release.cmake000066400000000000000000000007221523411710200241050ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 include("${VCPKG_ROOT_DIR}/triplets/community/x64-osx-release.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../cmake/MacOSDeploymentTarget.cmake") set(VCPKG_OSX_DEPLOYMENT_TARGET "${TREMOTESF_MACOS_DEPLOYMENT_TARGET}") set(VCPKG_C_FLAGS "-g -ftrivial-auto-var-init=pattern -fstack-protector-strong -fcf-protection=full -D_FORTIFY_SOURCE=3") set(VCPKG_CXX_FLAGS "${VCPKG_C_FLAGS}") tremotesf-2.10.1/vcpkg-overlay-triplets/x64-windows-static.cmake000066400000000000000000000007421523411710200246370ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2015-2025 Alexey Rochev # # SPDX-License-Identifier: CC0-1.0 include("${VCPKG_ROOT_DIR}/triplets/x64-windows-static.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../cmake/WindowsMinimumVersion.cmake") # /await:strict - use C++20 coroutines ABI when building C++17 dependencies set(flags "/await:strict /DWINVER=${TREMOTESF_WINDOWS_WINVER_MACRO} /D_WIN32_WINNT=${TREMOTESF_WINDOWS_WINVER_MACRO}") set(VCPKG_CXX_FLAGS "${flags}") set(VCPKG_C_FLAGS "${flags}") tremotesf-2.10.1/vcpkg.json000066400000000000000000000027251523411710200156120ustar00rootroot00000000000000{ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "dependencies": [ { "name": "qtbase", "host": true, "default-features": false }, { "name": "qtbase", "default-features": false, "features": [ "brotli", "concurrent", "gui", "network", "pcre2", "png", "shared-mime-info", "testlib", "thread", "widgets", { "name": "openssl", "platform": "!windows" } ] }, { "name": "qttools", "host": true, "default-features": false, "features": [ "linguist" ] }, { "name": "qttranslations", "host": true }, "qtsvg", "fmt", "kwidgetsaddons", "cxxopts", { "name": "cpp-httplib", "features": [ "openssl" ] }, { "name": "pkgconf", "platform": "windows", "host": true } ], "overrides": [ { "name": "double-conversion", "version": "3.3.0", "port-version": 1 } ] }