Compare commits

...

42 Commits

Author SHA1 Message Date
sledgehammer999
50f676c305 Bump to 4.2.4 2020-04-22 18:49:52 +03:00
sledgehammer999
7103ae73cd Update Changelog 2020-04-22 18:46:15 +03:00
sledgehammer999
7d4b8b010f Sync translations from Transifex and run lupdate 2020-04-22 18:45:26 +03:00
an0n666
09bf033226 Use configured net interface even when it is missing 2020-04-22 18:19:33 +03:00
NotTsunami
4e62608802 WebUI: Fix UPnP lease duration get/set
Add missing setProperty and getProperty calls for the new UPnP lease
duration setting.

Fixes 6b4925d222.
Closes #12566.
2020-04-22 18:19:32 +03:00
Chocobo1
37b29bf91a Reduce ambiguity for selecting icons
Related: #12554.
2020-04-22 18:19:31 +03:00
thalieht
d67037136b Don't uncheck Authentication checkbox when changing proxy type
Closes #12525
2020-04-22 18:19:29 +03:00
thalieht
73292137b7 Make a few cosmetic changes in code 2020-04-22 18:19:28 +03:00
C.W. Betts
56f7a4e803 Update Info.plist
Wrap the UTTypeTagSpecifications in arrays, as Xcode does.
2020-04-22 18:19:26 +03:00
Chocobo1
1cea6a25af Add final specifier to GUI classes
Follow up d3d3f7dbb3.
2020-04-22 18:19:23 +03:00
Chocobo1
ebc704ef14 Reduce padding in class
TorrentHandleImpl size is reduced from 768 bytes to 736 bytes.
CreateTorrentParams size didn't change.
Size numbers are from x64.
2020-04-22 18:19:22 +03:00
Chocobo1
a2a1a78f44 Move initialization default values to header 2020-04-22 18:19:21 +03:00
Chocobo1
3a54d574b0 Reduce padding in structure
Log::Msg originally takes 32 bytes, now shrinks to 24 bytes.
Log::Peer originally takes 40 bytes, now shrinks to 32 bytes.
2020-04-22 18:19:20 +03:00
an0n666
d0be71c225 Change default upload slot choking limits 2020-04-22 18:19:19 +03:00
Chocobo1
7c04b4acd8 Add final specifier to classes
This allow compilers to generate more efficient code.
2020-04-22 18:19:18 +03:00
sledgehammer999
eda3747c08 Use static_cast for explicit type conversions 2020-04-22 18:19:17 +03:00
sledgehammer999
7d23ea1f80 TravisCI: Use libtorrent with deprecated functions disabled for Linux builds 2020-04-22 18:19:16 +03:00
Vladimir Golovnev (Glassez)
698ee94d0b Split TorrentHandle interface and implementation 2020-04-22 18:19:14 +03:00
jagannatharjun
65d1b588d9 Convert the Log widget to use custom View/Model
Co-authored-by: sledgehammer999 <hammered999@gmail.com>
2020-04-22 18:19:13 +03:00
an0n666
eea693979a Change default stop_tracker_timeout settings 2020-04-22 18:19:12 +03:00
Raif Atef
654bf85a71 Do not use 0.0.0.0 or [::] for outgoing interfaces
Fixes #12443
2020-04-22 18:19:11 +03:00
an0n666
8706a7c973 Remove deprecated strict super seeding mode from advanced settings 2020-04-22 18:19:10 +03:00
Chocobo1
439a2ef597 Fix date format for "Last seen complete"
Closes #12462.
2020-04-22 18:19:08 +03:00
Sepro
c5a7aa7668 Fix unable to add multiple peers in WebUI
Wrong delimiter was used.
2020-04-22 18:19:07 +03:00
Chocobo1
e5bf83a594 Preallocate output buffer 2020-04-22 18:19:06 +03:00
Chocobo1
2a3e64933b Fix header inclusion order 2020-04-22 18:19:05 +03:00
Chocobo1
35f8af32a3 Suppress unused variable warning on macOS 2020-04-22 18:19:04 +03:00
Chocobo1
37354a9e29 Avoid holding encoded resume data in memory
Now it the encoded resume data will be streamed to file instead of a
temporary buffer holding the whole of it.
2020-04-22 18:19:02 +03:00
József Sallai
7cb14e2a5b Detect python3 executable on Windows 2020-04-22 18:19:01 +03:00
Vladimir Golovnev (Glassez)
4aae7266a5 Save "resume data" when torrent storage is moved 2020-04-22 18:19:00 +03:00
Raif Atef
075245c915 Fix outgoing interface is not getting assigned
Assignment was missing in main branch of condition statement.
Closes https://github.com/qbittorrent/qBittorrent/issues/12421
2020-04-22 18:18:59 +03:00
adem
476707cc80 Remove white outline around mascot.png 2020-04-22 18:18:58 +03:00
Chocobo1
7b0b5e3d7f Avoid inefficient behavior
Since the class needs to be copy-constructible, there may be many
copies of an instance. So instead of writing to the device on every
destructor call, only flush buffer on the last destructor call.
2020-04-22 18:18:56 +03:00
Chocobo1
4142722303 Sort locale language list 2020-04-22 18:18:55 +03:00
NotTsunami
8ebb11f981 Set disk cache size for older libtorrent versions
Libtorrent versions older than 1.2.6 have a bug when setting disk
cache size to auto.

See 6c880159c9.
2020-04-22 18:18:54 +03:00
Chocobo1
80016db781 Fix wrong logic that disables "prevent sleeping" timer
Also update power management state early so we don't need to wait for
the timer timeout to have the effect.
2020-04-22 18:18:53 +03:00
Chocobo1
a9f43bd5d2 Clean up coding style 2020-04-22 18:18:52 +03:00
Chocobo1
2f0c3f047a Avoid holding entire file in memory
Previously we need a file buffer that is as large as the file size and
this could be a problem when user has less free memory available or
having very large data. Now with the help of `FileOutputIterator`,
we can have a much smaller, fixed size immediate file buffer and also
the code looks nice with `lt::bencode()`.
2020-04-22 18:18:50 +03:00
Vladimir Golovnev (Glassez)
f40a36ecb3 Fix sub-sorting of Transfer list
Closes #12330.
2020-04-22 18:18:50 +03:00
Chocobo1
a1ee1c0448 Remove redundant type attribute
It already defaults to `text/css` if value is absent (in HTML5).
2020-04-22 18:18:49 +03:00
Chocobo1
939f83bdd5 Fix mismatch ID 2020-04-22 18:18:48 +03:00
Chocobo1
e98a887286 Improve logging for errors
This commit also allows the strings to be translated.
2020-04-22 18:18:37 +03:00
227 changed files with 32095 additions and 28171 deletions

View File

@@ -55,14 +55,13 @@ addons:
apt:
sources:
# sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json
- sourceline: 'ppa:qbittorrent-team/qbittorrent-stable'
- sourceline: 'ppa:qbittorrent-team/qbt-libtorrent-travisci'
- sourceline: 'ppa:beineri/opt-qt59-xenial'
packages:
# packages list: https://github.com/travis-ci/apt-package-safelist/blob/master/ubuntu-trusty
- [autoconf, automake, colormake]
- [libboost-dev, libboost-system-dev]
- libssl-dev
- libtorrent-rasterbar-dev
- [qt59base, qt59svg, qt59tools]
- zlib1g-dev
# required for Qt 5.9 from 'beineri' PPA
@@ -132,7 +131,7 @@ install:
ccache -V && ccache --show-stats && ccache --zero-stats
fi
- |
if [ "$libt_branch" = "RC_1_2" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then
if [ "$libt_branch" = "RC_1_1" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then
wget https://builds.shiki.hu/travis/deb/version
if ! cmp --quiet "version" "$HOME/travis/deb/version" ; then
echo "Cached files are different from server. Downloading new ones."
@@ -141,11 +140,16 @@ install:
mkdir "$HOME/travis/deb"
cp "version" $HOME/travis/deb
cd "$HOME/travis/deb"
wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar-dev_1.2.x_amd64.deb
wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar10_1.2.x_amd64.deb
wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar-dev_1.1.x_amd64.deb
wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar9_1.1.x_amd64.deb
fi
sudo dpkg -i "$HOME/travis/deb/libtorrent-rasterbar-dev_1.2.x_amd64.deb" "$HOME/travis/deb/libtorrent-rasterbar10_1.2.x_amd64.deb"
sudo dpkg -i "$HOME/travis/deb/libtorrent-rasterbar-dev_1.1.x_amd64.deb" "$HOME/travis/deb/libtorrent-rasterbar9_1.1.x_amd64.deb"
fi
- |
if [ "$libt_branch" = "RC_1_2" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then
# Will install latest 1.2.x daily build from the PPA
sudo apt-get -y install libtorrent-rasterbar-dev
fi
- |
if [ "$libt_branch" = "RC_1_1" ] && [ "$TRAVIS_OS_NAME" = "osx" ]; then

View File

@@ -1,3 +1,23 @@
Wed Apr 22 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.2.4
- BUGFIX: Fix sub-sorting of Transfer list (glassez)
- BUGFIX: Fix wrong logic that disables "prevent sleeping" timer (Chocobo1)
- BUGFIX: Set disk cache size for older libtorrent versions (NotTsunami)
- BUGFIX: Sort locale language list (Chocobo1)
- BUGFIX: Remove white outline around mascot.png (adem)
- BUGFIX: Various fixes in configuring the chosen network interface and not leaking the IP (Raif Atef, an0n666)
- BUGFIX: Save "resume data" when torrent storage is moved (glassez)
- BUGFIX: Avoid holding encoded resume data in memory (Chocobo1)
- BUGFIX: Fix date format for "Last seen complete" (Chocobo1)
- BUGFIX: Remove deprecated strict super seeding mode from advanced settings (an0n666)
- BUGFIX: Change default stop_tracker_timeout settings (an0n666)
- BUGFIX: Convert the Log widget to use custom View/Model (jagannatharjun)
- BUGFIX: Change default upload slot choking limits (an0n666)
- BUGFIX: Don't uncheck Authentication checkbox when changing proxy type (thalieht)
- BUGFIX: Reduce ambiguity for selecting tray icons (Chocobo1)
- WEBUI: Fix unable to add multiple peers in WebUI (Sepro)
- WEBUI: Fix UPnP lease duration get/set (NotTsunami)
- SEARCH: Detect python3 executable on Windows (József Sallai)
Wed Apr 01 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.2.3
- FEATURE: Add logging for SOCKS5 proxy errors (Chocobo1)
- FEATURE: Add UPnP lease duration advanced option (NotTsunami)

24
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.2.3.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.2.4.
#
# Report bugs to <bugs.qbittorrent.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v4.2.3'
PACKAGE_STRING='qbittorrent v4.2.3'
PACKAGE_VERSION='v4.2.4'
PACKAGE_STRING='qbittorrent v4.2.4'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='https://www.qbittorrent.org/'
@@ -1302,7 +1302,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.2.3 to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.2.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1373,7 +1373,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v4.2.3:";;
short | recursive ) echo "Configuration of qbittorrent v4.2.4:";;
esac
cat <<\_ACEOF
@@ -1509,7 +1509,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v4.2.3
qbittorrent configure v4.2.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 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.2.3, which was
It was created by qbittorrent $as_me v4.2.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3826,7 +3826,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v4.2.3'
VERSION='v4.2.4'
cat >>confdefs.h <<_ACEOF
@@ -6343,7 +6343,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.2.3, which was
This file was extended by qbittorrent $as_me v4.2.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6401,7 +6401,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v4.2.3
qbittorrent config.status v4.2.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -7659,7 +7659,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.2.3, which was
This file was extended by qbittorrent $as_me v4.2.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7717,7 +7717,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v4.2.3
qbittorrent config.status v4.2.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@@ -1,4 +1,4 @@
AC_INIT([qbittorrent], [v4.2.3], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.2.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC

10
dist/mac/Info.plist vendored
View File

@@ -55,7 +55,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.2.3</string>
<string>4.2.4</string>
<key>CFBundleExecutable</key>
<string>@EXECUTABLE@</string>
<key>CFBundleIdentifier</key>
@@ -88,13 +88,17 @@
<key>UTTypeTagSpecification</key>
<dict>
<key>com.apple.ostype</key>
<string>TORR</string>
<array>
<string>TORR</string>
</array>
<key>public.filename-extension</key>
<array>
<string>torrent</string>
</array>
<key>public.mime-type</key>
<string>application/x-bittorrent</string>
<array>
<string>application/x-bittorrent</string>
</array>
</dict>
</dict>
</array>

View File

@@ -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.2.3" date="2020-04-01"/>
<release version="4.2.4" date="2020-04-20"/>
</releases>
</component>

View File

@@ -28,7 +28,7 @@ XPStyle on
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
; Program specific
!define PROG_VERSION "4.2.3"
!define PROG_VERSION "4.2.4"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun

View File

@@ -61,6 +61,7 @@
#endif // Q_OS_MACOS
#endif
#include "base/bittorrent/infohash.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/exceptions.h"
@@ -133,6 +134,7 @@ Application::Application(int &argc, char **argv)
, m_commandLineArgs(parseCommandLine(this->arguments()))
{
qRegisterMetaType<Log::Msg>("Log::Msg");
qRegisterMetaType<Log::Peer>("Log::Peer");
setApplicationName("qBittorrent");
setOrganizationDomain("qbittorrent.org");

View File

@@ -68,7 +68,7 @@ namespace RSS
class AutoDownloader;
}
class Application : public BaseApplication
class Application final : public BaseApplication
{
Q_OBJECT
Q_DISABLE_COPY(Application)

View File

@@ -77,7 +77,7 @@
namespace QtLP_Private
{
class QtLockedFile : public QFile
class QtLockedFile final : public QFile
{
public:
enum LockMode

View File

@@ -22,6 +22,7 @@ bittorrent/session.h
bittorrent/sessionstatus.h
bittorrent/torrentcreatorthread.h
bittorrent/torrenthandle.h
bittorrent/torrenthandleimpl.h
bittorrent/torrentinfo.h
bittorrent/tracker.h
bittorrent/trackerentry.h
@@ -58,6 +59,7 @@ utils/bytearray.h
utils/foreignapps.h
utils/fs.h
utils/gzip.h
utils/io.h
utils/misc.h
utils/net.h
utils/password.h
@@ -99,6 +101,7 @@ bittorrent/private/statistics.cpp
bittorrent/session.cpp
bittorrent/torrentcreatorthread.cpp
bittorrent/torrenthandle.cpp
bittorrent/torrenthandleimpl.cpp
bittorrent/torrentinfo.cpp
bittorrent/tracker.cpp
bittorrent/trackerentry.cpp
@@ -133,6 +136,7 @@ utils/bytearray.cpp
utils/foreignapps.cpp
utils/fs.cpp
utils/gzip.cpp
utils/io.cpp
utils/misc.cpp
utils/net.cpp
utils/password.cpp

View File

@@ -21,6 +21,7 @@ HEADERS += \
$$PWD/bittorrent/sessionstatus.h \
$$PWD/bittorrent/torrentcreatorthread.h \
$$PWD/bittorrent/torrenthandle.h \
$$PWD/bittorrent/torrenthandleimpl.h \
$$PWD/bittorrent/torrentinfo.h \
$$PWD/bittorrent/tracker.h \
$$PWD/bittorrent/trackerentry.h \
@@ -73,6 +74,7 @@ HEADERS += \
$$PWD/utils/foreignapps.h \
$$PWD/utils/fs.h \
$$PWD/utils/gzip.h \
$$PWD/utils/io.h \
$$PWD/utils/misc.h \
$$PWD/utils/net.h \
$$PWD/utils/password.h \
@@ -98,6 +100,7 @@ SOURCES += \
$$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/torrentcreatorthread.cpp \
$$PWD/bittorrent/torrenthandle.cpp \
$$PWD/bittorrent/torrenthandleimpl.cpp \
$$PWD/bittorrent/torrentinfo.cpp \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/trackerentry.cpp \
@@ -143,6 +146,7 @@ SOURCES += \
$$PWD/utils/foreignapps.cpp \
$$PWD/utils/fs.cpp \
$$PWD/utils/gzip.cpp \
$$PWD/utils/io.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/net.cpp \
$$PWD/utils/password.cpp \

View File

@@ -28,6 +28,8 @@
#include "peerinfo.h"
#include <libtorrent/version.hpp>
#include <QBitArray>
#include "base/bittorrent/torrenthandle.h"

View File

@@ -35,7 +35,7 @@
class QDataStream;
class FilterParserThread : public QThread
class FilterParserThread final : public QThread
{
Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include <libtorrent/extensions.hpp>
#include <libtorrent/version.hpp>
class NativeSessionExtension : public lt::plugin
class NativeSessionExtension final : public lt::plugin
{
#if (LIBTORRENT_VERSION_NUM >= 10200)
lt::feature_flags_t implemented_features() override;

View File

@@ -47,7 +47,7 @@ namespace
#if (LIBTORRENT_VERSION_NUM < 10200)
return torrentStatus.auto_managed;
#else
return bool {torrentStatus.flags & lt::torrent_flags::auto_managed};
return static_cast<bool>(torrentStatus.flags & lt::torrent_flags::auto_managed);
#endif
}
}

View File

@@ -32,7 +32,7 @@
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/version.hpp>
class NativeTorrentExtension : public lt::torrent_plugin
class NativeTorrentExtension final : public lt::torrent_plugin
{
public:
explicit NativeTorrentExtension(const lt::torrent_handle &torrentHandle);

View File

@@ -44,7 +44,7 @@ using LTPortMapping = int;
using LTPortMapping = lt::port_mapping_t;
#endif
class PortForwarderImpl : public Net::PortForwarder
class PortForwarderImpl final : public Net::PortForwarder
{
Q_OBJECT
Q_DISABLE_COPY(PortForwarderImpl)

View File

@@ -28,11 +28,15 @@
#include "resumedatasavingmanager.h"
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include <QByteArray>
#include <QSaveFile>
#include "base/logger.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
: m_resumeDataDir(resumeFolderPath)
@@ -44,12 +48,27 @@ void ResumeDataSavingManager::save(const QString &filename, const QByteArray &da
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
if (file.open(QIODevice::WriteOnly)) {
file.write(data);
if (!file.commit()) {
Logger::instance()->addMessage(QString("Couldn't save data in '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::WARNING);
}
if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.commit()) {
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
}
}
void ResumeDataSavingManager::save(const QString &filename, const std::shared_ptr<lt::entry> &data) const
{
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
if (!file.open(QIODevice::WriteOnly)) {
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
return;
}
lt::bencode(Utils::IO::FileDeviceOutputIterator {file}, *data);
if ((file.error() != QFileDevice::NoError) || !file.commit()) {
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
}
}

View File

@@ -28,6 +28,10 @@
#pragma once
#include <memory>
#include <libtorrent/fwd.hpp>
#include <QDir>
#include <QObject>
@@ -43,8 +47,9 @@ public:
public slots:
void save(const QString &filename, const QByteArray &data) const;
void save(const QString &filename, const std::shared_ptr<lt::entry> &data) const;
void remove(const QString &filename) const;
private:
QDir m_resumeDataDir;
const QDir m_resumeDataDir;
};

View File

@@ -40,25 +40,6 @@
#include <iphlpapi.h>
#endif
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QHostAddress>
#include <QNetworkAddressEntry>
#include <QNetworkConfigurationManager>
#include <QNetworkInterface>
#include <QRegularExpression>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QUuid>
#ifdef Q_OS_WIN
// TODO: Remove together with fixBrokenSavePath()
#define NEED_TO_FIX_BROKEN_PATH
#include <QSaveFile>
#endif
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bdecode.hpp>
#include <libtorrent/bencode.hpp>
@@ -78,6 +59,25 @@
#include <libtorrent/read_resume_data.hpp>
#endif
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QHostAddress>
#include <QNetworkAddressEntry>
#include <QNetworkConfigurationManager>
#include <QNetworkInterface>
#include <QRegularExpression>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QUuid>
#ifdef Q_OS_WIN
// TODO: Remove together with fixBrokenSavePath()
#define NEED_TO_FIX_BROKEN_PATH
#include <QSaveFile>
#endif
#include "base/algorithm.h"
#include "base/exceptions.h"
#include "base/global.h"
@@ -101,7 +101,7 @@
#include "private/portforwarderimpl.h"
#include "private/resumedatasavingmanager.h"
#include "private/statistics.h"
#include "torrenthandle.h"
#include "torrenthandleimpl.h"
#include "tracker.h"
#include "trackerentry.h"
@@ -438,7 +438,11 @@ Session::Session(QObject *parent)
, m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 4)
, m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 40)
, m_checkingMemUsage(BITTORRENT_SESSION_KEY("CheckingMemUsageSize"), 32)
#if (LIBTORRENT_VERSION_NUM >= 10206)
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), -1)
#else
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), 64)
#endif
, m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
, m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
#ifdef Q_OS_WIN
@@ -467,12 +471,15 @@ Session::Session(QObject *parent)
, m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY("IgnoreLimitsOnLAN"), false)
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
, m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
#if (LIBTORRENT_VERSION_NUM >= 10206)
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 5)
#else
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 1)
, m_isSuperSeedingEnabled(BITTORRENT_SESSION_KEY("SuperSeedingEnabled"), false)
#endif
, m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
, m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), -1, lowerLimited(0, -1))
, m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), 20, lowerLimited(0, -1))
, m_maxConnectionsPerTorrent(BITTORRENT_SESSION_KEY("MaxConnectionsPerTorrent"), 100, lowerLimited(0, -1))
, m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), -1, lowerLimited(0, -1))
, m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), 4, lowerLimited(0, -1))
, m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both
, clampValue(BTProtocol::Both, BTProtocol::UTP))
, m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
@@ -657,7 +664,7 @@ void Session::setTempPathEnabled(const bool enabled)
{
if (enabled != isTempPathEnabled()) {
m_isTempPathEnabled = enabled;
for (TorrentHandle *const torrent : asConst(m_torrents))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleTempPathChanged();
}
}
@@ -671,7 +678,7 @@ void Session::setAppendExtensionEnabled(const bool enabled)
{
if (isAppendExtensionEnabled() != enabled) {
// append or remove .!qB extension for incomplete files
for (TorrentHandle *const torrent : asConst(m_torrents))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleAppendExtensionToggled();
m_isAppendExtensionEnabled = enabled;
@@ -822,12 +829,12 @@ bool Session::editCategory(const QString &name, const QString &savePath)
m_categories[name] = savePath;
m_storedCategories = map_cast(m_categories);
if (isDisableAutoTMMWhenCategorySavePathChanged()) {
for (TorrentHandle *const torrent : asConst(torrents()))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
if (torrent->category() == name)
torrent->setAutoTMMEnabled(false);
}
else {
for (TorrentHandle *const torrent : asConst(torrents()))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
if (torrent->category() == name)
torrent->handleCategorySavePathChanged();
}
@@ -837,7 +844,7 @@ bool Session::editCategory(const QString &name, const QString &savePath)
bool Session::removeCategory(const QString &name)
{
for (TorrentHandle *const torrent : asConst(torrents()))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
if (torrent->belongsToCategory(name))
torrent->setCategory("");
@@ -924,7 +931,7 @@ bool Session::addTag(const QString &tag)
bool Session::removeTag(const QString &tag)
{
if (m_tags.remove(tag)) {
for (TorrentHandle *const torrent : asConst(torrents()))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->removeTag(tag);
m_storedTags = m_tags.values();
emit tagRemoved(tag);
@@ -1439,8 +1446,6 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
settingsPack.set_str(lt::settings_pack::announce_ip, announceIP().toStdString());
// Stop tracker timeout
settingsPack.set_int(lt::settings_pack::stop_tracker_timeout, stopTrackerTimeout());
// Super seeding
settingsPack.set_bool(lt::settings_pack::strict_super_seeding, isSuperSeedingEnabled());
// * Max connections limit
settingsPack.set_int(lt::settings_pack::connections_limit, maxConnections());
// * Global max upload slots
@@ -1529,10 +1534,13 @@ void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
for (const QString &ip : asConst(getListeningIPs())) {
const QHostAddress addr {ip};
if (!addr.isNull()) {
endpoints << ((addr.protocol() == QAbstractSocket::IPv6Protocol)
const QString ip = ((addr.protocol() == QAbstractSocket::IPv6Protocol)
? ('[' + Utils::Net::canonicalIPv6Addr(addr).toString() + ']')
: addr.toString())
+ portString;
: addr.toString());
endpoints << (ip + portString);
if ((ip != "0.0.0.0") && (ip != "[::]"))
outgoingInterfaces << ip;
}
else {
// ip holds an interface name
@@ -1547,6 +1555,10 @@ void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
}
else {
LogMsg(tr("Could not get GUID of network interface: %1").arg(ip) , Log::WARNING);
// Since we can't get the GUID, we'll pass the interface name instead.
// Otherwise an empty string will be passed to outgoing_interface which will cause IP leak.
endpoints << (ip + portString);
outgoingInterfaces << ip;
}
#else
endpoints << (ip + portString);
@@ -1555,24 +1567,6 @@ void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
}
}
if (outgoingInterfaces.isEmpty()) {
#ifdef Q_OS_WIN
// On Vista+ versions and after Qt 5.5 QNetworkInterface::name() returns
// the interface's LUID and not the GUID.
// Libtorrent expects GUIDs for the 'outgoing_interfaces' setting.
const QString netInterface = networkInterface();
if (!netInterface.isEmpty()) {
const QString guid = convertIfaceNameToGuid(netInterface);
if (!guid.isEmpty())
outgoingInterfaces << guid;
else
LogMsg(tr("Could not get GUID of network interface: %1").arg(netInterface) , Log::WARNING);
}
#else
outgoingInterfaces << networkInterface();
#endif // Q_OS_WIN
}
const QString finalEndpoints = endpoints.join(',');
settingsPack.set_str(lt::settings_pack::listen_interfaces, finalEndpoints.toStdString());
LogMsg(tr("Trying to listen on: %1", "e.g: Trying to listen on: 192.168.0.1:6881")
@@ -1699,7 +1693,7 @@ void Session::processShareLimits()
{
qDebug("Processing share limits...");
for (TorrentHandle *const torrent : asConst(torrents())) {
for (TorrentHandleImpl *const torrent : asConst(m_torrents)) {
if (torrent->isSeed() && !torrent->isForced()) {
if (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT) {
const qreal ratio = torrent->realRatio();
@@ -1791,7 +1785,7 @@ TorrentHandle *Session::findTorrent(const InfoHash &hash) const
bool Session::hasActiveTorrents() const
{
return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentHandle *torrent)
return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentHandleImpl *torrent)
{
return TorrentFilter::ActiveTorrent.match(torrent);
});
@@ -1799,7 +1793,7 @@ bool Session::hasActiveTorrents() const
bool Session::hasUnfinishedTorrents() const
{
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandle *torrent)
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandleImpl *torrent)
{
return (!torrent->isSeed() && !torrent->isPaused());
});
@@ -1807,7 +1801,7 @@ bool Session::hasUnfinishedTorrents() const
bool Session::hasRunningSeed() const
{
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandle *torrent)
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandleImpl *torrent)
{
return (torrent->isSeed() && !torrent->isPaused());
});
@@ -1835,7 +1829,7 @@ void Session::banIP(const QString &ip)
// and from the disk, if the corresponding deleteOption is chosen
bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOption)
{
TorrentHandle *const torrent = m_torrents.take(hash);
TorrentHandleImpl *const torrent = m_torrents.take(hash);
if (!torrent) return false;
qDebug("Deleting torrent with hash: %s", qUtf8Printable(torrent->hash()));
@@ -1920,21 +1914,21 @@ bool Session::cancelLoadMetadata(const InfoHash &hash)
void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
{
using ElementType = std::pair<int, TorrentHandle *>;
using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType
, std::vector<ElementType>
, std::greater<ElementType>> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash);
TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent);
}
// Increase torrents queue position (starting with the one in the highest queue position)
while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second;
const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionUp(torrent->nativeHandle());
torrentQueue.pop();
}
@@ -1944,19 +1938,19 @@ void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
{
using ElementType = std::pair<int, TorrentHandle *>;
using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash);
TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent);
}
// Decrease torrents queue position (starting with the one in the lowest queue position)
while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second;
const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionDown(torrent->nativeHandle());
torrentQueue.pop();
}
@@ -1969,19 +1963,19 @@ void Session::decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::topTorrentsQueuePos(const QVector<InfoHash> &hashes)
{
using ElementType = std::pair<int, TorrentHandle *>;
using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash);
TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent);
}
// Top torrents queue position (starting with the one in the lowest queue position)
while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second;
const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionTop(torrent->nativeHandle());
torrentQueue.pop();
}
@@ -1991,21 +1985,21 @@ void Session::topTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::bottomTorrentsQueuePos(const QVector<InfoHash> &hashes)
{
using ElementType = std::pair<int, TorrentHandle *>;
using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType
, std::vector<ElementType>
, std::greater<ElementType>> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash);
TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent);
}
// Bottom torrents queue position (starting with the one in the highest queue position)
while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second;
const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionBottom(torrent->nativeHandle());
torrentQueue.pop();
}
@@ -2016,15 +2010,20 @@ void Session::bottomTorrentsQueuePos(const QVector<InfoHash> &hashes)
saveTorrentsQueue();
}
void Session::handleTorrentSaveResumeDataRequested(const TorrentHandle *torrent)
void Session::handleTorrentSaveResumeDataRequested(const TorrentHandleImpl *torrent)
{
qDebug("Saving resume data is requested for torrent '%s'...", qUtf8Printable(torrent->name()));
++m_numResumeData;
}
QHash<InfoHash, TorrentHandle *> Session::torrents() const
QVector<TorrentHandle *> Session::torrents() const
{
return m_torrents;
QVector<TorrentHandle *> result;
result.reserve(m_torrents.size());
for (TorrentHandleImpl *torrent : asConst(m_torrents))
result << torrent;
return result;
}
bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
@@ -2325,7 +2324,7 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash))
return false;
TorrentHandle *const torrent = m_torrents.value(hash);
TorrentHandleImpl *const torrent = m_torrents.value(hash);
if (torrent) { // a duplicate torrent is added
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate()))
return false;
@@ -2511,7 +2510,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
return true;
}
void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder)
void Session::exportTorrentFile(const TorrentHandle *torrent, TorrentExportFolder folder)
{
Q_ASSERT(((folder == TorrentExportFolder::Regular) && !torrentExportDirectory().isEmpty()) ||
((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
@@ -2537,7 +2536,7 @@ void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolde
void Session::generateResumeData(const bool final)
{
for (TorrentHandle *const torrent : asConst(m_torrents)) {
for (TorrentHandleImpl *const torrent : asConst(m_torrents)) {
if (!torrent->isValid()) continue;
if (!final && !torrent->needSaveResumeData()) continue;
@@ -2582,15 +2581,17 @@ void Session::saveResumeData()
void Session::saveTorrentsQueue()
{
// store hash in textual representation
QMap<int, QString> queue; // Use QMap since it should be ordered by key
for (const TorrentHandle *torrent : asConst(torrents())) {
for (const TorrentHandleImpl *torrent : asConst(m_torrents)) {
// We require actual (non-cached) queue position here!
const int queuePos = LTUnderlyingType<LTQueuePosition> {torrent->nativeHandle().queue_position()};
const int queuePos = static_cast<LTUnderlyingType<LTQueuePosition>>(torrent->nativeHandle().queue_position());
if (queuePos >= 0)
queue[queuePos] = torrent->hash();
}
QByteArray data;
data.reserve(((InfoHash::length() * 2) + 1) * queue.size());
for (const QString &hash : asConst(queue))
data += (hash.toLatin1() + '\n');
@@ -2623,10 +2624,10 @@ void Session::setDefaultSavePath(QString path)
m_defaultSavePath = path;
if (isDisableAutoTMMWhenDefaultSavePathChanged())
for (TorrentHandle *const torrent : asConst(torrents()))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->setAutoTMMEnabled(false);
else
for (TorrentHandle *const torrent : asConst(torrents()))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleCategorySavePathChanged();
}
@@ -2637,7 +2638,7 @@ void Session::setTempPath(QString path)
m_tempPath = path;
for (TorrentHandle *const torrent : asConst(m_torrents))
for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleTempPathChanged();
}
@@ -2673,7 +2674,11 @@ QStringList Session::getListeningIPs() const
if (!ifaceAddr.isEmpty() && !allIPv4 && !allIPv6 && configuredAddr.isNull()) {
LogMsg(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.158.1 isn't valid.").arg(ifaceAddr), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
// Pass the invalid user configured interface name/address to libtorrent
// in hopes that it will come online later.
// This will not cause IP leak but allow user to reconnect the interface
// and re-establish connection without restarting the client.
IPs.append(ifaceAddr);
return IPs;
}
@@ -2709,7 +2714,7 @@ QStringList Session::getListeningIPs() const
LogMsg(tr("Can't find the configured address '%1' to listen on"
, "Can't find the configured address '192.168.1.3' to listen on")
.arg(ifaceAddr), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
IPs.append(ifaceAddr);
}
return IPs;
@@ -2720,7 +2725,7 @@ QStringList Session::getListeningIPs() const
if (!networkIFace.isValid()) {
qDebug("Invalid network interface: %s", qUtf8Printable(ifaceName));
LogMsg(tr("The network interface defined is invalid: %1").arg(ifaceName), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
IPs.append(ifaceName);
return IPs;
}
@@ -2741,7 +2746,7 @@ QStringList Session::getListeningIPs() const
LogMsg(tr("Can't find the configured address '%1' to listen on"
, "Can't find the configured address '192.168.1.3' to listen on")
.arg(ifaceAddr), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
IPs.append(ifaceAddr);
}
return IPs;
@@ -3680,19 +3685,6 @@ void Session::setStopTrackerTimeout(const int value)
configureDeferred();
}
bool Session::isSuperSeedingEnabled() const
{
return m_isSuperSeedingEnabled;
}
void Session::setSuperSeedingEnabled(const bool enabled)
{
if (enabled != m_isSuperSeedingEnabled) {
m_isSuperSeedingEnabled = enabled;
configureDeferred();
}
}
int Session::maxConnections() const
{
return m_maxConnections;
@@ -3825,48 +3817,48 @@ void Session::updateSeedingLimitTimer()
}
}
void Session::handleTorrentShareLimitChanged(TorrentHandle *const torrent)
void Session::handleTorrentShareLimitChanged(TorrentHandleImpl *const torrent)
{
torrent->saveResumeData();
updateSeedingLimitTimer();
}
void Session::handleTorrentNameChanged(TorrentHandle *const torrent)
void Session::handleTorrentNameChanged(TorrentHandleImpl *const torrent)
{
torrent->saveResumeData();
}
void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent)
void Session::handleTorrentSavePathChanged(TorrentHandleImpl *const torrent)
{
torrent->saveResumeData();
emit torrentSavePathChanged(torrent);
}
void Session::handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory)
void Session::handleTorrentCategoryChanged(TorrentHandleImpl *const torrent, const QString &oldCategory)
{
torrent->saveResumeData();
emit torrentCategoryChanged(torrent, oldCategory);
}
void Session::handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag)
void Session::handleTorrentTagAdded(TorrentHandleImpl *const torrent, const QString &tag)
{
torrent->saveResumeData();
emit torrentTagAdded(torrent, tag);
}
void Session::handleTorrentTagRemoved(TorrentHandle *const torrent, const QString &tag)
void Session::handleTorrentTagRemoved(TorrentHandleImpl *const torrent, const QString &tag)
{
torrent->saveResumeData();
emit torrentTagRemoved(torrent, tag);
}
void Session::handleTorrentSavingModeChanged(TorrentHandle *const torrent)
void Session::handleTorrentSavingModeChanged(TorrentHandleImpl *const torrent)
{
torrent->saveResumeData();
emit torrentSavingModeChanged(torrent);
}
void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QVector<TrackerEntry> &newTrackers)
void Session::handleTorrentTrackersAdded(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &newTrackers)
{
torrent->saveResumeData();
@@ -3878,7 +3870,7 @@ void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QVe
emit trackersChanged(torrent);
}
void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QVector<TrackerEntry> &deletedTrackers)
void Session::handleTorrentTrackersRemoved(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers)
{
torrent->saveResumeData();
@@ -3890,27 +3882,27 @@ void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const Q
emit trackersChanged(torrent);
}
void Session::handleTorrentTrackersChanged(TorrentHandle *const torrent)
void Session::handleTorrentTrackersChanged(TorrentHandleImpl *const torrent)
{
torrent->saveResumeData();
emit trackersChanged(torrent);
}
void Session::handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QVector<QUrl> &newUrlSeeds)
void Session::handleTorrentUrlSeedsAdded(TorrentHandleImpl *const torrent, const QVector<QUrl> &newUrlSeeds)
{
torrent->saveResumeData();
for (const QUrl &newUrlSeed : newUrlSeeds)
LogMsg(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString(), torrent->name()));
}
void Session::handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QVector<QUrl> &urlSeeds)
void Session::handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, const QVector<QUrl> &urlSeeds)
{
torrent->saveResumeData();
for (const QUrl &urlSeed : urlSeeds)
LogMsg(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString(), torrent->name()));
}
void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent)
void Session::handleTorrentMetadataReceived(TorrentHandleImpl *const torrent)
{
torrent->saveResumeData();
@@ -3931,25 +3923,25 @@ void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent)
emit torrentMetadataLoaded(torrent);
}
void Session::handleTorrentPaused(TorrentHandle *const torrent)
void Session::handleTorrentPaused(TorrentHandleImpl *const torrent)
{
if (!torrent->hasError() && !torrent->hasMissingFiles())
torrent->saveResumeData();
emit torrentPaused(torrent);
}
void Session::handleTorrentResumed(TorrentHandle *const torrent)
void Session::handleTorrentResumed(TorrentHandleImpl *const torrent)
{
torrent->saveResumeData();
emit torrentResumed(torrent);
}
void Session::handleTorrentChecked(TorrentHandle *const torrent)
void Session::handleTorrentChecked(TorrentHandleImpl *const torrent)
{
emit torrentFinishedChecking(torrent);
}
void Session::handleTorrentFinished(TorrentHandle *const torrent)
void Session::handleTorrentFinished(TorrentHandleImpl *const torrent)
{
if (!torrent->hasError() && !torrent->hasMissingFiles())
torrent->saveResumeData();
@@ -3984,45 +3976,40 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent)
emit allTorrentsFinished();
}
void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const lt::entry &data)
void Session::handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, const std::shared_ptr<lt::entry> &data)
{
--m_numResumeData;
// Separated thread is used for the blocking IO which results in slow processing of many torrents.
// Encoding data in parallel while doing IO saves time. Copying lt::entry objects around
// isn't cheap too.
QByteArray out;
out.reserve(1024 * 1024); // most fastresume file sizes are under 1 MB
lt::bencode(std::back_inserter(out), data);
// Copying lt::entry objects around isn't cheap.
const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->hash());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QMetaObject::invokeMethod(m_resumeDataSavingManager
, [this, filename, out]() { m_resumeDataSavingManager->save(filename, out); });
, [this, filename, data]() { m_resumeDataSavingManager->save(filename, data); });
#else
QMetaObject::invokeMethod(m_resumeDataSavingManager, "save",
Q_ARG(QString, filename), Q_ARG(QByteArray, out));
QMetaObject::invokeMethod(m_resumeDataSavingManager, "save"
, Q_ARG(QString, filename), Q_ARG(std::shared_ptr<lt::entry>, data));
#endif
}
void Session::handleTorrentResumeDataFailed(TorrentHandle *const torrent)
void Session::handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent)
{
Q_UNUSED(torrent)
--m_numResumeData;
}
void Session::handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl)
void Session::handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{
emit trackerSuccess(torrent, trackerUrl);
}
void Session::handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl)
void Session::handleTorrentTrackerError(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{
emit trackerError(torrent, trackerUrl);
}
bool Session::addMoveTorrentStorageJob(TorrentHandle *torrent, const QString &newPath, const MoveStorageMode mode)
bool Session::addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, const MoveStorageMode mode)
{
Q_ASSERT(torrent);
@@ -4095,14 +4082,14 @@ void Session::handleMoveTorrentStorageJobFinished(const QString &errorMessage)
}
}
void Session::handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl)
void Session::handleTorrentTrackerWarning(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{
emit trackerWarning(torrent, trackerUrl);
}
bool Session::hasPerTorrentRatioLimit() const
{
return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandle *torrent)
return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandleImpl *torrent)
{
return (torrent->ratioLimit() >= 0);
});
@@ -4110,7 +4097,7 @@ bool Session::hasPerTorrentRatioLimit() const
bool Session::hasPerTorrentSeedingTimeLimit() const
{
return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandle *torrent)
return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandleImpl *torrent)
{
return (torrent->seedingTimeLimit() >= 0);
});
@@ -4184,7 +4171,7 @@ void Session::disableIPFilter()
void Session::recursiveTorrentDownload(const InfoHash &hash)
{
TorrentHandle *const torrent = m_torrents.value(hash);
TorrentHandleImpl *const torrent = m_torrents.value(hash);
if (!torrent) return;
for (int i = 0; i < torrent->filesCount(); ++i) {
@@ -4494,7 +4481,7 @@ void Session::handleAlert(const lt::alert *a)
void Session::dispatchTorrentAlert(const lt::alert *a)
{
TorrentHandle *const torrent = m_torrents.value(static_cast<const lt::torrent_alert*>(a)->handle.info_hash());
TorrentHandleImpl *const torrent = m_torrents.value(static_cast<const lt::torrent_alert*>(a)->handle.info_hash());
if (torrent) {
torrent->handleAlert(a);
return;
@@ -4514,7 +4501,7 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
const CreateTorrentParams params = m_addingTorrents.take(nativeHandle.info_hash());
TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, params);
TorrentHandleImpl *const torrent = new TorrentHandleImpl(this, nativeHandle, params);
m_torrents.insert(torrent->hash(), torrent);
const bool fromMagnetUri = !torrent->hasMetadata();
@@ -4658,7 +4645,7 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
void Session::handleFileErrorAlert(const lt::file_error_alert *p)
{
TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash());
TorrentHandleImpl *const torrent = m_torrents.value(p->handle.info_hash());
if (!torrent)
return;
@@ -4752,7 +4739,7 @@ void Session::handlePeerBanAlert(const lt::peer_ban_alert *p)
void Session::handleUrlSeedAlert(const lt::url_seed_alert *p)
{
const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash());
const TorrentHandleImpl *torrent = m_torrents.value(p->handle.info_hash());
if (!torrent)
return;
@@ -4983,7 +4970,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
{
if (m_moveStorageQueue.isEmpty()) return;
const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash());
const TorrentHandleImpl *torrent = m_torrents.value(p->handle.info_hash());
const MoveStorageJob &currentJob = m_moveStorageQueue.first();
if (currentJob.torrent != torrent) return;
@@ -4995,7 +4982,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
{
if (m_moveStorageQueue.isEmpty()) return;
const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash());
const TorrentHandleImpl *torrent = m_torrents.value(p->handle.info_hash());
const MoveStorageJob &currentJob = m_moveStorageQueue.first();
if (currentJob.torrent != torrent) return;
@@ -5008,7 +4995,7 @@ void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
updatedTorrents.reserve(p->status.size());
for (const lt::torrent_status &status : p->status) {
TorrentHandle *const torrent = m_torrents.value(status.info_hash);
TorrentHandleImpl *const torrent = m_torrents.value(status.info_hash);
if (!torrent)
continue;

View File

@@ -30,6 +30,7 @@
#ifndef BITTORRENT_SESSION_H
#define BITTORRENT_SESSION_H
#include <memory>
#include <vector>
#include <libtorrent/fwd.hpp>
@@ -92,6 +93,7 @@ namespace BitTorrent
class InfoHash;
class MagnetUri;
class TorrentHandle;
class TorrentHandleImpl;
class Tracker;
class TrackerEntry;
struct CreateTorrentParams;
@@ -377,8 +379,6 @@ namespace BitTorrent
void setAnnounceIP(const QString &ip);
int stopTrackerTimeout() const;
void setStopTrackerTimeout(int value);
bool isSuperSeedingEnabled() const;
void setSuperSeedingEnabled(bool enabled);
int maxConnections() const;
void setMaxConnections(int max);
int maxConnectionsPerTorrent() const;
@@ -412,7 +412,7 @@ namespace BitTorrent
void startUpTorrents();
TorrentHandle *findTorrent(const InfoHash &hash) const;
QHash<InfoHash, TorrentHandle *> torrents() const;
QVector<TorrentHandle *> torrents() const;
bool hasActiveTorrents() const;
bool hasUnfinishedTorrents() const;
bool hasRunningSeed() const;
@@ -441,31 +441,31 @@ namespace BitTorrent
void bottomTorrentsQueuePos(const QVector<InfoHash> &hashes);
// TorrentHandle interface
void handleTorrentSaveResumeDataRequested(const TorrentHandle *torrent);
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
void handleTorrentNameChanged(TorrentHandle *const torrent);
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
void handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag);
void handleTorrentTagRemoved(TorrentHandle *const torrent, const QString &tag);
void handleTorrentSavingModeChanged(TorrentHandle *const torrent);
void handleTorrentMetadataReceived(TorrentHandle *const torrent);
void handleTorrentPaused(TorrentHandle *const torrent);
void handleTorrentResumed(TorrentHandle *const torrent);
void handleTorrentChecked(TorrentHandle *const torrent);
void handleTorrentFinished(TorrentHandle *const torrent);
void handleTorrentTrackersAdded(TorrentHandle *const torrent, const QVector<TrackerEntry> &newTrackers);
void handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QVector<TrackerEntry> &deletedTrackers);
void handleTorrentTrackersChanged(TorrentHandle *const torrent);
void handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QVector<QUrl> &newUrlSeeds);
void handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QVector<QUrl> &urlSeeds);
void handleTorrentResumeDataReady(TorrentHandle *const torrent, const lt::entry &data);
void handleTorrentResumeDataFailed(TorrentHandle *const torrent);
void handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl);
void handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl);
void handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl);
void handleTorrentSaveResumeDataRequested(const TorrentHandleImpl *torrent);
void handleTorrentShareLimitChanged(TorrentHandleImpl *const torrent);
void handleTorrentNameChanged(TorrentHandleImpl *const torrent);
void handleTorrentSavePathChanged(TorrentHandleImpl *const torrent);
void handleTorrentCategoryChanged(TorrentHandleImpl *const torrent, const QString &oldCategory);
void handleTorrentTagAdded(TorrentHandleImpl *const torrent, const QString &tag);
void handleTorrentTagRemoved(TorrentHandleImpl *const torrent, const QString &tag);
void handleTorrentSavingModeChanged(TorrentHandleImpl *const torrent);
void handleTorrentMetadataReceived(TorrentHandleImpl *const torrent);
void handleTorrentPaused(TorrentHandleImpl *const torrent);
void handleTorrentResumed(TorrentHandleImpl *const torrent);
void handleTorrentChecked(TorrentHandleImpl *const torrent);
void handleTorrentFinished(TorrentHandleImpl *const torrent);
void handleTorrentTrackersAdded(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &newTrackers);
void handleTorrentTrackersRemoved(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers);
void handleTorrentTrackersChanged(TorrentHandleImpl *const torrent);
void handleTorrentUrlSeedsAdded(TorrentHandleImpl *const torrent, const QVector<QUrl> &newUrlSeeds);
void handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, const QVector<QUrl> &urlSeeds);
void handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, const std::shared_ptr<lt::entry> &data);
void handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent);
void handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl);
void handleTorrentTrackerWarning(TorrentHandleImpl *const torrent, const QString &trackerUrl);
void handleTorrentTrackerError(TorrentHandleImpl *const torrent, const QString &trackerUrl);
bool addMoveTorrentStorageJob(TorrentHandle *torrent, const QString &newPath, MoveStorageMode mode);
bool addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, MoveStorageMode mode);
signals:
void addTorrentFailed(const QString &error);
@@ -522,7 +522,7 @@ namespace BitTorrent
private:
struct MoveStorageJob
{
TorrentHandle *torrent;
TorrentHandleImpl *torrent;
QString path;
MoveStorageMode mode;
};
@@ -572,7 +572,7 @@ namespace BitTorrent
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
void updateSeedingLimitTimer();
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
void exportTorrentFile(const TorrentHandle *torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
void handleAlert(const lt::alert *a);
void dispatchTorrentAlert(const lt::alert *a);
@@ -657,7 +657,6 @@ namespace BitTorrent
CachedSettingValue<bool> m_includeOverheadInLimits;
CachedSettingValue<QString> m_announceIP;
CachedSettingValue<int> m_stopTrackerTimeout;
CachedSettingValue<bool> m_isSuperSeedingEnabled;
CachedSettingValue<int> m_maxConnections;
CachedSettingValue<int> m_maxUploads;
CachedSettingValue<int> m_maxConnectionsPerTorrent;
@@ -735,7 +734,7 @@ namespace BitTorrent
ResumeDataSavingManager *m_resumeDataSavingManager = nullptr;
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
QHash<InfoHash, TorrentHandle *> m_torrents;
QHash<InfoHash, TorrentHandleImpl *> m_torrents;
QHash<InfoHash, CreateTorrentParams> m_addingTorrents;
QHash<QString, AddTorrentParams> m_downloadedTorrents;
QHash<InfoHash, RemovingTorrentData> m_removingTorrents;

View File

@@ -41,8 +41,10 @@
#include <QFileInfo>
#include <QHash>
#include "base/exceptions.h"
#include "base/global.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
#include "base/utils/string.h"
#include "private/ltunderlyingtype.h"
@@ -162,7 +164,7 @@ void TorrentCreatorThread::run()
lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
, [this, &newTorrent](const LTPieceIndex n)
{
sendProgressSignal(LTUnderlyingType<LTPieceIndex> {n}, newTorrent.num_pieces());
sendProgressSignal(static_cast<LTUnderlyingType<LTPieceIndex>>(n), newTorrent.num_pieces());
});
// Set qBittorrent as creator and add user comment to
// torrent_info structure
@@ -182,19 +184,19 @@ void TorrentCreatorThread::run()
if (isInterruptionRequested()) return;
// create the torrent
std::ofstream outfile(
#ifdef _MSC_VER
Utils::Fs::toNativePath(m_params.savePath).toStdWString().c_str()
#else
Utils::Fs::toNativePath(m_params.savePath).toUtf8().constData()
#endif
, (std::ios_base::out | std::ios_base::binary | std::ios_base::trunc));
if (outfile.fail())
throw std::runtime_error(tr("create new torrent file failed").toStdString());
QFile outfile {m_params.savePath};
if (!outfile.open(QIODevice::WriteOnly)) {
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
.arg(outfile.errorString())};
}
if (isInterruptionRequested()) return;
lt::bencode(std::ostream_iterator<char>(outfile), entry);
lt::bencode(Utils::IO::FileDeviceOutputIterator {outfile}, entry);
if (outfile.error() != QFileDevice::NoError) {
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
.arg(outfile.errorString())};
}
outfile.close();
emit updateProgress(100);

View File

@@ -48,7 +48,7 @@ namespace BitTorrent
QStringList urlSeeds;
};
class TorrentCreatorThread : public QThread
class TorrentCreatorThread final : public QThread
{
Q_OBJECT

File diff suppressed because it is too large Load Diff

View File

@@ -27,84 +27,30 @@
* exception statement from your version.
*/
#ifndef BITTORRENT_TORRENTHANDLE_H
#define BITTORRENT_TORRENTHANDLE_H
#pragma once
#include <functional>
#include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>
#include <QDateTime>
#include <QHash>
#include <QObject>
#include <QQueue>
#include <QMetaType>
#include <QSet>
#include <QString>
#include <QVector>
#include "private/speedmonitor.h"
#include "infohash.h"
#include "torrentinfo.h"
extern const QString QB_EXT;
class QBitArray;
class QDateTime;
class QStringList;
class QUrl;
extern const QString QB_EXT;
namespace BitTorrent
{
enum class DownloadPriority;
class InfoHash;
class PeerInfo;
class Session;
class TorrentInfo;
class TrackerEntry;
struct AddTorrentParams;
struct PeerAddress;
struct CreateTorrentParams
{
bool restored; // is existing torrent job?
// for both new and restored torrents
QString name;
QString category;
QSet<QString> tags;
QString savePath;
bool disableTempPath;
bool sequential;
bool firstLastPiecePriority;
bool hasSeedStatus;
bool skipChecking;
bool hasRootFolder;
bool forced;
bool paused;
int uploadLimit;
int downloadLimit;
// for new torrents
QVector<DownloadPriority> filePriorities;
QDateTime addedTime;
// for restored torrents
qreal ratioLimit;
int seedingTimeLimit;
CreateTorrentParams();
explicit CreateTorrentParams(const AddTorrentParams &params);
};
struct TrackerInfo
{
QString lastMessage;
int numPeers = 0;
};
enum class MoveStorageMode
{
KeepExistingFiles,
Overwrite
};
enum class TorrentState
{
Unknown = -1,
@@ -135,13 +81,16 @@ namespace BitTorrent
Error
};
struct TrackerInfo
{
QString lastMessage;
int numPeers = 0;
};
uint qHash(TorrentState key, uint seed);
class TorrentHandle : public QObject
class TorrentHandle
{
Q_DISABLE_COPY(TorrentHandle)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandle)
public:
static const qreal USE_GLOBAL_RATIO;
static const qreal NO_RATIO_LIMIT;
@@ -152,24 +101,21 @@ namespace BitTorrent
static const qreal MAX_RATIO;
static const int MAX_SEEDING_TIME;
TorrentHandle(Session *session, const lt::torrent_handle &nativeHandle,
const CreateTorrentParams &params);
~TorrentHandle();
virtual ~TorrentHandle() = default;
bool isValid() const;
InfoHash hash() const;
QString name() const;
QDateTime creationDate() const;
QString creator() const;
QString comment() const;
bool isPrivate() const;
qlonglong totalSize() const;
qlonglong wantedSize() const;
qlonglong completedSize() const;
qlonglong incompletedSize() const;
qlonglong pieceLength() const;
qlonglong wastedSize() const;
QString currentTracker() const;
virtual InfoHash hash() const = 0;
virtual QString name() const = 0;
virtual QDateTime creationDate() const = 0;
virtual QString creator() const = 0;
virtual QString comment() const = 0;
virtual bool isPrivate() const = 0;
virtual qlonglong totalSize() const = 0;
virtual qlonglong wantedSize() const = 0;
virtual qlonglong completedSize() const = 0;
virtual qlonglong incompletedSize() const = 0;
virtual qlonglong pieceLength() const = 0;
virtual qlonglong wastedSize() const = 0;
virtual QString currentTracker() const = 0;
// 1. savePath() - the path where all the files and subfolders of torrent are stored (as always).
// 2. rootPath() - absolute path of torrent file tree (save path + first item from 1st torrent file path).
@@ -214,237 +160,140 @@ namespace BitTorrent
// | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 |
// | C | /home/user/torrents/file1 | /home/user/torrents/file1 |
QString savePath(bool actual = false) const;
QString rootPath(bool actual = false) const;
QString contentPath(bool actual = false) const;
virtual QString savePath(bool actual = false) const = 0;
virtual QString rootPath(bool actual = false) const = 0;
virtual QString contentPath(bool actual = false) const = 0;
bool useTempPath() const;
virtual bool useTempPath() const = 0;
bool isAutoTMMEnabled() const;
void setAutoTMMEnabled(bool enabled);
QString category() const;
bool belongsToCategory(const QString &category) const;
bool setCategory(const QString &category);
virtual bool isAutoTMMEnabled() const = 0;
virtual void setAutoTMMEnabled(bool enabled) = 0;
virtual QString category() const = 0;
virtual bool belongsToCategory(const QString &category) const = 0;
virtual bool setCategory(const QString &category) = 0;
QSet<QString> tags() const;
bool hasTag(const QString &tag) const;
bool addTag(const QString &tag);
bool removeTag(const QString &tag);
void removeAllTags();
virtual QSet<QString> tags() const = 0;
virtual bool hasTag(const QString &tag) const = 0;
virtual bool addTag(const QString &tag) = 0;
virtual bool removeTag(const QString &tag) = 0;
virtual void removeAllTags() = 0;
bool hasRootFolder() const;
virtual bool hasRootFolder() const = 0;
int filesCount() const;
int piecesCount() const;
int piecesHave() const;
qreal progress() const;
QDateTime addedTime() const;
qreal ratioLimit() const;
int seedingTimeLimit() const;
virtual int filesCount() const = 0;
virtual int piecesCount() const = 0;
virtual int piecesHave() const = 0;
virtual qreal progress() const = 0;
virtual QDateTime addedTime() const = 0;
virtual qreal ratioLimit() const = 0;
virtual int seedingTimeLimit() const = 0;
QString filePath(int index) const;
QString fileName(int index) const;
qlonglong fileSize(int index) const;
QStringList absoluteFilePaths() const;
QStringList absoluteFilePathsUnwanted() const;
QVector<DownloadPriority> filePriorities() const;
TorrentInfo info() const;
bool isSeed() const;
bool isPaused() const;
bool isResumed() const;
bool isQueued() const;
bool isForced() const;
bool isChecking() const;
bool isDownloading() const;
bool isUploading() const;
bool isCompleted() const;
bool isActive() const;
bool isInactive() const;
bool isErrored() const;
bool isSequentialDownload() const;
bool hasFirstLastPiecePriority() const;
TorrentState state() const;
bool hasMetadata() const;
bool hasMissingFiles() const;
bool hasError() const;
bool hasFilteredPieces() const;
int queuePosition() const;
QVector<TrackerEntry> trackers() const;
QHash<QString, TrackerInfo> trackerInfos() const;
QVector<QUrl> urlSeeds() const;
QString error() const;
qlonglong totalDownload() const;
qlonglong totalUpload() const;
qlonglong activeTime() const;
qlonglong finishedTime() const;
qlonglong seedingTime() const;
qlonglong eta() const;
QVector<qreal> filesProgress() const;
int seedsCount() const;
int peersCount() const;
int leechsCount() const;
int totalSeedsCount() const;
int totalPeersCount() const;
int totalLeechersCount() const;
int completeCount() const;
int incompleteCount() const;
QDateTime lastSeenComplete() const;
QDateTime completedTime() const;
qlonglong timeSinceUpload() const;
qlonglong timeSinceDownload() const;
qlonglong timeSinceActivity() const;
int downloadLimit() const;
int uploadLimit() const;
bool superSeeding() const;
QVector<PeerInfo> peers() const;
QBitArray pieces() const;
QBitArray downloadingPieces() const;
QVector<int> pieceAvailability() const;
qreal distributedCopies() const;
qreal maxRatio() const;
int maxSeedingTime() const;
qreal realRatio() const;
int uploadPayloadRate() const;
int downloadPayloadRate() const;
qlonglong totalPayloadUpload() const;
qlonglong totalPayloadDownload() const;
int connectionsCount() const;
int connectionsLimit() const;
qlonglong nextAnnounce() const;
void setName(const QString &name);
void setSequentialDownload(bool enable);
void toggleSequentialDownload();
void setFirstLastPiecePriority(bool enabled);
void toggleFirstLastPiecePriority();
void pause();
void resume(bool forced = false);
void move(QString path);
void forceReannounce(int index = -1);
void forceDHTAnnounce();
void forceRecheck();
void renameFile(int index, const QString &name);
void prioritizeFiles(const QVector<DownloadPriority> &priorities);
void setRatioLimit(qreal limit);
void setSeedingTimeLimit(int limit);
void setUploadLimit(int limit);
void setDownloadLimit(int limit);
void setSuperSeeding(bool enable);
void flushCache() const;
void addTrackers(const QVector<TrackerEntry> &trackers);
void replaceTrackers(const QVector<TrackerEntry> &trackers);
void addUrlSeeds(const QVector<QUrl> &urlSeeds);
void removeUrlSeeds(const QVector<QUrl> &urlSeeds);
bool connectPeer(const PeerAddress &peerAddress);
QString toMagnetUri() const;
bool needSaveResumeData() const;
// Session interface
lt::torrent_handle nativeHandle() const;
void handleAlert(const lt::alert *a);
void handleStateUpdate(const lt::torrent_status &nativeStatus);
void handleTempPathChanged();
void handleCategorySavePathChanged();
void handleAppendExtensionToggled();
void saveResumeData();
void handleStorageMoved(const QString &newPath, const QString &errorMessage);
virtual QString filePath(int index) const = 0;
virtual QString fileName(int index) const = 0;
virtual qlonglong fileSize(int index) const = 0;
virtual QStringList absoluteFilePaths() const = 0;
virtual QStringList absoluteFilePathsUnwanted() const = 0;
virtual QVector<DownloadPriority> filePriorities() const = 0;
virtual TorrentInfo info() const = 0;
virtual bool isSeed() const = 0;
virtual bool isPaused() const = 0;
virtual bool isResumed() const = 0;
virtual bool isQueued() const = 0;
virtual bool isForced() const = 0;
virtual bool isChecking() const = 0;
virtual bool isDownloading() const = 0;
virtual bool isUploading() const = 0;
virtual bool isCompleted() const = 0;
virtual bool isActive() const = 0;
virtual bool isInactive() const = 0;
virtual bool isErrored() const = 0;
virtual bool isSequentialDownload() const = 0;
virtual bool hasFirstLastPiecePriority() const = 0;
virtual TorrentState state() const = 0;
virtual bool hasMetadata() const = 0;
virtual bool hasMissingFiles() const = 0;
virtual bool hasError() const = 0;
virtual bool hasFilteredPieces() const = 0;
virtual int queuePosition() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0;
virtual QHash<QString, TrackerInfo> trackerInfos() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0;
virtual QString error() const = 0;
virtual qlonglong totalDownload() const = 0;
virtual qlonglong totalUpload() const = 0;
virtual qlonglong activeTime() const = 0;
virtual qlonglong finishedTime() const = 0;
virtual qlonglong seedingTime() const = 0;
virtual qlonglong eta() const = 0;
virtual QVector<qreal> filesProgress() const = 0;
virtual int seedsCount() const = 0;
virtual int peersCount() const = 0;
virtual int leechsCount() const = 0;
virtual int totalSeedsCount() const = 0;
virtual int totalPeersCount() const = 0;
virtual int totalLeechersCount() const = 0;
virtual int completeCount() const = 0;
virtual int incompleteCount() const = 0;
virtual QDateTime lastSeenComplete() const = 0;
virtual QDateTime completedTime() const = 0;
virtual qlonglong timeSinceUpload() const = 0;
virtual qlonglong timeSinceDownload() const = 0;
virtual qlonglong timeSinceActivity() const = 0;
virtual int downloadLimit() const = 0;
virtual int uploadLimit() const = 0;
virtual bool superSeeding() const = 0;
virtual QVector<PeerInfo> peers() const = 0;
virtual QBitArray pieces() const = 0;
virtual QBitArray downloadingPieces() const = 0;
virtual QVector<int> pieceAvailability() const = 0;
virtual qreal distributedCopies() const = 0;
virtual qreal maxRatio() const = 0;
virtual int maxSeedingTime() const = 0;
virtual qreal realRatio() const = 0;
virtual int uploadPayloadRate() const = 0;
virtual int downloadPayloadRate() const = 0;
virtual qlonglong totalPayloadUpload() const = 0;
virtual qlonglong totalPayloadDownload() const = 0;
virtual int connectionsCount() const = 0;
virtual int connectionsLimit() const = 0;
virtual qlonglong nextAnnounce() const = 0;
/**
* @brief fraction of file pieces that are available at least from one peer
*
* This is not the same as torrrent availability, it is just a fraction of pieces
* that can be downloaded right now. It varies between 0 to 1.
*/
QVector<qreal> availableFileFractions() const;
virtual QVector<qreal> availableFileFractions() const = 0;
private:
typedef std::function<void ()> EventTrigger;
virtual void setName(const QString &name) = 0;
virtual void setSequentialDownload(bool enable) = 0;
virtual void setFirstLastPiecePriority(bool enabled) = 0;
virtual void pause() = 0;
virtual void resume(bool forced = false) = 0;
virtual void move(QString path) = 0;
virtual void forceReannounce(int index = -1) = 0;
virtual void forceDHTAnnounce() = 0;
virtual void forceRecheck() = 0;
virtual void renameFile(int index, const QString &name) = 0;
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
virtual void setRatioLimit(qreal limit) = 0;
virtual void setSeedingTimeLimit(int limit) = 0;
virtual void setUploadLimit(int limit) = 0;
virtual void setDownloadLimit(int limit) = 0;
virtual void setSuperSeeding(bool enable) = 0;
virtual void flushCache() const = 0;
virtual void addTrackers(const QVector<TrackerEntry> &trackers) = 0;
virtual void replaceTrackers(const QVector<TrackerEntry> &trackers) = 0;
virtual void addUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
#if (LIBTORRENT_VERSION_NUM < 10200)
using LTFileIndex = int;
#else
using LTFileIndex = lt::file_index_t;
#endif
virtual QString createMagnetURI() const = 0;
void updateStatus();
void updateStatus(const lt::torrent_status &nativeStatus);
void updateState();
void updateTorrentInfo();
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
void handleFileCompletedAlert(const lt::file_completed_alert *p);
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p);
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p);
void handlePerformanceAlert(const lt::performance_alert *p) const;
void handleSaveResumeDataAlert(const lt::save_resume_data_alert *p);
void handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p);
void handleTorrentCheckedAlert(const lt::torrent_checked_alert *p);
void handleTorrentFinishedAlert(const lt::torrent_finished_alert *p);
void handleTorrentPausedAlert(const lt::torrent_paused_alert *p);
void handleTorrentResumedAlert(const lt::torrent_resumed_alert *p);
void handleTrackerErrorAlert(const lt::tracker_error_alert *p);
void handleTrackerReplyAlert(const lt::tracker_reply_alert *p);
void handleTrackerWarningAlert(const lt::tracker_warning_alert *p);
void resume_impl(bool forced);
bool isMoveInProgress() const;
QString actualStorageLocation() const;
bool isAutoManaged() const;
void setAutoManaged(bool enable);
void adjustActualSavePath();
void adjustActualSavePath_impl();
void move_impl(QString path, MoveStorageMode mode);
void moveStorage(const QString &newPath, MoveStorageMode mode);
void manageIncompleteFiles();
void setFirstLastPiecePriorityImpl(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
Session *const m_session;
lt::torrent_handle m_nativeHandle;
lt::torrent_status m_nativeStatus;
TorrentState m_state;
TorrentInfo m_torrentInfo;
SpeedMonitor m_speedMonitor;
InfoHash m_hash;
bool m_storageIsMoving = false;
// m_moveFinishedTriggers is activated only when the following conditions are met:
// all file rename jobs complete, all file move jobs complete
QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount;
// Until libtorrent provide an "old_name" field in `file_renamed_alert`
// we will rely on this workaround to remove empty leftover folders
QHash<LTFileIndex, QVector<QString>> m_oldPath;
bool m_useAutoTMM;
// Persistent data
QString m_name;
QString m_savePath;
QString m_category;
QSet<QString> m_tags;
bool m_hasSeedStatus;
qreal m_ratioLimit;
int m_seedingTimeLimit;
bool m_tempPathDisabled;
bool m_fastresumeDataRejected;
bool m_hasMissingFiles;
bool m_hasRootFolder;
bool m_needsToSetFirstLastPiecePriority;
QHash<QString, TrackerInfo> m_trackerInfos;
bool m_unchecked = false;
void toggleSequentialDownload();
void toggleFirstLastPiecePriority();
};
}
Q_DECLARE_METATYPE(BitTorrent::TorrentState)
#endif // BITTORRENT_TORRENTHANDLE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,338 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* 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 <functional>
#include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>
#include <QDateTime>
#include <QHash>
#include <QObject>
#include <QQueue>
#include <QSet>
#include <QString>
#include <QVector>
#include "private/speedmonitor.h"
#include "infohash.h"
#include "torrenthandle.h"
#include "torrentinfo.h"
namespace BitTorrent
{
class Session;
struct AddTorrentParams;
struct CreateTorrentParams
{
CreateTorrentParams() = default;
explicit CreateTorrentParams(const AddTorrentParams &params);
// for both new and restored torrents
QString name;
QString category;
QSet<QString> tags;
QString savePath;
int uploadLimit = -1;
int downloadLimit = -1;
bool disableTempPath = false;
bool sequential = false;
bool firstLastPiecePriority = false;
bool hasSeedStatus = false;
bool skipChecking = false;
bool hasRootFolder = true;
bool forced = false;
bool paused = false;
bool restored = false; // is existing torrent job?
// for new torrents
QVector<DownloadPriority> filePriorities;
QDateTime addedTime;
// for restored torrents
qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO;
int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME;
};
enum class MoveStorageMode
{
KeepExistingFiles,
Overwrite
};
class TorrentHandleImpl final : public QObject, public TorrentHandle
{
Q_DISABLE_COPY(TorrentHandleImpl)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandleImpl)
public:
TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle,
const CreateTorrentParams &params);
~TorrentHandleImpl() override;
bool isValid() const;
InfoHash hash() const override;
QString name() const override;
QDateTime creationDate() const override;
QString creator() const override;
QString comment() const override;
bool isPrivate() const override;
qlonglong totalSize() const override;
qlonglong wantedSize() const override;
qlonglong completedSize() const override;
qlonglong incompletedSize() const override;
qlonglong pieceLength() const override;
qlonglong wastedSize() const override;
QString currentTracker() const override;
QString savePath(bool actual = false) const override;
QString rootPath(bool actual = false) const override;
QString contentPath(bool actual = false) const override;
bool useTempPath() const override;
bool isAutoTMMEnabled() const override;
void setAutoTMMEnabled(bool enabled) override;
QString category() const override;
bool belongsToCategory(const QString &category) const override;
bool setCategory(const QString &category) override;
QSet<QString> tags() const override;
bool hasTag(const QString &tag) const override;
bool addTag(const QString &tag) override;
bool removeTag(const QString &tag) override;
void removeAllTags() override;
bool hasRootFolder() const override;
int filesCount() const override;
int piecesCount() const override;
int piecesHave() const override;
qreal progress() const override;
QDateTime addedTime() const override;
qreal ratioLimit() const override;
int seedingTimeLimit() const override;
QString filePath(int index) const override;
QString fileName(int index) const override;
qlonglong fileSize(int index) const override;
QStringList absoluteFilePaths() const override;
QStringList absoluteFilePathsUnwanted() const override;
QVector<DownloadPriority> filePriorities() const override;
TorrentInfo info() const override;
bool isSeed() const override;
bool isPaused() const override;
bool isResumed() const override;
bool isQueued() const override;
bool isForced() const override;
bool isChecking() const override;
bool isDownloading() const override;
bool isUploading() const override;
bool isCompleted() const override;
bool isActive() const override;
bool isInactive() const override;
bool isErrored() const override;
bool isSequentialDownload() const override;
bool hasFirstLastPiecePriority() const override;
TorrentState state() const override;
bool hasMetadata() const override;
bool hasMissingFiles() const override;
bool hasError() const override;
bool hasFilteredPieces() const override;
int queuePosition() const override;
QVector<TrackerEntry> trackers() const override;
QHash<QString, TrackerInfo> trackerInfos() const override;
QVector<QUrl> urlSeeds() const override;
QString error() const override;
qlonglong totalDownload() const override;
qlonglong totalUpload() const override;
qlonglong activeTime() const override;
qlonglong finishedTime() const override;
qlonglong seedingTime() const override;
qlonglong eta() const override;
QVector<qreal> filesProgress() const override;
int seedsCount() const override;
int peersCount() const override;
int leechsCount() const override;
int totalSeedsCount() const override;
int totalPeersCount() const override;
int totalLeechersCount() const override;
int completeCount() const override;
int incompleteCount() const override;
QDateTime lastSeenComplete() const override;
QDateTime completedTime() const override;
qlonglong timeSinceUpload() const override;
qlonglong timeSinceDownload() const override;
qlonglong timeSinceActivity() const override;
int downloadLimit() const override;
int uploadLimit() const override;
bool superSeeding() const override;
QVector<PeerInfo> peers() const override;
QBitArray pieces() const override;
QBitArray downloadingPieces() const override;
QVector<int> pieceAvailability() const override;
qreal distributedCopies() const override;
qreal maxRatio() const override;
int maxSeedingTime() const override;
qreal realRatio() const override;
int uploadPayloadRate() const override;
int downloadPayloadRate() const override;
qlonglong totalPayloadUpload() const override;
qlonglong totalPayloadDownload() const override;
int connectionsCount() const override;
int connectionsLimit() const override;
qlonglong nextAnnounce() const override;
QVector<qreal> availableFileFractions() const override;
void setName(const QString &name) override;
void setSequentialDownload(bool enable) override;
void setFirstLastPiecePriority(bool enabled) override;
void pause() override;
void resume(bool forced = false) override;
void move(QString path) override;
void forceReannounce(int index = -1) override;
void forceDHTAnnounce() override;
void forceRecheck() override;
void renameFile(int index, const QString &name) override;
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
void setRatioLimit(qreal limit) override;
void setSeedingTimeLimit(int limit) override;
void setUploadLimit(int limit) override;
void setDownloadLimit(int limit) override;
void setSuperSeeding(bool enable) override;
void flushCache() const override;
void addTrackers(const QVector<TrackerEntry> &trackers) override;
void replaceTrackers(const QVector<TrackerEntry> &trackers) override;
void addUrlSeeds(const QVector<QUrl> &urlSeeds) override;
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
bool connectPeer(const PeerAddress &peerAddress) override;
QString createMagnetURI() const override;
bool needSaveResumeData() const;
// Session interface
lt::torrent_handle nativeHandle() const;
void handleAlert(const lt::alert *a);
void handleStateUpdate(const lt::torrent_status &nativeStatus);
void handleTempPathChanged();
void handleCategorySavePathChanged();
void handleAppendExtensionToggled();
void saveResumeData();
void handleStorageMoved(const QString &newPath, const QString &errorMessage);
private:
typedef std::function<void ()> EventTrigger;
#if (LIBTORRENT_VERSION_NUM < 10200)
using LTFileIndex = int;
#else
using LTFileIndex = lt::file_index_t;
#endif
void updateStatus();
void updateStatus(const lt::torrent_status &nativeStatus);
void updateState();
void updateTorrentInfo();
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
void handleFileCompletedAlert(const lt::file_completed_alert *p);
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p);
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p);
void handlePerformanceAlert(const lt::performance_alert *p) const;
void handleSaveResumeDataAlert(const lt::save_resume_data_alert *p);
void handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p);
void handleTorrentCheckedAlert(const lt::torrent_checked_alert *p);
void handleTorrentFinishedAlert(const lt::torrent_finished_alert *p);
void handleTorrentPausedAlert(const lt::torrent_paused_alert *p);
void handleTorrentResumedAlert(const lt::torrent_resumed_alert *p);
void handleTrackerErrorAlert(const lt::tracker_error_alert *p);
void handleTrackerReplyAlert(const lt::tracker_reply_alert *p);
void handleTrackerWarningAlert(const lt::tracker_warning_alert *p);
void resume_impl(bool forced);
bool isMoveInProgress() const;
QString actualStorageLocation() const;
bool isAutoManaged() const;
void setAutoManaged(bool enable);
void adjustActualSavePath();
void adjustActualSavePath_impl();
void move_impl(QString path, MoveStorageMode mode);
void moveStorage(const QString &newPath, MoveStorageMode mode);
void manageIncompleteFiles();
void setFirstLastPiecePriorityImpl(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
Session *const m_session;
lt::torrent_handle m_nativeHandle;
lt::torrent_status m_nativeStatus;
TorrentState m_state = TorrentState::Unknown;
TorrentInfo m_torrentInfo;
SpeedMonitor m_speedMonitor;
InfoHash m_hash;
// m_moveFinishedTriggers is activated only when the following conditions are met:
// all file rename jobs complete, all file move jobs complete
QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount = 0;
bool m_storageIsMoving = false;
// Until libtorrent provide an "old_name" field in `file_renamed_alert`
// we will rely on this workaround to remove empty leftover folders
QHash<LTFileIndex, QVector<QString>> m_oldPath;
QHash<QString, TrackerInfo> m_trackerInfos;
// Persistent data
QString m_name;
QString m_savePath;
QString m_category;
QSet<QString> m_tags;
qreal m_ratioLimit;
int m_seedingTimeLimit;
bool m_hasSeedStatus;
bool m_tempPathDisabled;
bool m_fastresumeDataRejected = false;
bool m_hasMissingFiles = false;
bool m_hasRootFolder;
bool m_needsToSetFirstLastPiecePriority = false;
bool m_useAutoTMM;
bool m_unchecked = false;
};
}

View File

@@ -47,6 +47,7 @@
#include "base/exceptions.h"
#include "base/global.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
#include "base/utils/misc.h"
#include "infohash.h"
#include "trackerentry.h"
@@ -166,12 +167,12 @@ void TorrentInfo::saveToFile(const QString &path) const
#endif
const lt::entry torrentEntry = torrentCreator.generate();
QByteArray out;
out.reserve(1024 * 1024); // most torrent file sizes are under 1 MB
lt::bencode(std::back_inserter(out), torrentEntry);
QFile torrentFile {path};
if (!torrentFile.open(QIODevice::WriteOnly))
throw RuntimeError {torrentFile.errorString()};
QFile torrentFile{path};
if (!torrentFile.open(QIODevice::WriteOnly) || (torrentFile.write(out) != out.size()))
lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry);
if (torrentFile.error() != QFileDevice::NoError)
throw RuntimeError {torrentFile.errorString()};
}

View File

@@ -70,7 +70,7 @@ namespace BitTorrent
// *Basic* Bittorrent tracker implementation
// [BEP-3] The BitTorrent Protocol Specification
// also see: https://wiki.theory.org/index.php/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol
class Tracker : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder
class Tracker final : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder
{
Q_OBJECT
Q_DISABLE_COPY(Tracker)

View File

@@ -41,7 +41,7 @@ namespace Http
class IRequestHandler;
class Connection;
class Server : public QTcpServer
class Server final : public QTcpServer
{
Q_OBJECT
Q_DISABLE_COPY(Server)

View File

@@ -72,7 +72,7 @@ void Logger::freeInstance()
void Logger::addMessage(const QString &message, const Log::MsgType &type)
{
QWriteLocker locker(&m_lock);
const Log::Msg msg = {m_msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped()};
const Log::Msg msg = {m_msgCounter++, type, QDateTime::currentMSecsSinceEpoch(), message};
m_messages.push_back(msg);
locker.unlock();
@@ -82,7 +82,7 @@ void Logger::addMessage(const QString &message, const Log::MsgType &type)
void Logger::addPeer(const QString &ip, const bool blocked, const QString &reason)
{
QWriteLocker locker(&m_lock);
const Log::Peer msg = {m_peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped()};
const Log::Peer msg = {m_peerCounter++, blocked, QDateTime::currentMSecsSinceEpoch(), ip, reason};
m_peers.push_back(msg);
locker.unlock();

View File

@@ -53,17 +53,17 @@ namespace Log
struct Msg
{
int id;
qint64 timestamp;
MsgType type;
qint64 timestamp;
QString message;
};
struct Peer
{
int id;
bool blocked;
qint64 timestamp;
QString ip;
bool blocked;
QString reason;
};
}

View File

@@ -52,7 +52,7 @@ namespace
// Disguise as Firefox to avoid web server banning
const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0";
class NetworkCookieJar : public QNetworkCookieJar
class NetworkCookieJar final : public QNetworkCookieJar
{
public:
explicit NetworkCookieJar(QObject *parent = nullptr)

View File

@@ -36,7 +36,7 @@
class QObject;
class QUrl;
class DownloadHandlerImpl : public Net::DownloadHandler
class DownloadHandlerImpl final : public Net::DownloadHandler
{
Q_OBJECT
Q_DISABLE_COPY(DownloadHandlerImpl)

View File

@@ -63,7 +63,7 @@ namespace Private
};
/// Default implementation. Takes paths from system
class DefaultProfile : public Profile
class DefaultProfile final : public Profile
{
public:
explicit DefaultProfile(const QString &configurationName);
@@ -86,7 +86,7 @@ namespace Private
};
/// Custom tree: creates directories under the specified root directory
class CustomProfile : public Profile
class CustomProfile final : public Profile
{
public:
CustomProfile(const QString &rootPath, const QString &configurationName);
@@ -114,14 +114,14 @@ namespace Private
virtual ~PathConverter() = default;
};
class NoConvertConverter : public PathConverter
class NoConvertConverter final : public PathConverter
{
public:
QString toPortablePath(const QString &path) const override;
QString fromPortablePath(const QString &portablePath) const override;
};
class Converter : public PathConverter
class Converter final : public PathConverter
{
public:
explicit Converter(const QString &basePath);

View File

@@ -44,7 +44,7 @@
namespace
{
class XmlStreamEntityResolver : public QXmlStreamEntityResolver
class XmlStreamEntityResolver final : public QXmlStreamEntityResolver
{
public:
QString resolveUndeclaredEntity(const QString &name) override

View File

@@ -36,7 +36,7 @@ class QStringList;
class FileSystemWatcher;
class ScanFoldersModel : public QAbstractListModel
class ScanFoldersModel final : public QAbstractListModel
{
Q_OBJECT
Q_DISABLE_COPY(ScanFoldersModel)

View File

@@ -28,6 +28,7 @@
#include "torrentfilter.h"
#include "bittorrent/infohash.h"
#include "bittorrent/torrenthandle.h"
const QString TorrentFilter::AnyCategory;

View File

@@ -36,58 +36,60 @@
// Because of the poor handling of UTF-8 characters in MSVC (emits warning C4819),
// we put all problematic UTF-8 chars/strings in this file.
// See issue #3059 for more details (https://github.com/qbittorrent/qBittorrent/issues/3059).
const char C_INFINITY[] = "";
const char C_NON_BREAKING_SPACE[] = " ";
const char C_COPYRIGHT[] = "©";
const char C_INFINITY[] = "";
const char C_NON_BREAKING_SPACE[] = " ";
const char C_THIN_SPACE[] = "";
const char C_UTP[] = "μTP";
const char C_LOCALE_ARABIC[] = "عربي";
const char C_LOCALE_ARMENIAN[] = "Հայերեն";
const char C_LOCALE_BASQUE[] = "Euskara";
const char C_LOCALE_BULGARIAN[] = "Български";
const char C_LOCALE_BYELORUSSIAN[] = "Беларуская";
const char C_LOCALE_CATALAN[] = "Català";
const char C_LOCALE_CHINESE_SIMPLIFIED[] = "简体中文";
const char C_LOCALE_CHINESE_TRADITIONAL_HK[] = "香港正體字";
const char C_LOCALE_CHINESE_TRADITIONAL_TW[] = "正體中文";
const char C_LOCALE_CROATIAN[] = "Hrvatski";
const char C_LOCALE_CZECH[] = "Čeština";
const char C_LOCALE_DANISH[] = "Dansk";
const char C_LOCALE_DUTCH[] = "Nederlands";
const char C_LOCALE_ENGLISH[] = "English";
const char C_LOCALE_ENGLISH_AUSTRALIA[] = "English(Australia)";
const char C_LOCALE_ENGLISH_UNITEDKINGDOM[] = "English(United Kingdom)";
const char C_LOCALE_ESPERANTO[] = "Esperanto";
const char C_LOCALE_FINNISH[] = "Suomi";
const char C_LOCALE_FRENCH[] = "Français";
const char C_LOCALE_GALICIAN[] = "Galego";
const char C_LOCALE_GEORGIAN[] = "ქართული";
const char C_LOCALE_GERMAN[] = "Deutsch";
const char C_LOCALE_GREEK[] = "Ελληνικά";
const char C_LOCALE_HEBREW[] = "עברית";
const char C_LOCALE_HINDI[] = "हिन्दी, हिंदी";
const char C_LOCALE_HUNGARIAN[] = "Magyar";
const char C_LOCALE_ICELANDIC[] = "Íslenska";
const char C_LOCALE_INDONESIAN[] = "Bahasa Indonesia";
const char C_LOCALE_ITALIAN[] = "Italiano";
const char C_LOCALE_DUTCH[] = "Nederlands";
const char C_LOCALE_SPANISH[] = "Español";
const char C_LOCALE_CATALAN[] = "Català";
const char C_LOCALE_GALICIAN[] = "Galego";
const char C_LOCALE_OCCITAN[] = "lenga d'òc";
const char C_LOCALE_PORTUGUESE[] = "Português";
const char C_LOCALE_PORTUGUESE_BRAZIL[] = "Português brasileiro";
const char C_LOCALE_POLISH[] = "Polski";
const char C_LOCALE_JAPANESE[] = "日本語";
const char C_LOCALE_KOREAN[] = "한글";
const char C_LOCALE_LATVIAN[] = "latviešu valoda";
const char C_LOCALE_LITHUANIAN[] = "Lietuvių";
const char C_LOCALE_MALAY[] = "بهاس ملايو";
const char C_LOCALE_CZECH[] = "Čeština";
const char C_LOCALE_NORWEGIAN[] = "Norsk";
const char C_LOCALE_OCCITAN[] = "lenga d'òc";
const char C_LOCALE_POLISH[] = "Polski";
const char C_LOCALE_PORTUGUESE[] = "Português";
const char C_LOCALE_PORTUGUESE_BRAZIL[] = "Português brasileiro";
const char C_LOCALE_ROMANIAN[] = "Română";
const char C_LOCALE_RUSSIAN[] = "Русский";
const char C_LOCALE_SERBIAN[] = "Српски";
const char C_LOCALE_SLOVAK[] = "Slovenčina";
const char C_LOCALE_SLOVENIAN[] = "Slovenščina";
const char C_LOCALE_SERBIAN[] = "Српски";
const char C_LOCALE_CROATIAN[] = "Hrvatski";
const char C_LOCALE_ARMENIAN[] = "Հայերեն";
const char C_LOCALE_ROMANIAN[] = "Română";
const char C_LOCALE_TURKISH[] = "Türkçe";
const char C_LOCALE_GREEK[] = "Ελληνικά";
const char C_LOCALE_SPANISH[] = "Español";
const char C_LOCALE_SWEDISH[] = "Svenska";
const char C_LOCALE_FINNISH[] = "Suomi";
const char C_LOCALE_NORWEGIAN[] = "Norsk";
const char C_LOCALE_DANISH[] = "Dansk";
const char C_LOCALE_BULGARIAN[] = "Български";
const char C_LOCALE_TURKISH[] = "Türkçe";
const char C_LOCALE_UKRAINIAN[] = "Українська";
const char C_LOCALE_UZBEK[] = "أۇزبېك‎";
const char C_LOCALE_RUSSIAN[] = "Русский";
const char C_LOCALE_JAPANESE[] = "日本語";
const char C_LOCALE_HEBREW[] = "עברית";
const char C_LOCALE_HINDI[] = "हिन्दी, हिंदी";
const char C_LOCALE_ARABIC[] = "عربي";
const char C_LOCALE_GEORGIAN[] = "ქართული";
const char C_LOCALE_BYELORUSSIAN[] = "Беларуская";
const char C_LOCALE_BASQUE[] = "Euskara";
const char C_LOCALE_VIETNAMESE[] = "tiếng Việt";
const char C_LOCALE_CHINESE_TRADITIONAL_TW[] = "正體中文";
const char C_LOCALE_CHINESE_TRADITIONAL_HK[] = "香港正體字";
const char C_LOCALE_CHINESE_SIMPLIFIED[] = "简体中文";
const char C_LOCALE_KOREAN[] = "한글";

View File

@@ -190,9 +190,17 @@ namespace
path = getRegValue(hkInstallPath);
::RegCloseKey(hkInstallPath);
if (!path.isEmpty() && QDir(path).exists("python.exe")) {
found = true;
path = QDir(path).filePath("python.exe");
if (!path.isEmpty()) {
const QDir baseDir {path};
if (baseDir.exists("python3.exe")) {
found = true;
path = baseDir.filePath("python3.exe");
}
else if (baseDir.exists("python.exe")) {
found = true;
path = baseDir.filePath("python.exe");
}
}
}
}
@@ -223,9 +231,13 @@ namespace
// Fallback: Detect python from default locations
const QFileInfoList dirs = QDir("C:/").entryInfoList({"Python*"}, QDir::Dirs, (QDir::Name | QDir::Reversed));
for (const QFileInfo &info : dirs) {
const QString path {info.absolutePath() + "/python.exe"};
if (QFile::exists(path))
return path;
const QString py3Path {info.absolutePath() + "/python3.exe"};
if (QFile::exists(py3Path))
return py3Path;
const QString pyPath {info.absolutePath() + "/python.exe"};
if (QFile::exists(pyPath))
return pyPath;
}
return {};
@@ -247,14 +259,9 @@ PythonInfo Utils::ForeignApps::pythonInfo()
{
static PythonInfo pyInfo;
if (!pyInfo.isValid()) {
#if defined(Q_OS_UNIX)
// On Unix-Like systems python3 should always exist
// https://www.python.org/dev/peps/pep-0394/
if (testPythonInstallation("python3", pyInfo))
return pyInfo;
#endif
// Look for "python" in Windows and in UNIX if "python3" is
// not detected.
if (testPythonInstallation("python", pyInfo))
return pyInfo;

View File

@@ -28,6 +28,7 @@
#include "fs.h"
#include <cerrno>
#include <cstring>
#if defined(Q_OS_WIN)

75
src/base/utils/io.cpp Normal file
View File

@@ -0,0 +1,75 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 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.
*/
#include "io.h"
#include <QByteArray>
#include <QFileDevice>
Utils::IO::FileDeviceOutputIterator::FileDeviceOutputIterator(QFileDevice &device, const int bufferSize)
: m_device {&device}
, m_buffer {std::make_shared<QByteArray>()}
, m_bufferSize {bufferSize}
{
m_buffer->reserve(bufferSize);
}
Utils::IO::FileDeviceOutputIterator::~FileDeviceOutputIterator()
{
if (m_buffer.use_count() == 1) {
if (m_device->error() == QFileDevice::NoError)
m_device->write(*m_buffer);
m_buffer->clear();
}
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator=(const char c)
{
m_buffer->append(c);
if (m_buffer->size() >= m_bufferSize) {
if (m_device->error() == QFileDevice::NoError)
m_device->write(*m_buffer);
m_buffer->clear();
}
return *this;
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator*()
{
return *this;
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator++()
{
return *this;
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator++(int)
{
return *this;
}

63
src/base/utils/io.h Normal file
View File

@@ -0,0 +1,63 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 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.
*/
#pragma once
#include <iterator>
#include <memory>
class QByteArray;
class QFileDevice;
namespace Utils
{
namespace IO
{
// A wrapper class that satisfy LegacyOutputIterator requirement
class FileDeviceOutputIterator
: public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
explicit FileDeviceOutputIterator(QFileDevice &device, const int bufferSize = (4 * 1024));
FileDeviceOutputIterator(const FileDeviceOutputIterator &other) = default;
~FileDeviceOutputIterator();
// mimic std::ostream_iterator behavior
FileDeviceOutputIterator &operator=(char c);
// TODO: make these `constexpr` in C++17
FileDeviceOutputIterator &operator*();
FileDeviceOutputIterator &operator++();
FileDeviceOutputIterator &operator++(int);
private:
QFileDevice *m_device;
std::shared_ptr<QByteArray> m_buffer;
int m_bufferSize;
};
}
}

View File

@@ -28,7 +28,9 @@ fspathedit.h
hidabletabwidget.h
ipsubnetwhitelistoptionsdialog.h
lineedit.h
loglistwidget.h
log/logfiltermodel.h
log/loglistview.h
log/logmodel.h
mainwindow.h
optionsdialog.h
previewlistdelegate.h
@@ -81,7 +83,9 @@ fspathedit.cpp
hidabletabwidget.cpp
ipsubnetwhitelistoptionsdialog.cpp
lineedit.cpp
loglistwidget.cpp
log/logfiltermodel.cpp
log/loglistview.cpp
log/logmodel.cpp
mainwindow.cpp
optionsdialog.cpp
previewlistdelegate.cpp

View File

@@ -57,7 +57,7 @@ class PropListDelegate;
class TorrentContentFilterModel;
class TorrentFileGuard;
class AddNewTorrentDialog : public QDialog
class AddNewTorrentDialog final : public QDialog
{
Q_OBJECT
Q_DISABLE_COPY(AddNewTorrentDialog)

View File

@@ -114,7 +114,6 @@ enum AdvSettingsRows
// seeding
CHOKING_ALGORITHM,
SEED_CHOKING_ALGORITHM,
SUPER_SEEDING,
// tracker
ANNOUNCE_ALL_TRACKERS,
ANNOUNCE_ALL_TIERS,
@@ -223,8 +222,6 @@ void AdvancedSettings::saveAdvancedSettings()
// Peer resolution
pref->resolvePeerCountries(m_checkBoxResolveCountries.isChecked());
pref->resolvePeerHostNames(m_checkBoxResolveHosts.isChecked());
// Super seeding
session->setSuperSeedingEnabled(m_checkBoxSuperSeeding.isChecked());
// Network interface
if (m_comboBoxInterface.currentIndex() == 0) {
// All interfaces (default)
@@ -516,10 +513,6 @@ void AdvancedSettings::loadAdvancedSettings()
// Resolve peer hosts
m_checkBoxResolveHosts.setChecked(pref->resolvePeerHostNames());
addRow(RESOLVE_HOSTS, tr("Resolve peer host names"), &m_checkBoxResolveHosts);
// Super seeding
m_checkBoxSuperSeeding.setChecked(session->isSuperSeedingEnabled());
addRow(SUPER_SEEDING, (tr("Strict super seeding") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#strict_super_seeding", "(?)"))
, &m_checkBoxSuperSeeding);
// Network interface
m_comboBoxInterface.addItem(tr("Any interface", "i.e. Any network interface"));
const QString currentInterface = session->networkInterface();

View File

@@ -61,7 +61,7 @@ private:
m_spinBoxSaveResumeDataInterval, m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration,
m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxCacheTTL, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark,
m_spinBoxSendBufferWatermarkFactor, m_spinBoxSocketBacklogSize, m_spinBoxStopTrackerTimeout, m_spinBoxSavePathHistoryLength;
QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts, m_checkBoxSuperSeeding,
QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts,
m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus,
m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers,
m_checkBoxMultiConnectionsPerIp, m_checkBoxPieceExtentAffinity, m_checkBoxSuggestMode, m_checkBoxCoalesceRW, m_checkBoxSpeedWidgetEnabled;

View File

@@ -39,7 +39,7 @@ namespace Ui
class AutoExpandableDialog;
}
class AutoExpandableDialog : public QDialog
class AutoExpandableDialog final : public QDialog
{
Q_OBJECT

View File

@@ -40,7 +40,7 @@ namespace BitTorrent
class TorrentHandle;
}
class CategoryFilterModel : public QAbstractItemModel
class CategoryFilterModel final : public QAbstractItemModel
{
Q_OBJECT

View File

@@ -33,7 +33,7 @@
class QString;
class CategoryFilterProxyModel : public QSortFilterProxyModel
class CategoryFilterProxyModel final : public QSortFilterProxyModel
{
public:
explicit CategoryFilterProxyModel(QObject *parent = nullptr);

View File

@@ -30,7 +30,7 @@
#include <QTreeView>
class CategoryFilterWidget : public QTreeView
class CategoryFilterWidget final : public QTreeView
{
Q_OBJECT
Q_DISABLE_COPY(CategoryFilterWidget)

View File

@@ -38,7 +38,7 @@ namespace Ui
class CookiesDialog;
}
class CookiesDialog : public QDialog
class CookiesDialog final : public QDialog
{
Q_OBJECT

View File

@@ -33,7 +33,7 @@
#include <QList>
#include <QNetworkCookie>
class CookiesModel : public QAbstractItemModel
class CookiesModel final : public QAbstractItemModel
{
Q_OBJECT

View File

@@ -29,35 +29,48 @@
#include "executionlogwidget.h"
#include <QDateTime>
#include <QMenu>
#include <QPalette>
#include "base/global.h"
#include "loglistwidget.h"
#include "log/logfiltermodel.h"
#include "log/loglistview.h"
#include "log/logmodel.h"
#include "ui_executionlogwidget.h"
#include "uithememanager.h"
ExecutionLogWidget::ExecutionLogWidget(QWidget *parent, const Log::MsgTypes &types)
ExecutionLogWidget::ExecutionLogWidget(const Log::MsgTypes types, QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::ExecutionLogWidget)
, m_msgList(new LogListWidget(MAX_LOG_MESSAGES, types, this))
, m_peerList(new LogListWidget(MAX_LOG_MESSAGES, Log::ALL, this))
, m_messageFilterModel(new LogFilterModel(types, this))
{
m_ui->setupUi(this);
LogMessageModel *messageModel = new LogMessageModel(this);
m_messageFilterModel->setSourceModel(messageModel);
LogListView *messageView = new LogListView(this);
messageView->setModel(m_messageFilterModel);
messageView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(messageView, &LogListView::customContextMenuRequested, this, [this, messageView, messageModel](const QPoint &pos)
{
displayContextMenu(pos, messageView, messageModel);
});
LogPeerModel *peerModel = new LogPeerModel(this);
LogListView *peerView = new LogListView(this);
peerView->setModel(peerModel);
peerView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(peerView, &LogListView::customContextMenuRequested, this, [this, peerView, peerModel](const QPoint &pos)
{
displayContextMenu(pos, peerView, peerModel);
});
m_ui->tabGeneral->layout()->addWidget(messageView);
m_ui->tabBan->layout()->addWidget(peerView);
#ifndef Q_OS_MACOS
m_ui->tabConsole->setTabIcon(0, UIThemeManager::instance()->getIcon("view-calendar-journal"));
m_ui->tabConsole->setTabIcon(1, UIThemeManager::instance()->getIcon("view-filter"));
#endif
m_ui->tabGeneral->layout()->addWidget(m_msgList);
m_ui->tabBan->layout()->addWidget(m_peerList);
const Logger *const logger = Logger::instance();
for (const Log::Msg &msg : asConst(logger->getMessages()))
addLogMessage(msg);
for (const Log::Peer &peer : asConst(logger->getPeers()))
addPeerMessage(peer);
connect(logger, &Logger::newLogMessage, this, &ExecutionLogWidget::addLogMessage);
connect(logger, &Logger::newLogPeer, this, &ExecutionLogWidget::addPeerMessage);
}
ExecutionLogWidget::~ExecutionLogWidget()
@@ -65,42 +78,24 @@ ExecutionLogWidget::~ExecutionLogWidget()
delete m_ui;
}
void ExecutionLogWidget::showMsgTypes(const Log::MsgTypes &types)
void ExecutionLogWidget::setMessageTypes(const Log::MsgTypes types)
{
m_msgList->showMsgTypes(types);
m_messageFilterModel->setMessageTypes(types);
}
void ExecutionLogWidget::addLogMessage(const Log::Msg &msg)
void ExecutionLogWidget::displayContextMenu(const QPoint &pos, const LogListView *view, const BaseLogModel *model) const
{
QString colorName;
switch (msg.type) {
case Log::INFO:
colorName = QLatin1String("blue");
break;
case Log::WARNING:
colorName = QLatin1String("orange");
break;
case Log::CRITICAL:
colorName = QLatin1String("red");
break;
default:
colorName = QApplication::palette().color(QPalette::WindowText).name();
QMenu *menu = new QMenu;
menu->setAttribute(Qt::WA_DeleteOnClose);
// only show copy action if any of the row is selected
if (view->currentIndex().isValid()) {
const QAction *copyAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-copy"), tr("Copy"));
connect(copyAct, &QAction::triggered, view, &LogListView::copySelection);
}
const QDateTime time = QDateTime::fromMSecsSinceEpoch(msg.timestamp);
const QString text = QString::fromLatin1("<font color='grey'>%1</font> - <font color='%2'>%3</font>")
.arg(time.toString(Qt::SystemLocaleShortDate), colorName, msg.message);
m_msgList->appendLine(text, msg.type);
}
const QAction *clearAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-clear"), tr("Clear"));
connect(clearAct, &QAction::triggered, model, &BaseLogModel::reset);
void ExecutionLogWidget::addPeerMessage(const Log::Peer &peer)
{
const QDateTime time = QDateTime::fromMSecsSinceEpoch(peer.timestamp);
const QString msg = QString::fromLatin1("<font color='grey'>%1</font> - <font color='red'>%2</font>")
.arg(time.toString(Qt::SystemLocaleShortDate), peer.ip);
const QString text = peer.blocked
? tr("%1 was blocked %2", "0.0.0.0 was blocked due to reason").arg(msg, peer.reason)
: tr("%1 was banned", "0.0.0.0 was banned").arg(msg);
m_peerList->appendLine(text, Log::NORMAL);
menu->popup(view->mapToGlobal(pos));
}

View File

@@ -30,33 +30,33 @@
#define EXECUTIONLOGWIDGET_H
#include <QWidget>
#include "base/logger.h"
class LogListWidget;
#include "base/logger.h"
namespace Ui
{
class ExecutionLogWidget;
}
class BaseLogModel;
class LogFilterModel;
class LogListView;
class ExecutionLogWidget : public QWidget
{
Q_OBJECT
public:
ExecutionLogWidget(QWidget *parent, const Log::MsgTypes &types);
void showMsgTypes(const Log::MsgTypes &types);
ExecutionLogWidget(Log::MsgTypes types, QWidget *parent);
~ExecutionLogWidget();
private slots:
void addLogMessage(const Log::Msg &msg);
void addPeerMessage(const Log::Peer &peer);
void setMessageTypes(Log::MsgTypes types);
private:
Ui::ExecutionLogWidget *m_ui;
void displayContextMenu(const QPoint &pos, const LogListView *view, const BaseLogModel *model) const;
LogListWidget *m_msgList;
LogListWidget *m_peerList;
Ui::ExecutionLogWidget *m_ui;
LogFilterModel *m_messageFilterModel;
};
#endif // EXECUTIONLOGWIDGET_H

View File

@@ -108,7 +108,7 @@ private:
};
/// Widget which uses QLineEdit for path editing
class FileSystemPathLineEdit : public FileSystemPathEdit
class FileSystemPathLineEdit final : public FileSystemPathEdit
{
using base = FileSystemPathEdit;
using WidgetType = Private::FileLineEdit;
@@ -124,7 +124,7 @@ private:
};
/// Widget which uses QComboBox for path editing
class FileSystemPathComboEdit : public FileSystemPathEdit
class FileSystemPathComboEdit final : public FileSystemPathEdit
{
using base = FileSystemPathEdit;
using WidgetType = Private::FileComboEdit;

View File

@@ -22,7 +22,9 @@ HEADERS += \
$$PWD/hidabletabwidget.h \
$$PWD/ipsubnetwhitelistoptionsdialog.h \
$$PWD/lineedit.h \
$$PWD/loglistwidget.h \
$$PWD/log/logfiltermodel.h \
$$PWD/log/loglistview.h \
$$PWD/log/logmodel.h \
$$PWD/mainwindow.h \
$$PWD/optionsdialog.h \
$$PWD/previewlistdelegate.h \
@@ -86,7 +88,9 @@ SOURCES += \
$$PWD/hidabletabwidget.cpp \
$$PWD/ipsubnetwhitelistoptionsdialog.cpp \
$$PWD/lineedit.cpp \
$$PWD/loglistwidget.cpp \
$$PWD/log/logfiltermodel.cpp \
$$PWD/log/loglistview.cpp \
$$PWD/log/logmodel.cpp \
$$PWD/mainwindow.cpp \
$$PWD/optionsdialog.cpp \
$$PWD/previewlistdelegate.cpp \

View File

@@ -35,7 +35,7 @@
class QPaintEvent;
#endif
class HidableTabWidget : public QTabWidget
class HidableTabWidget final : public QTabWidget
{
public:
explicit HidableTabWidget(QWidget *parent = nullptr);

View File

@@ -14,7 +14,7 @@
class QToolButton;
class LineEdit : public QLineEdit
class LineEdit final : public QLineEdit
{
Q_OBJECT

View File

@@ -0,0 +1,52 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* 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 "logfiltermodel.h"
#include "logmodel.h"
LogFilterModel::LogFilterModel(const Log::MsgTypes types, QObject *parent)
: QSortFilterProxyModel(parent)
, m_types(types)
{
}
void LogFilterModel::setMessageTypes(const Log::MsgTypes types)
{
m_types = types;
invalidateFilter();
}
bool LogFilterModel::filterAcceptsRow(const int sourceRow, const QModelIndex &sourceParent) const
{
const QAbstractItemModel *const sourceModel = this->sourceModel();
const QModelIndex index = sourceModel->index(sourceRow, 0, sourceParent);
const Log::MsgType type = static_cast<Log::MsgType>(sourceModel->data(index, BaseLogModel::TypeRole).toInt());
return m_types.testFlag(type);
}

View File

@@ -0,0 +1,48 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* 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 <QSortFilterProxyModel>
#include "base/logger.h"
class LogFilterModel final : public QSortFilterProxyModel
{
Q_OBJECT
Q_DISABLE_COPY(LogFilterModel)
public:
explicit LogFilterModel(Log::MsgTypes types = Log::ALL, QObject *parent = nullptr);
void setMessageTypes(Log::MsgTypes types);
private:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
Log::MsgTypes m_types;
};

140
src/gui/log/loglistview.cpp Normal file
View File

@@ -0,0 +1,140 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* 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 "loglistview.h"
#include <QApplication>
#include <QClipboard>
#include <QFontMetrics>
#include <QKeyEvent>
#include <QPainter>
#include <QStyle>
#include <QStyledItemDelegate>
#include "logmodel.h"
#include "uithememanager.h"
namespace
{
const QString SEPARATOR = QStringLiteral(" - ");
int horizontalAdvance(const QFontMetrics &fontMetrics, const QString &text)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
return fontMetrics.horizontalAdvance(text);
#else
return fontMetrics.width(text);
#endif
}
QString logText(const QModelIndex &index)
{
return QString::fromLatin1("%1%2%3").arg(index.data(BaseLogModel::TimeRole).toString(), SEPARATOR
, index.data(BaseLogModel::MessageRole).toString());
}
class LogItemDelegate final : public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
private:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
painter->save();
QStyledItemDelegate::paint(painter, option, index); // paints background, focus rect and selection rect
const QStyle *style = option.widget ? option.widget->style() : QApplication::style();;
const QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &option, option.widget)
.adjusted(1, 0, 0, 0); // shift 1 to avoid text being too close to focus rect
QFont font = option.font;
if (option.font.pointSize() > 0)
font.setPointSize(option.font.pointSize()); // somehow this needs to be set directly otherwise painter will use default font
painter->setFont(font);
const QPen originalPen = painter->pen();
QPen coloredPen = originalPen;
coloredPen.setColor(Qt::darkGray);
painter->setPen(coloredPen);
const QString time = index.data(BaseLogModel::TimeRole).toString();
style->drawItemText(painter, textRect, option.displayAlignment, option.palette, (option.state & QStyle::State_Enabled), time);
painter->setPen(originalPen);
const QFontMetrics fontMetrics = painter->fontMetrics(); // option.fontMetrics adds extra padding to QFontMetrics::width
const int separatorCoordinateX = horizontalAdvance(fontMetrics, time);
style->drawItemText(painter, textRect.adjusted(separatorCoordinateX, 0, 0, 0), option.displayAlignment, option.palette
, (option.state & QStyle::State_Enabled), SEPARATOR);
coloredPen.setColor(index.data(BaseLogModel::ForegroundRole).value<QColor>());
painter->setPen(coloredPen);
const int messageCoordinateX = separatorCoordinateX + horizontalAdvance(fontMetrics, SEPARATOR);
style->drawItemText(painter, textRect.adjusted(messageCoordinateX, 0, 0, 0), option.displayAlignment, option.palette
, (option.state & QStyle::State_Enabled), index.data(BaseLogModel::MessageRole).toString());
painter->restore();
}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
const QSize minimumFontPadding(4, 4);
const QSize fontSize = option.fontMetrics.size(0, logText(index)) + minimumFontPadding;
const QSize defaultSize = QStyledItemDelegate::sizeHint(option, index);
const QSize margins = (defaultSize - fontSize).expandedTo({0, 0});
return fontSize + margins;
}
};
}
LogListView::LogListView(QWidget *parent)
: QListView(parent)
{
setSelectionMode(QAbstractItemView::ExtendedSelection);
setItemDelegate(new LogItemDelegate(this));
#if defined(Q_OS_MAC)
setAttribute(Qt::WA_MacShowFocusRect, false);
#endif
}
void LogListView::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Copy))
copySelection();
else
QListView::keyPressEvent(event);
}
void LogListView::copySelection() const
{
QStringList list;
const QModelIndexList selectedIndexes = selectionModel()->selectedRows();
for (const QModelIndex &index : selectedIndexes)
list.append(logText(index));
QApplication::clipboard()->setText(list.join('\n'));
}

View File

@@ -1,6 +1,7 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,35 +27,21 @@
* exception statement from your version.
*/
#ifndef LOGLISTWIDGET_H
#define LOGLISTWIDGET_H
#pragma once
#include <QListWidget>
#include "base/logger.h"
#include <QListView>
class QKeyEvent;
class LogListWidget : public QListWidget
class LogListView final : public QListView
{
Q_OBJECT
Q_DISABLE_COPY(LogListView)
public:
// -1 is the portable way to have all the bits set
explicit LogListWidget(int maxLines, const Log::MsgTypes &types = Log::ALL, QWidget *parent = nullptr);
void showMsgTypes(const Log::MsgTypes &types);
explicit LogListView(QWidget *parent = nullptr);
public slots:
void appendLine(const QString &line, const Log::MsgType &type);
protected slots:
void copySelection();
protected:
void keyPressEvent(QKeyEvent *event) override;
void copySelection() const;
private:
const int m_maxLines;
Log::MsgTypes m_types;
void keyPressEvent(QKeyEvent *event) override;
};
#endif // LOGLISTWIDGET_H

188
src/gui/log/logmodel.cpp Normal file
View File

@@ -0,0 +1,188 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* 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 "logmodel.h"
#include <QApplication>
#include <QDateTime>
#include <QColor>
#include <QPalette>
#include "base/global.h"
namespace
{
const int MAX_VISIBLE_MESSAGES = 20000;
}
BaseLogModel::Message::Message(const QString &time, const QString &message, const QColor &foreground, const Log::MsgType type)
: m_time(time)
, m_message(message)
, m_foreground(foreground)
, m_type(type)
{
}
QVariant BaseLogModel::Message::time() const
{
return m_time;
}
QVariant BaseLogModel::Message::message() const
{
return m_message;
}
QVariant BaseLogModel::Message::foreground() const
{
return m_foreground;
}
QVariant BaseLogModel::Message::type() const
{
return m_type;
}
BaseLogModel::BaseLogModel(QObject *parent)
: QAbstractListModel(parent)
, m_messages(MAX_VISIBLE_MESSAGES)
{
}
int BaseLogModel::rowCount(const QModelIndex &) const
{
return m_messages.size();
}
int BaseLogModel::columnCount(const QModelIndex &) const
{
return 1;
}
QVariant BaseLogModel::data(const QModelIndex &index, const int role) const
{
if (!index.isValid())
return {};
const int messageIndex = index.row();
if ((messageIndex < 0) || (messageIndex >= static_cast<int>(m_messages.size())))
return {};
const Message &message = m_messages[messageIndex];
switch (role) {
case TimeRole:
return message.time();
case MessageRole:
return message.message();
case ForegroundRole:
return message.foreground();
case TypeRole:
return message.type();
default:
return {};
}
}
void BaseLogModel::addNewMessage(const BaseLogModel::Message &message)
{
// if row is inserted on filled up buffer, the size will not change
// but because of calling of beginInsertRows function we'll have ghost rows.
if (m_messages.size() == MAX_VISIBLE_MESSAGES) {
const int lastMessage = m_messages.size() - 1;
beginRemoveRows(QModelIndex(), lastMessage, lastMessage);
m_messages.pop_back();
endRemoveRows();
}
beginInsertRows(QModelIndex(), 0, 0);
m_messages.push_front(message);
endInsertRows();
}
void BaseLogModel::reset()
{
beginResetModel();
m_messages.clear();
endResetModel();
}
LogMessageModel::LogMessageModel(QObject *parent)
: BaseLogModel(parent)
{
for (const Log::Msg &msg : asConst(Logger::instance()->getMessages()))
handleNewMessage(msg);
connect(Logger::instance(), &Logger::newLogMessage, this, &LogMessageModel::handleNewMessage);
}
void LogMessageModel::handleNewMessage(const Log::Msg &message)
{
const QString time = QDateTime::fromMSecsSinceEpoch(message.timestamp).toString(Qt::SystemLocaleShortDate);
const QString messageText = message.message;
QColor foreground;
switch (message.type) {
// The RGB QColor constructor is used for performance
case Log::NORMAL:
foreground = QApplication::palette().color(QPalette::WindowText);
break;
case Log::INFO:
foreground = QColor(0, 0, 255); // blue
break;
case Log::WARNING:
foreground = QColor(255, 165, 0); // orange
break;
case Log::CRITICAL:
foreground = QColor(255, 0, 0); // red
break;
default:
Q_ASSERT(false);
break;
}
addNewMessage({time, messageText, foreground, message.type});
}
LogPeerModel::LogPeerModel(QObject *parent)
: BaseLogModel(parent)
{
for (const Log::Peer &peer : asConst(Logger::instance()->getPeers()))
handleNewMessage(peer);
connect(Logger::instance(), &Logger::newLogPeer, this, &LogPeerModel::handleNewMessage);
}
void LogPeerModel::handleNewMessage(const Log::Peer &peer)
{
const QString time = QDateTime::fromMSecsSinceEpoch(peer.timestamp).toString(Qt::SystemLocaleShortDate);
const QString message = peer.blocked
? tr("%1 was blocked due to %2", "0.0.0.0 was blocked due to reason").arg(peer.ip, peer.reason)
: tr("%1 was banned", "0.0.0.0 was banned").arg(peer.ip);
const QColor foreground = Qt::red;
addNewMessage({time, message, foreground, Log::NORMAL});
}

104
src/gui/log/logmodel.h Normal file
View File

@@ -0,0 +1,104 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* 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 <boost/circular_buffer.hpp>
#include <QAbstractListModel>
#include "base/logger.h"
class BaseLogModel : public QAbstractListModel
{
Q_DISABLE_COPY(BaseLogModel)
public:
enum MessageTypeRole
{
TimeRole = Qt::UserRole,
MessageRole,
ForegroundRole,
TypeRole
};
explicit BaseLogModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = {}) const override;
int columnCount(const QModelIndex &parent = {}) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void reset();
protected:
class Message
{
public:
Message(const QString &time, const QString &message, const QColor &foreground, Log::MsgType type);
QVariant time() const;
QVariant message() const;
QVariant foreground() const;
QVariant type() const;
private:
QVariant m_time;
QVariant m_message;
QVariant m_foreground;
QVariant m_type;
};
void addNewMessage(const Message &message);
private:
boost::circular_buffer_space_optimized<Message> m_messages;
};
class LogMessageModel : public BaseLogModel
{
Q_OBJECT
Q_DISABLE_COPY(LogMessageModel)
public:
explicit LogMessageModel(QObject *parent = nullptr);
private slots:
void handleNewMessage(const Log::Msg &message);
};
class LogPeerModel : public BaseLogModel
{
Q_OBJECT
Q_DISABLE_COPY(LogPeerModel)
public:
explicit LogPeerModel(QObject *parent = nullptr);
private slots:
void handleNewMessage(const Log::Peer &peer);
};

View File

@@ -1,108 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
*
* 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 "loglistwidget.h"
#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QLabel>
#include <QListWidgetItem>
#include <QRegularExpression>
#include "base/global.h"
#include "uithememanager.h"
LogListWidget::LogListWidget(const int maxLines, const Log::MsgTypes &types, QWidget *parent)
: QListWidget(parent)
, m_maxLines(maxLines)
, m_types(types)
{
// Allow multiple selections
setSelectionMode(QAbstractItemView::ExtendedSelection);
// Context menu
auto *copyAct = new QAction(UIThemeManager::instance()->getIcon("edit-copy"), tr("Copy"), this);
auto *clearAct = new QAction(UIThemeManager::instance()->getIcon("edit-clear"), tr("Clear"), this);
connect(copyAct, &QAction::triggered, this, &LogListWidget::copySelection);
connect(clearAct, &QAction::triggered, this, &LogListWidget::clear);
addAction(copyAct);
addAction(clearAct);
setContextMenuPolicy(Qt::ActionsContextMenu);
}
void LogListWidget::showMsgTypes(const Log::MsgTypes &types)
{
m_types = types;
for (int i = 0; i < count(); ++i) {
QListWidgetItem *tempItem = item(i);
if (!tempItem) continue;
Log::MsgType itemType = static_cast<Log::MsgType>(tempItem->data(Qt::UserRole).toInt());
setRowHidden(i, !(m_types & itemType));
}
}
void LogListWidget::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Copy))
copySelection();
else
QListWidget::keyPressEvent(event);
}
void LogListWidget::appendLine(const QString &line, const Log::MsgType &type)
{
// We need to use QLabel here to support rich text
auto *lbl = new QLabel(line);
lbl->setTextFormat(Qt::RichText);
lbl->setContentsMargins(4, 2, 4, 2);
auto *item = new QListWidgetItem;
item->setSizeHint(lbl->sizeHint());
item->setData(Qt::UserRole, type);
insertItem(0, item);
setItemWidget(item, lbl);
setRowHidden(0, !(m_types & type));
const int nbLines = count();
// Limit log size
if (nbLines > m_maxLines)
delete takeItem(nbLines - 1);
}
void LogListWidget::copySelection()
{
const QRegularExpression htmlTag("<[^>]+>");
QStringList strings;
for (QListWidgetItem *it : asConst(selectedItems()))
strings << static_cast<QLabel*>(itemWidget(it))->text().remove(htmlTag);
QApplication::clipboard()->setText(strings.join('\n'));
}

View File

@@ -28,6 +28,8 @@
#include "mainwindow.h"
#include <chrono>
#include <QCloseEvent>
#include <QDebug>
#include <QDesktopServices>
@@ -99,9 +101,6 @@
#include "programupdater.h"
#endif
#define TIME_TRAY_BALLOON 5000
#define PREVENT_SUSPEND_INTERVAL 60000
namespace
{
#define SETTINGS_KEY(name) "GUI/" name
@@ -119,6 +118,11 @@ namespace
// Misc
const QString KEY_DOWNLOAD_TRACKER_FAVICON = QStringLiteral(SETTINGS_KEY("DownloadTrackerFavicon"));
const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60};
#if !defined(Q_OS_MACOS)
const int TIME_TRAY_BALLOON = 5000;
#endif
// just a shortcut
inline SettingsStorage *settings()
{
@@ -487,7 +491,7 @@ int MainWindow::executionLogMsgTypes() const
void MainWindow::setExecutionLogMsgTypes(const int value)
{
m_executionLog->showMsgTypes(static_cast<Log::MsgTypes>(value));
m_executionLog->setMessageTypes(static_cast<Log::MsgTypes>(value));
settings()->storeValue(KEY_EXECUTIONLOG_TYPES, value);
}
@@ -776,8 +780,9 @@ void MainWindow::cleanup()
if (m_systrayCreator)
m_systrayCreator->stop();
#endif
if (m_preventTimer)
m_preventTimer->stop();
m_preventTimer->stop();
#if (defined(Q_OS_WIN) || defined(Q_OS_MACOS))
m_programUpdateTimer->stop();
#endif
@@ -1401,7 +1406,7 @@ void MainWindow::showStatusBar(bool show)
}
}
void MainWindow::loadPreferences(bool configureSession)
void MainWindow::loadPreferences(const bool configureSession)
{
Logger::instance()->addMessage(tr("Options were saved successfully."));
const Preferences *const pref = Preferences::instance();
@@ -1451,8 +1456,11 @@ void MainWindow::loadPreferences(bool configureSession)
showStatusBar(pref->isStatusbarDisplayed());
if ((pref->preventFromSuspendWhenDownloading() || pref->preventFromSuspendWhenSeeding()) && !m_preventTimer->isActive()) {
m_preventTimer->start(PREVENT_SUSPEND_INTERVAL);
if (pref->preventFromSuspendWhenDownloading() || pref->preventFromSuspendWhenSeeding()) {
if (!m_preventTimer->isActive()) {
updatePowerManagementState();
m_preventTimer->start(PREVENT_SUSPEND_INTERVAL);
}
}
else {
m_preventTimer->stop();
@@ -1866,7 +1874,7 @@ void MainWindow::on_actionExecutionLogs_triggered(bool checked)
{
if (checked) {
Q_ASSERT(!m_executionLog);
m_executionLog = new ExecutionLogWidget(m_tabs, static_cast<Log::MsgType>(executionLogMsgTypes()));
m_executionLog = new ExecutionLogWidget(static_cast<Log::MsgType>(executionLogMsgTypes()), m_tabs);
#ifdef Q_OS_MACOS
m_tabs->addTab(m_executionLog, tr("Execution Log"));
#else

View File

@@ -72,7 +72,7 @@ namespace Ui
class MainWindow;
}
class MainWindow : public QMainWindow
class MainWindow final : public QMainWindow
{
Q_OBJECT

View File

@@ -81,9 +81,76 @@ namespace
ret.append(locale.toString(date.addDays(i), "dddd"));
return ret;
}
QString languageToLocalizedString(const QLocale &locale)
{
switch (locale.language()) {
case QLocale::Arabic: return QString::fromUtf8(C_LOCALE_ARABIC);
case QLocale::Armenian: return QString::fromUtf8(C_LOCALE_ARMENIAN);
case QLocale::Basque: return QString::fromUtf8(C_LOCALE_BASQUE);
case QLocale::Bulgarian: return QString::fromUtf8(C_LOCALE_BULGARIAN);
case QLocale::Byelorussian: return QString::fromUtf8(C_LOCALE_BYELORUSSIAN);
case QLocale::Catalan: return QString::fromUtf8(C_LOCALE_CATALAN);
case QLocale::Chinese:
switch (locale.country()) {
case QLocale::China: return QString::fromUtf8(C_LOCALE_CHINESE_SIMPLIFIED);
case QLocale::HongKong: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_HK);
default: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_TW);
}
case QLocale::Croatian: return QString::fromUtf8(C_LOCALE_CROATIAN);
case QLocale::Czech: return QString::fromUtf8(C_LOCALE_CZECH);
case QLocale::Danish: return QString::fromUtf8(C_LOCALE_DANISH);
case QLocale::Dutch: return QString::fromUtf8(C_LOCALE_DUTCH);
case QLocale::English:
switch (locale.country()) {
case QLocale::Australia: return QString::fromUtf8(C_LOCALE_ENGLISH_AUSTRALIA);
case QLocale::UnitedKingdom: return QString::fromUtf8(C_LOCALE_ENGLISH_UNITEDKINGDOM);
default: return QString::fromUtf8(C_LOCALE_ENGLISH);
}
case QLocale::Finnish: return QString::fromUtf8(C_LOCALE_FINNISH);
case QLocale::French: return QString::fromUtf8(C_LOCALE_FRENCH);
case QLocale::Galician: return QString::fromUtf8(C_LOCALE_GALICIAN);
case QLocale::Georgian: return QString::fromUtf8(C_LOCALE_GEORGIAN);
case QLocale::German: return QString::fromUtf8(C_LOCALE_GERMAN);
case QLocale::Greek: return QString::fromUtf8(C_LOCALE_GREEK);
case QLocale::Hebrew: return QString::fromUtf8(C_LOCALE_HEBREW);
case QLocale::Hindi: return QString::fromUtf8(C_LOCALE_HINDI);
case QLocale::Hungarian: return QString::fromUtf8(C_LOCALE_HUNGARIAN);
case QLocale::Icelandic: return QString::fromUtf8(C_LOCALE_ICELANDIC);
case QLocale::Indonesian: return QString::fromUtf8(C_LOCALE_INDONESIAN);
case QLocale::Italian: return QString::fromUtf8(C_LOCALE_ITALIAN);
case QLocale::Japanese: return QString::fromUtf8(C_LOCALE_JAPANESE);
case QLocale::Korean: return QString::fromUtf8(C_LOCALE_KOREAN);
case QLocale::Latvian: return QString::fromUtf8(C_LOCALE_LATVIAN);
case QLocale::Lithuanian: return QString::fromUtf8(C_LOCALE_LITHUANIAN);
case QLocale::Malay: return QString::fromUtf8(C_LOCALE_MALAY);
case QLocale::Norwegian: return QString::fromUtf8(C_LOCALE_NORWEGIAN);
case QLocale::Occitan: return QString::fromUtf8(C_LOCALE_OCCITAN);
case QLocale::Polish: return QString::fromUtf8(C_LOCALE_POLISH);
case QLocale::Portuguese:
if (locale.country() == QLocale::Brazil)
return QString::fromUtf8(C_LOCALE_PORTUGUESE_BRAZIL);
return QString::fromUtf8(C_LOCALE_PORTUGUESE);
case QLocale::Romanian: return QString::fromUtf8(C_LOCALE_ROMANIAN);
case QLocale::Russian: return QString::fromUtf8(C_LOCALE_RUSSIAN);
case QLocale::Serbian: return QString::fromUtf8(C_LOCALE_SERBIAN);
case QLocale::Slovak: return QString::fromUtf8(C_LOCALE_SLOVAK);
case QLocale::Slovenian: return QString::fromUtf8(C_LOCALE_SLOVENIAN);
case QLocale::Spanish: return QString::fromUtf8(C_LOCALE_SPANISH);
case QLocale::Swedish: return QString::fromUtf8(C_LOCALE_SWEDISH);
case QLocale::Turkish: return QString::fromUtf8(C_LOCALE_TURKISH);
case QLocale::Ukrainian: return QString::fromUtf8(C_LOCALE_UKRAINIAN);
case QLocale::Uzbek: return QString::fromUtf8(C_LOCALE_UZBEK);
case QLocale::Vietnamese: return QString::fromUtf8(C_LOCALE_VIETNAMESE);
default:
const QString lang = QLocale::languageToString(locale.language());
qWarning() << "Unrecognized language name: " << lang;
return lang;
}
}
}
class WheelEventEater : public QObject
class WheelEventEater final : public QObject
{
public:
using QObject::QObject;
@@ -815,7 +882,6 @@ Net::ProxyType OptionsDialog::getProxyType() const
switch (m_ui->comboProxyType->currentIndex()) {
case 1:
return Net::ProxyType::SOCKS4;
break;
case 2:
if (isProxyAuthEnabled())
return Net::ProxyType::SOCKS5_PW;
@@ -1376,28 +1442,27 @@ void OptionsDialog::toggleComboRatioLimitAct()
m_ui->comboRatioLimitAct->setEnabled(m_ui->checkMaxRatio->isChecked() || m_ui->checkMaxSeedingMinutes->isChecked());
}
void OptionsDialog::enableProxy(int index)
void OptionsDialog::enableProxy(const int index)
{
if (index) {
if (index >= 1) { // Any proxy type is used
//enable
m_ui->lblProxyIP->setEnabled(true);
m_ui->textProxyIP->setEnabled(true);
m_ui->lblProxyPort->setEnabled(true);
m_ui->spinProxyPort->setEnabled(true);
m_ui->checkProxyPeerConnecs->setEnabled(true);
if (index > 1) {
if (index >= 2) { // SOCKS5 or HTTP
m_ui->checkProxyAuth->setEnabled(true);
m_ui->isProxyOnlyForTorrents->setEnabled(true);
}
else {
m_ui->checkProxyAuth->setEnabled(false);
m_ui->checkProxyAuth->setChecked(false);
m_ui->isProxyOnlyForTorrents->setEnabled(false);
m_ui->isProxyOnlyForTorrents->setChecked(true);
}
}
else {
//disable
else { // No proxy
// disable
m_ui->lblProxyIP->setEnabled(false);
m_ui->textProxyIP->setEnabled(false);
m_ui->lblProxyPort->setEnabled(false);
@@ -1405,7 +1470,6 @@ void OptionsDialog::enableProxy(int index)
m_ui->checkProxyPeerConnecs->setEnabled(false);
m_ui->isProxyOnlyForTorrents->setEnabled(false);
m_ui->checkProxyAuth->setEnabled(false);
m_ui->checkProxyAuth->setChecked(false);
}
}
@@ -1698,81 +1762,6 @@ void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount)
disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
}
QString OptionsDialog::languageToLocalizedString(const QLocale &locale)
{
switch (locale.language()) {
case QLocale::English: {
if (locale.country() == QLocale::Australia)
return QString::fromUtf8(C_LOCALE_ENGLISH_AUSTRALIA);
if (locale.country() == QLocale::UnitedKingdom)
return QString::fromUtf8(C_LOCALE_ENGLISH_UNITEDKINGDOM);
return QString::fromUtf8(C_LOCALE_ENGLISH);
}
case QLocale::French: return QString::fromUtf8(C_LOCALE_FRENCH);
case QLocale::German: return QString::fromUtf8(C_LOCALE_GERMAN);
case QLocale::Hungarian: return QString::fromUtf8(C_LOCALE_HUNGARIAN);
case QLocale::Icelandic: return QString::fromUtf8(C_LOCALE_ICELANDIC);
case QLocale::Indonesian: return QString::fromUtf8(C_LOCALE_INDONESIAN);
case QLocale::Italian: return QString::fromUtf8(C_LOCALE_ITALIAN);
case QLocale::Dutch: return QString::fromUtf8(C_LOCALE_DUTCH);
case QLocale::Spanish: return QString::fromUtf8(C_LOCALE_SPANISH);
case QLocale::Catalan: return QString::fromUtf8(C_LOCALE_CATALAN);
case QLocale::Galician: return QString::fromUtf8(C_LOCALE_GALICIAN);
case QLocale::Occitan: return QString::fromUtf8(C_LOCALE_OCCITAN);
case QLocale::Portuguese: {
if (locale.country() == QLocale::Brazil)
return QString::fromUtf8(C_LOCALE_PORTUGUESE_BRAZIL);
return QString::fromUtf8(C_LOCALE_PORTUGUESE);
}
case QLocale::Polish: return QString::fromUtf8(C_LOCALE_POLISH);
case QLocale::Latvian: return QString::fromUtf8(C_LOCALE_LATVIAN);
case QLocale::Lithuanian: return QString::fromUtf8(C_LOCALE_LITHUANIAN);
case QLocale::Malay: return QString::fromUtf8(C_LOCALE_MALAY);
case QLocale::Czech: return QString::fromUtf8(C_LOCALE_CZECH);
case QLocale::Slovak: return QString::fromUtf8(C_LOCALE_SLOVAK);
case QLocale::Slovenian: return QString::fromUtf8(C_LOCALE_SLOVENIAN);
case QLocale::Serbian: return QString::fromUtf8(C_LOCALE_SERBIAN);
case QLocale::Croatian: return QString::fromUtf8(C_LOCALE_CROATIAN);
case QLocale::Armenian: return QString::fromUtf8(C_LOCALE_ARMENIAN);
case QLocale::Romanian: return QString::fromUtf8(C_LOCALE_ROMANIAN);
case QLocale::Turkish: return QString::fromUtf8(C_LOCALE_TURKISH);
case QLocale::Greek: return QString::fromUtf8(C_LOCALE_GREEK);
case QLocale::Swedish: return QString::fromUtf8(C_LOCALE_SWEDISH);
case QLocale::Finnish: return QString::fromUtf8(C_LOCALE_FINNISH);
case QLocale::Norwegian: return QString::fromUtf8(C_LOCALE_NORWEGIAN);
case QLocale::Danish: return QString::fromUtf8(C_LOCALE_DANISH);
case QLocale::Bulgarian: return QString::fromUtf8(C_LOCALE_BULGARIAN);
case QLocale::Ukrainian: return QString::fromUtf8(C_LOCALE_UKRAINIAN);
case QLocale::Uzbek: return QString::fromUtf8(C_LOCALE_UZBEK);
case QLocale::Russian: return QString::fromUtf8(C_LOCALE_RUSSIAN);
case QLocale::Japanese: return QString::fromUtf8(C_LOCALE_JAPANESE);
case QLocale::Hebrew: return QString::fromUtf8(C_LOCALE_HEBREW);
case QLocale::Hindi: return QString::fromUtf8(C_LOCALE_HINDI);
case QLocale::Arabic: return QString::fromUtf8(C_LOCALE_ARABIC);
case QLocale::Georgian: return QString::fromUtf8(C_LOCALE_GEORGIAN);
case QLocale::Byelorussian: return QString::fromUtf8(C_LOCALE_BYELORUSSIAN);
case QLocale::Basque: return QString::fromUtf8(C_LOCALE_BASQUE);
case QLocale::Vietnamese: return QString::fromUtf8(C_LOCALE_VIETNAMESE);
case QLocale::Chinese: {
switch (locale.country()) {
case QLocale::China:
return QString::fromUtf8(C_LOCALE_CHINESE_SIMPLIFIED);
case QLocale::HongKong:
return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_HK);
default:
return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_TW);
}
}
case QLocale::Korean: return QString::fromUtf8(C_LOCALE_KOREAN);
default: {
// Fallback to English
const QString engLang = QLocale::languageToString(locale.language());
qWarning() << "Unrecognized language name: " << engLang;
return engLang;
}
}
}
bool OptionsDialog::schedTimesOk()
{
if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time()) {

View File

@@ -56,7 +56,7 @@ namespace Ui
class OptionsDialog;
}
class OptionsDialog : public QDialog
class OptionsDialog final : public QDialog
{
Q_OBJECT
using ThisType = OptionsDialog;
@@ -82,7 +82,7 @@ class OptionsDialog : public QDialog
public:
// Constructor / Destructor
OptionsDialog(QWidget *parent = nullptr);
~OptionsDialog();
~OptionsDialog() override;
public slots:
void showConnectionTab();
@@ -117,7 +117,6 @@ private:
void saveOptions();
void loadOptions();
void initializeLanguageCombo();
static QString languageToLocalizedString(const QLocale &locale);
// General options
QString getLocale() const;
#ifndef Q_OS_MACOS

View File

@@ -446,7 +446,7 @@
<item>
<widget class="QCheckBox" name="checkCloseToSystray">
<property name="toolTip">
<string>The systray icon will still be visible when closing the main window</string>
<string>The systray icon will still be visible when closing the main window</string>
</property>
<property name="text">
<string extracomment="The systray icon will still be visible when closing the main window">Close qBittorrent to notification area</string>
@@ -471,12 +471,12 @@
</item>
<item>
<property name="text">
<string>Monochrome (Dark theme)</string>
<string>Monochrome (for dark theme)</string>
</property>
</item>
<item>
<property name="text">
<string>Monochrome (Light theme)</string>
<string>Monochrome (for light theme)</string>
</property>
</item>
</widget>

View File

@@ -30,10 +30,6 @@
#include <QtGlobal>
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
#include "powermanagement_x11.h"
#endif
#ifdef Q_OS_MACOS
#include <IOKit/pwr_mgt/IOPMLib.h>
#endif
@@ -42,9 +38,12 @@
#include <windows.h>
#endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
#include "powermanagement_x11.h"
#endif
PowerManagement::PowerManagement(QObject *parent)
: QObject(parent)
, m_busy(false)
{
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
m_inhibitor = new PowerManagementInhibitor(this);
@@ -55,7 +54,7 @@ PowerManagement::~PowerManagement()
{
}
void PowerManagement::setActivityState(bool busy)
void PowerManagement::setActivityState(const bool busy)
{
if (busy)
setBusy();

View File

@@ -52,11 +52,11 @@ public:
void setActivityState(bool busy);
private:
bool m_busy;
void setBusy();
void setIdle();
bool m_busy = false;
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
PowerManagementInhibitor *m_inhibitor;
#endif

View File

@@ -31,7 +31,7 @@
#include <QItemDelegate>
class PreviewListDelegate : public QItemDelegate
class PreviewListDelegate final : public QItemDelegate
{
Q_OBJECT
Q_DISABLE_COPY(PreviewListDelegate)

View File

@@ -45,7 +45,7 @@ namespace Ui
}
class PreviewListDelegate;
class PreviewSelectDialog : public QDialog
class PreviewSelectDialog final : public QDialog
{
Q_OBJECT

View File

@@ -45,7 +45,7 @@ class QStringRef;
namespace Private
{
class FileSystemPathValidator : public QValidator
class FileSystemPathValidator final : public QValidator
{
Q_OBJECT
@@ -111,7 +111,7 @@ namespace Private
virtual QWidget *widget() = 0;
};
class FileLineEdit : public QLineEdit, public FileEditorWithCompletion
class FileLineEdit final : public QLineEdit, public FileEditorWithCompletion
{
Q_OBJECT
Q_DISABLE_COPY(FileLineEdit)
@@ -141,7 +141,7 @@ namespace Private
QAction *m_warningAction;
};
class FileComboEdit : public QComboBox, public FileEditorWithCompletion
class FileComboEdit final : public QComboBox, public FileEditorWithCompletion
{
Q_OBJECT

View File

@@ -32,7 +32,7 @@
class QString;
class TriStateWidget : public QWidget
class TriStateWidget final : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY(TriStateWidget)

View File

@@ -36,7 +36,7 @@
class QWidget;
class DownloadedPiecesBar : public PiecesBar
class DownloadedPiecesBar final : public PiecesBar
{
using base = PiecesBar;
Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include <QSortFilterProxyModel>
class PeerListSortModel : public QSortFilterProxyModel
class PeerListSortModel final : public QSortFilterProxyModel
{
Q_OBJECT
Q_DISABLE_COPY(PeerListSortModel)

View File

@@ -53,7 +53,7 @@ namespace Net
class ReverseResolution;
}
class PeerListWidget : public QTreeView
class PeerListWidget final : public QTreeView
{
Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include "piecesbar.h"
class PieceAvailabilityBar : public PiecesBar
class PieceAvailabilityBar final : public PiecesBar
{
using base = PiecesBar;
Q_OBJECT

View File

@@ -37,7 +37,9 @@
#include <QTextStream>
#include <QToolTip>
#include "base/indexrange.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/utils/misc.h"
namespace

View File

@@ -43,6 +43,7 @@
#include <QUrl>
#include "base/bittorrent/downloadpriority.h"
#include "base/bittorrent/infohash.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/preferences.h"

View File

@@ -49,7 +49,7 @@ enum PropColumn
AVAILABILITY
};
class PropListDelegate : public QItemDelegate
class PropListDelegate final : public QItemDelegate
{
Q_OBJECT

View File

@@ -38,7 +38,7 @@
class QPen;
class SpeedPlotView : public QGraphicsView
class SpeedPlotView final : public QGraphicsView
{
Q_OBJECT

View File

@@ -40,7 +40,7 @@ class QVBoxLayout;
class PropertiesWidget;
class SpeedPlotView;
class ComboBoxMenuButton : public QComboBox
class ComboBoxMenuButton final : public QComboBox
{
Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include <QMessageBox>
class RaisedMessageBox : public QMessageBox
class RaisedMessageBox final : public QMessageBox
{
Q_OBJECT

View File

@@ -41,7 +41,7 @@ namespace RSS
class Item;
}
class FeedListWidget : public QTreeWidget
class FeedListWidget final : public QTreeWidget
{
Q_OBJECT

View File

@@ -36,7 +36,7 @@ class QNetworkAccessManager;
class QNetworkDiskCache;
class QNetworkReply;
class HtmlBrowser : public QTextBrowser
class HtmlBrowser final : public QTextBrowser
{
Q_OBJECT
@@ -44,7 +44,7 @@ public:
explicit HtmlBrowser(QWidget* parent = nullptr);
~HtmlBrowser();
virtual QVariant loadResource(int type, const QUrl &name) override;
QVariant loadResource(int type, const QUrl &name) override;
protected:
QNetworkAccessManager *m_netManager;

View File

@@ -39,7 +39,7 @@ class QTreeView;
class PropertiesWidget;
class ScanFoldersDelegate : public QItemDelegate
class ScanFoldersDelegate final : public QItemDelegate
{
Q_OBJECT

View File

@@ -48,7 +48,7 @@ namespace Ui
class PluginSelectDialog;
}
class PluginSelectDialog : public QDialog
class PluginSelectDialog final : public QDialog
{
Q_OBJECT
Q_DISABLE_COPY(PluginSelectDialog)

View File

@@ -51,7 +51,7 @@ namespace Ui
class SearchJobWidget;
}
class SearchJobWidget : public QWidget
class SearchJobWidget final : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY(SearchJobWidget)

View File

@@ -31,7 +31,7 @@
#include <QItemDelegate>
class SearchListDelegate : public QItemDelegate
class SearchListDelegate final : public QItemDelegate
{
Q_OBJECT

View File

@@ -32,7 +32,7 @@
#include <QSortFilterProxyModel>
#include <QStringList>
class SearchSortModel : public QSortFilterProxyModel
class SearchSortModel final : public QSortFilterProxyModel
{
using base = QSortFilterProxyModel;

View File

@@ -39,7 +39,7 @@ namespace Ui
class ShutdownConfirmDialog;
}
class ShutdownConfirmDialog : public QDialog
class ShutdownConfirmDialog final : public QDialog
{
Q_OBJECT

View File

@@ -42,7 +42,7 @@ namespace BitTorrent
class TorrentHandle;
}
class TagFilterModel : public QAbstractListModel
class TagFilterModel final : public QAbstractListModel
{
Q_OBJECT

View File

@@ -33,7 +33,7 @@
class QString;
class TagFilterProxyModel : public QSortFilterProxyModel
class TagFilterProxyModel final : public QSortFilterProxyModel
{
public:
explicit TagFilterProxyModel(QObject *parent = nullptr);

View File

@@ -31,7 +31,7 @@
#include <QTreeView>
class TagFilterWidget : public QTreeView
class TagFilterWidget final : public QTreeView
{
Q_OBJECT

View File

@@ -35,7 +35,7 @@
class TorrentContentModel;
class TorrentContentFilterModel : public QSortFilterProxyModel
class TorrentContentFilterModel final : public QSortFilterProxyModel
{
Q_OBJECT

Some files were not shown because too many files have changed in this diff Show More