Compare commits
112 Commits
release-4.
...
release-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8763f08da | ||
|
|
b8259969ac | ||
|
|
d3a57e3e01 | ||
|
|
fbd228b360 | ||
|
|
23176b1a56 | ||
|
|
46afeb0f32 | ||
|
|
114e2ee1ab | ||
|
|
b5af0f71b9 | ||
|
|
bd9c42e004 | ||
|
|
272ff11d65 | ||
|
|
04bd33e5b3 | ||
|
|
6a4c423b88 | ||
|
|
112ab86a6b | ||
|
|
fcd38a497e | ||
|
|
6e75866ed7 | ||
|
|
1f9dde0c37 | ||
|
|
8b13d8f222 | ||
|
|
8d654f9802 | ||
|
|
cfc73da312 | ||
|
|
9801cd0323 | ||
|
|
c01d7a34c1 | ||
|
|
af41a0eece | ||
|
|
c509f57b66 | ||
|
|
8de091f4dd | ||
|
|
e246745776 | ||
|
|
9d42657468 | ||
|
|
2b4fcda463 | ||
|
|
70a2d7bd58 | ||
|
|
a9c85ab321 | ||
|
|
e574bc1c57 | ||
|
|
fc90953f91 | ||
|
|
7016aa372b | ||
|
|
cbe9a27a92 | ||
|
|
97853f31f2 | ||
|
|
66ffb7328d | ||
|
|
9d9101186d | ||
|
|
621ec4e92f | ||
|
|
4b752cba4f | ||
|
|
38c0864bf2 | ||
|
|
c21c3d2300 | ||
|
|
3be5273246 | ||
|
|
37e348ed92 | ||
|
|
36364121ba | ||
|
|
df08bd331c | ||
|
|
abd1ab5539 | ||
|
|
632d33b266 | ||
|
|
35f7e1c896 | ||
|
|
792301dfe4 | ||
|
|
e31cf5ac23 | ||
|
|
0bfe6ff64b | ||
|
|
d40c7e8833 | ||
|
|
8e81d44b3c | ||
|
|
97a30218bc | ||
|
|
e9884b9513 | ||
|
|
a63269e3e1 | ||
|
|
d03e715708 | ||
|
|
927732f190 | ||
|
|
88c991880f | ||
|
|
29290fa109 | ||
|
|
0a8d604ef3 | ||
|
|
532c985b50 | ||
|
|
a32182f794 | ||
|
|
1aebcd3258 | ||
|
|
9f743aab86 | ||
|
|
ece839739e | ||
|
|
2204757eca | ||
|
|
bfda520ef4 | ||
|
|
af91f4ed51 | ||
|
|
41c3a8af01 | ||
|
|
cc7f8372a8 | ||
|
|
d20633f9cc | ||
|
|
961e05e9a8 | ||
|
|
eb98a04245 | ||
|
|
5dc1c10848 | ||
|
|
dcbff74dc0 | ||
|
|
5e29960da2 | ||
|
|
aa43fc8ff4 | ||
|
|
2517e688d9 | ||
|
|
40d94fd8e9 | ||
|
|
eb97e640cb | ||
|
|
2123c1c259 | ||
|
|
6cf1351a77 | ||
|
|
c924904308 | ||
|
|
904bcc14d5 | ||
|
|
c3abe4c2a6 | ||
|
|
7144454a1f | ||
|
|
daaa88fa0d | ||
|
|
0b7c8497f9 | ||
|
|
e3562be0d6 | ||
|
|
e0d0efcc20 | ||
|
|
fb22b58ce6 | ||
|
|
c78ac614f5 | ||
|
|
de15907ea7 | ||
|
|
a4289a517f | ||
|
|
967c3bb55d | ||
|
|
c57896df8f | ||
|
|
911f0d4039 | ||
|
|
e822d4fca7 | ||
|
|
0da132b69e | ||
|
|
691cb4fe2b | ||
|
|
97a053916b | ||
|
|
24bf8eef6d | ||
|
|
4314bbdf9c | ||
|
|
65611cd3dc | ||
|
|
6a4bb1356a | ||
|
|
06593e3678 | ||
|
|
18577d9cb0 | ||
|
|
701b84dc48 | ||
|
|
9a95237b85 | ||
|
|
a6a99fbd36 | ||
|
|
86671bee46 | ||
|
|
f1432a2e3d |
@@ -37,8 +37,6 @@ install:
|
||||
RMDIR /S /Q "%CACHE_DIR%" & MKDIR "%CACHE_DIR%" &&
|
||||
appveyor DownloadFile "%QBT_LIB_URL%" -FileName "c:\qbt_lib.7z" && 7z x "c:\qbt_lib.7z" -o"%CACHE_DIR%" > nul &&
|
||||
COPY "c:\version_new" "%CACHE_DIR%\version")
|
||||
# Qt stay compressed in cache
|
||||
- 7z x "%CACHE_DIR%\qt5_64.7z" -o"c:\qbt" > nul
|
||||
|
||||
before_build:
|
||||
# setup env
|
||||
@@ -47,6 +45,7 @@ before_build:
|
||||
# setup project
|
||||
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
|
||||
# workarounds
|
||||
- MKDIR "c:\qbt"
|
||||
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
|
||||
|
||||
build_script:
|
||||
@@ -69,8 +68,11 @@ after_build:
|
||||
- COPY src\release\qbittorrent.exe upload
|
||||
- COPY src\release\qbittorrent.pdb upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libcrypto-1_1-x64.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libcrypto-1_1-x64.pdb" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libssl-1_1-x64.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\lib\torrent-rasterbar.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\libssl-1_1-x64.pdb" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\torrent-rasterbar.dll" upload
|
||||
- COPY "%CACHE_DIR%\base\bin\torrent-rasterbar.pdb" upload
|
||||
- COPY "%CACHE_DIR%\base\lib\zlib1.dll" upload
|
||||
- COPY C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll upload
|
||||
- COPY C:\Qt\5.15.2\msvc2019_64\bin\Qt5Gui.dll upload
|
||||
|
||||
2
.github/workflows/ci_file_health.yaml
vendored
@@ -2,6 +2,8 @@ name: CI - File health
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: ${{ github.head_ref != '' }}
|
||||
|
||||
27
.github/workflows/ci_macos.yaml
vendored
@@ -2,6 +2,9 @@ name: CI - macOS
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: ${{ github.head_ref != '' }}
|
||||
@@ -31,6 +34,9 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
export \
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 \
|
||||
HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
brew update > /dev/null
|
||||
brew install \
|
||||
cmake ninja \
|
||||
@@ -46,7 +52,7 @@ jobs:
|
||||
curl \
|
||||
-L \
|
||||
-o "${{ runner.temp }}/boost.tar.bz2" \
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.bz2"
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.bz2"
|
||||
tar -xf "${{ runner.temp }}/boost.tar.bz2" -C "${{ github.workspace }}/.."
|
||||
mv "${{ github.workspace }}/.."/boost_* "${{ env.boost_path }}"
|
||||
|
||||
@@ -68,6 +74,7 @@ jobs:
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
@@ -80,11 +87,12 @@ jobs:
|
||||
- name: Build qBittorrent (Qt5)
|
||||
if: ${{ startsWith(matrix.qt_version, 5) }}
|
||||
run: |
|
||||
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
||||
LDFLAGS="$LDFLAGS -gz" \
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS="-Werror -Wno-error=deprecated-declarations" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
||||
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}" \
|
||||
@@ -98,11 +106,12 @@ jobs:
|
||||
- name: Build qBittorrent (Qt6)
|
||||
if: ${{ startsWith(matrix.qt_version, 6) }}
|
||||
run: |
|
||||
CXXFLAGS="$CXXFLAGS -Wno-gnu-zero-variadic-macro-arguments -Werror -Wno-error=deprecated-declarations" \
|
||||
LDFLAGS="$LDFLAGS -gz" \
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS="-Wno-gnu-zero-variadic-macro-arguments -Werror -Wno-error=deprecated-declarations" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DBOOST_ROOT="${{ env.boost_path }}" \
|
||||
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}" \
|
||||
@@ -116,7 +125,17 @@ jobs:
|
||||
|
||||
- name: Prepare build artifacts
|
||||
run: |
|
||||
# create .dmg
|
||||
appName="qbittorrent"
|
||||
if [ "${{ matrix.qbt_gui }}" = "GUI=OFF" ]; then
|
||||
appName="qbittorrent-nox"
|
||||
fi
|
||||
pushd build
|
||||
macdeployqt "$appName.app" -dmg -no-strip
|
||||
popd
|
||||
# prepare upload folder
|
||||
mkdir upload
|
||||
cp "build/$appName.dmg" upload
|
||||
mkdir upload/cmake
|
||||
cp build/compile_commands.json upload/cmake
|
||||
mkdir upload/cmake/libtorrent
|
||||
@@ -125,5 +144,5 @@ jobs:
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-info_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
path: upload
|
||||
|
||||
40
.github/workflows/ci_ubuntu.yaml
vendored
@@ -2,6 +2,9 @@ name: CI - Ubuntu
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: ${{ github.head_ref != '' }}
|
||||
@@ -30,7 +33,7 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt install \
|
||||
build-essential cmake ninja-build pkg-config \
|
||||
libboost-dev libssl-dev zlib1g-dev
|
||||
libboost-dev libssl-dev libxkbcommon-x11-dev zlib1g-dev
|
||||
|
||||
- name: Setup ccache
|
||||
uses: Chocobo1/setup-ccache-action@v1
|
||||
@@ -65,11 +68,12 @@ jobs:
|
||||
- name: Build qBittorrent (Qt5)
|
||||
if: ${{ startsWith(matrix.qt_version, 5) }}
|
||||
run: |
|
||||
CXXFLAGS="$CXXFLAGS -Werror -Wno-error=deprecated-declarations" \
|
||||
LDFLAGS="$LDFLAGS -gz" \
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS="-Werror -Wno-error=deprecated-declarations" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DTESTING=ON \
|
||||
@@ -83,11 +87,12 @@ jobs:
|
||||
- name: Build qBittorrent (Qt6)
|
||||
if: ${{ startsWith(matrix.qt_version, 6) }}
|
||||
run: |
|
||||
CXXFLAGS="$CXXFLAGS -Werror" \
|
||||
LDFLAGS="$LDFLAGS -gz" \
|
||||
cmake \
|
||||
-B build \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS="-Werror" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DQT6=ON \
|
||||
@@ -107,8 +112,35 @@ jobs:
|
||||
mkdir upload/cmake/libtorrent
|
||||
cp libtorrent/build/compile_commands.json upload/cmake/libtorrent
|
||||
|
||||
- name: 'AppImage: Prepare env'
|
||||
run: |
|
||||
sudo apt install libfuse2
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage
|
||||
chmod +x linuxdeploy-x86_64.AppImage
|
||||
chmod +x linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod +x linuxdeploy-plugin-appimage-x86_64.AppImage
|
||||
|
||||
- name: 'AppImage: Prepare nox'
|
||||
if: matrix.qbt_gui == 'GUI=OFF'
|
||||
run: |
|
||||
mkdir -p qbittorrent/usr/share/icons/hicolor/scalable/apps/
|
||||
mkdir -p qbittorrent/usr/share/applications/
|
||||
cp dist/unix/menuicons/scalable/apps/qbittorrent.svg qbittorrent/usr/share/icons/hicolor/scalable/apps/qbittorrent.svg
|
||||
cp .github/workflows/helper/appimage/org.qbittorrent.qBittorrent.desktop qbittorrent/usr/share/applications/org.qbittorrent.qBittorrent.desktop
|
||||
|
||||
- name: 'AppImage: Package'
|
||||
run: |
|
||||
./linuxdeploy-x86_64.AppImage --appdir=qbittorrent --plugin qt
|
||||
rm qbittorrent/apprun-hooks/*
|
||||
cp .github/workflows/helper/appimage/export_vars.sh qbittorrent/apprun-hooks/export_vars.sh
|
||||
NO_APPSTREAM=1 \
|
||||
OUTPUT=upload/qbittorrent-CI_Ubuntu_x86_64.AppImage \
|
||||
./linuxdeploy-x86_64.AppImage --appdir=qbittorrent --output appimage
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-info_ubuntu-x64_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
name: qBittorrent-CI_Ubuntu-x64_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
|
||||
path: upload
|
||||
|
||||
2
.github/workflows/ci_webui.yaml
vendored
@@ -2,6 +2,8 @@ name: CI - WebUI
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: ${{ github.head_ref != '' }}
|
||||
|
||||
7
.github/workflows/ci_windows.yaml
vendored
@@ -2,6 +2,9 @@ name: CI - Windows
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: ${{ github.head_ref != '' }}
|
||||
@@ -67,7 +70,7 @@ jobs:
|
||||
- name: Install boost
|
||||
run: |
|
||||
aria2c `
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.7z" `
|
||||
"https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.7z" `
|
||||
-d "${{ runner.temp }}" `
|
||||
-o "boost.7z"
|
||||
7z x "${{ runner.temp }}/boost.7z" -o"${{ github.workspace }}/.."
|
||||
@@ -149,6 +152,8 @@ jobs:
|
||||
copy "${{ env.Qt6_DIR }}/plugins/sqldrivers/qsqlite.dll" upload/plugins/sqldrivers
|
||||
mkdir upload/plugins/styles
|
||||
copy "${{ env.Qt6_DIR }}/plugins/styles/qwindowsvistastyle.dll" upload/plugins/styles
|
||||
mkdir upload/plugins/tls
|
||||
copy "${{ env.Qt6_DIR }}/plugins/tls/qschannelbackend.dll" upload/plugins/tls
|
||||
# cmake additionals
|
||||
mkdir upload/cmake
|
||||
copy build/compile_commands.json upload/cmake
|
||||
|
||||
2
.github/workflows/coverity-scan.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
- cron: '0 0 1 * *' # Monthly (1st day of month at midnight)
|
||||
workflow_dispatch: # Mainly for testing. Don't forget the Coverity usage limits.
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
coverity_scan:
|
||||
name: Scan
|
||||
|
||||
11
.github/workflows/helper/appimage/export_vars.sh
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# this file is called from AppRun so 'root_dir' will point to where AppRun is
|
||||
root_dir="$(readlink -f "$(dirname "$0")")"
|
||||
|
||||
# Insert the default values because after the test we prepend our path
|
||||
# and it will create problems with DEs (eg KDE) that don't set the variable
|
||||
# and rely on the default paths
|
||||
if [[ -z ${XDG_DATA_DIRS} ]]; then
|
||||
XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
|
||||
fi
|
||||
|
||||
export XDG_DATA_DIRS="${root_dir}/usr/share:${XDG_DATA_DIRS}"
|
||||
6
.github/workflows/helper/appimage/org.qbittorrent.qBittorrent.desktop
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Name=qBittorrent
|
||||
Exec=qbittorrent-nox %U
|
||||
Icon=qbittorrent
|
||||
Type=Application
|
||||
Categories=Network
|
||||
3
.github/workflows/stale_bot.yaml
vendored
@@ -4,6 +4,9 @@ on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
35
.tx/config
@@ -1,27 +1,24 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[qbittorrent.qbittorrent_master]
|
||||
file_filter = src/lang/qbittorrent_<lang>.ts
|
||||
lang_map = pt: pt_PT, zh: zh_CN
|
||||
source_file = src/lang/qbittorrent_en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_v45x]
|
||||
file_filter = src/lang/qbittorrent_<lang>.ts
|
||||
source_file = src/lang/qbittorrent_en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
minimum_perc = 23
|
||||
mode = developer
|
||||
lang_map = pt: pt_PT, zh: zh_CN
|
||||
|
||||
[qbittorrent.qbittorrentdesktop_master]
|
||||
source_file = dist/unix/org.qbittorrent.qBittorrent.desktop
|
||||
source_lang = en
|
||||
type = DESKTOP
|
||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrent_webui_v45x]
|
||||
file_filter = src/webui/www/translations/webui_<lang>.ts
|
||||
source_file = src/webui/www/translations/webui_en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
minimum_perc = 23
|
||||
mode = developer
|
||||
lang_map = pt: pt_PT, zh: zh_CN
|
||||
|
||||
[qbittorrent.qbittorrent_webui]
|
||||
file_filter = src/webui/www/translations/webui_<lang>.ts
|
||||
lang_map = pt: pt_PT, zh: zh_CN
|
||||
source_file = src/webui/www/translations/webui_en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
[o:sledgehammer999:p:qbittorrent:r:qbittorrentdesktop_master]
|
||||
source_file = dist/unix/org.qbittorrent.qBittorrent.desktop
|
||||
source_lang = en
|
||||
type = DESKTOP
|
||||
minimum_perc = 23
|
||||
mode = developer
|
||||
|
||||
61
Changelog
@@ -1,3 +1,64 @@
|
||||
Sun Jun 18 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.4
|
||||
- BUGFIX: Allow to disable confirmation of Pause/Resume All (glassez)
|
||||
- BUGFIX: Sync flag icons with upstream (Priit Uring)
|
||||
- WEBUI: Fix category save path (Raymond Ha)
|
||||
|
||||
Sun May 28 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.3
|
||||
- BUGFIX: Correctly check if database needs to be updated (glassez)
|
||||
- BUGFIX: Prevent incorrect log message about torrent content deletion (glassez)
|
||||
- BUGFIX: Improve finished torrent handling (glassez)
|
||||
- BUGFIX: Correctly initialize group box children as disabled in Preferences (thalieht)
|
||||
- BUGFIX: Don't miss saving "download path" in SQLite storage (glassez)
|
||||
- BUGFIX: Improve logging of running external program (glassez)
|
||||
- WEBUI: Disable UPnP for web UI by default (glassez)
|
||||
- WEBUI: Use workaround for IOS file picker (DivineHawk)
|
||||
- WEBUI: Work around Chrome download limit (Chocobo1)
|
||||
- WEBUI: Improve 'exporting torrent' behavior (Chocobo1)
|
||||
- WINDOWS: NSIS: Add Slovak translation (Christian Danížek)
|
||||
|
||||
Tue Feb 28 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.2
|
||||
- BUGFIX: Don't unexpectedly activate queued torrents when prefetching metadata for added magnets (glassez)
|
||||
- BUGFIX: Update the cached torrent state once recheck is started (glassez)
|
||||
- BUGFIX: Be more likely to allow the system to use power saving modes (glassez)
|
||||
- WEBUI: Migrate away from unsafe function (Chocobo1)
|
||||
- WEBUI: Blacklist bad ciphers for TLS in the server (sledgehammer999)
|
||||
- WEBUI: Allow only TLS 1.2+ in the server (sledgehammer999)
|
||||
- WEBUI: Allow to set read-only directory as torrent location (glassez)
|
||||
- WEBUI: Reject requests that contain backslash in path (glassez)
|
||||
- RSS: Prevent RSS folder from being moved into itself (glassez)
|
||||
- WINDOWS: NSIS: Update Turkish, Uzbek translation (Burak Yavuz, shitcod3r)
|
||||
|
||||
Sun Feb 12 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.1
|
||||
- FEATURE: Re-allow to use icons from system theme (glassez)
|
||||
- BUGFIX: Fix Speed limit icon size (Nowshed H. Imran)
|
||||
- BUGFIX: Revise and fix some text colors (Chocobo1, Nowshed H. Imran)
|
||||
- BUGFIX: Correctly load folder based UI theme (glassez)
|
||||
- BUGFIX: Fix crash due to invalid encoding of tracker URLs (glassez)
|
||||
- BUGFIX: Don't drop !qB extension when renaming incomplete file (glassez)
|
||||
- BUGFIX: Correctly count the number of torrents in subcategories (glassez)
|
||||
- BUGFIX: Use "additional trackers" when metadata retrieving (glassez)
|
||||
- BUGFIX: Apply correct tab order to Category options dialog (glassez)
|
||||
- BUGFIX: Add all torrents passed via the command line (glassez)
|
||||
- BUGFIX: Fix startup performance on Qt5 (glassez)
|
||||
- BUGFIX: Automatic move will now overwrite existing files (aka previous behavior) (glassez)
|
||||
- BUGFIX: Some fixes for loading Chinese locales (sledgehammer999)
|
||||
- BUGFIX: New Pause icon color for toolbar/menu (Nowshed H. Imran, sledgehammer999)
|
||||
- BUGFIX: Adjust env variable for PDB discovery (sledgehammer999)
|
||||
- WEBUI: Fix missing "queued" icon (thalieht)
|
||||
- WEBUI: Return paths using platform-independent separator format (glassez)
|
||||
- WEBUI: Change order of accepted types of file input (Jason Carr)
|
||||
- WEBUI: Add missing icons (brvphoenix)
|
||||
- WEBUI: Add "Resume data storage type" option (thalieht)
|
||||
- WEBUI: Make rename file dialog resizable (Torsten Schwarz)
|
||||
- WEBUI: Prevent incorrect line breaking (David Xuang)
|
||||
- WEBUI: Improve hotkeys (Fidel Selva)
|
||||
- WEBUI: Remove suggestions while searching for torrents (Midhun V Nadh)
|
||||
- WEBUI: Expose "IS PRIVATE" flag (sotiris-bos)
|
||||
- WEBUI: Return name/hash/infohash_v1/infohash_v2 torrent properties (qbittorrentfan)
|
||||
- WINDOWS: Correctly detect drive letter in path (glassez)
|
||||
- WINDOWS: NSIS: Update Swedish, Lithuanian translations (Jonatan, Deividas)
|
||||
- LINUX: Fix tray icon issues (glassez)
|
||||
|
||||
Sat Nov 26 2022 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.5.0
|
||||
- FEATURE: Add `Auto resize columns` functionality (Chocobo1)
|
||||
- FEATURE: Allow to use Category paths in `Manual` mode (glassez)
|
||||
|
||||
20
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.71 for qbittorrent v4.5.0.
|
||||
# Generated by GNU Autoconf 2.71 for qbittorrent v4.5.4.
|
||||
#
|
||||
# Report bugs to <bugs.qbittorrent.org>.
|
||||
#
|
||||
@@ -611,8 +611,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='qbittorrent'
|
||||
PACKAGE_TARNAME='qbittorrent'
|
||||
PACKAGE_VERSION='v4.5.0'
|
||||
PACKAGE_STRING='qbittorrent v4.5.0'
|
||||
PACKAGE_VERSION='v4.5.4'
|
||||
PACKAGE_STRING='qbittorrent v4.5.4'
|
||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||
PACKAGE_URL='https://www.qbittorrent.org/'
|
||||
|
||||
@@ -1329,7 +1329,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures qbittorrent v4.5.0 to adapt to many kinds of systems.
|
||||
\`configure' configures qbittorrent v4.5.4 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1400,7 +1400,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.5.0:";;
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.5.4:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1533,7 +1533,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
qbittorrent configure v4.5.0
|
||||
qbittorrent configure v4.5.4
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@@ -1648,7 +1648,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by qbittorrent $as_me v4.5.0, which was
|
||||
It was created by qbittorrent $as_me v4.5.4, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@@ -4779,7 +4779,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='qbittorrent'
|
||||
VERSION='v4.5.0'
|
||||
VERSION='v4.5.4'
|
||||
|
||||
|
||||
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
||||
@@ -7237,7 +7237,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by qbittorrent $as_me v4.5.0, which was
|
||||
This file was extended by qbittorrent $as_me v4.5.4, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -7297,7 +7297,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
qbittorrent config.status v4.5.0
|
||||
qbittorrent config.status v4.5.4
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
AC_INIT([qbittorrent], [v4.5.0], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_INIT([qbittorrent], [v4.5.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
: ${CFLAGS=""}
|
||||
|
||||
8
dist/docker/.env
vendored
@@ -1,8 +0,0 @@
|
||||
# refer to Readme.md for an explanation of the variables
|
||||
|
||||
QBT_EULA=
|
||||
QBT_VERSION=devel
|
||||
QBT_WEBUI_PORT=8080
|
||||
|
||||
QBT_CONFIG_PATH=<your_path>/config
|
||||
QBT_DOWNLOADS_PATH=<your_path>/downloads
|
||||
62
dist/docker/Dockerfile
vendored
@@ -1,62 +0,0 @@
|
||||
# image for building
|
||||
FROM alpine:latest AS builder
|
||||
|
||||
ARG QBT_VERSION
|
||||
|
||||
# alpine linux qbittorrent package: https://git.alpinelinux.org/aports/tree/community/qbittorrent/APKBUILD
|
||||
|
||||
RUN \
|
||||
apk --update-cache add \
|
||||
boost-dev \
|
||||
cmake \
|
||||
g++ \
|
||||
libtorrent-rasterbar-dev \
|
||||
ninja \
|
||||
qt6-qtbase-dev \
|
||||
qt6-qttools-dev
|
||||
|
||||
RUN \
|
||||
if [ "$QBT_VERSION" = "devel" ]; then \
|
||||
wget https://github.com/qbittorrent/qBittorrent/archive/refs/heads/master.zip && \
|
||||
unzip master.zip && \
|
||||
cd qBittorrent-master ; \
|
||||
else \
|
||||
wget "https://github.com/qbittorrent/qBittorrent/archive/refs/tags/release-${QBT_VERSION}.tar.gz" && \
|
||||
tar -xf "release-${QBT_VERSION}.tar.gz" && \
|
||||
cd "qBittorrent-release-${QBT_VERSION}" ; \
|
||||
fi && \
|
||||
cmake \
|
||||
-B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DGUI=OFF \
|
||||
-DQT6=ON \
|
||||
-DSTACKTRACE=OFF && \
|
||||
cmake --build build && \
|
||||
cmake --install build
|
||||
|
||||
# image for running
|
||||
FROM alpine:latest
|
||||
|
||||
RUN \
|
||||
apk --no-cache add \
|
||||
doas \
|
||||
libtorrent-rasterbar \
|
||||
python3 \
|
||||
qt6-qtbase \
|
||||
tini
|
||||
|
||||
RUN \
|
||||
adduser \
|
||||
-D \
|
||||
-H \
|
||||
-s /sbin/nologin \
|
||||
-u 1000 \
|
||||
qbtUser && \
|
||||
echo "permit nopass :root" >> "/etc/doas.d/doas.conf"
|
||||
|
||||
COPY --from=builder /usr/local/bin/qbittorrent-nox /usr/bin/qbittorrent-nox
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "-g", "--", "/entrypoint.sh"]
|
||||
101
dist/docker/Readme.md
vendored
@@ -1,101 +0,0 @@
|
||||
# qBittorrent-nox Docker Image
|
||||
|
||||
This Dockerfile allows you to build a Docker Image containing qBittorrent-nox
|
||||
|
||||
## Prerequisites
|
||||
|
||||
In order to build/run this image you'll need Docker installed: https://docs.docker.com/get-docker/
|
||||
|
||||
If you don't need the GUI, you can just install Docker Engine: https://docs.docker.com/engine/install/
|
||||
|
||||
It is also recommended to install Docker Compose as it can significantly ease the process: https://docs.docker.com/compose/install/
|
||||
|
||||
## Building Docker Image
|
||||
|
||||
* If you are using Docker (not Docker Compose) then run the following commands in this folder:
|
||||
```shell
|
||||
export \
|
||||
QBT_VERSION=devel
|
||||
docker build \
|
||||
--build-arg QBT_VERSION \
|
||||
-t qbittorrent-nox:"$QBT_VERSION" \
|
||||
.
|
||||
```
|
||||
|
||||
* If you are using Docker Compose then you should edit `.env` file first.
|
||||
You can find an explanation of the variables in the following [Parameters](#parameters) section. \
|
||||
Then run the following commands in this folder:
|
||||
```shell
|
||||
docker compose build \
|
||||
--build-arg QBT_VERSION
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
#### Environment variables
|
||||
|
||||
* `QBT_EULA` \
|
||||
This environment variable defines whether you accept the end-user license agreement (EULA) of qBittorrent. \
|
||||
**Put `accept` only if you understand and accepted the EULA.** You can find
|
||||
the EULA [here](https://github.com/qbittorrent/qBittorrent/blob/56667e717b82c79433ecb8a5ff6cc2d7b315d773/src/app/main.cpp#L320-L323).
|
||||
* `QBT_VERSION` \
|
||||
This environment variable specifies the version of qBittorrent-nox to be built. \
|
||||
For example, `4.4.0` is a valid entry. You can find all tagged versions [here](https://github.com/qbittorrent/qBittorrent/tags). \
|
||||
Or you can put `devel` to build the latest development version.
|
||||
* `QBT_WEBUI_PORT` \
|
||||
This environment variable sets the port number which qBittorrent WebUI will be binded to.
|
||||
|
||||
#### Volumes
|
||||
|
||||
There are some paths involved:
|
||||
* `<your_path>/config` \
|
||||
Full path to a folder on your host machine which will store qBittorrent configurations.
|
||||
Using relative path won't work.
|
||||
* `<your_path>/downloads` \
|
||||
Full path to a folder on your host machine which will store the files downloaded by qBittorrent.
|
||||
Using relative path won't work.
|
||||
|
||||
## Running container
|
||||
|
||||
* Using Docker (not Docker Compose), simply run:
|
||||
```shell
|
||||
export \
|
||||
QBT_EULA=accept \
|
||||
QBT_VERSION=devel \
|
||||
QBT_WEBUI_PORT=8080 \
|
||||
QBT_CONFIG_PATH="/tmp/bbb/config"
|
||||
QBT_DOWNLOADS_PATH="/tmp/bbb/downloads"
|
||||
docker run \
|
||||
-t \
|
||||
--read-only \
|
||||
--rm \
|
||||
--tmpfs /tmp \
|
||||
--name qbittorrent-nox \
|
||||
-e QBT_EULA \
|
||||
-e QBT_WEBUI_PORT \
|
||||
-p "$QBT_WEBUI_PORT":"$QBT_WEBUI_PORT"/tcp \
|
||||
-p 6881:6881/tcp \
|
||||
-p 6881:6881/udp \
|
||||
-v "$QBT_CONFIG_PATH":/config \
|
||||
-v "$QBT_DOWNLOADS_PATH":/downloads \
|
||||
qbittorrent-nox:"$QBT_VERSION"
|
||||
```
|
||||
|
||||
* Using Docker Compose:
|
||||
```shell
|
||||
docker compose up
|
||||
```
|
||||
|
||||
Then you can login at: `http://127.0.0.1:8080`
|
||||
|
||||
## Stopping container
|
||||
|
||||
* Using Docker (not Docker Compose):
|
||||
```shell
|
||||
docker stop -t 1800 qbittorrent-nox
|
||||
```
|
||||
|
||||
* Using Docker Compose:
|
||||
```shell
|
||||
docker compose down
|
||||
```
|
||||
25
dist/docker/docker-compose.yml
vendored
@@ -1,25 +0,0 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
qbittorrent-nox:
|
||||
build: .
|
||||
container_name: qbittorrent-nox
|
||||
environment:
|
||||
- QBT_EULA=${QBT_EULA}
|
||||
- QBT_VERSION=${QBT_VERSION}
|
||||
- QBT_WEBUI_PORT=${QBT_WEBUI_PORT}
|
||||
image: qbittorrent-nox:${QBT_VERSION}
|
||||
ports:
|
||||
# for bittorrent traffic
|
||||
- 6881:6881/tcp
|
||||
- 6881:6881/udp
|
||||
# for WebUI
|
||||
- ${QBT_WEBUI_PORT}:${QBT_WEBUI_PORT}/tcp
|
||||
read_only: true
|
||||
stop_grace_period: 30m
|
||||
tmpfs:
|
||||
- /tmp
|
||||
tty: true
|
||||
volumes:
|
||||
- ${QBT_CONFIG_PATH}:/config
|
||||
- ${QBT_DOWNLOADS_PATH}:/downloads
|
||||
35
dist/docker/entrypoint.sh
vendored
@@ -1,35 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
downloadsPath="/downloads"
|
||||
profilePath="/config"
|
||||
qbtConfigFile="$profilePath/qBittorrent/config/qBittorrent.conf"
|
||||
|
||||
if [ ! -f "$qbtConfigFile" ]; then
|
||||
mkdir -p "$(dirname $qbtConfigFile)"
|
||||
cat << EOF > "$qbtConfigFile"
|
||||
[BitTorrent]
|
||||
Session\DefaultSavePath=/downloads
|
||||
Session\Port=6881
|
||||
Session\TempPath=/downloads/temp
|
||||
|
||||
[LegalNotice]
|
||||
Accepted=false
|
||||
EOF
|
||||
|
||||
if [ "$QBT_EULA" = "accept" ]; then
|
||||
sed -i '/^\[LegalNotice\]$/{$!{N;s|\(\[LegalNotice\]\nAccepted=\).*|\1true|}}' "$qbtConfigFile"
|
||||
else
|
||||
sed -i '/^\[LegalNotice\]$/{$!{N;s|\(\[LegalNotice\]\nAccepted=\).*|\1false|}}' "$qbtConfigFile"
|
||||
fi
|
||||
fi
|
||||
|
||||
# those are owned by root by default
|
||||
# don't change existing files owner in `$downloadsPath`
|
||||
chown qbtUser:qbtUser "$downloadsPath"
|
||||
chown qbtUser:qbtUser -R "$profilePath"
|
||||
|
||||
doas -u qbtUser \
|
||||
qbittorrent-nox \
|
||||
--profile="$profilePath" \
|
||||
--webui-port="$QBT_WEBUI_PORT" \
|
||||
"$@"
|
||||
2
dist/mac/Info.plist
vendored
@@ -55,7 +55,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.5.0</string>
|
||||
<string>4.5.4</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
||||
@@ -74,6 +74,6 @@
|
||||
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<releases>
|
||||
<release version="4.5.0" date="2022-11-26"/>
|
||||
<release version="4.5.4" date="2023-06-18"/>
|
||||
</releases>
|
||||
</component>
|
||||
|
||||
10
dist/unix/org.qbittorrent.qBittorrent.desktop
vendored
@@ -98,8 +98,8 @@ Name[is]=qBittorrent
|
||||
Comment[it]=Scarica e condividi file tramite BitTorrent
|
||||
GenericName[it]=Client BitTorrent
|
||||
Name[it]=qBittorrent
|
||||
Comment[ja]=BitTorrent でファイルをダウンロードおよび共有
|
||||
GenericName[ja]=BitTorrent クライアント
|
||||
Comment[ja]=BitTorrentでファイルのダウンロードと共有
|
||||
GenericName[ja]=BitTorrentクライアント
|
||||
Name[ja]=qBittorrent
|
||||
Comment[ka]=გადმოტვირთეთ და გააზიარეთ ფაილები BitTorrent-ის საშუალებით
|
||||
GenericName[ka]=BitTorrent კლიენტი
|
||||
@@ -160,7 +160,7 @@ Comment[te]=క్యు బిట్ టొరెంట్ తో ఫైల్
|
||||
GenericName[te]=క్యు బిట్ టొరెంట్ క్లయింట్
|
||||
Name[te]=qBittorrent
|
||||
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่าน BitTorrent
|
||||
GenericName[th]=ไคลเอนต์ BitTorrent
|
||||
GenericName[th]=โปรแกรมบิททอเร้นท์
|
||||
Name[th]=qBittorrent
|
||||
Comment[tr]=Dosyaları BitTorrent üzerinden indirin ve paylaşın
|
||||
GenericName[tr]=BitTorrent istemcisi
|
||||
@@ -178,7 +178,7 @@ Comment[zh_HK]=經由BitTorrent下載並分享檔案
|
||||
GenericName[zh_HK]=BitTorrent用戶端
|
||||
Name[zh_HK]=qBittorrent
|
||||
Comment[zh_TW]=經由 BitTorrent 下載並分享檔案
|
||||
GenericName[zh_TW]=BitTorrent 客戶端
|
||||
GenericName[zh_TW]=BitTorrent 用戶端
|
||||
Name[zh_TW]=qBittorrent
|
||||
Comment[eo]=Elŝutu kaj kunhavigu dosierojn per BitTorrent
|
||||
GenericName[eo]=BitTorrent-kliento
|
||||
@@ -208,7 +208,7 @@ Name[ltg]=qBittorrent
|
||||
Comment[hi_IN]=BitTorrent द्वारा फाइल डाउनलोड व सहभाजन
|
||||
GenericName[hi_IN]=Bittorrent साधन
|
||||
Name[hi_IN]=qBittorrent
|
||||
Comment[az@latin]=Faylları BitTorrent vasitəsilə göndərin və paylaşın
|
||||
Comment[az@latin]=Faylları BitTorrent vasitəsilə endirin və paylaşın
|
||||
GenericName[az@latin]=BitTorrent client
|
||||
Name[az@latin]=qBittorrent
|
||||
Comment[lv_LV]=Lejupielādēt un koplietot failus ar BitTorrent
|
||||
|
||||
2
dist/windows/config.nsi
vendored
@@ -25,7 +25,7 @@
|
||||
; 4.5.1.3 -> good
|
||||
; 4.5.1.3.2 -> bad
|
||||
; 4.5.0beta -> bad
|
||||
!define /ifndef QBT_VERSION "4.5.0"
|
||||
!define /ifndef QBT_VERSION "4.5.4"
|
||||
|
||||
; Option that controls the installer's window name
|
||||
; If set, its value will be used like this:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
||||
LangString inst_qbt_req ${LANG_LITHUANIAN} "qBittorrent (reikalingas)"
|
||||
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
||||
LangString inst_dekstop ${LANG_LITHUANIAN} "Sukurti darbalaukyje nuorodą"
|
||||
LangString inst_dekstop ${LANG_LITHUANIAN} "Sukurti nuorodą darbalaukyje"
|
||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_LITHUANIAN} "Sukurti Pradėti meniu nuorodą"
|
||||
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
||||
@@ -13,27 +13,27 @@ LangString inst_torrent ${LANG_LITHUANIAN} "Atidaryti .torrent failus su qBittor
|
||||
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
||||
LangString inst_magnet ${LANG_LITHUANIAN} "Atidaryti magneto nuorodas su qBittorrent"
|
||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||
LangString inst_firewall ${LANG_LITHUANIAN} "Sukurti Windows užkardos leidimą"
|
||||
LangString inst_firewall ${LANG_LITHUANIAN} "Sukurti Windows interneto užkardos leidimą"
|
||||
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_LITHUANIAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_LITHUANIAN} "Išjungti Windows path ilgio limitaciją (260 ženklų MAX_PATH limitacija, reikalinga versija yra Windows 10 1607 ar naujesnė)"
|
||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||
LangString inst_firewallinfo ${LANG_LITHUANIAN} "Pridedu Windows užkardos leidimą"
|
||||
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
||||
LangString inst_warning ${LANG_LITHUANIAN} "qBittorrent yra paleistas. Prašau uždaryti programą prieš įdiegiant."
|
||||
LangString inst_warning ${LANG_LITHUANIAN} "qBittorrent yra paleistas. Prašome uždaryti programą prieš įdiegiant."
|
||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_LITHUANIAN} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_LITHUANIAN} "Dabartinė versija bus pašalinta. Naudotojo nustatymai ir torrentai liks nepakeisti."
|
||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||
LangString inst_unist ${LANG_LITHUANIAN} "Šalinu ankstesnę versiją."
|
||||
LangString inst_unist ${LANG_LITHUANIAN} "Šalinama ankstesnė versija."
|
||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_LITHUANIAN} "Paleisti qBittorrent."
|
||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_LITHUANIAN} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_LITHUANIAN} "Šis įdiegėjas veikia tik su 64 bitų Windows versija."
|
||||
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_LITHUANIAN} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_LITHUANIAN} "Ši qBittorent versija reikalauja bent Windows 7."
|
||||
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_LITHUANIAN} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_LITHUANIAN} "Šis įdiegėjas reikalauja bent Windows 10 1809."
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_LITHUANIAN} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_LITHUANIAN} "Pašalinti qBittorrent"
|
||||
|
||||
;------------------------------------
|
||||
;Uninstaller strings
|
||||
@@ -49,14 +49,14 @@ LangString remove_registry ${LANG_LITHUANIAN} "Pašalinti registro raktus"
|
||||
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
||||
LangString remove_conf ${LANG_LITHUANIAN} "Pašalinti nustatymų failus"
|
||||
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
||||
LangString remove_firewall ${LANG_LITHUANIAN} "Pašalinti Windows užkardos leidimą"
|
||||
LangString remove_firewall ${LANG_LITHUANIAN} "Pašalinti Windows interneto užkardos leidimą"
|
||||
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
||||
LangString remove_firewallinfo ${LANG_LITHUANIAN} "Šalinu Windows užkardos leidimą"
|
||||
LangString remove_firewallinfo ${LANG_LITHUANIAN} "Šalinamas Windows interneto užkardos leidimas"
|
||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||
LangString remove_cache ${LANG_LITHUANIAN} "Pašalinti torentus ir podėlio duomenis"
|
||||
LangString remove_cache ${LANG_LITHUANIAN} "Pašalinti torentus ir talpyklos duomenis"
|
||||
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||
LangString uninst_warning ${LANG_LITHUANIAN} "qBittorrent yra paleistas. Prašau uždarykite programą prieš išdiegiant."
|
||||
LangString uninst_warning ${LANG_LITHUANIAN} "qBittorrent yra paleistas. Prašome uždaryti programą prieš išdiegiant."
|
||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||
LangString uninst_tor_warn ${LANG_LITHUANIAN} "Negaliu pašalinti .torrent asociacijos. Ji yra susieti su:"
|
||||
LangString uninst_tor_warn ${LANG_LITHUANIAN} "Neįmanoma pašalinti .torrent asociacijos. Ji yra susieta su:"
|
||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||
LangString uninst_mag_warn ${LANG_LITHUANIAN} "Negaliu pašalinti magneto asociacijos. Jis susietas su:"
|
||||
LangString uninst_mag_warn ${LANG_LITHUANIAN} "Neįmanoma pašalinti magneto asociacijos. Ji yra susieta su:"
|
||||
|
||||
56
dist/windows/installer-translations/slovak.nsi
vendored
@@ -1,62 +1,62 @@
|
||||
;Installer strings
|
||||
|
||||
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
||||
LangString inst_qbt_req ${LANG_SLOVAK} "qBittorrent (required)"
|
||||
LangString inst_qbt_req ${LANG_SLOVAK} "qBittorrent (požadované)"
|
||||
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
||||
LangString inst_dekstop ${LANG_SLOVAK} "Create Desktop Shortcut"
|
||||
LangString inst_dekstop ${LANG_SLOVAK} "Vytvoriť odkaz na pracovnej ploche"
|
||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_SLOVAK} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_SLOVAK} "Vytvoriť odkaz v štart menu"
|
||||
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
||||
LangString inst_startup ${LANG_SLOVAK} "Start qBittorrent on Windows start up"
|
||||
LangString inst_startup ${LANG_SLOVAK} "Sputiť qBittorrent pri štarte Windowsu"
|
||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||
LangString inst_torrent ${LANG_SLOVAK} "Open .torrent files with qBittorrent"
|
||||
LangString inst_torrent ${LANG_SLOVAK} "Otvárať .torrent súbory v qBittorrent"
|
||||
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
||||
LangString inst_magnet ${LANG_SLOVAK} "Open magnet links with qBittorrent"
|
||||
LangString inst_magnet ${LANG_SLOVAK} "Otvárať magnet odkazy v qBittorrent"
|
||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||
LangString inst_firewall ${LANG_SLOVAK} "Add Windows Firewall rule"
|
||||
LangString inst_firewall ${LANG_SLOVAK} "Pridať pravidlo do Windows Firewall"
|
||||
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_SLOVAK} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_SLOVAK} "Vypnúť limit dĺžky cesty Windowsu (260 znaková MAX_PATH limitácia, vyžaduje Windows 10 1607 alebo novšie)"
|
||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||
LangString inst_firewallinfo ${LANG_SLOVAK} "Adding Windows Firewall rule"
|
||||
LangString inst_firewallinfo ${LANG_SLOVAK} "Pridáva sa pravidlo do Windows Firewall"
|
||||
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
||||
LangString inst_warning ${LANG_SLOVAK} "qBittorrent is running. Please close the application before installing."
|
||||
LangString inst_warning ${LANG_SLOVAK} "qBittorrent je spustený. Zatvorte prosím aplikáciu pred inštaláciou."
|
||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_SLOVAK} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_SLOVAK} "Aktuálna verzia bude odinštalovaná. Užívateľské nastavenia a torrenty sa zachovajú."
|
||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||
LangString inst_unist ${LANG_SLOVAK} "Uninstalling previous version."
|
||||
LangString inst_unist ${LANG_SLOVAK} "Odinštalácia predchádzajúcej verzie."
|
||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_SLOVAK} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_SLOVAK} "Spustiť qBittorrent."
|
||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_SLOVAK} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_SLOVAK} "Táto inštalácia funguje iba na 64-bitových verziách Windowsu."
|
||||
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_SLOVAK} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_SLOVAK} "Táto qBittorrent verzia vyžaduje aspoň Windows 7."
|
||||
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_SLOVAK} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_SLOVAK} "Tento inštalátor vyžaduje aspoň Windows 10 1809."
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_SLOVAK} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_SLOVAK} "Odinštalovať qBittorrent"
|
||||
|
||||
;------------------------------------
|
||||
;Uninstaller strings
|
||||
|
||||
;LangString remove_files ${LANG_ENGLISH} "Remove files"
|
||||
LangString remove_files ${LANG_SLOVAK} "Remove files"
|
||||
LangString remove_files ${LANG_SLOVAK} "Odstrániť súbory"
|
||||
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
|
||||
LangString remove_shortcuts ${LANG_SLOVAK} "Remove shortcuts"
|
||||
LangString remove_shortcuts ${LANG_SLOVAK} "Odstrániť odkazy"
|
||||
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
|
||||
LangString remove_associations ${LANG_SLOVAK} "Remove file associations"
|
||||
LangString remove_associations ${LANG_SLOVAK} "Odstrániť asociácie súborov"
|
||||
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
|
||||
LangString remove_registry ${LANG_SLOVAK} "Remove registry keys"
|
||||
LangString remove_registry ${LANG_SLOVAK} "Odstrániť kľúče registrov"
|
||||
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
||||
LangString remove_conf ${LANG_SLOVAK} "Remove configuration files"
|
||||
LangString remove_conf ${LANG_SLOVAK} "Odstrániť konfiguračné súbory"
|
||||
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
||||
LangString remove_firewall ${LANG_SLOVAK} "Remove Windows Firewall rule"
|
||||
LangString remove_firewall ${LANG_SLOVAK} "Odstrániť pravidlo z Windows Firewall"
|
||||
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
||||
LangString remove_firewallinfo ${LANG_SLOVAK} "Removing Windows Firewall rule"
|
||||
LangString remove_firewallinfo ${LANG_SLOVAK} "Odstraňuje sa pravidlo z Windows Firewall"
|
||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||
LangString remove_cache ${LANG_SLOVAK} "Remove torrents and cached data"
|
||||
LangString remove_cache ${LANG_SLOVAK} "Odstrániť torrenty a dáta z vyrovnávacej pamäti"
|
||||
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||
LangString uninst_warning ${LANG_SLOVAK} "qBittorrent is running. Please close the application before uninstalling."
|
||||
LangString uninst_warning ${LANG_SLOVAK} "qBittorrent je spustený. Zatvorte prosím aplikáciu pred odinštaláciou."
|
||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||
LangString uninst_tor_warn ${LANG_SLOVAK} "Not removing .torrent association. It is associated with:"
|
||||
LangString uninst_tor_warn ${LANG_SLOVAK} "Neodstraňuje sa .torrent asociácia. Asociované s:"
|
||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||
LangString uninst_mag_warn ${LANG_SLOVAK} "Not removing magnet association. It is associated with:"
|
||||
LangString uninst_mag_warn ${LANG_SLOVAK} "Neodstraňuje sa magnet asociácia. Asociované s:"
|
||||
|
||||
@@ -27,11 +27,11 @@ LangString inst_unist ${LANG_SWEDISH} "Avinstallerar tidigare version."
|
||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_SWEDISH} "Kör qBittorrent."
|
||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_SWEDISH} "Installationsprogrammet fungerar endast i 64-bitars Windows-versioner."
|
||||
LangString inst_requires_64bit ${LANG_SWEDISH} "Det här installationsprogrammet fungerar endast i 64-bitars Windows-versioner."
|
||||
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_SWEDISH} "Den här qBittorrent-versionen kräver minst Windows 7."
|
||||
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_SWEDISH} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_SWEDISH} "Det här installationsprogrammet kräver minst Windows 10 1809."
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_SWEDISH} "Avinstallera qBittorrent"
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ LangString inst_requires_64bit ${LANG_TURKISH} "Bu yükleyici sadece 64-bit Wind
|
||||
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_TURKISH} "Bu qBittorrent sürümü en az Windows 7 gerektirir."
|
||||
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_TURKISH} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_TURKISH} "Bu yükleyici en az Windows 10 1809 gerektirir."
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_TURKISH} "qBittorrent'i kaldır"
|
||||
|
||||
|
||||
56
dist/windows/installer-translations/uzbek.nsi
vendored
@@ -1,62 +1,62 @@
|
||||
;Installer strings
|
||||
|
||||
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
||||
LangString inst_qbt_req ${LANG_UZBEK} "qBittorrent (required)"
|
||||
LangString inst_qbt_req ${LANG_UZBEK} "qBittorrent (talab qilinadi)"
|
||||
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
||||
LangString inst_dekstop ${LANG_UZBEK} "Create Desktop Shortcut"
|
||||
LangString inst_dekstop ${LANG_UZBEK} "Ish Stolida Yorliq Yaratilsin"
|
||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_UZBEK} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_UZBEK} "Boshlash Menyusida Yorliq Yaratilsin"
|
||||
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
|
||||
LangString inst_startup ${LANG_UZBEK} "Start qBittorrent on Windows start up"
|
||||
LangString inst_startup ${LANG_UZBEK} "qBittorrent Windows bilan birga ishga tushirilsin"
|
||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||
LangString inst_torrent ${LANG_UZBEK} "Open .torrent files with qBittorrent"
|
||||
LangString inst_torrent ${LANG_UZBEK} ".torrent fayllar qBittorrent bilan ochilsin"
|
||||
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
|
||||
LangString inst_magnet ${LANG_UZBEK} "Open magnet links with qBittorrent"
|
||||
LangString inst_magnet ${LANG_UZBEK} "Magnit havolalar qBittorrent bilan ochilsin"
|
||||
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
|
||||
LangString inst_firewall ${LANG_UZBEK} "Add Windows Firewall rule"
|
||||
LangString inst_firewall ${LANG_UZBEK} "Windows Xavfsizlik Devori qoidasi qoʻshilsin"
|
||||
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_UZBEK} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
|
||||
LangString inst_pathlimit ${LANG_UZBEK} "Windows yoʻl uzunligi cheklovi olib tashlansin (260 belgi MAX_PATH cheklovi, Windows 10 1607 va yuqorisi talab qilinadi)"
|
||||
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
|
||||
LangString inst_firewallinfo ${LANG_UZBEK} "Adding Windows Firewall rule"
|
||||
LangString inst_firewallinfo ${LANG_UZBEK} "Windows Xavfsizlik Devori qoidasini qoʻshish"
|
||||
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
|
||||
LangString inst_warning ${LANG_UZBEK} "qBittorrent is running. Please close the application before installing."
|
||||
LangString inst_warning ${LANG_UZBEK} "qBittorrent ishga tushgan. Iltimos, oʻrnatishdan oldin dasturni yoping."
|
||||
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_UZBEK} "Current version will be uninstalled. User settings and torrents will remain intact."
|
||||
LangString inst_uninstall_question ${LANG_UZBEK} "Hozirgi versiya oʻchiriladi. Foydalanuvchi sozlamalari va torrentlar oʻzgarishsiz qoladi."
|
||||
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
|
||||
LangString inst_unist ${LANG_UZBEK} "Uninstalling previous version."
|
||||
LangString inst_unist ${LANG_UZBEK} "Oldingi versiyani oʻchirish."
|
||||
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_UZBEK} "Launch qBittorrent."
|
||||
LangString launch_qbt ${LANG_UZBEK} "qBittorrent ishga tushirilsin."
|
||||
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_UZBEK} "This installer works only in 64-bit Windows versions."
|
||||
LangString inst_requires_64bit ${LANG_UZBEK} "Bu oʻrnatuvchi faqat Windows 64-bit versiyalarda ishlaydi."
|
||||
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_UZBEK} "This qBittorrent version requires at least Windows 7."
|
||||
LangString inst_requires_win7 ${LANG_UZBEK} "qBittorrent bu versiyasi kamida Windows 7 talab qiladi."
|
||||
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_UZBEK} "This installer requires at least Windows 10 1809."
|
||||
LangString inst_requires_win10 ${LANG_UZBEK} "Bu oʻrnatuvchi kamida Windows 10 1809 talab qiladi."
|
||||
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_UZBEK} "Uninstall qBittorrent"
|
||||
LangString inst_uninstall_link_description ${LANG_UZBEK} "qBittorrent oʻchirilsin"
|
||||
|
||||
;------------------------------------
|
||||
;Uninstaller strings
|
||||
|
||||
;LangString remove_files ${LANG_ENGLISH} "Remove files"
|
||||
LangString remove_files ${LANG_UZBEK} "Remove files"
|
||||
LangString remove_files ${LANG_UZBEK} "Fayllar oʻchirilsin"
|
||||
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
|
||||
LangString remove_shortcuts ${LANG_UZBEK} "Remove shortcuts"
|
||||
LangString remove_shortcuts ${LANG_UZBEK} "Yorliqlar oʻchirilsin"
|
||||
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
|
||||
LangString remove_associations ${LANG_UZBEK} "Remove file associations"
|
||||
LangString remove_associations ${LANG_UZBEK} "Fayl birlashmalari oʻchirilsin"
|
||||
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
|
||||
LangString remove_registry ${LANG_UZBEK} "Remove registry keys"
|
||||
LangString remove_registry ${LANG_UZBEK} "Reyester kalitlari oʻchirilsin"
|
||||
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
|
||||
LangString remove_conf ${LANG_UZBEK} "Remove configuration files"
|
||||
LangString remove_conf ${LANG_UZBEK} "Sozlama fayllari oʻchirilsin"
|
||||
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
||||
LangString remove_firewall ${LANG_UZBEK} "Remove Windows Firewall rule"
|
||||
LangString remove_firewall ${LANG_UZBEK} "Windows Xavfsizlik Devori qoidasi oʻchirilsin"
|
||||
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
||||
LangString remove_firewallinfo ${LANG_UZBEK} "Removing Windows Firewall rule"
|
||||
LangString remove_firewallinfo ${LANG_UZBEK} "Windows Xavfsizlik Devori qoidasini oʻchirish"
|
||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||
LangString remove_cache ${LANG_UZBEK} "Remove torrents and cached data"
|
||||
LangString remove_cache ${LANG_UZBEK} "Torrentlar va keshlangan maʼlumotlar oʻchirilsin"
|
||||
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||
LangString uninst_warning ${LANG_UZBEK} "qBittorrent is running. Please close the application before uninstalling."
|
||||
LangString uninst_warning ${LANG_UZBEK} "qBittorrent ishga tushgan. Iltimos, oʻchirishdan oldin dasturni yoping."
|
||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||
LangString uninst_tor_warn ${LANG_UZBEK} "Not removing .torrent association. It is associated with:"
|
||||
LangString uninst_tor_warn ${LANG_UZBEK} ".torrent birlashmasi oʻchirilmadi. U quyidagi bilan birlashgan:"
|
||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||
LangString uninst_mag_warn ${LANG_UZBEK} "Not removing magnet association. It is associated with:"
|
||||
LangString uninst_mag_warn ${LANG_UZBEK} "Magnit birlashmasi oʻchirilmadi. U quyidagi bilan birlashgan:"
|
||||
|
||||
2
dist/windows/installer.nsi
vendored
@@ -35,6 +35,8 @@ Section $(inst_qbt_req) ;"qBittorrent (required)"
|
||||
SetOutPath "$INSTDIR\translations"
|
||||
; Put files there
|
||||
File /r "translations\qt*.qm"
|
||||
; Restore output path because it affects `CreateShortCut`. It affects the "Start in" field.
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Write the installation path into the registry
|
||||
WriteRegStr HKLM "Software\qBittorrent" "InstallLocation" "$INSTDIR"
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
#include "base/version.h"
|
||||
#include "applicationinstancemanager.h"
|
||||
#include "filelogger.h"
|
||||
#include "upgrade.h"
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
#include "gui/addnewtorrentdialog.h"
|
||||
@@ -171,6 +172,18 @@ Application::Application(int &argc, char **argv)
|
||||
SettingsStorage::initInstance();
|
||||
Preferences::initInstance();
|
||||
|
||||
const bool firstTimeUser = !Preferences::instance()->getAcceptedLegal();
|
||||
if (!firstTimeUser)
|
||||
{
|
||||
if (!upgrade())
|
||||
throw RuntimeError(u"Failed migration of old settings"_qs); // Not translatable. Translation isn't configured yet.
|
||||
handleChangedDefaults(DefaultPreferencesMode::Legacy);
|
||||
}
|
||||
else
|
||||
{
|
||||
handleChangedDefaults(DefaultPreferencesMode::Current);
|
||||
}
|
||||
|
||||
initializeTranslation();
|
||||
|
||||
connect(this, &QCoreApplication::aboutToQuit, this, &Application::cleanup);
|
||||
@@ -440,6 +453,7 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
};
|
||||
|
||||
const QString logMsg = tr("Running external program. Torrent: \"%1\". Command: `%2`");
|
||||
const QString logMsgError = tr("Failed to run external program. Torrent: \"%1\". Command: `%2`");
|
||||
|
||||
// The processing sequenece is different for Windows and other OS, this is intentional
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -459,8 +473,6 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
for (int i = 1; i < argCount; ++i)
|
||||
argList += QString::fromWCharArray(args[i]);
|
||||
|
||||
LogMsg(logMsg.arg(torrent->name(), program));
|
||||
|
||||
QProcess proc;
|
||||
proc.setProgram(QString::fromWCharArray(args[0]));
|
||||
proc.setArguments(argList);
|
||||
@@ -485,7 +497,11 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
args->startupInfo->hStdOutput = nullptr;
|
||||
args->startupInfo->hStdError = nullptr;
|
||||
});
|
||||
proc.startDetached();
|
||||
|
||||
if (proc.startDetached())
|
||||
LogMsg(logMsg.arg(torrent->name(), program));
|
||||
else
|
||||
LogMsg(logMsgError.arg(torrent->name(), program));
|
||||
#else // Q_OS_WIN
|
||||
QStringList args = Utils::String::splitCommand(programTemplate);
|
||||
|
||||
@@ -501,11 +517,21 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo
|
||||
arg = replaceVariables(arg);
|
||||
}
|
||||
|
||||
// show intended command in log
|
||||
LogMsg(logMsg.arg(torrent->name(), replaceVariables(programTemplate)));
|
||||
|
||||
const QString command = args.takeFirst();
|
||||
QProcess::startDetached(command, args);
|
||||
QProcess proc;
|
||||
proc.setProgram(command);
|
||||
proc.setArguments(args);
|
||||
|
||||
if (proc.startDetached())
|
||||
{
|
||||
// show intended command in log
|
||||
LogMsg(logMsg.arg(torrent->name(), replaceVariables(programTemplate)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// show intended command in log
|
||||
LogMsg(logMsgError.arg(torrent->name(), replaceVariables(programTemplate)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -658,8 +684,7 @@ Application::AddTorrentParams Application::parseParams(const QStringList ¶ms
|
||||
continue;
|
||||
}
|
||||
|
||||
parsedParams.torrentSource = param;
|
||||
break;
|
||||
parsedParams.torrentSources.append(param);
|
||||
}
|
||||
|
||||
return parsedParams;
|
||||
@@ -675,10 +700,16 @@ void Application::processParams(const AddTorrentParams ¶ms)
|
||||
// should be overridden.
|
||||
const bool showDialogForThisTorrent = !params.skipTorrentDialog.value_or(!AddNewTorrentDialog::isEnabled());
|
||||
if (showDialogForThisTorrent)
|
||||
AddNewTorrentDialog::show(params.torrentSource, params.torrentParams, m_window);
|
||||
{
|
||||
for (const QString &torrentSource : params.torrentSources)
|
||||
AddNewTorrentDialog::show(torrentSource, params.torrentParams, m_window);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
BitTorrent::Session::instance()->addTorrent(params.torrentSource, params.torrentParams);
|
||||
{
|
||||
for (const QString &torrentSource : params.torrentSources)
|
||||
BitTorrent::Session::instance()->addTorrent(torrentSource, params.torrentParams);
|
||||
}
|
||||
}
|
||||
|
||||
int Application::exec(const QStringList ¶ms)
|
||||
@@ -706,7 +737,7 @@ try
|
||||
#ifndef DISABLE_GUI
|
||||
UIThemeManager::initInstance();
|
||||
|
||||
m_desktopIntegration = new DesktopIntegration(this);
|
||||
m_desktopIntegration = new DesktopIntegration;
|
||||
m_desktopIntegration->setToolTip(tr("Loading torrents..."));
|
||||
#ifndef Q_OS_MACOS
|
||||
auto *desktopIntegrationMenu = new QMenu;
|
||||
@@ -714,7 +745,7 @@ try
|
||||
actionExit->setIcon(UIThemeManager::instance()->getIcon(u"application-exit"_qs));
|
||||
actionExit->setMenuRole(QAction::QuitRole);
|
||||
actionExit->setShortcut(Qt::CTRL | Qt::Key_Q);
|
||||
connect(actionExit, &QAction::triggered, this, [this]()
|
||||
connect(actionExit, &QAction::triggered, this, []
|
||||
{
|
||||
QApplication::exit();
|
||||
});
|
||||
@@ -788,12 +819,9 @@ try
|
||||
});
|
||||
|
||||
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
|
||||
// we must not delete menu while it is used by DesktopIntegration
|
||||
auto *oldMenu = m_desktopIntegration->menu();
|
||||
const MainWindow::State windowState = (!m_startupProgressDialog || (m_startupProgressDialog->windowState() & Qt::WindowMinimized))
|
||||
? MainWindow::Minimized : MainWindow::Normal;
|
||||
m_window = new MainWindow(this, windowState);
|
||||
delete oldMenu;
|
||||
delete m_startupProgressDialog;
|
||||
#ifdef Q_OS_WIN
|
||||
auto *pref = Preferences::instance();
|
||||
@@ -1201,6 +1229,7 @@ void Application::cleanup()
|
||||
::ShutdownBlockReasonDestroy(reinterpret_cast<HWND>(m_window->effectiveWinId()));
|
||||
#endif // Q_OS_WIN
|
||||
delete m_window;
|
||||
delete m_desktopIntegration;
|
||||
UIThemeManager::freeInstance();
|
||||
}
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
@@ -148,7 +148,7 @@ private slots:
|
||||
private:
|
||||
struct AddTorrentParams
|
||||
{
|
||||
QString torrentSource;
|
||||
QStringList torrentSources;
|
||||
BitTorrent::AddTorrentParams torrentParams;
|
||||
std::optional<bool> skipTorrentDialog;
|
||||
};
|
||||
|
||||
@@ -118,6 +118,17 @@ int main(int argc, char *argv[])
|
||||
// Create Application
|
||||
auto app = std::make_unique<Application>(argc, argv);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// QCoreApplication::applicationDirPath() needs an Application object instantiated first
|
||||
// Let's hope that there won't be a crash before this line
|
||||
const char *envName = "_NT_SYMBOL_PATH";
|
||||
const QString envValue = qEnvironmentVariable(envName);
|
||||
if (envValue.isEmpty())
|
||||
qputenv(envName, Application::applicationDirPath().toLocal8Bit());
|
||||
else
|
||||
qputenv(envName, u"%1;%2"_qs.arg(envValue, Application::applicationDirPath()).toLocal8Bit());
|
||||
#endif
|
||||
|
||||
const QBtCommandLineParameters params = app->commandLineArgs();
|
||||
if (!params.unknownParameter.isEmpty())
|
||||
{
|
||||
@@ -221,26 +232,6 @@ int main(int argc, char *argv[])
|
||||
app->setAttribute(Qt::AA_DontShowIconsInMenus);
|
||||
#endif
|
||||
|
||||
if (!firstTimeUser)
|
||||
{
|
||||
handleChangedDefaults(DefaultPreferencesMode::Legacy);
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
if (!upgrade()) return EXIT_FAILURE;
|
||||
#elif defined(Q_OS_WIN)
|
||||
if (!upgrade(_isatty(_fileno(stdin))
|
||||
&& _isatty(_fileno(stdout)))) return EXIT_FAILURE;
|
||||
#else
|
||||
if (!upgrade(!params.shouldDaemonize
|
||||
&& isatty(fileno(stdin))
|
||||
&& isatty(fileno(stdout)))) return EXIT_FAILURE;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
handleChangedDefaults(DefaultPreferencesMode::Current);
|
||||
}
|
||||
|
||||
#if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
|
||||
if (params.shouldDaemonize)
|
||||
{
|
||||
@@ -274,6 +265,11 @@ int main(int argc, char *argv[])
|
||||
displayBadArgMessage(er.message());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (const RuntimeError &er)
|
||||
{
|
||||
qDebug() << er.message();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_GUI)
|
||||
@@ -287,7 +283,7 @@ void showSplashScreen()
|
||||
painter.drawText(224 - painter.fontMetrics().horizontalAdvance(version), 270, version);
|
||||
QSplashScreen *splash = new QSplashScreen(splashImg);
|
||||
splash->show();
|
||||
QTimer::singleShot(1500ms, splash, &QObject::deleteLater);
|
||||
QTimer::singleShot(1500ms, Qt::CoarseTimer, splash, &QObject::deleteLater);
|
||||
qApp->processEvents();
|
||||
}
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
@@ -384,9 +384,21 @@ namespace
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void migrateChineseLocale()
|
||||
{
|
||||
auto *settingsStorage = SettingsStorage::instance();
|
||||
const auto key = u"Preferences/General/Locale"_qs;
|
||||
if (settingsStorage->hasKey(key))
|
||||
{
|
||||
const auto locale = settingsStorage->loadValue<QString>(key);
|
||||
if (locale.compare(u"zh"_qs, Qt::CaseInsensitive) == 0)
|
||||
settingsStorage->storeValue(key, u"zh_CN"_qs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool upgrade(const bool /*ask*/)
|
||||
bool upgrade()
|
||||
{
|
||||
CachedSettingValue<int> version {MIGRATION_VERSION_KEY, 0};
|
||||
|
||||
@@ -413,6 +425,9 @@ bool upgrade(const bool /*ask*/)
|
||||
migrateMemoryPrioritySettings();
|
||||
#endif
|
||||
|
||||
{
|
||||
migrateChineseLocale();
|
||||
}
|
||||
version = MIGRATION_VERSION;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,5 +35,5 @@ enum class DefaultPreferencesMode
|
||||
};
|
||||
|
||||
void handleChangedDefaults(DefaultPreferencesMode mode);
|
||||
bool upgrade(bool ask = true);
|
||||
bool upgrade();
|
||||
void setCurrentMigrationVersion();
|
||||
|
||||
@@ -101,6 +101,7 @@ add_library(qbt_base STATIC
|
||||
utils/password.h
|
||||
utils/random.h
|
||||
utils/string.h
|
||||
utils/thread.h
|
||||
utils/version.h
|
||||
version.h
|
||||
|
||||
@@ -183,6 +184,7 @@ add_library(qbt_base STATIC
|
||||
utils/password.cpp
|
||||
utils/random.cpp
|
||||
utils/string.cpp
|
||||
utils/thread.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(qbt_base
|
||||
|
||||
@@ -101,6 +101,7 @@ HEADERS += \
|
||||
$$PWD/utils/password.h \
|
||||
$$PWD/utils/random.h \
|
||||
$$PWD/utils/string.h \
|
||||
$$PWD/utils/thread.h \
|
||||
$$PWD/utils/version.h \
|
||||
$$PWD/version.h
|
||||
|
||||
@@ -182,4 +183,5 @@ SOURCES += \
|
||||
$$PWD/utils/net.cpp \
|
||||
$$PWD/utils/password.cpp \
|
||||
$$PWD/utils/random.cpp \
|
||||
$$PWD/utils/string.cpp
|
||||
$$PWD/utils/string.cpp \
|
||||
$$PWD/utils/thread.cpp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -91,7 +91,7 @@ namespace
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const Path &path, QObject *parent)
|
||||
: ResumeDataStorage(path, parent)
|
||||
, m_ioThread {new QThread(this)}
|
||||
, m_ioThread {new QThread}
|
||||
, m_asyncWorker {new Worker(path)}
|
||||
{
|
||||
Q_ASSERT(path.isAbsolute());
|
||||
@@ -117,17 +117,11 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const Path &path,
|
||||
|
||||
qDebug() << "Registered torrents count: " << m_registeredTorrents.size();
|
||||
|
||||
m_asyncWorker->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
||||
m_asyncWorker->moveToThread(m_ioThread.get());
|
||||
connect(m_ioThread.get(), &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
||||
m_ioThread->start();
|
||||
}
|
||||
|
||||
BitTorrent::BencodeResumeDataStorage::~BencodeResumeDataStorage()
|
||||
{
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
}
|
||||
|
||||
QVector<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredTorrents() const
|
||||
{
|
||||
return m_registeredTorrents;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -32,6 +32,8 @@
|
||||
#include <QVector>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "base/utils/thread.h"
|
||||
|
||||
#include "resumedatastorage.h"
|
||||
|
||||
class QByteArray;
|
||||
@@ -46,7 +48,6 @@ namespace BitTorrent
|
||||
|
||||
public:
|
||||
explicit BencodeResumeDataStorage(const Path &path, QObject *parent = nullptr);
|
||||
~BencodeResumeDataStorage() override;
|
||||
|
||||
QVector<TorrentID> registeredTorrents() const override;
|
||||
LoadResumeDataResult load(const TorrentID &id) const override;
|
||||
@@ -60,7 +61,7 @@ namespace BitTorrent
|
||||
LoadResumeDataResult loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
|
||||
|
||||
QVector<TorrentID> m_registeredTorrents;
|
||||
QThread *m_ioThread = nullptr;
|
||||
Utils::Thread::UniquePtr m_ioThread;
|
||||
|
||||
class Worker;
|
||||
Worker *m_asyncWorker = nullptr;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2021-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -256,7 +256,7 @@ namespace BitTorrent
|
||||
|
||||
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject *parent)
|
||||
: ResumeDataStorage(dbPath, parent)
|
||||
, m_ioThread {new QThread(this)}
|
||||
, m_ioThread {new QThread}
|
||||
{
|
||||
const bool needCreateDB = !dbPath.exists();
|
||||
|
||||
@@ -272,13 +272,13 @@ BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const Path &dbPath, QObject
|
||||
else
|
||||
{
|
||||
const int dbVersion = (!db.record(DB_TABLE_TORRENTS).contains(DB_COLUMN_DOWNLOAD_PATH.name) ? 1 : currentDBVersion());
|
||||
if (dbVersion != DB_VERSION)
|
||||
if (dbVersion < DB_VERSION)
|
||||
updateDB(dbVersion);
|
||||
}
|
||||
|
||||
m_asyncWorker = new Worker(dbPath, u"ResumeDataStorageWorker"_qs, m_dbLock);
|
||||
m_asyncWorker->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
||||
m_asyncWorker->moveToThread(m_ioThread.get());
|
||||
connect(m_ioThread.get(), &QThread::finished, m_asyncWorker, &QObject::deleteLater);
|
||||
m_ioThread->start();
|
||||
|
||||
RuntimeError *errPtr = nullptr;
|
||||
@@ -302,9 +302,6 @@ BitTorrent::DBResumeDataStorage::~DBResumeDataStorage()
|
||||
{
|
||||
QMetaObject::invokeMethod(m_asyncWorker, &Worker::closeDatabase);
|
||||
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
|
||||
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
}
|
||||
|
||||
QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
|
||||
@@ -534,18 +531,28 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
||||
{
|
||||
if (fromVersion == 1)
|
||||
{
|
||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
|
||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
|
||||
if (!query.exec(alterTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_qs
|
||||
.arg(quoted(DB_COLUMN_DOWNLOAD_PATH.name), quoted(DB_TABLE_TORRENTS));
|
||||
if (!query.exec(testQuery))
|
||||
{
|
||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
|
||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
|
||||
if (!query.exec(alterTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
}
|
||||
}
|
||||
|
||||
if (fromVersion <= 2)
|
||||
{
|
||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
|
||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"));
|
||||
if (!query.exec(alterTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_qs
|
||||
.arg(quoted(DB_COLUMN_STOP_CONDITION.name), quoted(DB_TABLE_TORRENTS));
|
||||
if (!query.exec(testQuery))
|
||||
{
|
||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_qs
|
||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"));
|
||||
if (!query.exec(alterTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
}
|
||||
}
|
||||
|
||||
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
||||
@@ -620,6 +627,7 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
||||
DB_COLUMN_CATEGORY,
|
||||
DB_COLUMN_TAGS,
|
||||
DB_COLUMN_TARGET_SAVE_PATH,
|
||||
DB_COLUMN_DOWNLOAD_PATH,
|
||||
DB_COLUMN_CONTENT_LAYOUT,
|
||||
DB_COLUMN_RATIO_LIMIT,
|
||||
DB_COLUMN_SEEDING_TIME_LIMIT,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2021-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
#include "base/utils/thread.h"
|
||||
#include "resumedatastorage.h"
|
||||
|
||||
class QThread;
|
||||
@@ -59,7 +60,7 @@ namespace BitTorrent
|
||||
void createDB() const;
|
||||
void updateDB(int fromVersion) const;
|
||||
|
||||
QThread *m_ioThread = nullptr;
|
||||
Utils::Thread::UniquePtr m_ioThread;
|
||||
|
||||
class Worker;
|
||||
Worker *m_asyncWorker = nullptr;
|
||||
|
||||
@@ -35,26 +35,6 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
void handleAddTorrentAlert([[maybe_unused]] const lt::add_torrent_alert *alert)
|
||||
{
|
||||
#ifndef QBT_USES_LIBTORRENT2
|
||||
if (alert->error)
|
||||
return;
|
||||
|
||||
// libtorrent < 2.0.7 has a bug that add_torrent_alert is posted too early
|
||||
// (before torrent is fully initialized and torrent extensions are created)
|
||||
// so we have to fill "extension data" in add_torrent_alert handler
|
||||
|
||||
// NOTE: `data` may not exist if a torrent is added behind the scenes to download metadata
|
||||
auto *data = static_cast<ExtensionData *>(alert->params.userdata);
|
||||
if (data)
|
||||
{
|
||||
data->status = alert->handle.status({});
|
||||
data->trackers = alert->handle.trackers();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void handleFastresumeRejectedAlert(const lt::fastresume_rejected_alert *alert)
|
||||
{
|
||||
alert->handle.unset_flags(lt::torrent_flags::auto_managed);
|
||||
@@ -76,9 +56,6 @@ void NativeSessionExtension::on_alert(const lt::alert *alert)
|
||||
{
|
||||
switch (alert->type())
|
||||
{
|
||||
case lt::add_torrent_alert::alert_type:
|
||||
handleAddTorrentAlert(static_cast<const lt::add_torrent_alert *>(alert));
|
||||
break;
|
||||
case lt::fastresume_rejected_alert::alert_type:
|
||||
handleFastresumeRejectedAlert(static_cast<const lt::fastresume_rejected_alert *>(alert));
|
||||
break;
|
||||
|
||||
@@ -36,18 +36,11 @@ NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrent
|
||||
{
|
||||
// NOTE: `data` may not exist if a torrent is added behind the scenes to download metadata
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
// libtorrent < 2.0.7 has a bug that add_torrent_alert is posted too early
|
||||
// (before torrent is fully initialized and torrent extensions are created)
|
||||
// so we have to fill "extension data" in add_torrent_alert handler and
|
||||
// we have it already filled at this point
|
||||
|
||||
if (m_data)
|
||||
{
|
||||
m_data->status = m_torrentHandle.status({});
|
||||
m_data->status = m_torrentHandle.status();
|
||||
m_data->trackers = m_torrentHandle.trackers();
|
||||
}
|
||||
#endif
|
||||
|
||||
on_state(m_data ? m_data->status.state : m_torrentHandle.status({}).state);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <QMetaObject>
|
||||
#include <QMutexLocker>
|
||||
#include <QThread>
|
||||
#include <QVector>
|
||||
|
||||
const int TORRENTIDLIST_TYPEID = qRegisterMetaType<QVector<BitTorrent::TorrentID>>();
|
||||
|
||||
@@ -59,11 +60,11 @@ void BitTorrent::ResumeDataStorage::loadAll() const
|
||||
loadingThread->start();
|
||||
}
|
||||
|
||||
QVector<BitTorrent::LoadedResumeData> BitTorrent::ResumeDataStorage::fetchLoadedResumeData() const
|
||||
QList<BitTorrent::LoadedResumeData> BitTorrent::ResumeDataStorage::fetchLoadedResumeData() const
|
||||
{
|
||||
const QMutexLocker locker {&m_loadedResumeDataMutex};
|
||||
|
||||
const QVector<BitTorrent::LoadedResumeData> loadedResumeData = m_loadedResumeData;
|
||||
const QList<BitTorrent::LoadedResumeData> loadedResumeData = m_loadedResumeData;
|
||||
m_loadedResumeData.clear();
|
||||
|
||||
return loadedResumeData;
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/3rdparty/expected.hpp"
|
||||
#include "base/path.h"
|
||||
@@ -65,7 +65,7 @@ namespace BitTorrent
|
||||
virtual void storeQueue(const QVector<TorrentID> &queue) const = 0;
|
||||
|
||||
void loadAll() const;
|
||||
QVector<LoadedResumeData> fetchLoadedResumeData() const;
|
||||
QList<LoadedResumeData> fetchLoadedResumeData() const;
|
||||
|
||||
signals:
|
||||
void loadStarted(const QVector<BitTorrent::TorrentID> &torrents);
|
||||
@@ -78,7 +78,7 @@ namespace BitTorrent
|
||||
virtual void doLoadAll() const = 0;
|
||||
|
||||
const Path m_path;
|
||||
mutable QVector<LoadedResumeData> m_loadedResumeData;
|
||||
mutable QList<LoadedResumeData> m_loadedResumeData;
|
||||
mutable QMutex m_loadedResumeDataMutex;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -403,9 +403,6 @@ namespace BitTorrent
|
||||
virtual Torrent *findTorrent(const InfoHash &infoHash) const = 0;
|
||||
virtual QVector<Torrent *> torrents() const = 0;
|
||||
virtual qsizetype torrentsCount() const = 0;
|
||||
virtual bool hasActiveTorrents() const = 0;
|
||||
virtual bool hasUnfinishedTorrents() const = 0;
|
||||
virtual bool hasRunningSeed() const = 0;
|
||||
virtual const SessionStatus &status() const = 0;
|
||||
virtual const CacheStatus &cacheStatus() const = 0;
|
||||
virtual bool isListening() const = 0;
|
||||
|
||||
@@ -322,7 +322,7 @@ struct BitTorrent::SessionImpl::ResumeSessionContext final : public QObject
|
||||
|
||||
ResumeDataStorage *startupStorage = nullptr;
|
||||
ResumeDataStorageType currentStorageType = ResumeDataStorageType::Legacy;
|
||||
QVector<LoadedResumeData> loadedResumeData;
|
||||
QList<LoadedResumeData> loadedResumeData;
|
||||
int processingResumeDataCount = 0;
|
||||
int64_t totalResumeDataCount = 0;
|
||||
int64_t finishedResumeDataCount = 0;
|
||||
@@ -511,7 +511,7 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||
, m_resumeDataStorageType(BITTORRENT_SESSION_KEY(u"ResumeDataStorageType"_qs), ResumeDataStorageType::Legacy)
|
||||
, m_seedingLimitTimer {new QTimer {this}}
|
||||
, m_resumeDataTimer {new QTimer {this}}
|
||||
, m_ioThread {new QThread {this}}
|
||||
, m_ioThread {new QThread}
|
||||
, m_recentErroredTorrentsTimer {new QTimer {this}}
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
, m_networkManager {new QNetworkConfigurationManager {this}}
|
||||
@@ -562,8 +562,8 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||
#endif
|
||||
|
||||
m_fileSearcher = new FileSearcher;
|
||||
m_fileSearcher->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_fileSearcher, &QObject::deleteLater);
|
||||
m_fileSearcher->moveToThread(m_ioThread.get());
|
||||
connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater);
|
||||
connect(m_fileSearcher, &FileSearcher::searchFinished, this, &SessionImpl::fileSearchFinished);
|
||||
|
||||
m_ioThread->start();
|
||||
@@ -596,11 +596,8 @@ SessionImpl::~SessionImpl()
|
||||
// we delete lt::session
|
||||
delete Net::PortForwarder::instance();
|
||||
|
||||
qDebug("Deleting the session");
|
||||
qDebug("Deleting libtorrent session...");
|
||||
delete m_nativeSession;
|
||||
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
}
|
||||
|
||||
bool SessionImpl::isDHTEnabled() const
|
||||
@@ -1053,18 +1050,6 @@ void SessionImpl::setGlobalMaxSeedingMinutes(int minutes)
|
||||
}
|
||||
}
|
||||
|
||||
void SessionImpl::adjustLimits()
|
||||
{
|
||||
if (isQueueingSystemEnabled())
|
||||
{
|
||||
lt::settings_pack settingsPack;
|
||||
// Internally increase the queue limits to ensure that the magnet is started
|
||||
settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads()));
|
||||
settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents()));
|
||||
m_nativeSession->apply_settings(std::move(settingsPack));
|
||||
}
|
||||
}
|
||||
|
||||
void SessionImpl::applyBandwidthLimits()
|
||||
{
|
||||
lt::settings_pack settingsPack;
|
||||
@@ -1401,27 +1386,29 @@ void SessionImpl::endStartup(ResumeSessionContext *context)
|
||||
}
|
||||
|
||||
context->deleteLater();
|
||||
|
||||
m_nativeSession->resume();
|
||||
if (m_refreshEnqueued)
|
||||
m_refreshEnqueued = false;
|
||||
else
|
||||
enqueueRefresh();
|
||||
|
||||
m_statisticsLastUpdateTimer.start();
|
||||
|
||||
// Regular saving of fastresume data
|
||||
connect(m_resumeDataTimer, &QTimer::timeout, this, &SessionImpl::generateResumeData);
|
||||
const int saveInterval = saveResumeDataInterval();
|
||||
if (saveInterval > 0)
|
||||
connect(context, &QObject::destroyed, this, [this]
|
||||
{
|
||||
m_resumeDataTimer->setInterval(std::chrono::minutes(saveInterval));
|
||||
m_resumeDataTimer->start();
|
||||
}
|
||||
m_nativeSession->resume();
|
||||
if (m_refreshEnqueued)
|
||||
m_refreshEnqueued = false;
|
||||
else
|
||||
enqueueRefresh();
|
||||
|
||||
m_isRestored = true;
|
||||
emit startupProgressUpdated(100);
|
||||
emit restored();
|
||||
m_statisticsLastUpdateTimer.start();
|
||||
|
||||
// Regular saving of fastresume data
|
||||
connect(m_resumeDataTimer, &QTimer::timeout, this, &SessionImpl::generateResumeData);
|
||||
const int saveInterval = saveResumeDataInterval();
|
||||
if (saveInterval > 0)
|
||||
{
|
||||
m_resumeDataTimer->setInterval(std::chrono::minutes(saveInterval));
|
||||
m_resumeDataTimer->start();
|
||||
}
|
||||
|
||||
m_isRestored = true;
|
||||
emit startupProgressUpdated(100);
|
||||
emit restored();
|
||||
});
|
||||
}
|
||||
|
||||
void SessionImpl::initializeNativeSession()
|
||||
@@ -1505,16 +1492,6 @@ void SessionImpl::processBannedIPs(lt::ip_filter &filter)
|
||||
}
|
||||
}
|
||||
|
||||
int SessionImpl::adjustLimit(const int limit) const
|
||||
{
|
||||
if (limit <= -1)
|
||||
return limit;
|
||||
// check for overflow: (limit + m_extraLimit) < std::numeric_limits<int>::max()
|
||||
return (m_extraLimit < (std::numeric_limits<int>::max() - limit))
|
||||
? (limit + m_extraLimit)
|
||||
: std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
void SessionImpl::initMetrics()
|
||||
{
|
||||
const auto findMetricIndex = [](const char *name) -> int
|
||||
@@ -1716,10 +1693,8 @@ lt::settings_pack SessionImpl::loadLTSettings() const
|
||||
// Queueing System
|
||||
if (isQueueingSystemEnabled())
|
||||
{
|
||||
// Internally increase the queue limits to ensure that the magnet is started
|
||||
settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads()));
|
||||
settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents()));
|
||||
|
||||
settingsPack.set_int(lt::settings_pack::active_downloads, maxActiveDownloads());
|
||||
settingsPack.set_int(lt::settings_pack::active_limit, maxActiveTorrents());
|
||||
settingsPack.set_int(lt::settings_pack::active_seeds, maxActiveUploads());
|
||||
settingsPack.set_bool(lt::settings_pack::dont_count_slow_torrents, ignoreSlowTorrentsForQueueing());
|
||||
settingsPack.set_int(lt::settings_pack::inactive_down_rate, downloadRateForSlowTorrents() * 1024); // KiB to Bytes
|
||||
@@ -2193,30 +2168,6 @@ Torrent *SessionImpl::findTorrent(const InfoHash &infoHash) const
|
||||
return m_torrents.value(altID);
|
||||
}
|
||||
|
||||
bool SessionImpl::hasActiveTorrents() const
|
||||
{
|
||||
return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentImpl *torrent)
|
||||
{
|
||||
return TorrentFilter::ActiveTorrent.match(torrent);
|
||||
});
|
||||
}
|
||||
|
||||
bool SessionImpl::hasUnfinishedTorrents() const
|
||||
{
|
||||
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentImpl *torrent)
|
||||
{
|
||||
return (!torrent->isSeed() && !torrent->isPaused() && !torrent->isErrored() && torrent->hasMetadata());
|
||||
});
|
||||
}
|
||||
|
||||
bool SessionImpl::hasRunningSeed() const
|
||||
{
|
||||
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentImpl *torrent)
|
||||
{
|
||||
return (torrent->isSeed() && !torrent->isPaused());
|
||||
});
|
||||
}
|
||||
|
||||
void SessionImpl::banIP(const QString &ip)
|
||||
{
|
||||
QStringList bannedIPs = m_bannedIPs;
|
||||
@@ -2317,8 +2268,6 @@ bool SessionImpl::cancelDownloadMetadata(const TorrentID &id)
|
||||
}
|
||||
#endif
|
||||
m_downloadedMetadata.erase(downloadedMetadataIter);
|
||||
--m_extraLimit;
|
||||
adjustLimits();
|
||||
m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_files);
|
||||
return true;
|
||||
}
|
||||
@@ -2814,6 +2763,19 @@ bool SessionImpl::downloadMetadata(const MagnetUri &magnetUri)
|
||||
|
||||
lt::add_torrent_params p = magnetUri.addTorrentParams();
|
||||
|
||||
if (isAddTrackersEnabled())
|
||||
{
|
||||
// Use "additional trackers" when metadata retrieving (this can help when the DHT nodes are few)
|
||||
p.trackers.reserve(p.trackers.size() + static_cast<std::size_t>(m_additionalTrackerList.size()));
|
||||
p.tracker_tiers.reserve(p.trackers.size() + static_cast<std::size_t>(m_additionalTrackerList.size()));
|
||||
p.tracker_tiers.resize(p.trackers.size(), 0);
|
||||
for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerList))
|
||||
{
|
||||
p.trackers.push_back(trackerEntry.url.toStdString());
|
||||
p.tracker_tiers.push_back(trackerEntry.tier);
|
||||
}
|
||||
}
|
||||
|
||||
// Flags
|
||||
// Preallocation mode
|
||||
if (isPreallocationEnabled())
|
||||
@@ -2853,8 +2815,6 @@ bool SessionImpl::downloadMetadata(const MagnetUri &magnetUri)
|
||||
const auto altID = TorrentID::fromSHA1Hash(infoHash.v1());
|
||||
m_downloadedMetadata.insert(altID);
|
||||
}
|
||||
++m_extraLimit;
|
||||
adjustLimits();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -4641,7 +4601,11 @@ void SessionImpl::handleTorrentFinished(TorrentImpl *const torrent)
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasUnfinishedTorrents())
|
||||
const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
|
||||
{
|
||||
return !(torrent->isSeed() || torrent->isPaused() || torrent->isErrored());
|
||||
});
|
||||
if (!hasUnfinishedTorrents)
|
||||
emit allTorrentsFinished();
|
||||
}
|
||||
|
||||
@@ -4967,7 +4931,7 @@ void SessionImpl::enqueueRefresh()
|
||||
{
|
||||
Q_ASSERT(!m_refreshEnqueued);
|
||||
|
||||
QTimer::singleShot(refreshInterval(), this, [this] ()
|
||||
QTimer::singleShot(refreshInterval(), Qt::CoarseTimer, this, [this]
|
||||
{
|
||||
m_nativeSession->post_torrent_updates();
|
||||
m_nativeSession->post_session_stats();
|
||||
@@ -5277,10 +5241,13 @@ void SessionImpl::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p)
|
||||
#endif
|
||||
|
||||
const auto removingTorrentDataIter = m_removingTorrents.find(id);
|
||||
|
||||
if (removingTorrentDataIter == m_removingTorrents.end())
|
||||
return;
|
||||
|
||||
// torrent_deleted_alert can also be posted due to deletion of partfile. Ignore it in such a case.
|
||||
if (removingTorrentDataIter->deleteOption == DeleteTorrent)
|
||||
return;
|
||||
|
||||
Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter->pathToRemove);
|
||||
LogMsg(tr("Removed torrent and deleted its content. Torrent: \"%1\"").arg(removingTorrentDataIter->name));
|
||||
m_removingTorrents.erase(removingTorrentDataIter);
|
||||
@@ -5295,7 +5262,6 @@ void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed
|
||||
#endif
|
||||
|
||||
const auto removingTorrentDataIter = m_removingTorrents.find(id);
|
||||
|
||||
if (removingTorrentDataIter == m_removingTorrents.end())
|
||||
return;
|
||||
|
||||
@@ -5305,7 +5271,7 @@ void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed
|
||||
// so we remove the directory ourselves
|
||||
Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter->pathToRemove);
|
||||
|
||||
LogMsg(tr("Removed torrent but failed to delete its content. Torrent: \"%1\". Error: \"%2\"")
|
||||
LogMsg(tr("Removed torrent but failed to delete its content and/or partfile. Torrent: \"%1\". Error: \"%2\"")
|
||||
.arg(removingTorrentDataIter->name, QString::fromLocal8Bit(p->error.message().c_str()))
|
||||
, Log::WARNING);
|
||||
}
|
||||
@@ -5313,6 +5279,7 @@ void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed
|
||||
{
|
||||
LogMsg(tr("Removed torrent. Torrent: \"%1\"").arg(removingTorrentDataIter->name));
|
||||
}
|
||||
|
||||
m_removingTorrents.erase(removingTorrentDataIter);
|
||||
}
|
||||
|
||||
@@ -5341,9 +5308,6 @@ void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert
|
||||
if (found)
|
||||
{
|
||||
const TorrentInfo metadata {*p->handle.torrent_file()};
|
||||
|
||||
--m_extraLimit;
|
||||
adjustLimits();
|
||||
m_nativeSession->remove_torrent(p->handle, lt::session::delete_files);
|
||||
|
||||
emit metadataDownloaded(metadata);
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "base/path.h"
|
||||
#include "base/settingvalue.h"
|
||||
#include "base/types.h"
|
||||
#include "base/utils/thread.h"
|
||||
#include "addtorrentparams.h"
|
||||
#include "cachestatus.h"
|
||||
#include "categoryoptions.h"
|
||||
@@ -375,9 +376,6 @@ namespace BitTorrent
|
||||
Torrent *findTorrent(const InfoHash &infoHash) const override;
|
||||
QVector<Torrent *> torrents() const override;
|
||||
qsizetype torrentsCount() const override;
|
||||
bool hasActiveTorrents() const override;
|
||||
bool hasUnfinishedTorrents() const override;
|
||||
bool hasRunningSeed() const override;
|
||||
const SessionStatus &status() const override;
|
||||
const CacheStatus &cacheStatus() const override;
|
||||
bool isListening() const override;
|
||||
@@ -477,9 +475,7 @@ namespace BitTorrent
|
||||
lt::settings_pack loadLTSettings() const;
|
||||
void applyNetworkInterfacesSettings(lt::settings_pack &settingsPack) const;
|
||||
void configurePeerClasses();
|
||||
int adjustLimit(int limit) const;
|
||||
void initMetrics();
|
||||
void adjustLimits();
|
||||
void applyBandwidthLimits();
|
||||
void processBannedIPs(lt::ip_filter &filter);
|
||||
QStringList getListeningIPs() const;
|
||||
@@ -670,7 +666,6 @@ namespace BitTorrent
|
||||
const bool m_wasPexEnabled = m_isPeXEnabled;
|
||||
|
||||
int m_numResumeData = 0;
|
||||
int m_extraLimit = 0;
|
||||
QVector<TrackerEntry> m_additionalTrackerList;
|
||||
QVector<QRegularExpression> m_excludedFileNamesRegExpList;
|
||||
|
||||
@@ -689,7 +684,7 @@ namespace BitTorrent
|
||||
// Tracker
|
||||
QPointer<Tracker> m_tracker;
|
||||
|
||||
QThread *m_ioThread = nullptr;
|
||||
Utils::Thread::UniquePtr m_ioThread;
|
||||
ResumeDataStorage *m_resumeDataStorage = nullptr;
|
||||
FileSearcher *m_fileSearcher = nullptr;
|
||||
|
||||
|
||||
@@ -520,6 +520,17 @@ void TorrentImpl::setAutoManaged(const bool enable)
|
||||
m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed);
|
||||
}
|
||||
|
||||
Path TorrentImpl::wantedActualPath(int index, const Path &path) const
|
||||
{
|
||||
if (m_session->isAppendExtensionEnabled()
|
||||
&& (fileSize(index) > 0) && !m_completedFiles.at(index))
|
||||
{
|
||||
return path + QB_EXT;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
QVector<TrackerEntry> TorrentImpl::trackers() const
|
||||
{
|
||||
if (!m_updatedTrackerEntries.isEmpty())
|
||||
@@ -723,7 +734,13 @@ qreal TorrentImpl::progress() const
|
||||
return 1.;
|
||||
|
||||
const qreal progress = static_cast<qreal>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
|
||||
Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
|
||||
if ((progress < 0.f) || (progress > 1.f))
|
||||
{
|
||||
LogMsg(tr("Unexpected data detected. Torrent: %1. Data: total_wanted=%2 total_wanted_done=%3.")
|
||||
.arg(name(), QString::number(m_nativeStatus.total_wanted), QString::number(m_nativeStatus.total_wanted_done))
|
||||
, Log::WARNING);
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
@@ -1430,6 +1447,10 @@ void TorrentImpl::forceRecheck()
|
||||
if (!hasMetadata()) return;
|
||||
|
||||
m_nativeHandle.force_recheck();
|
||||
// We have to force update the cached state, otherwise someone will be able to get
|
||||
// an incorrect one during the interval until the cached state is updated in a regular way.
|
||||
m_nativeStatus.state = lt::torrent_status::checking_resume_data;
|
||||
|
||||
m_hasMissingFiles = false;
|
||||
m_unchecked = false;
|
||||
m_completedFiles.fill(false);
|
||||
@@ -1530,20 +1551,21 @@ void TorrentImpl::refreshTrackerEntries() const
|
||||
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
|
||||
Q_ASSERT(nativeTrackers.size() == m_trackerEntries.size());
|
||||
|
||||
for (TrackerEntry &trackerEntry : m_trackerEntries)
|
||||
for (const lt::announce_entry &announceEntry : nativeTrackers)
|
||||
{
|
||||
const auto updatedTrackerIter = m_updatedTrackerEntries.find(trackerEntry.url);
|
||||
const auto trackerURL = QString::fromStdString(announceEntry.url);
|
||||
const auto updatedTrackerIter = m_updatedTrackerEntries.find(trackerURL);
|
||||
if (updatedTrackerIter == m_updatedTrackerEntries.end())
|
||||
continue;
|
||||
|
||||
const auto nativeTrackerIter = std::find_if(nativeTrackers.cbegin(), nativeTrackers.cend()
|
||||
, [trackerURL = trackerEntry.url.toStdString()](const lt::announce_entry &announceEntry)
|
||||
const auto trackerIter = std::find_if(m_trackerEntries.begin(), m_trackerEntries.end()
|
||||
, [&trackerURL](const TrackerEntry &trackerEntry)
|
||||
{
|
||||
return (announceEntry.url == trackerURL);
|
||||
return (trackerEntry.url == trackerURL);
|
||||
});
|
||||
Q_ASSERT(nativeTrackerIter != nativeTrackers.cend());
|
||||
Q_ASSERT(trackerIter != m_trackerEntries.end());
|
||||
|
||||
const lt::announce_entry &announceEntry = *nativeTrackerIter;
|
||||
TrackerEntry &trackerEntry = *trackerIter;
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
updateTrackerEntry(trackerEntry, announceEntry, m_nativeHandle.info_hashes(), updatedTrackerIter.value());
|
||||
#else
|
||||
@@ -1730,15 +1752,12 @@ void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageMode mode)
|
||||
|
||||
void TorrentImpl::renameFile(const int index, const Path &path)
|
||||
{
|
||||
const QVector<lt::file_index_t> nativeIndexes = m_torrentInfo.nativeIndexes();
|
||||
|
||||
Q_ASSERT(index >= 0);
|
||||
Q_ASSERT(index < nativeIndexes.size());
|
||||
if ((index < 0) || (index >= nativeIndexes.size()))
|
||||
Q_ASSERT((index >= 0) && (index < filesCount()));
|
||||
if (Q_UNLIKELY((index < 0) || (index >= filesCount())))
|
||||
return;
|
||||
|
||||
++m_renameCount;
|
||||
m_nativeHandle.rename_file(nativeIndexes[index], path.toString().toStdString());
|
||||
const Path wantedPath = wantedActualPath(index, path);
|
||||
doRenameFile(index, wantedPath);
|
||||
}
|
||||
|
||||
void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
||||
@@ -1828,25 +1847,24 @@ void TorrentImpl::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p
|
||||
|
||||
m_statusUpdatedTriggers.enqueue([this]()
|
||||
{
|
||||
m_hasSeedStatus = true;
|
||||
|
||||
adjustStorageLocation();
|
||||
manageIncompleteFiles();
|
||||
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
|
||||
const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion();
|
||||
if (isMoveInProgress() || (m_renameCount > 0))
|
||||
if (recheckTorrentsOnCompletion && m_unchecked)
|
||||
{
|
||||
if (recheckTorrentsOnCompletion)
|
||||
m_moveFinishedTriggers.enqueue([this]() { forceRecheck(); });
|
||||
m_moveFinishedTriggers.enqueue([this]() { m_session->handleTorrentFinished(this); });
|
||||
forceRecheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (recheckTorrentsOnCompletion && m_unchecked)
|
||||
forceRecheck();
|
||||
m_session->handleTorrentFinished(this);
|
||||
m_hasSeedStatus = true;
|
||||
|
||||
if (isMoveInProgress() || (m_renameCount > 0))
|
||||
m_moveFinishedTriggers.enqueue([this]() { m_session->handleTorrentFinished(this); });
|
||||
else
|
||||
m_session->handleTorrentFinished(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1990,13 +2008,12 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
|
||||
// For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
|
||||
// be removed if they are empty
|
||||
const Path oldFilePath = m_filePaths.at(fileIndex);
|
||||
const Path newFilePath {QString::fromUtf8(p->new_name())};
|
||||
const Path newFilePath = Path(QString::fromUtf8(p->new_name())).removedExtension(QB_EXT);
|
||||
|
||||
// Check if ".!qB" extension was just added or removed
|
||||
// We should compare path in a case sensitive manner even on case insensitive
|
||||
// platforms since it can be renamed by only changing case of some character(s)
|
||||
if ((oldFilePath.data() != newFilePath.data())
|
||||
&& ((oldFilePath + QB_EXT) != newFilePath))
|
||||
if (oldFilePath.data() != newFilePath.data())
|
||||
{
|
||||
m_filePaths[fileIndex] = newFilePath;
|
||||
|
||||
@@ -2048,7 +2065,7 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
||||
if (actualPath != path)
|
||||
{
|
||||
qDebug("Renaming %s to %s", qUtf8Printable(actualPath.toString()), qUtf8Printable(path.toString()));
|
||||
renameFile(fileIndex, path);
|
||||
doRenameFile(fileIndex, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2155,7 +2172,6 @@ void TorrentImpl::manageIncompleteFiles()
|
||||
{
|
||||
const std::shared_ptr<const lt::torrent_info> nativeInfo = nativeTorrentInfo();
|
||||
const lt::file_storage &nativeFiles = nativeInfo->files();
|
||||
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
|
||||
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
{
|
||||
@@ -2163,23 +2179,11 @@ void TorrentImpl::manageIncompleteFiles()
|
||||
|
||||
const auto nativeIndex = m_torrentInfo.nativeIndexes().at(i);
|
||||
const Path actualPath {nativeFiles.file_path(nativeIndex)};
|
||||
|
||||
if (isAppendExtensionEnabled && (fileSize(i) > 0) && !m_completedFiles.at(i))
|
||||
const Path wantedPath = wantedActualPath(i, path);
|
||||
if (actualPath != wantedPath)
|
||||
{
|
||||
const Path wantedPath = path + QB_EXT;
|
||||
if (actualPath != wantedPath)
|
||||
{
|
||||
qDebug() << "Renaming" << actualPath.toString() << "to" << wantedPath.toString();
|
||||
renameFile(i, wantedPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (actualPath != path)
|
||||
{
|
||||
qDebug() << "Renaming" << actualPath.toString() << "to" << path.toString();
|
||||
renameFile(i, path);
|
||||
}
|
||||
qDebug() << "Renaming" << actualPath.toString() << "to" << wantedPath.toString();
|
||||
doRenameFile(i, wantedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2191,7 +2195,20 @@ void TorrentImpl::adjustStorageLocation()
|
||||
const Path targetPath = ((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath);
|
||||
|
||||
if ((targetPath != actualStorageLocation()) || isMoveInProgress())
|
||||
moveStorage(targetPath, MoveStorageMode::FailIfExist);
|
||||
moveStorage(targetPath, MoveStorageMode::Overwrite);
|
||||
}
|
||||
|
||||
void TorrentImpl::doRenameFile(int index, const Path &path)
|
||||
{
|
||||
const QVector<lt::file_index_t> nativeIndexes = m_torrentInfo.nativeIndexes();
|
||||
|
||||
Q_ASSERT(index >= 0);
|
||||
Q_ASSERT(index < nativeIndexes.size());
|
||||
if (Q_UNLIKELY((index < 0) || (index >= nativeIndexes.size())))
|
||||
return;
|
||||
|
||||
++m_renameCount;
|
||||
m_nativeHandle.rename_file(nativeIndexes[index], path.toString().toStdString());
|
||||
}
|
||||
|
||||
lt::torrent_handle TorrentImpl::nativeHandle() const
|
||||
|
||||
@@ -281,7 +281,9 @@ namespace BitTorrent
|
||||
|
||||
void setAutoManaged(bool enable);
|
||||
|
||||
Path wantedActualPath(int index, const Path &path) const;
|
||||
void adjustStorageLocation();
|
||||
void doRenameFile(int index, const Path &path);
|
||||
void moveStorage(const Path &newPath, MoveStorageMode mode);
|
||||
void manageIncompleteFiles();
|
||||
void applyFirstLastPiecePriority(bool enabled);
|
||||
|
||||
@@ -56,10 +56,33 @@ namespace
|
||||
QList<QSslCipher> safeCipherList()
|
||||
{
|
||||
const QStringList badCiphers {u"idea"_qs, u"rc4"_qs};
|
||||
// Contains Ciphersuites that use RSA for the Key Exchange but they don't mention it in their name
|
||||
const QStringList badRSAShorthandSuites {
|
||||
u"AES256-GCM-SHA384"_qs, u"AES128-GCM-SHA256"_qs, u"AES256-SHA256"_qs,
|
||||
u"AES128-SHA256"_qs, u"AES256-SHA"_qs, u"AES128-SHA"_qs};
|
||||
// Contains Ciphersuites that use AES CBC mode but they don't mention it in their name
|
||||
const QStringList badAESShorthandSuites {
|
||||
u"ECDHE-ECDSA-AES256-SHA384"_qs, u"ECDHE-RSA-AES256-SHA384"_qs, u"DHE-RSA-AES256-SHA256"_qs,
|
||||
u"ECDHE-ECDSA-AES128-SHA256"_qs, u"ECDHE-RSA-AES128-SHA256"_qs, u"DHE-RSA-AES128-SHA256"_qs,
|
||||
u"ECDHE-ECDSA-AES256-SHA"_qs, u"ECDHE-RSA-AES256-SHA"_qs, u"DHE-RSA-AES256-SHA"_qs,
|
||||
u"ECDHE-ECDSA-AES128-SHA"_qs, u"ECDHE-RSA-AES128-SHA"_qs, u"DHE-RSA-AES128-SHA"_qs};
|
||||
const QList<QSslCipher> allCiphers {QSslConfiguration::supportedCiphers()};
|
||||
QList<QSslCipher> safeCiphers;
|
||||
std::copy_if(allCiphers.cbegin(), allCiphers.cend(), std::back_inserter(safeCiphers), [&badCiphers](const QSslCipher &cipher)
|
||||
std::copy_if(allCiphers.cbegin(), allCiphers.cend(), std::back_inserter(safeCiphers),
|
||||
[&badCiphers, &badRSAShorthandSuites, &badAESShorthandSuites](const QSslCipher &cipher)
|
||||
{
|
||||
const QString name = cipher.name();
|
||||
if (name.contains(u"-cbc-"_qs, Qt::CaseInsensitive) // AES CBC mode is considered vulnerable to BEAST attack
|
||||
|| name.startsWith(u"adh-"_qs, Qt::CaseInsensitive) // Key Exchange: Diffie-Hellman, doesn't support Perfect Forward Secrecy
|
||||
|| name.startsWith(u"aecdh-"_qs, Qt::CaseInsensitive) // Key Exchange: Elliptic Curve Diffie-Hellman, doesn't support Perfect Forward Secrecy
|
||||
|| name.startsWith(u"psk-"_qs, Qt::CaseInsensitive) // Key Exchange: Pre-Shared Key, doesn't support Perfect Forward Secrecy
|
||||
|| name.startsWith(u"rsa-"_qs, Qt::CaseInsensitive) // Key Exchange: Rivest Shamir Adleman (RSA), doesn't support Perfect Forward Secrecy
|
||||
|| badRSAShorthandSuites.contains(name, Qt::CaseInsensitive)
|
||||
|| badAESShorthandSuites.contains(name, Qt::CaseInsensitive))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::none_of(badCiphers.cbegin(), badCiphers.cend(), [&cipher](const QString &badCipher)
|
||||
{
|
||||
return cipher.name().contains(badCipher, Qt::CaseInsensitive);
|
||||
@@ -78,6 +101,7 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
||||
setProxy(QNetworkProxy::NoProxy);
|
||||
|
||||
QSslConfiguration sslConf {QSslConfiguration::defaultConfiguration()};
|
||||
sslConf.setProtocol(QSsl::TlsV1_2OrLater);
|
||||
sslConf.setCiphers(safeCipherList());
|
||||
QSslConfiguration::setDefaultConfiguration(sslConf);
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ Path Path::rootItem() const
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// should be `c:/` instead of `c:`
|
||||
if (m_pathStr.at(slashIndex - 1) == u':')
|
||||
if ((slashIndex == 2) && hasDriveLetter(m_pathStr))
|
||||
return createUnchecked(m_pathStr.left(slashIndex + 1));
|
||||
#endif
|
||||
return createUnchecked(m_pathStr.left(slashIndex));
|
||||
|
||||
@@ -599,11 +599,7 @@ void Preferences::setWebUiPort(const quint16 port)
|
||||
|
||||
bool Preferences::useUPnPForWebUIPort() const
|
||||
{
|
||||
#ifdef DISABLE_GUI
|
||||
return value(u"Preferences/WebUI/UseUPnP"_qs, true);
|
||||
#else
|
||||
return value(u"Preferences/WebUI/UseUPnP"_qs, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Preferences::setUPnPForWebUIPort(const bool enabled)
|
||||
@@ -996,6 +992,18 @@ void Preferences::resolvePeerHostNames(const bool resolve)
|
||||
setValue(u"Preferences/Connection/ResolvePeerHostNames"_qs, resolve);
|
||||
}
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
bool Preferences::useSystemIcons() const
|
||||
{
|
||||
return value(u"Preferences/Advanced/useSystemIconTheme"_qs, false);
|
||||
}
|
||||
|
||||
void Preferences::useSystemIcons(const bool enabled)
|
||||
{
|
||||
setValue(u"Preferences/Advanced/useSystemIconTheme"_qs, enabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Preferences::isRecursiveDownloadEnabled() const
|
||||
{
|
||||
return !value(u"Preferences/Advanced/DisableRecursiveDownload"_qs, false);
|
||||
@@ -1216,6 +1224,16 @@ void Preferences::setConfirmRemoveAllTags(const bool enabled)
|
||||
setValue(u"Preferences/Advanced/confirmRemoveAllTags"_qs, enabled);
|
||||
}
|
||||
|
||||
bool Preferences::confirmPauseAndResumeAll() const
|
||||
{
|
||||
return value(u"GUI/ConfirmActions/PauseAndResumeAllTorrents"_qs, true);
|
||||
}
|
||||
|
||||
void Preferences::setConfirmPauseAndResumeAll(const bool enabled)
|
||||
{
|
||||
setValue(u"GUI/ConfirmActions/PauseAndResumeAllTorrents"_qs, enabled);
|
||||
}
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
TrayIcon::Style Preferences::trayIconStyle() const
|
||||
{
|
||||
|
||||
@@ -281,6 +281,10 @@ public:
|
||||
void resolvePeerCountries(bool resolve);
|
||||
bool resolvePeerHostNames() const;
|
||||
void resolvePeerHostNames(bool resolve);
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
bool useSystemIcons() const;
|
||||
void useSystemIcons(bool enabled);
|
||||
#endif
|
||||
bool isRecursiveDownloadEnabled() const;
|
||||
void setRecursiveDownloadEnabled(bool enable);
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -311,6 +315,8 @@ public:
|
||||
void setConfirmTorrentRecheck(bool enabled);
|
||||
bool confirmRemoveAllTags() const;
|
||||
void setConfirmRemoveAllTags(bool enabled);
|
||||
bool confirmPauseAndResumeAll() const;
|
||||
void setConfirmPauseAndResumeAll(bool enabled);
|
||||
#ifndef Q_OS_MACOS
|
||||
bool systemTrayEnabled() const;
|
||||
void setSystemTrayEnabled(bool enabled);
|
||||
|
||||
@@ -102,15 +102,15 @@ AutoDownloader::AutoDownloader()
|
||||
, m_storeSmartEpisodeFilter(u"RSS/AutoDownloader/SmartEpisodeFilter"_qs)
|
||||
, m_storeDownloadRepacks(u"RSS/AutoDownloader/DownloadRepacks"_qs)
|
||||
, m_processingTimer(new QTimer(this))
|
||||
, m_ioThread(new QThread(this))
|
||||
, m_ioThread(new QThread)
|
||||
{
|
||||
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||
m_instance = this;
|
||||
|
||||
m_fileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FOLDER_NAME));
|
||||
|
||||
m_fileStorage->moveToThread(m_ioThread);
|
||||
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
|
||||
m_fileStorage->moveToThread(m_ioThread.get());
|
||||
connect(m_ioThread.get(), &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
|
||||
connect(m_fileStorage, &AsyncFileStorage::failed, [](const Path &fileName, const QString &errorString)
|
||||
{
|
||||
LogMsg(tr("Couldn't save RSS AutoDownloader data in %1. Error: %2")
|
||||
@@ -155,9 +155,6 @@ AutoDownloader::AutoDownloader()
|
||||
AutoDownloader::~AutoDownloader()
|
||||
{
|
||||
store();
|
||||
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
}
|
||||
|
||||
AutoDownloader *AutoDownloader::instance()
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "base/exceptions.h"
|
||||
#include "base/settingvalue.h"
|
||||
#include "base/utils/thread.h"
|
||||
|
||||
class QThread;
|
||||
class QTimer;
|
||||
@@ -137,7 +138,7 @@ namespace RSS
|
||||
SettingValue<bool> m_storeDownloadRepacks;
|
||||
|
||||
QTimer *m_processingTimer = nullptr;
|
||||
QThread *m_ioThread = nullptr;
|
||||
Utils::Thread::UniquePtr m_ioThread;
|
||||
AsyncFileStorage *m_fileStorage = nullptr;
|
||||
QHash<QString, AutoDownloadRule> m_rules;
|
||||
QList<QSharedPointer<ProcessingJob>> m_processingQueue;
|
||||
|
||||
@@ -62,14 +62,14 @@ Session::Session()
|
||||
: m_storeProcessingEnabled(u"RSS/Session/EnableProcessing"_qs)
|
||||
, m_storeRefreshInterval(u"RSS/Session/RefreshInterval"_qs, 30)
|
||||
, m_storeMaxArticlesPerFeed(u"RSS/Session/MaxArticlesPerFeed"_qs, 50)
|
||||
, m_workingThread(new QThread(this))
|
||||
, m_workingThread(new QThread)
|
||||
{
|
||||
Q_ASSERT(!m_instance); // only one instance is allowed
|
||||
m_instance = this;
|
||||
|
||||
m_confFileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Config) / Path(CONF_FOLDER_NAME));
|
||||
m_confFileStorage->moveToThread(m_workingThread);
|
||||
connect(m_workingThread, &QThread::finished, m_confFileStorage, &AsyncFileStorage::deleteLater);
|
||||
m_confFileStorage->moveToThread(m_workingThread.get());
|
||||
connect(m_workingThread.get(), &QThread::finished, m_confFileStorage, &AsyncFileStorage::deleteLater);
|
||||
connect(m_confFileStorage, &AsyncFileStorage::failed, [](const Path &fileName, const QString &errorString)
|
||||
{
|
||||
LogMsg(tr("Couldn't save RSS session configuration. File: \"%1\". Error: \"%2\"")
|
||||
@@ -77,8 +77,8 @@ Session::Session()
|
||||
});
|
||||
|
||||
m_dataFileStorage = new AsyncFileStorage(specialFolderLocation(SpecialFolder::Data) / Path(DATA_FOLDER_NAME));
|
||||
m_dataFileStorage->moveToThread(m_workingThread);
|
||||
connect(m_workingThread, &QThread::finished, m_dataFileStorage, &AsyncFileStorage::deleteLater);
|
||||
m_dataFileStorage->moveToThread(m_workingThread.get());
|
||||
connect(m_workingThread.get(), &QThread::finished, m_dataFileStorage, &AsyncFileStorage::deleteLater);
|
||||
connect(m_dataFileStorage, &AsyncFileStorage::failed, [](const Path &fileName, const QString &errorString)
|
||||
{
|
||||
LogMsg(tr("Couldn't save RSS session data. File: \"%1\". Error: \"%2\"")
|
||||
@@ -126,11 +126,6 @@ Session::~Session()
|
||||
//store();
|
||||
delete m_itemsByPath[u""_qs]; // deleting root folder
|
||||
|
||||
// some items may add I/O operation at destruction
|
||||
// stop working thread after deleting root folder
|
||||
m_workingThread->quit();
|
||||
m_workingThread->wait();
|
||||
|
||||
qDebug() << "RSS Session deleted.";
|
||||
}
|
||||
|
||||
@@ -190,8 +185,11 @@ nonstd::expected<void, QString> Session::moveItem(Item *item, const QString &des
|
||||
if (!result)
|
||||
return result.get_unexpected();
|
||||
|
||||
auto srcFolder = static_cast<Folder *>(m_itemsByPath.value(Item::parentPath(item->path())));
|
||||
const auto destFolder = result.value();
|
||||
if (static_cast<Item *>(destFolder) == item)
|
||||
return nonstd::make_unexpected(tr("Couldn't move folder into itself."));
|
||||
|
||||
auto srcFolder = static_cast<Folder *>(m_itemsByPath.value(Item::parentPath(item->path())));
|
||||
if (srcFolder != destFolder)
|
||||
{
|
||||
srcFolder->removeItem(item);
|
||||
@@ -487,7 +485,7 @@ void Session::setRefreshInterval(const int refreshInterval)
|
||||
|
||||
QThread *Session::workingThread() const
|
||||
{
|
||||
return m_workingThread;
|
||||
return m_workingThread.get();
|
||||
}
|
||||
|
||||
void Session::handleItemAboutToBeDestroyed(Item *item)
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
|
||||
#include "base/3rdparty/expected.hpp"
|
||||
#include "base/settingvalue.h"
|
||||
#include "base/utils/thread.h"
|
||||
|
||||
class QThread;
|
||||
|
||||
@@ -158,7 +159,7 @@ namespace RSS
|
||||
CachedSettingValue<bool> m_storeProcessingEnabled;
|
||||
CachedSettingValue<int> m_storeRefreshInterval;
|
||||
CachedSettingValue<int> m_storeMaxArticlesPerFeed;
|
||||
QThread *m_workingThread = nullptr;
|
||||
Utils::Thread::UniquePtr m_workingThread;
|
||||
AsyncFileStorage *m_confFileStorage = nullptr;
|
||||
AsyncFileStorage *m_dataFileStorage = nullptr;
|
||||
QTimer m_refreshTimer;
|
||||
|
||||
@@ -254,7 +254,7 @@ TorrentFilesWatcher *TorrentFilesWatcher::instance()
|
||||
|
||||
TorrentFilesWatcher::TorrentFilesWatcher(QObject *parent)
|
||||
: QObject {parent}
|
||||
, m_ioThread {new QThread(this)}
|
||||
, m_ioThread {new QThread}
|
||||
{
|
||||
const auto *btSession = BitTorrent::Session::instance();
|
||||
if (btSession->isRestored())
|
||||
@@ -267,8 +267,7 @@ TorrentFilesWatcher::TorrentFilesWatcher(QObject *parent)
|
||||
|
||||
TorrentFilesWatcher::~TorrentFilesWatcher()
|
||||
{
|
||||
m_ioThread->quit();
|
||||
m_ioThread->wait();
|
||||
m_ioThread.reset();
|
||||
delete m_asyncWorker;
|
||||
}
|
||||
|
||||
@@ -281,7 +280,7 @@ void TorrentFilesWatcher::initWorker()
|
||||
connect(m_asyncWorker, &TorrentFilesWatcher::Worker::magnetFound, this, &TorrentFilesWatcher::onMagnetFound);
|
||||
connect(m_asyncWorker, &TorrentFilesWatcher::Worker::torrentFound, this, &TorrentFilesWatcher::onTorrentFound);
|
||||
|
||||
m_asyncWorker->moveToThread(m_ioThread);
|
||||
m_asyncWorker->moveToThread(m_ioThread.get());
|
||||
m_ioThread->start();
|
||||
|
||||
for (auto it = m_watchedFolders.cbegin(); it != m_watchedFolders.cend(); ++it)
|
||||
@@ -504,7 +503,7 @@ void TorrentFilesWatcher::Worker::removeWatchedFolder(const Path &path)
|
||||
|
||||
void TorrentFilesWatcher::Worker::scheduleWatchedFolderProcessing(const Path &path)
|
||||
{
|
||||
QTimer::singleShot(2s, this, [this, path]()
|
||||
QTimer::singleShot(2s, Qt::CoarseTimer, this, [this, path]
|
||||
{
|
||||
processWatchedFolder(path);
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "base/bittorrent/addtorrentparams.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/thread.h"
|
||||
|
||||
class QThread;
|
||||
|
||||
@@ -89,7 +90,7 @@ private:
|
||||
|
||||
QHash<Path, WatchedFolderOptions> m_watchedFolders;
|
||||
|
||||
QThread *m_ioThread = nullptr;
|
||||
Utils::Thread::UniquePtr m_ioThread;
|
||||
|
||||
class Worker;
|
||||
Worker *m_asyncWorker = nullptr;
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
#include <zlib.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QLocale>
|
||||
#include <QMimeDatabase>
|
||||
#include <QRegularExpression>
|
||||
#include <QSet>
|
||||
@@ -403,6 +405,94 @@ QString Utils::Misc::getUserIDString()
|
||||
return uid;
|
||||
}
|
||||
|
||||
QString Utils::Misc::languageToLocalizedString(const QString &localeStr)
|
||||
{
|
||||
if (localeStr.startsWith(u"eo", Qt::CaseInsensitive))
|
||||
{
|
||||
// QLocale doesn't work with that locale. Esperanto isn't a "real" language.
|
||||
return C_LOCALE_ESPERANTO;
|
||||
}
|
||||
|
||||
if (localeStr.startsWith(u"ltg", Qt::CaseInsensitive))
|
||||
{
|
||||
// QLocale doesn't work with that locale.
|
||||
return C_LOCALE_LATGALIAN;
|
||||
}
|
||||
|
||||
const QLocale locale {localeStr};
|
||||
switch (locale.language())
|
||||
{
|
||||
case QLocale::Arabic: return C_LOCALE_ARABIC;
|
||||
case QLocale::Armenian: return C_LOCALE_ARMENIAN;
|
||||
case QLocale::Azerbaijani: return C_LOCALE_AZERBAIJANI;
|
||||
case QLocale::Basque: return C_LOCALE_BASQUE;
|
||||
case QLocale::Bulgarian: return C_LOCALE_BULGARIAN;
|
||||
case QLocale::Byelorussian: return C_LOCALE_BYELORUSSIAN;
|
||||
case QLocale::Catalan: return C_LOCALE_CATALAN;
|
||||
case QLocale::Chinese:
|
||||
switch (locale.country())
|
||||
{
|
||||
case QLocale::China: return C_LOCALE_CHINESE_SIMPLIFIED;
|
||||
case QLocale::HongKong: return C_LOCALE_CHINESE_TRADITIONAL_HK;
|
||||
default: return C_LOCALE_CHINESE_TRADITIONAL_TW;
|
||||
}
|
||||
case QLocale::Croatian: return C_LOCALE_CROATIAN;
|
||||
case QLocale::Czech: return C_LOCALE_CZECH;
|
||||
case QLocale::Danish: return C_LOCALE_DANISH;
|
||||
case QLocale::Dutch: return C_LOCALE_DUTCH;
|
||||
case QLocale::English:
|
||||
switch (locale.country())
|
||||
{
|
||||
case QLocale::Australia: return C_LOCALE_ENGLISH_AUSTRALIA;
|
||||
case QLocale::UnitedKingdom: return C_LOCALE_ENGLISH_UNITEDKINGDOM;
|
||||
default: return C_LOCALE_ENGLISH;
|
||||
}
|
||||
case QLocale::Estonian: return C_LOCALE_ESTONIAN;
|
||||
case QLocale::Finnish: return C_LOCALE_FINNISH;
|
||||
case QLocale::French: return C_LOCALE_FRENCH;
|
||||
case QLocale::Galician: return C_LOCALE_GALICIAN;
|
||||
case QLocale::Georgian: return C_LOCALE_GEORGIAN;
|
||||
case QLocale::German: return C_LOCALE_GERMAN;
|
||||
case QLocale::Greek: return C_LOCALE_GREEK;
|
||||
case QLocale::Hebrew: return C_LOCALE_HEBREW;
|
||||
case QLocale::Hindi: return C_LOCALE_HINDI;
|
||||
case QLocale::Hungarian: return C_LOCALE_HUNGARIAN;
|
||||
case QLocale::Icelandic: return C_LOCALE_ICELANDIC;
|
||||
case QLocale::Indonesian: return C_LOCALE_INDONESIAN;
|
||||
case QLocale::Italian: return C_LOCALE_ITALIAN;
|
||||
case QLocale::Japanese: return C_LOCALE_JAPANESE;
|
||||
case QLocale::Korean: return C_LOCALE_KOREAN;
|
||||
case QLocale::Latvian: return C_LOCALE_LATVIAN;
|
||||
case QLocale::Lithuanian: return C_LOCALE_LITHUANIAN;
|
||||
case QLocale::Malay: return C_LOCALE_MALAY;
|
||||
case QLocale::Mongolian: return C_LOCALE_MONGOLIAN;
|
||||
case QLocale::NorwegianBokmal: return C_LOCALE_NORWEGIAN;
|
||||
case QLocale::Occitan: return C_LOCALE_OCCITAN;
|
||||
case QLocale::Persian: return C_LOCALE_PERSIAN;
|
||||
case QLocale::Polish: return C_LOCALE_POLISH;
|
||||
case QLocale::Portuguese:
|
||||
if (locale.country() == QLocale::Brazil)
|
||||
return C_LOCALE_PORTUGUESE_BRAZIL;
|
||||
return C_LOCALE_PORTUGUESE;
|
||||
case QLocale::Romanian: return C_LOCALE_ROMANIAN;
|
||||
case QLocale::Russian: return C_LOCALE_RUSSIAN;
|
||||
case QLocale::Serbian: return C_LOCALE_SERBIAN;
|
||||
case QLocale::Slovak: return C_LOCALE_SLOVAK;
|
||||
case QLocale::Slovenian: return C_LOCALE_SLOVENIAN;
|
||||
case QLocale::Spanish: return C_LOCALE_SPANISH;
|
||||
case QLocale::Swedish: return C_LOCALE_SWEDISH;
|
||||
case QLocale::Thai: return C_LOCALE_THAI;
|
||||
case QLocale::Turkish: return C_LOCALE_TURKISH;
|
||||
case QLocale::Ukrainian: return C_LOCALE_UKRAINIAN;
|
||||
case QLocale::Uzbek: return C_LOCALE_UZBEK;
|
||||
case QLocale::Vietnamese: return C_LOCALE_VIETNAMESE;
|
||||
default:
|
||||
const QString lang = QLocale::languageToString(locale.language());
|
||||
qWarning() << "Unrecognized language name: " << lang;
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
|
||||
QString Utils::Misc::parseHtmlLinks(const QString &rawText)
|
||||
{
|
||||
QString result = rawText;
|
||||
|
||||
@@ -85,6 +85,8 @@ namespace Utils::Misc
|
||||
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap = -1);
|
||||
QString getUserIDString();
|
||||
|
||||
QString languageToLocalizedString(const QString &localeStr);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
Path windowsSystemPath();
|
||||
|
||||
|
||||
38
src/base/utils/thread.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
void Utils::Thread::GracefulDeleter::operator()(QThread *thread) const
|
||||
{
|
||||
thread->quit();
|
||||
thread->wait();
|
||||
delete thread;
|
||||
}
|
||||
43
src/base/utils/thread.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QThread;
|
||||
|
||||
namespace Utils::Thread
|
||||
{
|
||||
struct GracefulDeleter
|
||||
{
|
||||
void operator()(QThread *thread) const;
|
||||
};
|
||||
|
||||
using UniquePtr = std::unique_ptr<QThread, GracefulDeleter>;
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
#define QBT_VERSION_MAJOR 4
|
||||
#define QBT_VERSION_MINOR 5
|
||||
#define QBT_VERSION_BUGFIX 0
|
||||
#define QBT_VERSION_BUGFIX 4
|
||||
#define QBT_VERSION_BUILD 0
|
||||
#define QBT_VERSION_STATUS "" // Should be empty for stable releases!
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
clear();
|
||||
if (m_parent)
|
||||
{
|
||||
m_parent->m_torrentsCount -= m_torrentsCount;
|
||||
m_parent->decreaseTorrentsCount(m_torrentsCount);
|
||||
const QString uid = m_parent->m_children.key(this);
|
||||
m_parent->m_children.remove(uid);
|
||||
m_parent->m_childUids.removeOne(uid);
|
||||
@@ -86,18 +86,18 @@ public:
|
||||
return m_torrentsCount;
|
||||
}
|
||||
|
||||
void increaseTorrentsCount()
|
||||
void increaseTorrentsCount(const int delta = 1)
|
||||
{
|
||||
++m_torrentsCount;
|
||||
m_torrentsCount += delta;
|
||||
if (m_parent)
|
||||
m_parent->increaseTorrentsCount();
|
||||
m_parent->increaseTorrentsCount(delta);
|
||||
}
|
||||
|
||||
void decreaseTorrentsCount()
|
||||
void decreaseTorrentsCount(const int delta = 1)
|
||||
{
|
||||
--m_torrentsCount;
|
||||
m_torrentsCount -= delta;
|
||||
if (m_parent)
|
||||
m_parent->decreaseTorrentsCount();
|
||||
m_parent->decreaseTorrentsCount(delta);
|
||||
}
|
||||
|
||||
int pos() const
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
item->m_parent = this;
|
||||
m_children[uid] = item;
|
||||
m_childUids.append(uid);
|
||||
m_torrentsCount += item->torrentsCount();
|
||||
increaseTorrentsCount(item->torrentsCount());
|
||||
}
|
||||
|
||||
void clear()
|
||||
@@ -211,7 +211,7 @@ QVariant CategoryFilterModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
if ((index.column() == 0) && (role == Qt::DecorationRole))
|
||||
{
|
||||
return UIThemeManager::instance()->getIcon(u"view-categories"_qs);
|
||||
return UIThemeManager::instance()->getIcon(u"view-categories"_qs, u"inode-directory"_qs);
|
||||
}
|
||||
|
||||
if ((index.column() == 0) && (role == Qt::DisplayRole))
|
||||
@@ -408,9 +408,9 @@ void CategoryFilterModel::populate()
|
||||
m_rootItem->addChild(UID_UNCATEGORIZED, new CategoryModelItem(nullptr, tr("Uncategorized"), torrentsCount));
|
||||
|
||||
using BitTorrent::Torrent;
|
||||
for (const QString &categoryName : asConst(session->categories()))
|
||||
if (m_isSubcategoriesEnabled)
|
||||
{
|
||||
if (m_isSubcategoriesEnabled)
|
||||
for (const QString &categoryName : asConst(session->categories()))
|
||||
{
|
||||
CategoryModelItem *parent = m_rootItem;
|
||||
for (const QString &subcat : asConst(session->expandCategory(categoryName)))
|
||||
@@ -419,16 +419,19 @@ void CategoryFilterModel::populate()
|
||||
if (!parent->hasChild(subcatName))
|
||||
{
|
||||
const int torrentsCount = std::count_if(torrents.cbegin(), torrents.cend()
|
||||
, [subcat](Torrent *torrent) { return torrent->category() == subcat; });
|
||||
, [subcat](Torrent *torrent) { return torrent->category() == subcat; });
|
||||
new CategoryModelItem(parent, subcatName, torrentsCount);
|
||||
}
|
||||
parent = parent->child(subcatName);
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const QString &categoryName : asConst(session->categories()))
|
||||
{
|
||||
const int torrentsCount = std::count_if(torrents.begin(), torrents.end()
|
||||
, [categoryName](Torrent *torrent) { return torrent->belongsToCategory(categoryName); });
|
||||
, [categoryName](Torrent *torrent) { return torrent->belongsToCategory(categoryName); });
|
||||
new CategoryModelItem(m_rootItem, categoryName, torrentsCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,18 +121,18 @@ void CategoryFilterWidget::showMenu()
|
||||
, this, &CategoryFilterWidget::addSubcategory);
|
||||
}
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs), tr("Edit category...")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs, u"document-edit"_qs), tr("Edit category...")
|
||||
, this, &CategoryFilterWidget::editCategory);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove category")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove category")
|
||||
, this, &CategoryFilterWidget::removeCategory);
|
||||
}
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove unused categories")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove unused categories")
|
||||
, this, &CategoryFilterWidget::removeUnusedCategories);
|
||||
menu->addSeparator();
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents")
|
||||
, this, &CategoryFilterWidget::actionResumeTorrentsTriggered);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents")
|
||||
, this, &CategoryFilterWidget::actionPauseTorrentsTriggered);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents")
|
||||
, this, &CategoryFilterWidget::actionDeleteTorrentsTriggered);
|
||||
|
||||
@@ -43,13 +43,13 @@ namespace Color
|
||||
inline const QColor accentEmphasis = 0x1f6feb;
|
||||
inline const QColor accentFg = 0x58a6ff;
|
||||
inline const QColor dangerFg = 0xf85149;
|
||||
inline const QColor doneFg = 0xa371f7;
|
||||
inline const QColor fgMuted = 0x8b949e;
|
||||
inline const QColor fgSubtle = 0x6e7681;
|
||||
inline const QColor severeFg = 0xdb6d28;
|
||||
inline const QColor successEmphasis = 0x238636;
|
||||
inline const QColor successFg = 0x1a7f37;
|
||||
inline const QColor successFg = 0x3fb950;
|
||||
// Scale variables
|
||||
inline const QColor scaleBlue4 = 0x388bfd;
|
||||
inline const QColor scaleYellow6 = 0x845306;
|
||||
}
|
||||
|
||||
@@ -59,13 +59,13 @@ namespace Color
|
||||
inline const QColor accentEmphasis = 0x0969da;
|
||||
inline const QColor accentFg = 0x0969da;
|
||||
inline const QColor dangerFg = 0xcf222e;
|
||||
inline const QColor doneFg = 0x8250df;
|
||||
inline const QColor fgMuted = 0x57606a;
|
||||
inline const QColor fgSubtle = 0x6e7781;
|
||||
inline const QColor severeFg = 0xbc4c00;
|
||||
inline const QColor successEmphasis = 0x2da44e;
|
||||
inline const QColor successFg = 0x1a7f37;
|
||||
// Scale variables
|
||||
inline const QColor scaleBlue4 = 0x218bff;
|
||||
inline const QColor scaleYellow6 = 0x7d4e00;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,12 +99,18 @@ DesktopIntegration::DesktopIntegration(QObject *parent)
|
||||
connect(Preferences::instance(), &Preferences::changed, this, &DesktopIntegration::onPreferencesChanged);
|
||||
}
|
||||
|
||||
DesktopIntegration::~DesktopIntegration()
|
||||
{
|
||||
if (m_menu)
|
||||
delete m_menu;
|
||||
}
|
||||
|
||||
bool DesktopIntegration::isActive() const
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
return true;
|
||||
#else
|
||||
return QSystemTrayIcon::isSystemTrayAvailable();
|
||||
return m_systrayIcon && QSystemTrayIcon::isSystemTrayAvailable();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -135,12 +141,36 @@ void DesktopIntegration::setMenu(QMenu *menu)
|
||||
if (menu == m_menu)
|
||||
return;
|
||||
|
||||
#if defined Q_OS_MACOS
|
||||
if (m_menu)
|
||||
delete m_menu;
|
||||
|
||||
m_menu = menu;
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
if (m_menu)
|
||||
m_menu->setAsDockMenu();
|
||||
#elif defined Q_OS_UNIX
|
||||
const bool systemTrayEnabled = m_systrayIcon;
|
||||
if (m_menu)
|
||||
{
|
||||
if (m_systrayIcon)
|
||||
{
|
||||
delete m_systrayIcon;
|
||||
m_systrayIcon = nullptr;
|
||||
}
|
||||
delete m_menu;
|
||||
}
|
||||
|
||||
m_menu = menu;
|
||||
|
||||
if (systemTrayEnabled && !m_systrayIcon)
|
||||
createTrayIcon();
|
||||
#else
|
||||
if (m_menu)
|
||||
delete m_menu;
|
||||
|
||||
m_menu = menu;
|
||||
|
||||
if (m_systrayIcon)
|
||||
m_systrayIcon->setContextMenu(m_menu);
|
||||
#endif
|
||||
|
||||
@@ -49,6 +49,7 @@ class DesktopIntegration final : public QObject
|
||||
|
||||
public:
|
||||
explicit DesktopIntegration(QObject *parent = nullptr);
|
||||
~DesktopIntegration() override;
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ ExecutionLogWidget::ExecutionLogWidget(const Log::MsgTypes types, QWidget *paren
|
||||
m_ui->tabBan->layout()->addWidget(peerView);
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
m_ui->tabConsole->setTabIcon(0, UIThemeManager::instance()->getIcon(u"help-contents"_qs));
|
||||
m_ui->tabConsole->setTabIcon(1, UIThemeManager::instance()->getIcon(u"ip-blocked"_qs));
|
||||
m_ui->tabConsole->setTabIcon(0, UIThemeManager::instance()->getIcon(u"help-contents"_qs, u"view-calendar-journal"_qs));
|
||||
m_ui->tabConsole->setTabIcon(1, UIThemeManager::instance()->getIcon(u"ip-blocked"_qs, u"view-filter"_qs));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,6 @@
|
||||
#include <QtGlobal>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef QBT_USES_CUSTOMDBUSNOTIFICATIONS
|
||||
#include "notifications/dbusnotifier.h"
|
||||
#endif
|
||||
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/sessionstatus.h"
|
||||
#include "base/global.h"
|
||||
@@ -134,8 +130,7 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState)
|
||||
m_displaySpeedInTitle = pref->speedInTitleBar();
|
||||
// Setting icons
|
||||
#ifndef Q_OS_MACOS
|
||||
const QIcon appLogo(UIThemeManager::instance()->getIcon(u"qbittorrent"_qs, u"qbittorrent-tray"_qs));
|
||||
setWindowIcon(appLogo);
|
||||
setWindowIcon(UIThemeManager::instance()->getIcon(u"qbittorrent"_qs));
|
||||
#endif // Q_OS_MACOS
|
||||
|
||||
#if (defined(Q_OS_UNIX))
|
||||
@@ -147,7 +142,7 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState)
|
||||
m_ui->actionOpen->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs));
|
||||
m_ui->actionDownloadFromURL->setIcon(UIThemeManager::instance()->getIcon(u"insert-link"_qs));
|
||||
m_ui->actionSetGlobalSpeedLimits->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_qs));
|
||||
m_ui->actionCreateTorrent->setIcon(UIThemeManager::instance()->getIcon(u"torrent-creator"_qs));
|
||||
m_ui->actionCreateTorrent->setIcon(UIThemeManager::instance()->getIcon(u"torrent-creator"_qs, u"document-edit"_qs));
|
||||
m_ui->actionAbout->setIcon(UIThemeManager::instance()->getIcon(u"help-about"_qs));
|
||||
m_ui->actionStatistics->setIcon(UIThemeManager::instance()->getIcon(u"view-statistics"_qs));
|
||||
m_ui->actionTopQueuePos->setIcon(UIThemeManager::instance()->getIcon(u"go-top"_qs));
|
||||
@@ -159,13 +154,13 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState)
|
||||
m_ui->actionDonateMoney->setIcon(UIThemeManager::instance()->getIcon(u"wallet-open"_qs));
|
||||
m_ui->actionExit->setIcon(UIThemeManager::instance()->getIcon(u"application-exit"_qs));
|
||||
m_ui->actionLock->setIcon(UIThemeManager::instance()->getIcon(u"object-locked"_qs));
|
||||
m_ui->actionOptions->setIcon(UIThemeManager::instance()->getIcon(u"configure"_qs));
|
||||
m_ui->actionPause->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs));
|
||||
m_ui->actionPauseAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs));
|
||||
m_ui->actionStart->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs));
|
||||
m_ui->actionStartAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs));
|
||||
m_ui->menuAutoShutdownOnDownloadsCompletion->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs));
|
||||
m_ui->actionManageCookies->setIcon(UIThemeManager::instance()->getIcon(u"browser-cookies"_qs));
|
||||
m_ui->actionOptions->setIcon(UIThemeManager::instance()->getIcon(u"configure"_qs, u"preferences-system"_qs));
|
||||
m_ui->actionPause->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs));
|
||||
m_ui->actionPauseAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs));
|
||||
m_ui->actionStart->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs));
|
||||
m_ui->actionStartAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs));
|
||||
m_ui->menuAutoShutdownOnDownloadsCompletion->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs, u"application-exit"_qs));
|
||||
m_ui->actionManageCookies->setIcon(UIThemeManager::instance()->getIcon(u"browser-cookies"_qs, u"preferences-web-browser-cookies"_qs));
|
||||
m_ui->menuLog->setIcon(UIThemeManager::instance()->getIcon(u"help-contents"_qs));
|
||||
m_ui->actionCheckForUpdates->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_qs));
|
||||
|
||||
@@ -464,7 +459,6 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState)
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
app()->desktopIntegration()->setMenu(nullptr);
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
@@ -1139,7 +1133,12 @@ void MainWindow::closeEvent(QCloseEvent *e)
|
||||
}
|
||||
#endif // Q_OS_MACOS
|
||||
|
||||
if (pref->confirmOnExit() && BitTorrent::Session::instance()->hasActiveTorrents())
|
||||
const QVector<BitTorrent::Torrent *> allTorrents = BitTorrent::Session::instance()->torrents();
|
||||
const bool hasActiveTorrents = std::any_of(allTorrents.cbegin(), allTorrents.cend(), [](BitTorrent::Torrent *torrent)
|
||||
{
|
||||
return torrent->isActive();
|
||||
});
|
||||
if (pref->confirmOnExit() && hasActiveTorrents)
|
||||
{
|
||||
if (e->spontaneous() || m_forceExit)
|
||||
{
|
||||
@@ -1569,7 +1568,7 @@ void MainWindow::downloadFromURLList(const QStringList &urlList)
|
||||
|
||||
QMenu *MainWindow::createDesktopIntegrationMenu()
|
||||
{
|
||||
auto *menu = new QMenu(this);
|
||||
auto *menu = new QMenu;
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
connect(menu, &QMenu::aboutToShow, this, [this]()
|
||||
@@ -1903,8 +1902,17 @@ void MainWindow::on_actionAutoShutdown_toggled(bool enabled)
|
||||
|
||||
void MainWindow::updatePowerManagementState()
|
||||
{
|
||||
const bool inhibitSuspend = (Preferences::instance()->preventFromSuspendWhenDownloading() && BitTorrent::Session::instance()->hasUnfinishedTorrents())
|
||||
|| (Preferences::instance()->preventFromSuspendWhenSeeding() && BitTorrent::Session::instance()->hasRunningSeed());
|
||||
const QVector<BitTorrent::Torrent *> allTorrents = BitTorrent::Session::instance()->torrents();
|
||||
const bool hasUnfinishedTorrents = std::any_of(allTorrents.cbegin(), allTorrents.cend(), [](const BitTorrent::Torrent *torrent)
|
||||
{
|
||||
return (!torrent->isSeed() && !torrent->isPaused() && !torrent->isErrored() && torrent->hasMetadata());
|
||||
});
|
||||
const bool hasRunningSeed = std::any_of(allTorrents.cbegin(), allTorrents.cend(), [](const BitTorrent::Torrent *torrent)
|
||||
{
|
||||
return (torrent->isSeed() && !torrent->isPaused());
|
||||
});
|
||||
const bool inhibitSuspend = (Preferences::instance()->preventFromSuspendWhenDownloading() && hasUnfinishedTorrents)
|
||||
|| (Preferences::instance()->preventFromSuspendWhenSeeding() && hasRunningSeed);
|
||||
m_pwr->setActivityState(inhibitSuspend);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,11 +68,6 @@ namespace Ui
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
|
||||
#define QBT_USES_CUSTOMDBUSNOTIFICATIONS
|
||||
class DBusNotifier;
|
||||
#endif
|
||||
|
||||
class MainWindow final : public QMainWindow, public GUIApplicationComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
#include "base/rss/rss_session.h"
|
||||
#include "base/torrentfileguard.h"
|
||||
#include "base/torrentfileswatcher.h"
|
||||
#include "base/unicodestrings.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "base/utils/net.h"
|
||||
#include "base/utils/password.h"
|
||||
#include "base/utils/random.h"
|
||||
@@ -89,81 +89,6 @@ namespace
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString languageToLocalizedString(const QLocale &locale)
|
||||
{
|
||||
switch (locale.language())
|
||||
{
|
||||
case QLocale::Arabic: return C_LOCALE_ARABIC;
|
||||
case QLocale::Armenian: return C_LOCALE_ARMENIAN;
|
||||
case QLocale::Azerbaijani: return C_LOCALE_AZERBAIJANI;
|
||||
case QLocale::Basque: return C_LOCALE_BASQUE;
|
||||
case QLocale::Bulgarian: return C_LOCALE_BULGARIAN;
|
||||
case QLocale::Byelorussian: return C_LOCALE_BYELORUSSIAN;
|
||||
case QLocale::Catalan: return C_LOCALE_CATALAN;
|
||||
case QLocale::Chinese:
|
||||
switch (locale.country())
|
||||
{
|
||||
case QLocale::China: return C_LOCALE_CHINESE_SIMPLIFIED;
|
||||
case QLocale::HongKong: return C_LOCALE_CHINESE_TRADITIONAL_HK;
|
||||
default: return C_LOCALE_CHINESE_TRADITIONAL_TW;
|
||||
}
|
||||
case QLocale::Croatian: return C_LOCALE_CROATIAN;
|
||||
case QLocale::Czech: return C_LOCALE_CZECH;
|
||||
case QLocale::Danish: return C_LOCALE_DANISH;
|
||||
case QLocale::Dutch: return C_LOCALE_DUTCH;
|
||||
case QLocale::English:
|
||||
switch (locale.country())
|
||||
{
|
||||
case QLocale::Australia: return C_LOCALE_ENGLISH_AUSTRALIA;
|
||||
case QLocale::UnitedKingdom: return C_LOCALE_ENGLISH_UNITEDKINGDOM;
|
||||
default: return C_LOCALE_ENGLISH;
|
||||
}
|
||||
case QLocale::Estonian: return C_LOCALE_ESTONIAN;
|
||||
case QLocale::Finnish: return C_LOCALE_FINNISH;
|
||||
case QLocale::French: return C_LOCALE_FRENCH;
|
||||
case QLocale::Galician: return C_LOCALE_GALICIAN;
|
||||
case QLocale::Georgian: return C_LOCALE_GEORGIAN;
|
||||
case QLocale::German: return C_LOCALE_GERMAN;
|
||||
case QLocale::Greek: return C_LOCALE_GREEK;
|
||||
case QLocale::Hebrew: return C_LOCALE_HEBREW;
|
||||
case QLocale::Hindi: return C_LOCALE_HINDI;
|
||||
case QLocale::Hungarian: return C_LOCALE_HUNGARIAN;
|
||||
case QLocale::Icelandic: return C_LOCALE_ICELANDIC;
|
||||
case QLocale::Indonesian: return C_LOCALE_INDONESIAN;
|
||||
case QLocale::Italian: return C_LOCALE_ITALIAN;
|
||||
case QLocale::Japanese: return C_LOCALE_JAPANESE;
|
||||
case QLocale::Korean: return C_LOCALE_KOREAN;
|
||||
case QLocale::Latvian: return C_LOCALE_LATVIAN;
|
||||
case QLocale::Lithuanian: return C_LOCALE_LITHUANIAN;
|
||||
case QLocale::Malay: return C_LOCALE_MALAY;
|
||||
case QLocale::Mongolian: return C_LOCALE_MONGOLIAN;
|
||||
case QLocale::NorwegianBokmal: return C_LOCALE_NORWEGIAN;
|
||||
case QLocale::Occitan: return C_LOCALE_OCCITAN;
|
||||
case QLocale::Persian: return C_LOCALE_PERSIAN;
|
||||
case QLocale::Polish: return C_LOCALE_POLISH;
|
||||
case QLocale::Portuguese:
|
||||
if (locale.country() == QLocale::Brazil)
|
||||
return C_LOCALE_PORTUGUESE_BRAZIL;
|
||||
return C_LOCALE_PORTUGUESE;
|
||||
case QLocale::Romanian: return C_LOCALE_ROMANIAN;
|
||||
case QLocale::Russian: return C_LOCALE_RUSSIAN;
|
||||
case QLocale::Serbian: return C_LOCALE_SERBIAN;
|
||||
case QLocale::Slovak: return C_LOCALE_SLOVAK;
|
||||
case QLocale::Slovenian: return C_LOCALE_SLOVENIAN;
|
||||
case QLocale::Spanish: return C_LOCALE_SPANISH;
|
||||
case QLocale::Swedish: return C_LOCALE_SWEDISH;
|
||||
case QLocale::Thai: return C_LOCALE_THAI;
|
||||
case QLocale::Turkish: return C_LOCALE_TURKISH;
|
||||
case QLocale::Ukrainian: return C_LOCALE_UKRAINIAN;
|
||||
case QLocale::Uzbek: return C_LOCALE_UZBEK;
|
||||
case QLocale::Vietnamese: return C_LOCALE_VIETNAMESE;
|
||||
default:
|
||||
const QString lang = QLocale::languageToString(locale.language());
|
||||
qWarning() << "Unrecognized language name: " << lang;
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
|
||||
class WheelEventEater final : public QObject
|
||||
{
|
||||
public:
|
||||
@@ -203,17 +128,17 @@ OptionsDialog::OptionsDialog(IGUIApplication *app, QWidget *parent)
|
||||
|
||||
// Main icons
|
||||
m_ui->tabSelection->item(TAB_UI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-desktop"_qs));
|
||||
m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-bittorrent"_qs));
|
||||
m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(UIThemeManager::instance()->getIcon(u"network-connect"_qs));
|
||||
m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(UIThemeManager::instance()->getIcon(u"download"_qs));
|
||||
m_ui->tabSelection->item(TAB_SPEED)->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_qs));
|
||||
m_ui->tabSelection->item(TAB_RSS)->setIcon(UIThemeManager::instance()->getIcon(u"application-rss"_qs));
|
||||
m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-bittorrent"_qs, u"preferences-system-network"_qs));
|
||||
m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(UIThemeManager::instance()->getIcon(u"network-connect"_qs, u"network-wired"_qs));
|
||||
m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(UIThemeManager::instance()->getIcon(u"download"_qs, u"folder-download"_qs));
|
||||
m_ui->tabSelection->item(TAB_SPEED)->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_qs, u"chronometer"_qs));
|
||||
m_ui->tabSelection->item(TAB_RSS)->setIcon(UIThemeManager::instance()->getIcon(u"application-rss"_qs, u"application-rss+xml"_qs));
|
||||
#ifdef DISABLE_WEBUI
|
||||
m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true);
|
||||
#else
|
||||
m_ui->tabSelection->item(TAB_WEBUI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-webui"_qs));
|
||||
m_ui->tabSelection->item(TAB_WEBUI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-webui"_qs, u"network-server"_qs));
|
||||
#endif
|
||||
m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-advanced"_qs));
|
||||
m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-advanced"_qs, u"preferences-other"_qs));
|
||||
|
||||
// set uniform size for all icons
|
||||
int maxHeight = -1;
|
||||
@@ -288,6 +213,11 @@ void OptionsDialog::loadBehaviorTabOptions()
|
||||
m_ui->customThemeFilePath->setMode(FileSystemPathEdit::Mode::FileOpen);
|
||||
m_ui->customThemeFilePath->setDialogCaption(tr("Select qBittorrent UI Theme file"));
|
||||
m_ui->customThemeFilePath->setFileNameFilter(tr("qBittorrent UI Theme file (*.qbtheme config.json)"));
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
m_ui->checkUseSystemIcon->setChecked(pref->useSystemIcons());
|
||||
#else
|
||||
m_ui->checkUseSystemIcon->setVisible(false);
|
||||
#endif
|
||||
|
||||
m_ui->confirmDeletion->setChecked(pref->confirmTorrentDeletion());
|
||||
m_ui->checkAltRowColors->setChecked(pref->useAlternatingRowColors());
|
||||
@@ -322,6 +252,7 @@ void OptionsDialog::loadBehaviorTabOptions()
|
||||
m_ui->checkStartMinimized->setChecked(pref->startMinimized());
|
||||
m_ui->checkProgramExitConfirm->setChecked(pref->confirmOnExit());
|
||||
m_ui->checkProgramAutoExitConfirm->setChecked(!pref->dontConfirmAutoExit());
|
||||
m_ui->checkConfirmPauseAndResumeAll->setChecked(pref->confirmPauseAndResumeAll());
|
||||
|
||||
#if !(defined(Q_OS_WIN) || defined(Q_OS_MACOS))
|
||||
m_ui->groupFileAssociation->setVisible(false);
|
||||
@@ -363,17 +294,18 @@ void OptionsDialog::loadBehaviorTabOptions()
|
||||
m_ui->checkPreventFromSuspendWhenDownloading->setChecked(pref->preventFromSuspendWhenDownloading());
|
||||
m_ui->checkPreventFromSuspendWhenSeeding->setChecked(pref->preventFromSuspendWhenSeeding());
|
||||
|
||||
m_ui->checkFileLog->setChecked(app()->isFileLoggerEnabled());
|
||||
const bool fileLogEnabled = app()->isFileLoggerEnabled();
|
||||
m_ui->checkFileLog->setChecked(fileLogEnabled);
|
||||
m_ui->textFileLogPath->setDialogCaption(tr("Choose a save directory"));
|
||||
m_ui->textFileLogPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||
m_ui->textFileLogPath->setSelectedPath(app()->fileLoggerPath());
|
||||
const bool fileLogBackup = app()->isFileLoggerBackup();
|
||||
m_ui->checkFileLogBackup->setChecked(fileLogBackup);
|
||||
m_ui->spinFileLogSize->setEnabled(fileLogBackup);
|
||||
m_ui->spinFileLogSize->setEnabled(fileLogEnabled && fileLogBackup);
|
||||
const bool fileLogDelete = app()->isFileLoggerDeleteOld();
|
||||
m_ui->checkFileLogDelete->setChecked(fileLogDelete);
|
||||
m_ui->spinFileLogAge->setEnabled(fileLogDelete);
|
||||
m_ui->comboFileLogAgeType->setEnabled(fileLogDelete);
|
||||
m_ui->spinFileLogAge->setEnabled(fileLogEnabled && fileLogDelete);
|
||||
m_ui->comboFileLogAgeType->setEnabled(fileLogEnabled && fileLogDelete);
|
||||
m_ui->spinFileLogSize->setValue(app()->fileLoggerMaxSize() / 1024);
|
||||
m_ui->spinFileLogAge->setValue(app()->fileLoggerAge());
|
||||
m_ui->comboFileLogAgeType->setCurrentIndex(app()->fileLoggerAgeType());
|
||||
@@ -382,6 +314,9 @@ void OptionsDialog::loadBehaviorTabOptions()
|
||||
|
||||
connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
connect(m_ui->checkUseSystemIcon, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
#endif
|
||||
connect(m_ui->checkUseCustomTheme, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->customThemeFilePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||
|
||||
@@ -400,6 +335,7 @@ void OptionsDialog::loadBehaviorTabOptions()
|
||||
connect(m_ui->checkStartMinimized, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkProgramExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkProgramAutoExitConfirm, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkConfirmPauseAndResumeAll, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkShowSystray, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkMinimizeToSysTray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkCloseToSystray, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
@@ -419,7 +355,14 @@ void OptionsDialog::loadBehaviorTabOptions()
|
||||
m_ui->checkPreventFromSuspendWhenSeeding->setDisabled(true);
|
||||
#endif
|
||||
|
||||
connect(m_ui->checkFileLog, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkFileLog, &QGroupBox::toggled, this, [this](const bool checked)
|
||||
{
|
||||
m_ui->spinFileLogSize->setEnabled(checked && m_ui->checkFileLogBackup->isChecked());
|
||||
const bool bothChecked = checked && m_ui->checkFileLogDelete->isChecked();
|
||||
m_ui->spinFileLogAge->setEnabled(bothChecked);
|
||||
m_ui->comboFileLogAgeType->setEnabled(bothChecked);
|
||||
enableApplyButton();
|
||||
});
|
||||
connect(m_ui->textFileLogPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||
connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, m_ui->spinFileLogSize, &QWidget::setEnabled);
|
||||
connect(m_ui->checkFileLogBackup, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||
@@ -451,6 +394,9 @@ void OptionsDialog::saveBehaviorTabOptions() const
|
||||
}
|
||||
pref->setLocale(locale);
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
pref->useSystemIcons(m_ui->checkUseSystemIcon->isChecked());
|
||||
#endif
|
||||
pref->setUseCustomUITheme(m_ui->checkUseCustomTheme->isChecked());
|
||||
pref->setCustomUIThemePath(m_ui->customThemeFilePath->selectedPath());
|
||||
|
||||
@@ -466,6 +412,7 @@ void OptionsDialog::saveBehaviorTabOptions() const
|
||||
pref->setStartMinimized(startMinimized());
|
||||
pref->setConfirmOnExit(m_ui->checkProgramExitConfirm->isChecked());
|
||||
pref->setDontConfirmAutoExit(!m_ui->checkProgramAutoExitConfirm->isChecked());
|
||||
pref->setConfirmPauseAndResumeAll(m_ui->checkConfirmPauseAndResumeAll->isChecked());
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
pref->setWinStartup(WinStartup());
|
||||
@@ -1315,25 +1262,8 @@ void OptionsDialog::initializeLanguageCombo()
|
||||
const QStringList langFiles = langDir.entryList(QStringList(u"qbittorrent_*.qm"_qs), QDir::Files);
|
||||
for (const QString &langFile : langFiles)
|
||||
{
|
||||
QString localeStr = langFile.mid(12); // remove "qbittorrent_"
|
||||
localeStr.chop(3); // Remove ".qm"
|
||||
QString languageName;
|
||||
if (localeStr.startsWith(u"eo", Qt::CaseInsensitive))
|
||||
{
|
||||
// QLocale doesn't work with that locale. Esperanto isn't a "real" language.
|
||||
languageName = C_LOCALE_ESPERANTO;
|
||||
}
|
||||
else if (localeStr.startsWith(u"ltg", Qt::CaseInsensitive))
|
||||
{
|
||||
// QLocale doesn't work with that locale.
|
||||
languageName = C_LOCALE_LATGALIAN;
|
||||
}
|
||||
else
|
||||
{
|
||||
QLocale locale(localeStr);
|
||||
languageName = languageToLocalizedString(locale);
|
||||
}
|
||||
m_ui->comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".svg"), */ languageName, localeStr);
|
||||
const QString localeStr = langFile.section(u"_"_qs, 1, -1).section(u"."_qs, 0, 0); // remove "qbittorrent_" and ".qm"
|
||||
m_ui->comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".svg"), */ Utils::Misc::languageToLocalizedString(localeStr), localeStr);
|
||||
qDebug() << "Supported locale:" << localeStr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,41 +133,18 @@
|
||||
<string>Interface</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_81">
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QGroupBox" name="checkUseCustomTheme">
|
||||
<property name="title">
|
||||
<string>Use custom UI Theme</string>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
<property name="text">
|
||||
<string>Changing Interface settings requires application restart</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>UI Theme file:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="FileSystemPathLineEdit" name="customThemeFilePath" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer_111">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
@@ -191,15 +168,45 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer_111">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QGroupBox" name="checkUseCustomTheme">
|
||||
<property name="title">
|
||||
<string>Use custom UI Theme</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>UI Theme file:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="FileSystemPathLineEdit" name="customThemeFilePath" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="checkUseSystemIcon">
|
||||
<property name="text">
|
||||
<string>Changing Interface settings requires application restart</string>
|
||||
<string>Use icons from system theme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -225,6 +232,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkConfirmPauseAndResumeAll">
|
||||
<property name="toolTip">
|
||||
<string>Shows a confirmation dialog upon pausing/resuming all the torrents</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Confirm "Pause/Resume all" actions</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkAltRowColors">
|
||||
<property name="text">
|
||||
|
||||
@@ -750,7 +750,7 @@ void PropertiesWidget::displayWebSeedListMenu()
|
||||
|
||||
if (!rows.isEmpty())
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove Web seed")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove Web seed")
|
||||
, this, &PropertiesWidget::deleteSelectedUrlSeeds);
|
||||
menu->addSeparator();
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy Web seed URL")
|
||||
|
||||
@@ -45,7 +45,7 @@ PropTabBar::PropTabBar(QWidget *parent)
|
||||
// General tab
|
||||
QPushButton *mainInfosButton = new QPushButton(
|
||||
#ifndef Q_OS_MACOS
|
||||
UIThemeManager::instance()->getIcon(u"help-about"_qs),
|
||||
UIThemeManager::instance()->getIcon(u"help-about"_qs, u"document-properties"_qs),
|
||||
#endif
|
||||
tr("General"), parent);
|
||||
mainInfosButton->setShortcut(Qt::ALT + Qt::Key_G);
|
||||
@@ -54,7 +54,7 @@ PropTabBar::PropTabBar(QWidget *parent)
|
||||
// Trackers tab
|
||||
QPushButton *trackersButton = new QPushButton(
|
||||
#ifndef Q_OS_MACOS
|
||||
UIThemeManager::instance()->getIcon(u"trackers"_qs),
|
||||
UIThemeManager::instance()->getIcon(u"trackers"_qs, u"network-server"_qs),
|
||||
#endif
|
||||
tr("Trackers"), parent);
|
||||
trackersButton->setShortcut(Qt::ALT + Qt::Key_C);
|
||||
|
||||
@@ -572,7 +572,7 @@ void TrackerListWidget::showTrackerListMenu()
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs),tr("Edit tracker URL...")
|
||||
, this, &TrackerListWidget::editSelectedTracker);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove tracker")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove tracker")
|
||||
, this, &TrackerListWidget::deleteSelectedTrackers);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy tracker URL")
|
||||
, this, &TrackerListWidget::copyTrackerUrl);
|
||||
@@ -580,10 +580,10 @@ void TrackerListWidget::showTrackerListMenu()
|
||||
|
||||
if (!torrent->isPaused())
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs), tr("Force reannounce to selected trackers")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs, u"view-refresh"_qs), tr("Force reannounce to selected trackers")
|
||||
, this, &TrackerListWidget::reannounceSelected);
|
||||
menu->addSeparator();
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs), tr("Force reannounce to all trackers")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs, u"view-refresh"_qs), tr("Force reannounce to all trackers")
|
||||
, this, [this]()
|
||||
{
|
||||
BitTorrent::Torrent *h = m_properties->getCurrentTorrent();
|
||||
|
||||
@@ -52,7 +52,7 @@ TrackersAdditionDialog::TrackersAdditionDialog(QWidget *parent, BitTorrent::Torr
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_ui->downloadButton->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs));
|
||||
m_ui->downloadButton->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs));
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Add"));
|
||||
|
||||
connect(m_ui->downloadButton, &QAbstractButton::clicked, this, &TrackersAdditionDialog::onDownloadButtonClicked);
|
||||
|
||||
@@ -105,7 +105,7 @@ void ArticleListWidget::handleArticleRead(RSS::Article *rssArticle)
|
||||
const QColor defaultColor {palette().color(QPalette::Inactive, QPalette::WindowText)};
|
||||
const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.ReadArticle"_qs, defaultColor)};
|
||||
item->setData(Qt::ForegroundRole, foregroundBrush);
|
||||
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs));
|
||||
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs, u"sphere"_qs));
|
||||
|
||||
checkInvariant();
|
||||
}
|
||||
@@ -133,14 +133,14 @@ QListWidgetItem *ArticleListWidget::createItem(RSS::Article *article) const
|
||||
const QColor defaultColor {palette().color(QPalette::Inactive, QPalette::WindowText)};
|
||||
const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.ReadArticle"_qs, defaultColor)};
|
||||
item->setData(Qt::ForegroundRole, foregroundBrush);
|
||||
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs));
|
||||
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs, u"sphere"_qs));
|
||||
}
|
||||
else
|
||||
{
|
||||
const QColor defaultColor {palette().color(QPalette::Active, QPalette::Link)};
|
||||
const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.UnreadArticle"_qs, defaultColor)};
|
||||
item->setData(Qt::ForegroundRole, foregroundBrush);
|
||||
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs));
|
||||
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs, u"sphere"_qs));
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -75,7 +75,7 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
// Icons
|
||||
m_ui->removeRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"edit-clear"_qs));
|
||||
m_ui->removeRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs));
|
||||
m_ui->addRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs));
|
||||
m_ui->addCategoryBtn->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs));
|
||||
|
||||
@@ -521,7 +521,7 @@ void AutomatedRssDownloader::displayRulesListMenu()
|
||||
{
|
||||
if (selection.count() == 1)
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Delete rule")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Delete rule")
|
||||
, this, &AutomatedRssDownloader::on_removeRuleBtn_clicked);
|
||||
menu->addSeparator();
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs), tr("Rename rule...")
|
||||
@@ -529,7 +529,7 @@ void AutomatedRssDownloader::displayRulesListMenu()
|
||||
}
|
||||
else
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Delete selected rules")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Delete selected rules")
|
||||
, this, &AutomatedRssDownloader::on_removeRuleBtn_clicked);
|
||||
}
|
||||
|
||||
@@ -759,7 +759,7 @@ void AutomatedRssDownloader::updateMustLineValidity()
|
||||
else
|
||||
{
|
||||
m_ui->lineContains->setStyleSheet(u"QLineEdit { color: #ff0000; }"_qs);
|
||||
m_ui->labelMustStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs).pixmap(16, 16));
|
||||
m_ui->labelMustStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs, u"task-attention"_qs).pixmap(16, 16));
|
||||
m_ui->labelMustStat->setToolTip(error);
|
||||
}
|
||||
}
|
||||
@@ -806,7 +806,7 @@ void AutomatedRssDownloader::updateMustNotLineValidity()
|
||||
else
|
||||
{
|
||||
m_ui->lineNotContains->setStyleSheet(u"QLineEdit { color: #ff0000; }"_qs);
|
||||
m_ui->labelMustNotStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs).pixmap(16, 16));
|
||||
m_ui->labelMustNotStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs, u"task-attention"_qs).pixmap(16, 16));
|
||||
m_ui->labelMustNotStat->setToolTip(error);
|
||||
}
|
||||
}
|
||||
@@ -824,7 +824,7 @@ void AutomatedRssDownloader::updateEpisodeFilterValidity()
|
||||
else
|
||||
{
|
||||
m_ui->lineEFilter->setStyleSheet(u"QLineEdit { color: #ff0000; }"_qs);
|
||||
m_ui->labelEpFilterStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs).pixmap(16, 16));
|
||||
m_ui->labelEpFilterStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs, u"task-attention"_qs).pixmap(16, 16));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace
|
||||
if (feed->isLoading())
|
||||
return UIThemeManager::instance()->getIcon(u"loading"_qs);
|
||||
if (feed->hasError())
|
||||
return UIThemeManager::instance()->getIcon(u"task-reject"_qs);
|
||||
return UIThemeManager::instance()->getIcon(u"task-reject"_qs, u"unavailable"_qs);
|
||||
|
||||
return loadIcon(feed->iconPath(), u"application-rss"_qs);
|
||||
}
|
||||
@@ -105,7 +105,8 @@ FeedListWidget::FeedListWidget(QWidget *parent)
|
||||
m_rssToTreeItemMapping[RSS::Session::instance()->rootFolder()] = invisibleRootItem();
|
||||
|
||||
m_unreadStickyItem = new FeedListItem(this);
|
||||
m_unreadStickyItem->setData(0, Qt::UserRole, QVariant::fromValue(RSS::Session::instance()->rootFolder()));
|
||||
m_unreadStickyItem->setData(0, Qt::UserRole, QVariant::fromValue(
|
||||
reinterpret_cast<intptr_t>(RSS::Session::instance()->rootFolder())));
|
||||
m_unreadStickyItem->setText(0, tr("Unread (%1)").arg(RSS::Session::instance()->rootFolder()->unreadCount()));
|
||||
m_unreadStickyItem->setData(0, Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"mail-inbox"_qs));
|
||||
m_unreadStickyItem->setData(0, StickyItemTagRole, true);
|
||||
@@ -211,9 +212,10 @@ QList<QTreeWidgetItem *> FeedListWidget::getAllOpenedFolders(QTreeWidgetItem *pa
|
||||
|
||||
RSS::Item *FeedListWidget::getRSSItem(QTreeWidgetItem *item) const
|
||||
{
|
||||
if (!item) return nullptr;
|
||||
if (!item)
|
||||
return nullptr;
|
||||
|
||||
return item->data(0, Qt::UserRole).value<RSS::Item *>();
|
||||
return reinterpret_cast<RSS::Item *>(item->data(0, Qt::UserRole).value<intptr_t>());
|
||||
}
|
||||
|
||||
QTreeWidgetItem *FeedListWidget::mapRSSItem(RSS::Item *rssItem) const
|
||||
@@ -275,7 +277,7 @@ QTreeWidgetItem *FeedListWidget::createItem(RSS::Item *rssItem, QTreeWidgetItem
|
||||
{
|
||||
auto *item = new FeedListItem;
|
||||
item->setData(0, Qt::DisplayRole, u"%1 (%2)"_qs.arg(rssItem->name(), QString::number(rssItem->unreadCount())));
|
||||
item->setData(0, Qt::UserRole, QVariant::fromValue(rssItem));
|
||||
item->setData(0, Qt::UserRole, QVariant::fromValue(reinterpret_cast<intptr_t>(rssItem)));
|
||||
m_rssToTreeItemMapping[rssItem] = item;
|
||||
|
||||
QIcon icon;
|
||||
|
||||
@@ -64,8 +64,8 @@ RSSWidget::RSSWidget(QWidget *parent)
|
||||
// Icons
|
||||
m_ui->actionCopyFeedURL->setIcon(UIThemeManager::instance()->getIcon(u"edit-copy"_qs));
|
||||
m_ui->actionDelete->setIcon(UIThemeManager::instance()->getIcon(u"edit-clear"_qs));
|
||||
m_ui->actionDownloadTorrent->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs));
|
||||
m_ui->actionMarkItemsRead->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs));
|
||||
m_ui->actionDownloadTorrent->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs));
|
||||
m_ui->actionMarkItemsRead->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs, u"mail-mark-read"_qs));
|
||||
m_ui->actionNewFolder->setIcon(UIThemeManager::instance()->getIcon(u"folder-new"_qs));
|
||||
m_ui->actionNewSubscription->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs));
|
||||
m_ui->actionOpenNewsURL->setIcon(UIThemeManager::instance()->getIcon(u"application-url"_qs));
|
||||
@@ -74,9 +74,9 @@ RSSWidget::RSSWidget(QWidget *parent)
|
||||
m_ui->actionUpdateAllFeeds->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_qs));
|
||||
#ifndef Q_OS_MACOS
|
||||
m_ui->newFeedButton->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs));
|
||||
m_ui->markReadButton->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs));
|
||||
m_ui->markReadButton->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs, u"mail-mark-read"_qs));
|
||||
m_ui->updateAllButton->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_qs));
|
||||
m_ui->rssDownloaderBtn->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs));
|
||||
m_ui->rssDownloaderBtn->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs));
|
||||
#endif
|
||||
|
||||
m_articleListWidget = new ArticleListWidget(m_ui->splitterMain);
|
||||
|
||||
@@ -392,7 +392,7 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"download"_qs)
|
||||
, tr("Open download window"), this, [this]() { downloadTorrents(AddTorrentOption::ShowDialog); });
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"downloading"_qs)
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs)
|
||||
, tr("Download"), this, [this]() { downloadTorrents(AddTorrentOption::SkipDialog); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"application-url"_qs), tr("Open description page")
|
||||
@@ -401,11 +401,11 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
QMenu *copySubMenu = menu->addMenu(
|
||||
UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy"));
|
||||
|
||||
copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"name"_qs), tr("Name")
|
||||
copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"name"_qs, u"edit-copy"_qs), tr("Name")
|
||||
, this, &SearchJobWidget::copyTorrentNames);
|
||||
copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"insert-link"_qs), tr("Download link")
|
||||
copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"insert-link"_qs, u"edit-copy"_qs), tr("Download link")
|
||||
, this, &SearchJobWidget::copyTorrentDownloadLinks);
|
||||
copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"application-url"_qs), tr("Description page URL")
|
||||
copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"application-url"_qs, u"edit-copy"_qs), tr("Description page URL")
|
||||
, this, &SearchJobWidget::copyTorrentURLs);
|
||||
|
||||
menu->popup(event->globalPos());
|
||||
|
||||
@@ -112,7 +112,7 @@ SearchWidget::SearchWidget(IGUIApplication *app, MainWindow *mainWindow)
|
||||
#ifndef Q_OS_MACOS
|
||||
// Icons
|
||||
m_ui->searchButton->setIcon(UIThemeManager::instance()->getIcon(u"edit-find"_qs));
|
||||
m_ui->pluginsButton->setIcon(UIThemeManager::instance()->getIcon(u"plugins"_qs));
|
||||
m_ui->pluginsButton->setIcon(UIThemeManager::instance()->getIcon(u"plugins"_qs, u"preferences-system-network"_qs));
|
||||
#else
|
||||
// On macOS the icons overlap the text otherwise
|
||||
QSize iconSize = m_ui->tabWidget->iconSize();
|
||||
|
||||
@@ -69,7 +69,7 @@ StatusBar::StatusBar(QWidget *parent)
|
||||
connect(m_connecStatusLblIcon, &QAbstractButton::clicked, this, &StatusBar::connectionButtonClicked);
|
||||
|
||||
m_dlSpeedLbl = new QPushButton(this);
|
||||
m_dlSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs));
|
||||
m_dlSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"downloading_small"_qs));
|
||||
connect(m_dlSpeedLbl, &QAbstractButton::clicked, this, &StatusBar::capSpeed);
|
||||
m_dlSpeedLbl->setFlat(true);
|
||||
m_dlSpeedLbl->setFocusPolicy(Qt::NoFocus);
|
||||
@@ -78,7 +78,7 @@ StatusBar::StatusBar(QWidget *parent)
|
||||
m_dlSpeedLbl->setMinimumWidth(200);
|
||||
|
||||
m_upSpeedLbl = new QPushButton(this);
|
||||
m_upSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"upload"_qs));
|
||||
m_upSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"upload"_qs, u"seeding"_qs));
|
||||
connect(m_upSpeedLbl, &QAbstractButton::clicked, this, &StatusBar::capSpeed);
|
||||
m_upSpeedLbl->setFlat(true);
|
||||
m_upSpeedLbl->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
@@ -123,7 +123,7 @@ QVariant TagFilterModel::data(const QModelIndex &index, int role) const
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DecorationRole:
|
||||
return UIThemeManager::instance()->getIcon(u"tags"_qs);
|
||||
return UIThemeManager::instance()->getIcon(u"tags"_qs, u"inode-directory"_qs);
|
||||
case Qt::DisplayRole:
|
||||
return u"%1 (%2)"_qs.arg(tagDisplayName(item.tag())).arg(item.torrentsCount());
|
||||
case Qt::UserRole:
|
||||
|
||||
@@ -113,16 +113,16 @@ void TagFilterWidget::showMenu()
|
||||
const auto selectedRows = selectionModel()->selectedRows();
|
||||
if (!selectedRows.empty() && !TagFilterModel::isSpecialItem(selectedRows.first()))
|
||||
{
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove tag")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove tag")
|
||||
, this, &TagFilterWidget::removeTag);
|
||||
}
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove unused tags")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove unused tags")
|
||||
, this, &TagFilterWidget::removeUnusedTags);
|
||||
menu->addSeparator();
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents")
|
||||
, this, &TagFilterWidget::actionResumeTorrentsTriggered);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents")
|
||||
, this, &TagFilterWidget::actionPauseTorrentsTriggered);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents")
|
||||
, this, &TagFilterWidget::actionDeleteTorrentsTriggered);
|
||||
|
||||
@@ -133,6 +133,9 @@ QString TorrentCategoryDialog::categoryName() const
|
||||
void TorrentCategoryDialog::setCategoryName(const QString &categoryName)
|
||||
{
|
||||
m_ui->textCategoryName->setText(categoryName);
|
||||
|
||||
const int subcategoryNameStart = categoryName.lastIndexOf(u"/") + 1;
|
||||
m_ui->textCategoryName->setSelection(subcategoryNameStart, (categoryName.size() - subcategoryNameStart));
|
||||
}
|
||||
|
||||
BitTorrent::CategoryOptions TorrentCategoryDialog::categoryOptions() const
|
||||
|
||||
@@ -29,13 +29,10 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="FileSystemPathComboEdit" name="comboSavePath" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelCategoryName">
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -49,10 +46,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelCategoryName">
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
<item row="1" column="1">
|
||||
<widget class="FileSystemPathComboEdit" name="comboSavePath" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -173,6 +173,12 @@
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>textCategoryName</tabstop>
|
||||
<tabstop>comboSavePath</tabstop>
|
||||
<tabstop>comboUseDownloadPath</tabstop>
|
||||
<tabstop>comboDownloadPath</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace
|
||||
{
|
||||
public:
|
||||
UnifiedFileIconProvider()
|
||||
: m_textPlainIcon {UIThemeManager::instance()->getIcon(u"help-about"_qs)}
|
||||
: m_textPlainIcon {UIThemeManager::instance()->getIcon(u"help-about"_qs, u"text-plain"_qs)}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -173,31 +173,31 @@ StatusFilterWidget::StatusFilterWidget(QWidget *parent, TransferListWidget *tran
|
||||
// Add status filters
|
||||
auto *all = new QListWidgetItem(this);
|
||||
all->setData(Qt::DisplayRole, tr("All (0)", "this is for the status filter"));
|
||||
all->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-all"_qs));
|
||||
all->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-all"_qs, u"filterall"_qs));
|
||||
auto *downloading = new QListWidgetItem(this);
|
||||
downloading->setData(Qt::DisplayRole, tr("Downloading (0)"));
|
||||
downloading->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"downloading"_qs));
|
||||
auto *seeding = new QListWidgetItem(this);
|
||||
seeding->setData(Qt::DisplayRole, tr("Seeding (0)"));
|
||||
seeding->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"upload"_qs));
|
||||
seeding->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"upload"_qs, u"uploading"_qs));
|
||||
auto *completed = new QListWidgetItem(this);
|
||||
completed->setData(Qt::DisplayRole, tr("Completed (0)"));
|
||||
completed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"checked-completed"_qs));
|
||||
completed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"checked-completed"_qs, u"completed"_qs));
|
||||
auto *resumed = new QListWidgetItem(this);
|
||||
resumed->setData(Qt::DisplayRole, tr("Resumed (0)"));
|
||||
resumed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"torrent-start"_qs));
|
||||
resumed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs));
|
||||
auto *paused = new QListWidgetItem(this);
|
||||
paused->setData(Qt::DisplayRole, tr("Paused (0)"));
|
||||
paused->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"torrent-stop"_qs));
|
||||
paused->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"stopped"_qs, u"media-playback-pause"_qs));
|
||||
auto *active = new QListWidgetItem(this);
|
||||
active->setData(Qt::DisplayRole, tr("Active (0)"));
|
||||
active->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-active"_qs));
|
||||
active->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-active"_qs, u"filteractive"_qs));
|
||||
auto *inactive = new QListWidgetItem(this);
|
||||
inactive->setData(Qt::DisplayRole, tr("Inactive (0)"));
|
||||
inactive->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-inactive"_qs));
|
||||
inactive->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-inactive"_qs, u"filterinactive"_qs));
|
||||
auto *stalled = new QListWidgetItem(this);
|
||||
stalled->setData(Qt::DisplayRole, tr("Stalled (0)"));
|
||||
stalled->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-stalled"_qs));
|
||||
stalled->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-stalled"_qs, u"filterstalled"_qs));
|
||||
auto *stalledUploading = new QListWidgetItem(this);
|
||||
stalledUploading->setData(Qt::DisplayRole, tr("Stalled Uploading (0)"));
|
||||
stalledUploading->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"stalledUP"_qs));
|
||||
@@ -206,7 +206,7 @@ StatusFilterWidget::StatusFilterWidget(QWidget *parent, TransferListWidget *tran
|
||||
stalledDownloading->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"stalledDL"_qs));
|
||||
auto *checking = new QListWidgetItem(this);
|
||||
checking->setData(Qt::DisplayRole, tr("Checking (0)"));
|
||||
checking->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"force-recheck"_qs));
|
||||
checking->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"force-recheck"_qs, u"checking"_qs));
|
||||
auto *moving = new QListWidgetItem(this);
|
||||
moving->setData(Qt::DisplayRole, tr("Moving (0)"));
|
||||
moving->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"set-location"_qs));
|
||||
@@ -308,9 +308,9 @@ void StatusFilterWidget::showMenu()
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents")
|
||||
, transferList, &TransferListWidget::startVisibleTorrents);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents")
|
||||
, transferList, &TransferListWidget::pauseVisibleTorrents);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents")
|
||||
, transferList, &TransferListWidget::deleteVisibleTorrents);
|
||||
@@ -371,16 +371,16 @@ TrackerFiltersList::TrackerFiltersList(QWidget *parent, TransferListWidget *tran
|
||||
{
|
||||
auto *allTrackers = new QListWidgetItem(this);
|
||||
allTrackers->setData(Qt::DisplayRole, tr("All (0)", "this is for the tracker filter"));
|
||||
allTrackers->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs));
|
||||
allTrackers->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs, u"network-server"_qs));
|
||||
auto *noTracker = new QListWidgetItem(this);
|
||||
noTracker->setData(Qt::DisplayRole, tr("Trackerless (0)"));
|
||||
noTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackerless"_qs));
|
||||
noTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackerless"_qs, u"network-server"_qs));
|
||||
auto *errorTracker = new QListWidgetItem(this);
|
||||
errorTracker->setData(Qt::DisplayRole, tr("Error (0)"));
|
||||
errorTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-error"_qs));
|
||||
errorTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-error"_qs, u"dialog-error"_qs));
|
||||
auto *warningTracker = new QListWidgetItem(this);
|
||||
warningTracker->setData(Qt::DisplayRole, tr("Warning (0)"));
|
||||
warningTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-warning"_qs));
|
||||
warningTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-warning"_qs, u"dialog-warning"_qs));
|
||||
|
||||
m_trackers[NULL_HOST] = {{}, noTracker};
|
||||
|
||||
@@ -474,7 +474,7 @@ void TrackerFiltersList::addItems(const QString &trackerURL, const QVector<BitTo
|
||||
else
|
||||
{
|
||||
trackerItem = new QListWidgetItem();
|
||||
trackerItem->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs));
|
||||
trackerItem->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs, u"network-server"_qs));
|
||||
|
||||
const TrackerData trackerData {{}, trackerItem};
|
||||
trackersIt = m_trackers.insert(host, trackerData);
|
||||
@@ -712,9 +712,9 @@ void TrackerFiltersList::showMenu()
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents")
|
||||
, transferList, &TransferListWidget::startVisibleTorrents);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents")
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents")
|
||||
, transferList, &TransferListWidget::pauseVisibleTorrents);
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents")
|
||||
, transferList, &TransferListWidget::deleteVisibleTorrents);
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace
|
||||
case BitTorrent::TorrentState::PausedDownloading:
|
||||
return (isDarkTheme ? Color::Primer::Dark::fgMuted : Color::Primer::Light::fgMuted);
|
||||
case BitTorrent::TorrentState::PausedUploading:
|
||||
return (isDarkTheme ? Color::Primer::Dark::scaleBlue4 : Color::Primer::Light::scaleBlue4);
|
||||
return (isDarkTheme ? Color::Primer::Dark::doneFg : Color::Primer::Light::doneFg);
|
||||
case BitTorrent::TorrentState::QueuedDownloading:
|
||||
case BitTorrent::TorrentState::QueuedUploading:
|
||||
return (isDarkTheme ? Color::Primer::Dark::scaleYellow6 : Color::Primer::Light::scaleYellow6);
|
||||
@@ -158,15 +158,15 @@ TransferListModel::TransferListModel(QObject *parent)
|
||||
{BitTorrent::TorrentState::Error, tr("Errored", "Torrent status, the torrent has an error")}
|
||||
}
|
||||
, m_stateThemeColors {torrentStateColorsFromUITheme()}
|
||||
, m_checkingIcon {UIThemeManager::instance()->getIcon(u"force-recheck"_qs)}
|
||||
, m_completedIcon {UIThemeManager::instance()->getIcon(u"checked-completed"_qs)}
|
||||
, m_checkingIcon {UIThemeManager::instance()->getIcon(u"force-recheck"_qs, u"checking"_qs)}
|
||||
, m_completedIcon {UIThemeManager::instance()->getIcon(u"checked-completed"_qs, u"completed"_qs)}
|
||||
, m_downloadingIcon {UIThemeManager::instance()->getIcon(u"downloading"_qs)}
|
||||
, m_errorIcon {UIThemeManager::instance()->getIcon(u"error"_qs)}
|
||||
, m_pausedIcon {UIThemeManager::instance()->getIcon(u"torrent-stop"_qs)}
|
||||
, m_pausedIcon {UIThemeManager::instance()->getIcon(u"stopped"_qs, u"media-playback-pause"_qs)}
|
||||
, m_queuedIcon {UIThemeManager::instance()->getIcon(u"queued"_qs)}
|
||||
, m_stalledDLIcon {UIThemeManager::instance()->getIcon(u"stalledDL"_qs)}
|
||||
, m_stalledUPIcon {UIThemeManager::instance()->getIcon(u"stalledUP"_qs)}
|
||||
, m_uploadingIcon {UIThemeManager::instance()->getIcon(u"upload"_qs)}
|
||||
, m_uploadingIcon {UIThemeManager::instance()->getIcon(u"upload"_qs, u"uploading"_qs)}
|
||||
{
|
||||
configure();
|
||||
connect(Preferences::instance(), &Preferences::changed, this, &TransferListModel::configure);
|
||||
|
||||
@@ -363,14 +363,15 @@ void TransferListWidget::setSelectedTorrentsLocation()
|
||||
|
||||
void TransferListWidget::pauseAllTorrents()
|
||||
{
|
||||
// Show confirmation if user would really like to Pause All
|
||||
const QMessageBox::StandardButton ret =
|
||||
QMessageBox::question(this, tr("Confirm pause")
|
||||
, tr("Would you like to pause all torrents?")
|
||||
, (QMessageBox::Yes | QMessageBox::No));
|
||||
if (Preferences::instance()->confirmPauseAndResumeAll())
|
||||
{
|
||||
// Show confirmation if user would really like to Pause All
|
||||
const QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Confirm pause")
|
||||
, tr("Would you like to pause all torrents?"), (QMessageBox::Yes | QMessageBox::No));
|
||||
|
||||
if (ret != QMessageBox::Yes)
|
||||
return;
|
||||
if (ret != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
||||
torrent->pause();
|
||||
@@ -378,14 +379,15 @@ void TransferListWidget::pauseAllTorrents()
|
||||
|
||||
void TransferListWidget::resumeAllTorrents()
|
||||
{
|
||||
// Show confirmation if user would really like to Resume All
|
||||
const QMessageBox::StandardButton ret =
|
||||
QMessageBox::question(this, tr("Confirm resume")
|
||||
, tr("Would you like to resume all torrents?")
|
||||
, (QMessageBox::Yes | QMessageBox::No));
|
||||
if (Preferences::instance()->confirmPauseAndResumeAll())
|
||||
{
|
||||
// Show confirmation if user would really like to Resume All
|
||||
const QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Confirm resume")
|
||||
, tr("Would you like to resume all torrents?"), (QMessageBox::Yes | QMessageBox::No));
|
||||
|
||||
if (ret != QMessageBox::Yes)
|
||||
return;
|
||||
if (ret != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
||||
torrent->resume();
|
||||
@@ -930,11 +932,11 @@ void TransferListWidget::displayListMenu()
|
||||
|
||||
// Create actions
|
||||
|
||||
auto *actionStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("&Resume", "Resume/start the torrent"), listMenu);
|
||||
auto *actionStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("&Resume", "Resume/start the torrent"), listMenu);
|
||||
connect(actionStart, &QAction::triggered, this, &TransferListWidget::startSelectedTorrents);
|
||||
auto *actionPause = new QAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("&Pause", "Pause the torrent"), listMenu);
|
||||
auto *actionPause = new QAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("&Pause", "Pause the torrent"), listMenu);
|
||||
connect(actionPause, &QAction::triggered, this, &TransferListWidget::pauseSelectedTorrents);
|
||||
auto *actionForceStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start-forced"_qs), tr("Force Resu&me", "Force Resume/start the torrent"), listMenu);
|
||||
auto *actionForceStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start-forced"_qs, u"media-playback-start"_qs), tr("Force Resu&me", "Force Resume/start the torrent"), listMenu);
|
||||
connect(actionForceStart, &QAction::triggered, this, &TransferListWidget::forceStartSelectedTorrents);
|
||||
auto *actionDelete = new QAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("&Remove", "Remove the torrent"), listMenu);
|
||||
connect(actionDelete, &QAction::triggered, this, &TransferListWidget::softDeleteSelectedTorrents);
|
||||
@@ -952,21 +954,21 @@ void TransferListWidget::displayListMenu()
|
||||
connect(actionTopQueuePos, &QAction::triggered, this, &TransferListWidget::topQueuePosSelectedTorrents);
|
||||
auto *actionBottomQueuePos = new QAction(UIThemeManager::instance()->getIcon(u"go-bottom"_qs), tr("Move to &bottom", "i.e. Move to bottom of the queue"), listMenu);
|
||||
connect(actionBottomQueuePos, &QAction::triggered, this, &TransferListWidget::bottomQueuePosSelectedTorrents);
|
||||
auto *actionSetTorrentPath = new QAction(UIThemeManager::instance()->getIcon(u"set-location"_qs), tr("Set loc&ation..."), listMenu);
|
||||
auto *actionSetTorrentPath = new QAction(UIThemeManager::instance()->getIcon(u"set-location"_qs, u"inode-directory"_qs), tr("Set loc&ation..."), listMenu);
|
||||
connect(actionSetTorrentPath, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsLocation);
|
||||
auto *actionForceRecheck = new QAction(UIThemeManager::instance()->getIcon(u"force-recheck"_qs), tr("Force rec&heck"), listMenu);
|
||||
auto *actionForceRecheck = new QAction(UIThemeManager::instance()->getIcon(u"force-recheck"_qs, u"document-edit-verify"_qs), tr("Force rec&heck"), listMenu);
|
||||
connect(actionForceRecheck, &QAction::triggered, this, &TransferListWidget::recheckSelectedTorrents);
|
||||
auto *actionForceReannounce = new QAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs), tr("Force r&eannounce"), listMenu);
|
||||
auto *actionForceReannounce = new QAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs, u"document-edit-verify"_qs), tr("Force r&eannounce"), listMenu);
|
||||
connect(actionForceReannounce, &QAction::triggered, this, &TransferListWidget::reannounceSelectedTorrents);
|
||||
auto *actionCopyMagnetLink = new QAction(UIThemeManager::instance()->getIcon(u"torrent-magnet"_qs), tr("&Magnet link"), listMenu);
|
||||
auto *actionCopyMagnetLink = new QAction(UIThemeManager::instance()->getIcon(u"torrent-magnet"_qs, u"kt-magnet"_qs), tr("&Magnet link"), listMenu);
|
||||
connect(actionCopyMagnetLink, &QAction::triggered, this, &TransferListWidget::copySelectedMagnetURIs);
|
||||
auto *actionCopyID = new QAction(UIThemeManager::instance()->getIcon(u"help-about"_qs), tr("Torrent &ID"), listMenu);
|
||||
auto *actionCopyID = new QAction(UIThemeManager::instance()->getIcon(u"help-about"_qs, u"edit-copy"_qs), tr("Torrent &ID"), listMenu);
|
||||
connect(actionCopyID, &QAction::triggered, this, &TransferListWidget::copySelectedIDs);
|
||||
auto *actionCopyName = new QAction(UIThemeManager::instance()->getIcon(u"name"_qs), tr("&Name"), listMenu);
|
||||
auto *actionCopyName = new QAction(UIThemeManager::instance()->getIcon(u"name"_qs, u"edit-copy"_qs), tr("&Name"), listMenu);
|
||||
connect(actionCopyName, &QAction::triggered, this, &TransferListWidget::copySelectedNames);
|
||||
auto *actionCopyHash1 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs), tr("Info &hash v1"), listMenu);
|
||||
auto *actionCopyHash1 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs, u"edit-copy"_qs), tr("Info &hash v1"), listMenu);
|
||||
connect(actionCopyHash1, &QAction::triggered, this, [this]() { copySelectedInfohashes(CopyInfohashPolicy::Version1); });
|
||||
auto *actionCopyHash2 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs), tr("Info h&ash v2"), listMenu);
|
||||
auto *actionCopyHash2 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs, u"edit-copy"_qs), tr("Info h&ash v2"), listMenu);
|
||||
connect(actionCopyHash2, &QAction::triggered, this, [this]() { copySelectedInfohashes(CopyInfohashPolicy::Version2); });
|
||||
auto *actionSuperSeedingMode = new TriStateAction(tr("Super seeding mode"), listMenu);
|
||||
connect(actionSuperSeedingMode, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsSuperSeeding);
|
||||
@@ -1152,7 +1154,7 @@ void TransferListWidget::displayListMenu()
|
||||
QStringList tags(BitTorrent::Session::instance()->tags().values());
|
||||
std::sort(tags.begin(), tags.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
||||
|
||||
QMenu *tagsMenu = listMenu->addMenu(UIThemeManager::instance()->getIcon(u"tags"_qs), tr("Ta&gs"));
|
||||
QMenu *tagsMenu = listMenu->addMenu(UIThemeManager::instance()->getIcon(u"tags"_qs, u"view-categories"_qs), tr("Ta&gs"));
|
||||
|
||||
tagsMenu->addAction(UIThemeManager::instance()->getIcon(u"list-add"_qs), tr("&Add...", "Add / assign multiple tags...")
|
||||
, this, &TransferListWidget::askAddTagsForSelection);
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace
|
||||
std::unique_ptr<UIThemeSource> createUIThemeSource(const Path &themePath)
|
||||
{
|
||||
if (themePath.filename() == CONFIG_FILE_NAME)
|
||||
return std::make_unique<FolderThemeSource>(themePath);
|
||||
return std::make_unique<FolderThemeSource>(themePath.parentPath());
|
||||
|
||||
if ((themePath.hasExtension(u".qbtheme"_qs))
|
||||
&& QResource::registerResource(themePath.data(), u"/uitheme"_qs))
|
||||
@@ -167,7 +167,10 @@ void UIThemeManager::initInstance()
|
||||
}
|
||||
|
||||
UIThemeManager::UIThemeManager()
|
||||
: m_useCustomTheme(Preferences::instance()->useCustomUITheme())
|
||||
: m_useCustomTheme {Preferences::instance()->useCustomUITheme()}
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
, m_useSystemIcons {Preferences::instance()->useSystemIcons()}
|
||||
#endif
|
||||
{
|
||||
if (m_useCustomTheme)
|
||||
{
|
||||
@@ -196,14 +199,25 @@ void UIThemeManager::applyStyleSheet() const
|
||||
qApp->setStyleSheet(QString::fromUtf8(m_themeSource->readStyleSheet()));
|
||||
}
|
||||
|
||||
QIcon UIThemeManager::getIcon(const QString &iconId, const QString &fallback) const
|
||||
QIcon UIThemeManager::getIcon(const QString &iconId, [[maybe_unused]] const QString &fallback) const
|
||||
{
|
||||
// Cache to avoid rescaling svg icons
|
||||
const auto iter = m_iconCache.find(iconId);
|
||||
if (iter != m_iconCache.end())
|
||||
return *iter;
|
||||
|
||||
const QIcon icon {getIconPathFromResources(iconId, fallback).data()};
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
// Don't cache system icons because users might change them at run time
|
||||
if (m_useSystemIcons)
|
||||
{
|
||||
auto icon = QIcon::fromTheme(iconId);
|
||||
if (icon.name() != iconId)
|
||||
icon = QIcon::fromTheme(fallback, QIcon(getIconPathFromResources(iconId).data()));
|
||||
return icon;
|
||||
}
|
||||
#endif
|
||||
|
||||
const QIcon icon {getIconPathFromResources(iconId).data()};
|
||||
m_iconCache[iconId] = icon;
|
||||
return icon;
|
||||
}
|
||||
@@ -259,23 +273,33 @@ QIcon UIThemeManager::getSystrayIcon() const
|
||||
|
||||
Path UIThemeManager::getIconPath(const QString &iconId) const
|
||||
{
|
||||
return getIconPathFromResources(iconId, {});
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
if (m_useSystemIcons)
|
||||
{
|
||||
Path path = Utils::Fs::tempPath() / Path(iconId + u".png");
|
||||
if (!path.exists())
|
||||
{
|
||||
const QIcon icon = QIcon::fromTheme(iconId);
|
||||
if (!icon.isNull())
|
||||
icon.pixmap(32).save(path.toString());
|
||||
else
|
||||
path = getIconPathFromResources(iconId);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
return getIconPathFromResources(iconId);
|
||||
}
|
||||
|
||||
Path UIThemeManager::getIconPathFromResources(const QString &iconId, const QString &fallback) const
|
||||
Path UIThemeManager::getIconPathFromResources(const QString &iconId) const
|
||||
{
|
||||
if (m_useCustomTheme && m_themeSource)
|
||||
{
|
||||
const Path customIcon = m_themeSource->iconPath(iconId);
|
||||
if (!customIcon.isEmpty())
|
||||
return customIcon;
|
||||
|
||||
if (!fallback.isEmpty())
|
||||
{
|
||||
const Path fallbackIcon = m_themeSource->iconPath(fallback);
|
||||
if (!fallbackIcon.isEmpty())
|
||||
return fallbackIcon;
|
||||
}
|
||||
}
|
||||
|
||||
return findIcon(iconId, DEFAULT_ICONS_DIR);
|
||||
|
||||
@@ -70,13 +70,16 @@ public:
|
||||
|
||||
private:
|
||||
UIThemeManager(); // singleton class
|
||||
Path getIconPathFromResources(const QString &iconId, const QString &fallback = {}) const;
|
||||
Path getIconPathFromResources(const QString &iconId) const;
|
||||
void loadColorsFromJSONConfig();
|
||||
void applyPalette() const;
|
||||
void applyStyleSheet() const;
|
||||
|
||||
static UIThemeManager *m_instance;
|
||||
const bool m_useCustomTheme;
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
const bool m_useSystemIcons;
|
||||
#endif
|
||||
std::unique_ptr<UIThemeSource> m_themeSource;
|
||||
QHash<QString, QColor> m_colors;
|
||||
mutable QHash<QString, QIcon> m_iconCache;
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -1 +1 @@
|
||||
<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="5.2" transform="matrix(.96198992 0 0 .96361031 .608162 .582235)"><path d="m4.0469562 10.349429 5.7224138-6.2783812 5.722414 6.2783812m-5.722414 17.579469v-22.6021738" stroke="#6495ed"/><path d="m27.953043 21.650571-5.722413 6.278382-5.722414-6.278382m5.722414-17.5794689v22.6021739" stroke="#3cb371"/></g></svg>
|
||||
<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="5.00656"><path d="m10.005859 2.0019531a2.5032799 2.5032799 0 0 0 -2.5039059 2.5039063v16.5156246l-1.1484375-1.261718a2.5032799 2.5032799 0 0 0 -3.5371093-.166016 2.5032799 2.5032799 0 0 0 -.1660157 3.535156l5.5039063 6.050781a2.5035303 2.5035303 0 0 0 3.7031251 0l5.505859-6.050781a2.5032799 2.5032799 0 0 0 -.167969-3.535156 2.5032799 2.5032799 0 0 0 -3.535156.166016l-1.15039 1.263671v-16.5175776a2.5032799 2.5032799 0 0 0 -2.503907-2.5039063z" fill="#3cb371"/><path d="m21.994141 2.0019531a2.5035303 2.5035303 0 0 0 -1.851563.8183594l-5.505859 6.0507813a2.5032799 2.5032799 0 0 0 .167969 3.5351562 2.5032799 2.5032799 0 0 0 3.535156-.166016l1.15039-1.263671v16.517578a2.5032799 2.5032799 0 0 0 2.503907 2.503906 2.5032799 2.5032799 0 0 0 2.503906-2.503906v-16.515625l1.148437 1.261718a2.5032799 2.5032799 0 0 0 3.53711.166016 2.5032799 2.5032799 0 0 0 .166015-3.5351562l-5.503906-6.0507813a2.5035303 2.5035303 0 0 0 -1.851562-.8183594z" fill="#1e90ff"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 470 B After Width: | Height: | Size: 1.1 KiB |
@@ -5,10 +5,10 @@
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g fill-rule="evenodd" clip-path="url(#a)" transform="translate(74.7) scale(.9375)">
|
||||
<path fill="#fff" d="M-120 0h763.3v511.5H-120z"/>
|
||||
<path d="M-118.3.6h760.9v216.1h-761z"/>
|
||||
<path fill="#0061ff" d="M21.3 203.2h505V317h-505z"/>
|
||||
<path fill="#e20000" d="M642.8 1.8V512H262L642.8 1.7zm-761.5 0V512H262L-118.7 1.7z"/>
|
||||
<path fill="#ffd600" d="M440.4 203.3 364 184l64.9-49-79.7 11.4 41-69.5-70.7 41L332.3 37l-47.9 63.8-19.3-74-21.7 76.3-47.8-65 13.7 83.2L138.5 78l41 69.5-77.4-12.5 63.8 47.8L86 203.3h354.3z"/>
|
||||
<path fill="#fff" d="M-79.68 0h682.67v512H-79.68V0Z"/>
|
||||
<path d="M-79.59 0h682.58v204.8H-79.68L-79.59 0Z"/>
|
||||
<path d="M21.3 203.2h480v112h-480v-112Z" fill="#0072c6"/>
|
||||
<path d="M602.99.1V512H261.6L602.99 0v.1ZM-79.68.1V512h341.29L-79.68 0v.1Z" fill="#ce1126"/>
|
||||
<path d="M440.4 203.3 364 184l64.9-49-79.7 11.4 41-69.5-70.7 41L332.3 37l-47.9 63.8-19.3-74-21.7 76.3-47.8-65 13.7 83.2L138.5 78l41 69.5-77.4-12.5 63.8 47.8L86 203.3h354.3z" fill="#fcd116"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 769 B |
@@ -1,5 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icons-al" viewBox="0 0 640 480">
|
||||
<path fill="#e41e20" d="M0 0h640v480H0z"/>
|
||||
<path fill="#f00" d="M0 0h640v480H0z"/>
|
||||
<path id="a" d="M272 93.3c-4.6 0-12.3 1.5-12.2 5-13-2.1-14.3 3.2-13.5 8 1.2-1.9 2.7-3 3.9-3.1 1.7-.3 3.5.3 5.4 1.4a21.6 21.6 0 0 1 4.8 4.1c-4.6 1.1-8.2.4-11.8-.2a16.5 16.5 0 0 1-5.7-2.4c-1.5-1-2-2-4.3-4.3-2.7-2.8-5.6-2-4.7 2.3 2.1 4 5.6 5.8 10 6.6 2.1.3 5.3 1 8.9 1 3.6 0 7.6-.5 9.8 0-1.3.8-2.8 2.3-5.8 2.8-3 .6-7.5-1.8-10.3-2.4.3 2.3 3.3 4.5 9.1 5.7 9.6 2 17.5 3.6 22.8 6.5a37.3 37.3 0 0 1 10.9 9.2c4.7 5.5 5 9.8 5.2 10.8 1 8.8-2.1 13.8-7.9 15.4-2.8.7-8-.7-9.8-2.9-2-2.2-3.7-6-3.2-12 .5-2.2 3.1-8.3.9-9.5a273.7 273.7 0 0 0-32.3-15.1c-2.5-1-4.5 2.4-5.3 3.8a50.2 50.2 0 0 1-36-23.7c-4.2-7.6-11.3 0-10.1 7.3 1.9 8 8 13.8 15.4 18 7.5 4.1 17 8.2 26.5 8 5.2 1 5.1 7.6-1 8.9-12.1 0-21.8-.2-30.9-9-6.9-6.3-10.7 1.2-8.8 5.4 3.4 13.1 22.1 16.8 41 12.6 7.4-1.2 3 6.6 1 6.7-8 5.7-22.1 11.2-34.6 0-5.7-4.4-9.6-.8-7.4 5.5 5.5 16.5 26.7 13 41.2 5 3.7-2.1 7.1 2.7 2.6 6.4-18.1 12.6-27.1 12.8-35.3 8-10.2-4.1-11 7.2-5 11 6.7 4 23.8 1 36.4-7 5.4-4 5.6 2.3 2.2 4.8-14.9 12.9-20.8 16.3-36.3 14.2-7.7-.6-7.6 8.9-1.6 12.6 8.3 5.1 24.5-3.3 37-13.8 5.3-2.8 6.2 1.8 3.6 7.3a53.9 53.9 0 0 1-21.8 18c-7 2.7-13.6 2.3-18.3.7-5.8-2-6.5 4-3.3 9.4 1.9 3.3 9.8 4.3 18.4 1.3 8.6-3 17.8-10.2 24.1-18.5 5.5-4.9 4.9 1.6 2.3 6.2-12.6 20-24.2 27.4-39.5 26.2-6.7-1.2-8.3 4-4 9 7.6 6.2 17 6 25.4-.2 7.3-7 21.4-22.4 28.8-30.6 5.2-4.1 6.9 0 5.3 8.4-1.4 4.8-4.8 10-14.3 13.6-6.5 3.7-1.6 8.8 3.2 9 2.7 0 8.1-3.2 12.3-7.8 5.4-6.2 5.8-10.3 8.8-19.9 2.8-4.6 7.9-2.4 7.9 2.4-2.5 9.6-4.5 11.3-9.5 15.2-4.7 4.5 3.3 6 6 4.1 7.8-5.2 10.6-12 13.2-18.2 2-4.4 7.4-2.3 4.8 5-6 17.4-16 24.2-33.3 27.8-1.7.3-2.8 1.3-2.2 3.3l7 7c-10.7 3.2-19.4 5-30.2 8l-14.8-9.8c-1.3-3.2-2-8.2-9.8-4.7-5.2-2.4-7.7-1.5-10.6 1 4.2 0 6 1.2 7.7 3.1 2.2 5.7 7.2 6.3 12.3 4.7 3.3 2.7 5 4.9 8.4 7.7l-16.7-.5c-6-6.3-10.6-6-14.8-1-3.3.5-4.6.5-6.8 4.4 3.4-1.4 5.6-1.8 7.1-.3 6.3 3.7 10.4 2.9 13.5 0l17.5 1.1c-2.2 2-5.2 3-7.5 4.8-9-2.6-13.8 1-15.4 8.3a17 17 0 0 0-1.2 9.3c.8-3 2.3-5.5 4.9-7 8 2 11-1.3 11.5-6.1 4-3.2 9.8-3.9 13.7-7.1 4.6 1.4 6.8 2.3 11.4 3.8 1.6 5 5.3 6.9 11.3 5.6 7 .2 5.8 3.2 6.4 5.5 2-3.3 1.9-6.6-2.5-9.6-1.6-4.3-5.2-6.3-9.8-3.8-4.4-1.2-5.5-3-9.9-4.3 11-3.5 18.8-4.3 29.8-7.8l7.7 6.8c1.5.9 2.9 1.1 3.8 0 6.9-10 10-18.7 16.3-25.3 2.5-2.8 5.6-6.4 9-7.3 1.7-.5 3.8-.2 5.2 1.3 1.3 1.4 2.4 4.1 2 8.2-.7 5.7-2.1 7.6-3.7 11-1.7 3.5-3.6 5.6-5.7 8.3-4 5.3-9.4 8.4-12.6 10.5-6.4 4.1-9 2.3-14 2-6.4.7-8 3.8-2.8 8.1 4.8 2.6 9.2 2.9 12.8 2.2 3-.6 6.6-4.5 9.2-6.6 2.8-3.3 7.6.6 4.3 4.5-5.9 7-11.7 11.6-19 11.5-7.7 1-6.2 5.3-1.2 7.4 9.2 3.7 17.4-3.3 21.6-8 3.2-3.5 5.5-3.6 5 1.9-3.3 9.9-7.6 13.7-14.8 14.2-5.8-.6-5.9 4-1.6 7 9.6 6.6 16.6-4.8 19.9-11.6 2.3-6.2 5.9-3.3 6.3 1.8 0 6.9-3 12.4-11.3 19.4 6.3 10.1 13.7 20.4 20 30.5l19.2-214L320 139c-2-1.8-8.8-9.8-10.5-11-.7-.6-1-1-.1-1.4.9-.4 3-.8 4.5-1-4-4.1-7.6-5.4-15.3-7.6 1.9-.8 3.7-.4 9.3-.6a30.2 30.2 0 0 0-13.5-10.2c4.2-3 5-3.2 9.2-6.7a86.3 86.3 0 0 1-19.5-3.8 37.4 37.4 0 0 0-12-3.4zm.8 8.4c3.8 0 6.1 1.3 6.1 2.9 0 1.6-2.3 2.9-6.1 2.9s-6.2-1.5-6.2-3c0-1.6 2.4-2.8 6.2-2.8z"/>
|
||||
<use xlink:href="#a" width="100%" height="100%" transform="matrix(-1 0 0 1 640 0)"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
@@ -1,7 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-bl" viewBox="0 0 640 480">
|
||||
<g fill-rule="evenodd" stroke-width="1pt">
|
||||
<path fill="#fff" d="M0 0h640v480H0z"/>
|
||||
<path fill="#00267f" d="M0 0h213.3v480H0z"/>
|
||||
<path fill="#f31830" d="M426.7 0H640v480H426.7z"/>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-fr" viewBox="0 0 640 480">
|
||||
<path fill="#fff" d="M0 0h640v480H0z"/>
|
||||
<path fill="#002654" d="M0 0h213.3v480H0z"/>
|
||||
<path fill="#ce1126" d="M426.7 0H640v480H426.7z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 231 B |