diff --git a/.github/workflows/ci_file_health.yaml b/.github/workflows/ci_file_health.yaml index bfad2f3ff..fb5cb44e6 100644 --- a/.github/workflows/ci_file_health.yaml +++ b/.github/workflows/ci_file_health.yaml @@ -32,7 +32,7 @@ jobs: curl \ -L \ -o "${{ runner.temp }}/pandoc.tar.gz" \ - "https://github.com/jgm/pandoc/releases/download/3.4/pandoc-3.4-linux-amd64.tar.gz" + "https://github.com/jgm/pandoc/releases/download/3.6/pandoc-3.6-linux-amd64.tar.gz" tar -xf "${{ runner.temp }}/pandoc.tar.gz" -C "${{ github.workspace }}/.." mv "${{ github.workspace }}/.."/pandoc-* "${{ env.pandoc_path }}" # run pandoc diff --git a/.github/workflows/ci_python.yaml b/.github/workflows/ci_python.yaml index 268dd1ecf..e3183da5a 100644 --- a/.github/workflows/ci_python.yaml +++ b/.github/workflows/ci_python.yaml @@ -34,7 +34,7 @@ jobs: - name: Lint code (auxiliary scripts) run: | pyflakes $PY_FILES - bandit --skip B314,B405 $PY_FILES + bandit --skip B101,B314,B405 $PY_FILES - name: Format code (auxiliary scripts) run: | @@ -61,7 +61,7 @@ jobs: echo $PY_FILES echo "PY_FILES=$PY_FILES" >> "$GITHUB_ENV" - - name: Check typings (search engine) + - name: Check typings (search engine) run: | MYPYPATH="src/searchengine/nova3" \ mypy \ diff --git a/.github/workflows/helper/pre-commit/check_grid_items_order.py b/.github/workflows/helper/pre-commit/check_grid_items_order.py new file mode 100755 index 000000000..0ab3d6715 --- /dev/null +++ b/.github/workflows/helper/pre-commit/check_grid_items_order.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +# A pre-commit hook for checking items order in grid layouts +# Copyright (C) 2024 Mike Tzou (Chocobo1) +# +# 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. + +from collections.abc import Callable, Sequence +from typing import Optional +import argparse +import re +import xml.etree.ElementTree as ElementTree +import sys + + +def traversePostOrder(root: ElementTree.Element, visitFunc: Callable[[ElementTree.Element], None]) -> None: + stack = [(root, False)] + + while len(stack) > 0: + (element, visit) = stack.pop() + if visit: + visitFunc(element) + else: + stack.append((element, True)) + stack.extend((child, False) for child in reversed(element)) + + +def modifyElement(element: ElementTree.Element) -> None: + def getSortKey(e: ElementTree.Element) -> tuple[int, int]: + if e.tag == 'item': + return (int(e.attrib['row']), int(e.attrib['column'])) + return (-1, -1) # don't care + + if element.tag == 'layout' and element.attrib['class'] == 'QGridLayout' and len(element) > 0: + element[:] = sorted(element, key=getSortKey) + + # workaround_2a: ElementTree will unescape `"` and we need to escape it back + if element.tag == 'string' and element.text is not None: + element.text = element.text.replace('"', '"') + + +def main(argv: Optional[Sequence[str]] = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument('filenames', nargs='*', help='Filenames to check') + args = parser.parse_args(argv) + + for filename in args.filenames: + with open(filename, 'r+') as f: + orig = f.read() + root = ElementTree.fromstring(orig) + traversePostOrder(root, modifyElement) + ElementTree.indent(root, ' ') + + # workaround_1: cannot use `xml_declaration=True` since it uses single quotes instead of Qt preferred double quotes + ret = f'\n{ElementTree.tostring(root, 'unicode')}\n' + + # workaround_2b: ElementTree will turn `"` into `&quot;`, so revert it back + ret = ret.replace('&quot;', '"') + + # workaround_3: Qt prefers no whitespaces in self-closing tags + ret = re.sub('<(.+) +/>', r'<\1/>', ret) + + if ret != orig: + print(f'Tip: run this script to apply the fix: `python {__file__} {filename}`', file=sys.stderr) + + f.seek(0) + f.write(ret) + f.truncate() + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/.github/workflows/helper/pre-commit/check_translation_tag.py b/.github/workflows/helper/pre-commit/check_translation_tag.py index f3d4457e2..4be80df49 100755 --- a/.github/workflows/helper/pre-commit/check_translation_tag.py +++ b/.github/workflows/helper/pre-commit/check_translation_tag.py @@ -26,9 +26,11 @@ # but you are not obligated to do so. If you do not wish to do so, delete this # exception statement from your version. -from typing import Optional, Sequence +from collections.abc import Sequence +from typing import Optional import argparse import re +import sys def main(argv: Optional[Sequence[str]] = None) -> int: @@ -67,4 +69,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int: if __name__ == '__main__': - exit(main()) + sys.exit(main()) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4c84ea2d..991f054cd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,12 @@ repos: - repo: local hooks: + - id: check-grid-order + name: Check items order in grid layouts + entry: .github/workflows/helper/pre-commit/check_grid_items_order.py + language: script + files: \.ui$ + - id: check-translation-tag name: Check newline characters in tag entry: .github/workflows/helper/pre-commit/check_translation_tag.py @@ -13,7 +19,7 @@ repos: - ts - repo: https://github.com/pre-commit/pre-commit-hooks.git - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-json name: Check JSON files @@ -82,7 +88,7 @@ repos: - ts - repo: https://github.com/crate-ci/typos.git - rev: v1.25.0 + rev: v1.28.4 hooks: - id: typos name: Check spelling (typos) diff --git a/dist/unix/org.qbittorrent.qBittorrent.metainfo.xml b/dist/unix/org.qbittorrent.qBittorrent.metainfo.xml index 7d4bc9457..aa17fbd96 100644 --- a/dist/unix/org.qbittorrent.qBittorrent.metainfo.xml +++ b/dist/unix/org.qbittorrent.qBittorrent.metainfo.xml @@ -62,6 +62,6 @@ https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md - + diff --git a/src/base/version.h.in b/src/base/version.h.in index b283be786..51b9e860c 100644 --- a/src/base/version.h.in +++ b/src/base/version.h.in @@ -32,7 +32,7 @@ #define QBT_VERSION_MINOR 1 #define QBT_VERSION_BUGFIX 0 #define QBT_VERSION_BUILD 0 -#define QBT_VERSION_STATUS "alpha1" // Should be empty for stable releases! +#define QBT_VERSION_STATUS "beta1" // Should be empty for stable releases! #define QBT__STRINGIFY(x) #x #define QBT_STRINGIFY(x) QBT__STRINGIFY(x) diff --git a/src/gui/aboutdialog.ui b/src/gui/aboutdialog.ui index a0efa8f49..68e2bf43b 100644 --- a/src/gui/aboutdialog.ui +++ b/src/gui/aboutdialog.ui @@ -75,6 +75,9 @@ true + + Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse + @@ -90,6 +93,27 @@ Current maintainer + + + + Name: + + + + + + + Sledgehammer999 + + + + + + + Nationality: + + + @@ -97,6 +121,13 @@ + + + + E-mail: + + + @@ -110,34 +141,6 @@ - - - - Nationality: - - - - - - - E-mail: - - - - - - - Name: - - - - - - - Sledgehammer999 - - - @@ -160,10 +163,10 @@ Original author - - + + - France + Name: @@ -174,6 +177,27 @@ + + + + Nationality: + + + + + + + France + + + + + + + E-mail: + + + @@ -187,27 +211,6 @@ - - - - Name: - - - - - - - E-mail: - - - - - - - Nationality: - - - @@ -365,8 +368,54 @@ - - + + + + Qt: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + Qt::Orientation::Horizontal + + + + 0 + 0 + + + + + + + + Libtorrent: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -385,32 +434,6 @@ - - - - Qt: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - Libtorrent: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - - @@ -424,33 +447,13 @@ - - + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - - - Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - Qt::Orientation::Horizontal - - - - 0 - 0 - - - - diff --git a/src/gui/addnewtorrentdialog.ui b/src/gui/addnewtorrentdialog.ui index 33711e2c9..7e17af2d6 100644 --- a/src/gui/addnewtorrentdialog.ui +++ b/src/gui/addnewtorrentdialog.ui @@ -378,6 +378,26 @@ Torrent information + + + + Size: + + + + + + + + + + Date: + + + + + + @@ -385,11 +405,36 @@ - - + + + + Qt::TextInteractionFlag::TextSelectableByMouse + + - - + + + + Info hash v2: + + + + + + + Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + Comment: + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + @@ -439,7 +484,7 @@ true - Qt::TextInteractionFlag::TextBrowserInteraction + Qt::TextInteractionFlag::TextSelectableByKeyboard|Qt::TextInteractionFlag::TextSelectableByMouse @@ -447,51 +492,6 @@ - - - - Comment: - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - - - - - Size: - - - - - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - Date: - - - - - - - Info hash v2: - - - - - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - diff --git a/src/gui/banlistoptionsdialog.ui b/src/gui/banlistoptionsdialog.ui index 33d91fe4d..51abbf6dd 100644 --- a/src/gui/banlistoptionsdialog.ui +++ b/src/gui/banlistoptionsdialog.ui @@ -102,12 +102,6 @@ - - bannedIPList - txtIP - buttonBanIP - buttonDeleteIP - diff --git a/src/gui/ipsubnetwhitelistoptionsdialog.ui b/src/gui/ipsubnetwhitelistoptionsdialog.ui index 1452eaea0..962055d36 100644 --- a/src/gui/ipsubnetwhitelistoptionsdialog.ui +++ b/src/gui/ipsubnetwhitelistoptionsdialog.ui @@ -86,12 +86,6 @@ - - whitelistedIPSubnetList - txtIPSubnet - buttonWhitelistIPSubnet - buttonDeleteIPSubnet - diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 91bac21fc..80b5eff8c 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -361,6 +361,19 @@ + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + @@ -397,19 +410,6 @@ - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - @@ -1496,6 +1496,9 @@ readme.txt: filter exact file name. ?.txt: filter 'a.txt', 'b.txt' but not 'aa.txt'. readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. + + true + QPlainTextEdit::LineWrapMode::NoWrap @@ -1518,8 +1521,18 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. - - + + + + Sender + + + From: + + + + + @@ -1531,6 +1544,9 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. + + + @@ -1541,19 +1557,6 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. - - - - - - - Sender - - - From: - - - @@ -1824,26 +1827,6 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.Connections Limits - - - - 500 - - - 4 - - - - - - - Maximum number of connections per torrent: - - - true - - - @@ -1870,6 +1853,29 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Maximum number of connections per torrent: + + + true + + + @@ -1883,13 +1889,6 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. - - - - Maximum number of upload slots per torrent: - - - @@ -1907,18 +1906,22 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. - - - - Qt::Orientation::Horizontal + + + + Maximum number of upload slots per torrent: - - - 40 - 20 - + + + + + + 500 - + + 4 + + @@ -1985,7 +1988,7 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. - <html><head/><body><p>If &quot;mixed mode&quot; is enabled I2P torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of I2P, but still wants to be able to connect to I2P peers.</p></body></html> + <html><head/><body><p>If "mixed mode" is enabled I2P torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of I2P, but still wants to be able to connect to I2P peers.</p></body></html> Mixed mode @@ -2294,6 +2297,16 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.Global Rate Limits + + + + + + + Upload: + + + @@ -2313,6 +2326,26 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Download: + + + @@ -2332,36 +2365,6 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - Upload: - - - - - - - Download: - - - @@ -2371,143 +2374,14 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.Alternative Rate Limits - - - - - - - KiB/s - - - 2000000 - - - QAbstractSpinBox::StepType::AdaptiveDecimalStepType - - - 10 - - - - - - - Schedule &the use of alternative rate limits + + + + Upload: - - true - - - false - - - - - - Start time - - - From: - - - - - - - true - - - hh:mm - - - - - - - - - - End time - - - To: - - - - - - - true - - - hh:mm - - - false - - - - - - - - - - When: - - - - - - - - 0 - 0 - - - - - Every day - - - - - Weekdays - - - - - Weekends - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - @@ -2542,13 +2416,6 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. - - - - Upload: - - - @@ -2556,6 +2423,142 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. + + + + + + + KiB/s + + + 2000000 + + + QAbstractSpinBox::StepType::AdaptiveDecimalStepType + + + 10 + + + + + + + Schedule &the use of alternative rate limits + + + true + + + false + + + + + + Start time + + + From: + + + + + + + true + + + hh:mm + + + false + + + + + + + + + + End time + + + To: + + + + + + + true + + + hh:mm + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + When: + + + + + + + + 0 + 0 + + + + + Every day + + + + + Weekdays + + + + + Weekends + + + + + + + @@ -2750,6 +2753,9 @@ Disable encryption: Only connect to peers without protocol encryption true + + Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse + @@ -2839,6 +2845,19 @@ Disable encryption: Only connect to peers without protocol encryption + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + @@ -2879,19 +2898,6 @@ Disable encryption: Only connect to peers without protocol encryption - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - @@ -2904,6 +2910,13 @@ Disable encryption: Only connect to peers without protocol encryption false + + + + Download rate threshold: + + + @@ -2920,6 +2933,26 @@ Disable encryption: Only connect to peers without protocol encryption + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Upload rate threshold: + + + @@ -2936,33 +2969,13 @@ Disable encryption: Only connect to peers without protocol encryption - - + + - Upload rate threshold: + Torrent inactivity timer: - - - - Download rate threshold: - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - @@ -2979,13 +2992,6 @@ Disable encryption: Only connect to peers without protocol encryption - - - - Torrent inactivity timer: - - - @@ -3133,7 +3139,11 @@ Disable encryption: Only connect to peers without protocol encryption - + + + true + + @@ -3208,26 +3218,6 @@ Disable encryption: Only connect to peers without protocol encryption - - - - sec - - - 2147483646 - - - 2 - - - - - - - Same host request delay: - - - @@ -3257,13 +3247,23 @@ Disable encryption: Only connect to peers without protocol encryption - - + + + + Same host request delay: + + + + + + + sec + 2147483646 - 100 + 2 @@ -3274,6 +3274,16 @@ Disable encryption: Only connect to peers without protocol encryption + + + + 2147483646 + + + 100 + + + @@ -3323,7 +3333,11 @@ Disable encryption: Only connect to peers without protocol encryption - + + + true + + @@ -3462,12 +3476,8 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv false - - - - Key: - - + + @@ -3476,12 +3486,22 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv - - + + + + + + Key: + + + + + + @@ -3490,14 +3510,11 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv true + + Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse + - - - - - - @@ -3574,6 +3591,16 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv + + + + Never + + + 2147483647 + + + @@ -3587,16 +3614,6 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv - - - - Never - - - 2147483647 - - - @@ -3696,6 +3713,9 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv true + + Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse + @@ -3776,6 +3796,9 @@ Use ';' to split multiple entries. Can use wildcard '*'. + + true + QPlainTextEdit::LineWrapMode::NoWrap @@ -3817,7 +3840,13 @@ Use ';' to split multiple entries. Can use wildcard '*'. - <html><head/><body><p><a href="https://github.com/qbittorrent/qBittorrent/wiki#reverse-proxy-setup-for-webui-access"><span style=" text-decoration: underline; color:#0000ff;">Reverse proxy setup examples</span></a></p></body></html> + <a href=https://github.com/qbittorrent/qBittorrent/wiki#reverse-proxy-setup-for-webui-access>Reverse proxy setup examples</a> + + + true + + + Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse @@ -3967,206 +3996,6 @@ Use ';' to split multiple entries. Can use wildcard '*'. 1 - - tabOption - tabSelection - comboLanguage - comboStyle - comboColorScheme - checkUseCustomTheme - customThemeFilePath - checkUseSystemIcon - buttonCustomizeUITheme - confirmDeletion - checkAltRowColors - checkHideZero - comboHideZero - actionTorrentDlOnDblClBox - actionTorrentFnOnDblClBox - checkBoxHideZeroStatusFilters - checkStartup - checkShowSplash - windowStateComboBox - checkProgramExitConfirm - checkProgramAutoExitConfirm - checkShowSystray - checkMinimizeToSysTray - checkCloseToSystray - comboTrayIcon - checkAssociateTorrents - checkAssociateMagnetLinks - checkProgramUpdates - checkPreventFromSuspendWhenDownloading - checkPreventFromSuspendWhenSeeding - checkFileLog - textFileLogPath - checkFileLogBackup - spinFileLogSize - checkFileLogDelete - spinFileLogAge - comboFileLogAgeType - checkBoxPerformanceWarning - scrollArea - checkAdditionDialog - checkAdditionDialogFront - contentLayoutComboBox - checkAddToQueueTop - checkAddStopped - stopConditionComboBox - checkMergeTrackers - checkConfirmMergeTrackers - deleteTorrentBox - deleteCancelledTorrentBox - checkPreallocateAll - checkAppendqB - checkUnwantedFolder - checkRecursiveDownload - comboSavingMode - comboTorrentCategoryChanged - comboCategoryDefaultPathChanged - comboCategoryChanged - checkUseSubcategories - checkUseCategoryPaths - textSavePath - checkUseDownloadPath - textDownloadPath - checkExportDir - textExportDir - checkExportDirFin - textExportDirFin - scanFoldersView - addWatchedFolderButton - editWatchedFolderButton - removeWatchedFolderButton - groupExcludedFileNames - textExcludedFileNames - groupMailNotification - senderEmailTxt - lineEditDestEmail - lineEditSmtpServer - checkSmtpSSL - groupMailNotifAuth - mailNotifUsername - mailNotifPassword - sendTestEmail - groupBoxRunOnAdded - lineEditRunOnAdded - groupBoxRunOnFinished - lineEditRunOnFinished - autoRunConsole - scrollArea_2 - comboProtocol - spinPort - randomButton - checkUPnP - checkMaxConnections - spinMaxConnec - checkMaxConnectionsPerTorrent - spinMaxConnecPerTorrent - checkMaxUploads - spinMaxUploads - checkMaxUploadsPerTorrent - spinMaxUploadsPerTorrent - groupI2P - textI2PHost - spinI2PPort - checkI2PMixed - comboProxyType - textProxyIP - spinProxyPort - checkProxyHostnameLookup - checkProxyAuth - textProxyUsername - textProxyPassword - checkProxyBitTorrent - checkProxyPeerConnections - checkProxyRSS - checkProxyMisc - checkIPFilter - textFilterPath - IpFilterRefreshBtn - banListButton - checkIpFilterTrackers - scrollArea_3 - spinUploadLimit - spinDownloadLimit - spinUploadLimitAlt - spinDownloadLimitAlt - groupBoxSchedule - timeEditScheduleFrom - timeEditScheduleTo - comboBoxScheduleDays - checkLimituTPConnections - checkLimitTransportOverhead - checkLimitLocalPeerRate - scrollArea_9 - checkDHT - checkPeX - checkLSD - comboEncryption - checkAnonymousMode - spinBoxMaxActiveCheckingTorrents - checkEnableQueueing - spinMaxActiveDownloads - spinMaxActiveUploads - spinMaxActiveTorrents - checkIgnoreSlowTorrentsForQueueing - spinDownloadRateForSlowTorrents - spinUploadRateForSlowTorrents - spinSlowTorrentsInactivityTimer - checkMaxRatio - spinMaxRatio - checkMaxSeedingMinutes - spinMaxSeedingMinutes - checkMaxInactiveSeedingMinutes - spinMaxInactiveSeedingMinutes - comboRatioLimitAct - checkEnableAddTrackers - textTrackers - scrollArea_4 - checkRSSEnable - spinRSSRefreshInterval - spinRSSFetchDelay - spinRSSMaxArticlesPerFeed - checkRSSAutoDownloaderEnable - btnEditRules - checkSmartFilterDownloadRepacks - textSmartEpisodeFilters - scrollArea_5 - checkWebUI - textWebUIAddress - spinWebUIPort - checkWebUIUPnP - checkWebUIHttps - textWebUIHttpsCert - textWebUIHttpsKey - textWebUIUsername - textWebUIPassword - checkBypassLocalAuth - checkBypassAuthSubnetWhitelist - IPSubnetWhitelistButton - spinBanCounter - spinBanDuration - spinSessionTimeout - groupAltWebUI - textWebUIRootFolder - checkClickjacking - checkCSRFProtection - checkSecureCookie - groupHostHeaderValidation - textServerDomains - groupWebUIAddCustomHTTPHeaders - textWebUICustomHTTPHeaders - groupEnableReverseProxySupport - textTrustedReverseProxiesList - checkDynDNS - comboDNSService - registerDNSBtn - domainNameTxt - DNSUsernameTxt - DNSPasswordTxt - scrollArea_7 - diff --git a/src/gui/properties/peersadditiondialog.ui b/src/gui/properties/peersadditiondialog.ui index ed9641be6..fcc39159a 100644 --- a/src/gui/properties/peersadditiondialog.ui +++ b/src/gui/properties/peersadditiondialog.ui @@ -23,6 +23,9 @@ + + true + QTextEdit::LineWrapMode::NoWrap diff --git a/src/gui/properties/propertieswidget.ui b/src/gui/properties/propertieswidget.ui index 564a88c82..dd21f6fef 100644 --- a/src/gui/properties/propertieswidget.ui +++ b/src/gui/properties/propertieswidget.ui @@ -85,6 +85,13 @@ + + + + Qt::TextFormat::PlainText + + + @@ -114,6 +121,13 @@ + + + + Qt::TextFormat::PlainText + + + @@ -127,20 +141,6 @@ - - - - Qt::TextFormat::PlainText - - - - - - - Qt::TextFormat::PlainText - - - @@ -163,6 +163,196 @@ 4 + + + + + 0 + 0 + + + + Time Active: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + ETA: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + Connections: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + Downloaded: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + Uploaded: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + Seeds: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + Download Speed: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + @@ -221,37 +411,8 @@ - - - - - 0 - 0 - - - - Connections: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - - - + + 0 @@ -279,43 +440,8 @@ - - - - - 0 - 0 - - - - Share Ratio: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 0 - 0 - - - - Ratio / Time Active (in months), indicates how popular the torrent is - - - Popularity: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - + + 0 @@ -327,35 +453,6 @@ - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - - - - - - 0 - 0 - - - - Downloaded: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - @@ -372,24 +469,8 @@ - - - - - 0 - 0 - - - - Last Seen Complete: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - + + 0 @@ -401,8 +482,53 @@ - - + + + + + 0 + 0 + + + + Wasted: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + Share Ratio: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + 0 @@ -430,6 +556,35 @@ + + + + + 0 + 0 + + + + Qt::TextFormat::PlainText + + + + + + + + 0 + 0 + + + + Last Seen Complete: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + @@ -443,77 +598,25 @@ - - + + 0 0 + + Ratio / Time Active (in months), indicates how popular the torrent is + - Seeds: + Popularity: Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - 0 - 0 - - - - Download Speed: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - @@ -530,109 +633,6 @@ - - - - - 0 - 0 - - - - Uploaded: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - - - - - - 0 - 0 - - - - Time Active: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 0 - 0 - - - - Qt::TextFormat::PlainText - - - - - - - - 0 - 0 - - - - ETA: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 0 - 0 - - - - Wasted: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - diff --git a/src/gui/rss/automatedrssdownloader.ui b/src/gui/rss/automatedrssdownloader.ui index 72bad112f..ad54a0eb0 100644 --- a/src/gui/rss/automatedrssdownloader.ui +++ b/src/gui/rss/automatedrssdownloader.ui @@ -182,22 +182,11 @@ - - - - Must Not Contain: - - + + - - - - Episode Filter: - - - - - + + 18 @@ -206,6 +195,16 @@ + + + + Must Not Contain: + + + + + + @@ -216,11 +215,18 @@ - - + + + + Episode Filter: + + - - + + + + + 18 @@ -239,12 +245,6 @@ - - - - - - @@ -427,20 +427,6 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also - - renameRuleBtn - removeRuleBtn - addRuleBtn - ruleList - lineContains - lineNotContains - lineEFilter - spinIgnorePeriod - listFeeds - matchingArticlesTree - importBtn - exportBtn - diff --git a/src/gui/rss/rsswidget.cpp b/src/gui/rss/rsswidget.cpp index a6ca52b6c..5d05df0b8 100644 --- a/src/gui/rss/rsswidget.cpp +++ b/src/gui/rss/rsswidget.cpp @@ -54,6 +54,48 @@ #include "feedlistwidget.h" #include "ui_rsswidget.h" +namespace +{ + void convertRelativeUrlToAbsolute(QString &html, const QString &baseUrl) + { + const QRegularExpression rx {uR"(((]*?href|]*?src)\s*=\s*["'])((https?|ftp):)?(\/\/[^\/]*)?(\/?[^\/"].*?)(["']))"_s + , QRegularExpression::CaseInsensitiveOption}; + + const QString normalizedBaseUrl = baseUrl.endsWith(u'/') ? baseUrl : (baseUrl + u'/'); + const QUrl url {normalizedBaseUrl}; + const QString defaultScheme = url.scheme(); + QRegularExpressionMatchIterator iter = rx.globalMatch(html); + + while (iter.hasNext()) + { + const QRegularExpressionMatch match = iter.next(); + const QString scheme = match.captured(4); + const QString host = match.captured(5); + if (!scheme.isEmpty()) + { + if (host.isEmpty()) + break; // invalid URL, should never happen + + // already absolute URL + continue; + } + + QString relativePath = match.captured(6); + if (relativePath.startsWith(u'/')) + relativePath = relativePath.mid(1); + + const QString absoluteUrl = !host.isEmpty() + ? QString(defaultScheme + u':' + host) : (normalizedBaseUrl + relativePath); + const QString fullMatch = match.captured(0); + const QString prefix = match.captured(1); + const QString suffix = match.captured(7); + + html.replace(fullMatch, (prefix + absoluteUrl + suffix)); + } + } +} + + RSSWidget::RSSWidget(IGUIApplication *app, QWidget *parent) : GUIApplicationComponent(app, parent) , m_ui {new Ui::RSSWidget} @@ -79,23 +121,19 @@ RSSWidget::RSSWidget(IGUIApplication *app, QWidget *parent) m_ui->rssDownloaderBtn->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_s, u"download"_s)); #endif - m_articleListWidget = new ArticleListWidget(m_ui->splitterMain); - m_ui->splitterMain->insertWidget(0, m_articleListWidget); - connect(m_articleListWidget, &ArticleListWidget::customContextMenuRequested, this, &RSSWidget::displayItemsListMenu); - connect(m_articleListWidget, &ArticleListWidget::currentItemChanged, this, &RSSWidget::handleCurrentArticleItemChanged); - connect(m_articleListWidget, &ArticleListWidget::itemDoubleClicked, this, &RSSWidget::downloadSelectedTorrents); + connect(m_ui->articleListWidget, &ArticleListWidget::customContextMenuRequested, this, &RSSWidget::displayItemsListMenu); + connect(m_ui->articleListWidget, &ArticleListWidget::currentItemChanged, this, &RSSWidget::handleCurrentArticleItemChanged); + connect(m_ui->articleListWidget, &ArticleListWidget::itemDoubleClicked, this, &RSSWidget::downloadSelectedTorrents); - m_feedListWidget = new FeedListWidget(m_ui->splitterSide); - m_ui->splitterSide->insertWidget(0, m_feedListWidget); - connect(m_feedListWidget, &QAbstractItemView::doubleClicked, this, &RSSWidget::renameSelectedRSSItem); - connect(m_feedListWidget, &QTreeWidget::currentItemChanged, this, &RSSWidget::handleCurrentFeedItemChanged); - connect(m_feedListWidget, &QWidget::customContextMenuRequested, this, &RSSWidget::displayRSSListMenu); + connect(m_ui->feedListWidget, &QAbstractItemView::doubleClicked, this, &RSSWidget::renameSelectedRSSItem); + connect(m_ui->feedListWidget, &QTreeWidget::currentItemChanged, this, &RSSWidget::handleCurrentFeedItemChanged); + connect(m_ui->feedListWidget, &QWidget::customContextMenuRequested, this, &RSSWidget::displayRSSListMenu); loadFoldersOpenState(); - m_feedListWidget->setCurrentItem(m_feedListWidget->stickyUnreadItem()); + m_ui->feedListWidget->setCurrentItem(m_ui->feedListWidget->stickyUnreadItem()); - const auto *editHotkey = new QShortcut(Qt::Key_F2, m_feedListWidget, nullptr, nullptr, Qt::WidgetShortcut); + const auto *editHotkey = new QShortcut(Qt::Key_F2, m_ui->feedListWidget, nullptr, nullptr, Qt::WidgetShortcut); connect(editHotkey, &QShortcut::activated, this, &RSSWidget::renameSelectedRSSItem); - const auto *deleteHotkey = new QShortcut(QKeySequence::Delete, m_feedListWidget, nullptr, nullptr, Qt::WidgetShortcut); + const auto *deleteHotkey = new QShortcut(QKeySequence::Delete, m_ui->feedListWidget, nullptr, nullptr, Qt::WidgetShortcut); connect(deleteHotkey, &QShortcut::activated, this, &RSSWidget::deleteSelectedItems); // Feeds list actions @@ -134,25 +172,24 @@ RSSWidget::~RSSWidget() { // we need it here to properly mark latest article // as read without having additional code - m_articleListWidget->clear(); + m_ui->articleListWidget->clear(); saveFoldersOpenState(); - delete m_feedListWidget; delete m_ui; } // display a right-click menu void RSSWidget::displayRSSListMenu(const QPoint &pos) { - if (!m_feedListWidget->indexAt(pos).isValid()) + if (!m_ui->feedListWidget->indexAt(pos).isValid()) // No item under the mouse, clear selection - m_feedListWidget->clearSelection(); + m_ui->feedListWidget->clearSelection(); QMenu *menu = new QMenu(this); menu->setAttribute(Qt::WA_DeleteOnClose); - const QList selectedItems = m_feedListWidget->selectedItems(); + const QList selectedItems = m_ui->feedListWidget->selectedItems(); if (!selectedItems.isEmpty()) { menu->addAction(m_ui->actionUpdate); @@ -162,14 +199,14 @@ void RSSWidget::displayRSSListMenu(const QPoint &pos) if (selectedItems.size() == 1) { QTreeWidgetItem *selectedItem = selectedItems.first(); - if (selectedItem != m_feedListWidget->stickyUnreadItem()) + if (selectedItem != m_ui->feedListWidget->stickyUnreadItem()) { menu->addAction(m_ui->actionRename); - if (m_feedListWidget->isFeed(selectedItem)) + if (m_ui->feedListWidget->isFeed(selectedItem)) menu->addAction(m_ui->actionEditFeedURL); menu->addAction(m_ui->actionDelete); menu->addSeparator(); - if (m_feedListWidget->isFolder(selectedItem)) + if (m_ui->feedListWidget->isFolder(selectedItem)) menu->addAction(m_ui->actionNewFolder); } } @@ -181,7 +218,7 @@ void RSSWidget::displayRSSListMenu(const QPoint &pos) menu->addAction(m_ui->actionNewSubscription); - if (m_feedListWidget->isFeed(selectedItems.first())) + if (m_ui->feedListWidget->isFeed(selectedItems.first())) { menu->addSeparator(); menu->addAction(m_ui->actionCopyFeedURL); @@ -202,7 +239,7 @@ void RSSWidget::displayItemsListMenu() { bool hasTorrent = false; bool hasLink = false; - for (const QListWidgetItem *item : asConst(m_articleListWidget->selectedItems())) + for (const QListWidgetItem *item : asConst(m_ui->articleListWidget->selectedItems())) { auto *article = item->data(Qt::UserRole).value(); Q_ASSERT(article); @@ -240,17 +277,17 @@ void RSSWidget::askNewFolder() // Determine destination folder for new item QTreeWidgetItem *destItem = nullptr; - QList selectedItems = m_feedListWidget->selectedItems(); + QList selectedItems = m_ui->feedListWidget->selectedItems(); if (!selectedItems.empty()) { destItem = selectedItems.first(); - if (!m_feedListWidget->isFolder(destItem)) + if (!m_ui->feedListWidget->isFolder(destItem)) destItem = destItem->parent(); } // Consider the case where the user clicked on Unread item - RSS::Folder *rssDestFolder = ((!destItem || (destItem == m_feedListWidget->stickyUnreadItem())) + RSS::Folder *rssDestFolder = ((!destItem || (destItem == m_ui->feedListWidget->stickyUnreadItem())) ? RSS::Session::instance()->rootFolder() - : qobject_cast(m_feedListWidget->getRSSItem(destItem))); + : qobject_cast(m_ui->feedListWidget->getRSSItem(destItem))); const QString newFolderPath = RSS::Item::joinPath(rssDestFolder->path(), newName); const nonstd::expected result = RSS::Session::instance()->addFolder(newFolderPath); @@ -258,10 +295,10 @@ void RSSWidget::askNewFolder() QMessageBox::warning(this, u"qBittorrent"_s, result.error(), QMessageBox::Ok); // Expand destination folder to display new feed - if (destItem && (destItem != m_feedListWidget->stickyUnreadItem())) + if (destItem && (destItem != m_ui->feedListWidget->stickyUnreadItem())) destItem->setExpanded(true); // As new RSS items are added synchronously, we can do the following here. - m_feedListWidget->setCurrentItem(m_feedListWidget->mapRSSItem(RSS::Session::instance()->itemByPath(newFolderPath))); + m_ui->feedListWidget->setCurrentItem(m_ui->feedListWidget->mapRSSItem(RSS::Session::instance()->itemByPath(newFolderPath))); } // add a stream by a button @@ -281,17 +318,17 @@ void RSSWidget::on_newFeedButton_clicked() // Determine destination folder for new item QTreeWidgetItem *destItem = nullptr; - QList selectedItems = m_feedListWidget->selectedItems(); + QList selectedItems = m_ui->feedListWidget->selectedItems(); if (!selectedItems.empty()) { destItem = selectedItems.first(); - if (!m_feedListWidget->isFolder(destItem)) + if (!m_ui->feedListWidget->isFolder(destItem)) destItem = destItem->parent(); } // Consider the case where the user clicked on Unread item - RSS::Folder *rssDestFolder = ((!destItem || (destItem == m_feedListWidget->stickyUnreadItem())) + RSS::Folder *rssDestFolder = ((!destItem || (destItem == m_ui->feedListWidget->stickyUnreadItem())) ? RSS::Session::instance()->rootFolder() - : qobject_cast(m_feedListWidget->getRSSItem(destItem))); + : qobject_cast(m_ui->feedListWidget->getRSSItem(destItem))); // NOTE: We still add feed using legacy way (with URL as feed name) const QString newFeedPath = RSS::Item::joinPath(rssDestFolder->path(), newURL); @@ -300,18 +337,18 @@ void RSSWidget::on_newFeedButton_clicked() QMessageBox::warning(this, u"qBittorrent"_s, result.error(), QMessageBox::Ok); // Expand destination folder to display new feed - if (destItem && (destItem != m_feedListWidget->stickyUnreadItem())) + if (destItem && (destItem != m_ui->feedListWidget->stickyUnreadItem())) destItem->setExpanded(true); // As new RSS items are added synchronously, we can do the following here. - m_feedListWidget->setCurrentItem(m_feedListWidget->mapRSSItem(RSS::Session::instance()->itemByPath(newFeedPath))); + m_ui->feedListWidget->setCurrentItem(m_ui->feedListWidget->mapRSSItem(RSS::Session::instance()->itemByPath(newFeedPath))); } void RSSWidget::deleteSelectedItems() { - const QList selectedItems = m_feedListWidget->selectedItems(); + const QList selectedItems = m_ui->feedListWidget->selectedItems(); if (selectedItems.isEmpty()) return; - if ((selectedItems.size() == 1) && (selectedItems.first() == m_feedListWidget->stickyUnreadItem())) + if ((selectedItems.size() == 1) && (selectedItems.first() == m_ui->feedListWidget->stickyUnreadItem())) return; QMessageBox::StandardButton answer = QMessageBox::question( @@ -321,8 +358,8 @@ void RSSWidget::deleteSelectedItems() return; for (QTreeWidgetItem *item : selectedItems) - if (item != m_feedListWidget->stickyUnreadItem()) - RSS::Session::instance()->removeItem(m_feedListWidget->itemPath(item)); + if (item != m_ui->feedListWidget->stickyUnreadItem()) + RSS::Session::instance()->removeItem(m_ui->feedListWidget->itemPath(item)); } void RSSWidget::loadFoldersOpenState() @@ -333,11 +370,11 @@ void RSSWidget::loadFoldersOpenState() QTreeWidgetItem *parent = nullptr; for (const QString &name : asConst(varPath.split(u'\\'))) { - int nbChildren = (parent ? parent->childCount() : m_feedListWidget->topLevelItemCount()); + int nbChildren = (parent ? parent->childCount() : m_ui->feedListWidget->topLevelItemCount()); for (int i = 0; i < nbChildren; ++i) { - QTreeWidgetItem *child = (parent ? parent->child(i) : m_feedListWidget->topLevelItem(i)); - if (m_feedListWidget->getRSSItem(child)->name() == name) + QTreeWidgetItem *child = (parent ? parent->child(i) : m_ui->feedListWidget->topLevelItem(i)); + if (m_ui->feedListWidget->getRSSItem(child)->name() == name) { parent = child; parent->setExpanded(true); @@ -351,8 +388,8 @@ void RSSWidget::loadFoldersOpenState() void RSSWidget::saveFoldersOpenState() { QStringList openedFolders; - for (QTreeWidgetItem *item : asConst(m_feedListWidget->getAllOpenedFolders())) - openedFolders << m_feedListWidget->itemPath(item); + for (QTreeWidgetItem *item : asConst(m_ui->feedListWidget->getAllOpenedFolders())) + openedFolders << m_ui->feedListWidget->itemPath(item); Preferences::instance()->setRssOpenFolders(openedFolders); } @@ -363,7 +400,7 @@ void RSSWidget::refreshAllFeeds() void RSSWidget::downloadSelectedTorrents() { - for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems())) + for (QListWidgetItem *item : asConst(m_ui->articleListWidget->selectedItems())) { auto *article = item->data(Qt::UserRole).value(); Q_ASSERT(article); @@ -378,7 +415,7 @@ void RSSWidget::downloadSelectedTorrents() // open the url of the selected RSS articles in the Web browser void RSSWidget::openSelectedArticlesUrls() { - for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems())) + for (QListWidgetItem *item : asConst(m_ui->articleListWidget->selectedItems())) { auto *article = item->data(Qt::UserRole).value(); Q_ASSERT(article); @@ -393,14 +430,14 @@ void RSSWidget::openSelectedArticlesUrls() void RSSWidget::renameSelectedRSSItem() { - QList selectedItems = m_feedListWidget->selectedItems(); + QList selectedItems = m_ui->feedListWidget->selectedItems(); if (selectedItems.size() != 1) return; QTreeWidgetItem *item = selectedItems.first(); - if (item == m_feedListWidget->stickyUnreadItem()) + if (item == m_ui->feedListWidget->stickyUnreadItem()) return; - RSS::Item *rssItem = m_feedListWidget->getRSSItem(item); + RSS::Item *rssItem = m_ui->feedListWidget->getRSSItem(item); const QString parentPath = RSS::Item::parentPath(rssItem->path()); bool ok = false; do @@ -422,12 +459,12 @@ void RSSWidget::renameSelectedRSSItem() void RSSWidget::editSelectedRSSFeedURL() { - QList selectedItems = m_feedListWidget->selectedItems(); + QList selectedItems = m_ui->feedListWidget->selectedItems(); if (selectedItems.size() != 1) return; QTreeWidgetItem *item = selectedItems.first(); - RSS::Feed *rssFeed = qobject_cast(m_feedListWidget->getRSSItem(item)); + RSS::Feed *rssFeed = qobject_cast(m_ui->feedListWidget->getRSSItem(item)); Q_ASSERT(rssFeed); if (!rssFeed) [[unlikely]] return; @@ -445,24 +482,24 @@ void RSSWidget::editSelectedRSSFeedURL() void RSSWidget::refreshSelectedItems() { - for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems())) + for (QTreeWidgetItem *item : asConst(m_ui->feedListWidget->selectedItems())) { - if (item == m_feedListWidget->stickyUnreadItem()) + if (item == m_ui->feedListWidget->stickyUnreadItem()) { refreshAllFeeds(); return; } - m_feedListWidget->getRSSItem(item)->refresh(); + m_ui->feedListWidget->getRSSItem(item)->refresh(); } } void RSSWidget::copySelectedFeedsURL() { QStringList URLs; - for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems())) + for (QTreeWidgetItem *item : asConst(m_ui->feedListWidget->selectedItems())) { - if (auto *feed = qobject_cast(m_feedListWidget->getRSSItem(item))) + if (auto *feed = qobject_cast(m_ui->feedListWidget->getRSSItem(item))) URLs << feed->url(); } qApp->clipboard()->setText(URLs.join(u'\n')); @@ -470,16 +507,16 @@ void RSSWidget::copySelectedFeedsURL() void RSSWidget::handleCurrentFeedItemChanged(QTreeWidgetItem *currentItem) { - m_articleListWidget->setRSSItem(m_feedListWidget->getRSSItem(currentItem) - , (currentItem == m_feedListWidget->stickyUnreadItem())); + m_ui->articleListWidget->setRSSItem(m_ui->feedListWidget->getRSSItem(currentItem) + , (currentItem == m_ui->feedListWidget->stickyUnreadItem())); } void RSSWidget::on_markReadButton_clicked() { - for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems())) + for (QTreeWidgetItem *item : asConst(m_ui->feedListWidget->selectedItems())) { - m_feedListWidget->getRSSItem(item)->markAsRead(); - if (item == m_feedListWidget->stickyUnreadItem()) + m_ui->feedListWidget->getRSSItem(item)->markAsRead(); + if (item == m_ui->feedListWidget->stickyUnreadItem()) break; // all items was read } } @@ -491,7 +528,7 @@ void RSSWidget::handleCurrentArticleItemChanged(QListWidgetItem *currentItem, QL if (previousItem) { - auto *article = m_articleListWidget->getRSSArticle(previousItem); + auto *article = m_ui->articleListWidget->getRSSArticle(previousItem); Q_ASSERT(article); article->markAsRead(); } @@ -499,7 +536,7 @@ void RSSWidget::handleCurrentArticleItemChanged(QListWidgetItem *currentItem, QL if (!currentItem) return; - auto *article = m_articleListWidget->getRSSArticle(currentItem); + auto *article = m_ui->articleListWidget->getRSSArticle(currentItem); renderArticle(article); } @@ -548,10 +585,10 @@ bool RSSWidget::eventFilter(QObject *obj, QEvent *event) { if ((obj == m_ui->textBrowser) && (event->type() == QEvent::PaletteChange)) { - QListWidgetItem *currentItem = m_articleListWidget->currentItem(); + QListWidgetItem *currentItem = m_ui->articleListWidget->currentItem(); if (currentItem) { - const RSS::Article *article = m_articleListWidget->getRSSArticle(currentItem); + const RSS::Article *article = m_ui->articleListWidget->getRSSArticle(currentItem); renderArticle(article); } } @@ -572,7 +609,7 @@ void RSSWidget::renderArticle(const RSS::Article *article) const u"
%3
"_s.arg(highlightedBaseColor, highlightedBaseTextColor, article->title()); if (article->date().isValid()) html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Date: "), QLocale::system().toString(article->date().toLocalTime())); - if (m_feedListWidget->currentItem() == m_feedListWidget->stickyUnreadItem()) + if (m_ui->feedListWidget->currentItem() == m_ui->feedListWidget->stickyUnreadItem()) html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Feed: "), article->feed()->title()); if (!article->author().isEmpty()) html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Author: "), article->author()); @@ -610,6 +647,11 @@ void RSSWidget::renderArticle(const RSS::Article *article) const html += u"
" + description + u"
"; } + + // Supplement relative URLs to absolute ones + const QUrl url {article->link()}; + const QString baseUrl = url.toString(QUrl::RemovePath | QUrl::RemoveQuery); + convertRelativeUrlToAbsolute(html, baseUrl); html += u""; m_ui->textBrowser->setHtml(html); } diff --git a/src/gui/rss/rsswidget.h b/src/gui/rss/rsswidget.h index cc1da53bc..98bceac30 100644 --- a/src/gui/rss/rsswidget.h +++ b/src/gui/rss/rsswidget.h @@ -37,9 +37,6 @@ class QListWidgetItem; class QTreeWidgetItem; -class ArticleListWidget; -class FeedListWidget; - namespace RSS { class Article; @@ -94,6 +91,4 @@ private: void renderArticle(const RSS::Article *article) const; Ui::RSSWidget *m_ui = nullptr; - ArticleListWidget *m_articleListWidget = nullptr; - FeedListWidget *m_feedListWidget = nullptr; }; diff --git a/src/gui/rss/rsswidget.ui b/src/gui/rss/rsswidget.ui index 401aa289f..97fc97e83 100644 --- a/src/gui/rss/rsswidget.ui +++ b/src/gui/rss/rsswidget.ui @@ -94,6 +94,7 @@ Qt::Orientation::Horizontal + @@ -119,6 +120,7 @@ Qt::Orientation::Horizontal + true @@ -211,6 +213,16 @@ QTextBrowser
gui/rss/htmlbrowser.h
+ + FeedListWidget + QTreeWidget +
gui/rss/feedlistwidget.h
+
+ + ArticleListWidget + QListWidget +
gui/rss/articlelistwidget.h
+
diff --git a/src/gui/statsdialog.ui b/src/gui/statsdialog.ui index 444843a2c..acb2767cc 100644 --- a/src/gui/statsdialog.ui +++ b/src/gui/statsdialog.ui @@ -20,29 +20,15 @@ User statistics
- - + + - TextLabel + All-time upload: - - - - Connected peers: - - - - - - - All-time share ratio: - - - - - + + TextLabel @@ -62,6 +48,13 @@ + + + + All-time share ratio: + + + @@ -76,15 +69,22 @@ - - + + - All-time upload: + TextLabel - - + + + + Connected peers: + + + + + TextLabel @@ -113,13 +113,6 @@ - - - - TextLabel - - - @@ -127,6 +120,13 @@ + + + + TextLabel + + +
@@ -136,17 +136,10 @@ Performance statistics - - + + - TextLabel - - - - - - - TextLabel + Write cache overload: @@ -157,6 +150,13 @@ + + + + Read cache overload: + + + @@ -171,10 +171,10 @@ - - + + - Write cache overload: + TextLabel @@ -185,10 +185,10 @@ - - + + - Read cache overload: + TextLabel diff --git a/src/gui/torrentcategorydialog.ui b/src/gui/torrentcategorydialog.ui index 3191232a7..804af09dd 100644 --- a/src/gui/torrentcategorydialog.ui +++ b/src/gui/torrentcategorydialog.ui @@ -173,12 +173,6 @@ 1 - - textCategoryName - comboSavePath - comboUseDownloadPath - comboDownloadPath - diff --git a/src/gui/torrentcreatordialog.ui b/src/gui/torrentcreatordialog.ui index 3a7d47bee..6a3eb33be 100644 --- a/src/gui/torrentcreatordialog.ui +++ b/src/gui/torrentcreatordialog.ui @@ -313,11 +313,21 @@ Fields + + + + Tracker URLs: + + + You can separate tracker tiers / groups with an empty line. + + true + false @@ -332,25 +342,14 @@ + + true + false - - - - false - - - - - - - Tracker URLs: - - - @@ -358,6 +357,16 @@ + + + + true + + + false + + + @@ -413,23 +422,6 @@ 1 - - textInputPath - addFileButton - addFolderButton - comboTorrentFormat - comboPieceSize - buttonCalcTotalPieces - checkPrivate - checkStartSeeding - checkIgnoreShareLimits - checkOptimizeAlignment - spinPaddedFileSizeLimit - trackersList - URLSeedsList - txtComment - lineEditSource - diff --git a/src/gui/torrentoptionsdialog.ui b/src/gui/torrentoptionsdialog.ui index 835dcc6b8..9fcb65d52 100644 --- a/src/gui/torrentoptionsdialog.ui +++ b/src/gui/torrentoptionsdialog.ui @@ -52,6 +52,13 @@ + + + + Category: + + + @@ -71,13 +78,6 @@ - - - - Category: - - - @@ -86,26 +86,17 @@ Torrent Speed Limits - - + + - Download: + Upload: - - - - - - - KiB/s - - - 2000000 - - - QAbstractSpinBox::StepType::AdaptiveDecimalStepType + + + + Qt::Orientation::Horizontal @@ -125,24 +116,10 @@ - - + + - These will not exceed the global limits - - - - - - - Upload: - - - - - - - Qt::Orientation::Horizontal + Download: @@ -153,6 +130,29 @@ + + + + + + + KiB/s + + + 2000000 + + + QAbstractSpinBox::StepType::AdaptiveDecimalStepType + + + + + + + These will not exceed the global limits + + + @@ -256,23 +256,6 @@ 1 - - checkAutoTMM - savePath - checkUseDownloadPath - downloadPath - comboCategory - sliderUploadLimit - spinUploadLimit - sliderDownloadLimit - spinDownloadLimit - torrentShareLimitsBox - checkDisableDHT - checkSequential - checkDisablePEX - checkFirstLastPieces - checkDisableLSD - diff --git a/src/gui/torrentsharelimitswidget.ui b/src/gui/torrentsharelimitswidget.ui index a532b0364..9ada9ed67 100644 --- a/src/gui/torrentsharelimitswidget.ui +++ b/src/gui/torrentsharelimitswidget.ui @@ -13,8 +13,60 @@ - - + + + + Ratio: + + + + + + + -1 + + + + Default + + + + + Unlimited + + + + + Set to + + + + + + + + false + + + 9998.000000000000000 + + + 0.050000000000000 + + + 1.000000000000000 + + + + + + + Seeding time: + + + + + -1 @@ -51,13 +103,6 @@ - - - - Ratio: - - - @@ -65,6 +110,28 @@ + + + + -1 + + + + Default + + + + + Unlimited + + + + + Set to + + + + @@ -81,73 +148,6 @@ - - - - false - - - 9998.000000000000000 - - - 0.050000000000000 - - - 1.000000000000000 - - - - - - - -1 - - - - Default - - - - - Unlimited - - - - - Set to - - - - - - - - -1 - - - - Default - - - - - Unlimited - - - - - Set to - - - - - - - - Seeding time: - - - @@ -208,15 +208,6 @@
- - comboBoxRatioMode - spinBoxRatioValue - comboBoxSeedingTimeMode - spinBoxSeedingTimeValue - comboBoxInactiveSeedingTimeMode - spinBoxInactiveSeedingTimeValue - comboBoxAction - diff --git a/src/gui/trackersadditiondialog.ui b/src/gui/trackersadditiondialog.ui index 7776016e6..75c574459 100644 --- a/src/gui/trackersadditiondialog.ui +++ b/src/gui/trackersadditiondialog.ui @@ -23,6 +23,9 @@
+ + true + QTextEdit::LineWrapMode::NoWrap