This commit is contained in:
Bert Verhelst
2017-01-06 20:14:15 +01:00
322 changed files with 175969 additions and 127574 deletions

View File

@@ -3,15 +3,9 @@ set(CMAKE_CXX_STANDARD "11")
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
include(MacroLinkQtComponents)
include(QbtTargetSources)
find_package(LibtorrentRasterbar REQUIRED)
include_directories(SYSTEM ${LibtorrentRasterbar_INCLUDE_DIRS})
add_compile_options(${LibtorrentRasterbar_DEFINITIONS})
# Boost
set(Boost_USE_MULTITHREADED ON)
find_package(Boost 1.35 REQUIRED COMPONENTS system)
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
# Qt
if (QT5)
@@ -88,16 +82,22 @@ set(QBT_USES_QT5 ${QT5})
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
if (GUI)
set(QBT_TARGET_NAME qbittorrent)
else (GUI)
set(QBT_TARGET_NAME qbittorrent-nox)
endif (GUI)
add_subdirectory(base)
if (SYSTEM_QTSINGLEAPPLICATION)
find_package(QtSingleApplication REQUIRED)
include_directories(${QTSINGLEAPPLICATION_INCLUDE_DIR})
else (SYSTEM_QTSINGLEAPPLICATION)
include_directories(app/qtsingleapplication)
add_subdirectory(app/qtsingleapplication)
endif (SYSTEM_QTSINGLEAPPLICATION)
add_subdirectory(app)
add_subdirectory(base)
if (GUI)
add_subdirectory(gui)
endif (GUI)
@@ -106,4 +106,3 @@ if (WEBUI)
add_subdirectory(webui)
endif (WEBUI)
add_subdirectory(app)

View File

@@ -1,11 +1,14 @@
project(qbt_executable)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(QBT_APP_HEADERS
application.h
filelogger.h
)
set(QBT_APP_SOURCES
application.cpp
filelogger.cpp
main.cpp
)
@@ -84,39 +87,93 @@ list(APPEND QBT_APP_HEADERS upgrade.h)
list(APPEND QBT_TARGET_LIBRARIES qbt_base)
if (GUI)
set(QBT_TARGET_NAME qbittorrent)
list(APPEND QBT_TARGET_LIBRARIES qbt_searchengine qbt_gui)
include_directories(../gui
${CMAKE_CURRENT_BINARY_DIR}/../gui
)
else (GUI)
set(QBT_TARGET_NAME qbittorrent-nox)
endif (GUI)
if (WEBUI)
list(APPEND QBT_TARGET_LIBRARIES qbt_webui)
endif (WEBUI)
# we have to include resources into the bundle
if (APPLE)
set(OSX_RES_SRC_DIR "${qBittorrent_SOURCE_DIR}/dist/mac")
list(APPEND QBT_APP_RESOURCE_SOURCE
"${OSX_RES_SRC_DIR}/qt.conf"
"${OSX_RES_SRC_DIR}/qBitTorrentDocument.icns"
"${OSX_RES_SRC_DIR}/qbittorrent_mac.icns")
set_source_files_properties(
"${OSX_RES_SRC_DIR}/qt.conf"
"${OSX_RES_SRC_DIR}/qBitTorrentDocument.icns"
"${OSX_RES_SRC_DIR}/qbittorrent_mac.icns"
PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
set(QT_TR_DIR "${qBittorrent_SOURCE_DIR}/dist/qt-translations")
set(QT_TRANSLATIONS
${QT_TR_DIR}/qt_ar.qm
${QT_TR_DIR}/qt_bg.qm
${QT_TR_DIR}/qt_ca.qm
${QT_TR_DIR}/qt_cs.qm
${QT_TR_DIR}/qt_da.qm
${QT_TR_DIR}/qt_de.qm
${QT_TR_DIR}/qt_es.qm
${QT_TR_DIR}/qt_eu.qm
${QT_TR_DIR}/qt_fi.qm
${QT_TR_DIR}/qt_fr.qm
${QT_TR_DIR}/qt_gl.qm
${QT_TR_DIR}/qt_he.qm
${QT_TR_DIR}/qt_hu.qm
${QT_TR_DIR}/qt_it.qm
${QT_TR_DIR}/qt_ja.qm
${QT_TR_DIR}/qt_ko.qm
${QT_TR_DIR}/qt_lt.qm
${QT_TR_DIR}/qt_nl.qm
${QT_TR_DIR}/qt_pl.qm
${QT_TR_DIR}/qt_pt.qm
${QT_TR_DIR}/qt_pt_BR.qm
${QT_TR_DIR}/qt_ru.qm
${QT_TR_DIR}/qt_sk.qm
${QT_TR_DIR}/qt_sv.qm
${QT_TR_DIR}/qt_tr.qm
${QT_TR_DIR}/qt_uk.qm
${QT_TR_DIR}/qt_zh_CN.qm
${QT_TR_DIR}/qt_zh_TW.qm
)
list(APPEND QBT_APP_RESOURCE_SOURCE ${QT_TRANSLATIONS})
set_source_files_properties(${QT_TRANSLATIONS}
PROPERTIES MACOSX_PACKAGE_LOCATION translations)
endif (APPLE)
add_executable(${QBT_TARGET_NAME} ${QBT_APP_HEADERS} ${QBT_APP_SOURCES} ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
set_target_properties(${QBT_TARGET_NAME} PROPERTIES AUTOUIC True)
set_target_properties(${QBT_TARGET_NAME}
PROPERTIES
AUTOUIC True
AUTORCC True
MACOSX_BUNDLE True
)
if (GUI)
if (WIN32)
set_target_properties(${QBT_TARGET_NAME} PROPERTIES WIN32_EXECUTABLE True)
endif (WIN32)
if (APPLE)
set_target_properties(${QBT_TARGET_NAME} PROPERTIES MACOSX_BUNDLE True)
endif (APPLE)
endif (GUI)
if (GUI AND WIN32)
set_target_properties(${QBT_TARGET_NAME} PROPERTIES WIN32_EXECUTABLE True)
endif (GUI AND WIN32)
target_link_libraries(${QBT_TARGET_NAME} ${QBT_TARGET_LIBRARIES})
target_link_libraries(${QBT_TARGET_NAME} ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
if (SYSTEM_QTSINGLEAPPLICATION)
target_link_libraries(${QBT_TARGET_NAME} ${QTSINGLEAPPLICATION_LIBRARIES})
else (SYSTEM_QTSINGLEAPPLICATION)
add_subdirectory(qtsingleapplication)
target_link_libraries(${QBT_TARGET_NAME} qtsingleapplication)
endif (SYSTEM_QTSINGLEAPPLICATION)
if (APPLE)
set(qbt_BUNDLE_NAME "${CMAKE_PROJECT_NAME}")
set_target_properties(${QBT_TARGET_NAME} PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "${qbt_BUNDLE_NAME}"
MACOSX_BUNDLE_INFO_PLIST ${qBittorrent_SOURCE_DIR}/dist/mac/Info.plist
)
endif (APPLE)
# installation
install(TARGETS ${QBT_TARGET_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime)
install(TARGETS ${QBT_TARGET_NAME}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION .
COMPONENT runtime)
if (APPLE)
install(SCRIPT ${OSX_RES_SRC_DIR}/bundle.cmake)
endif (APPLE)

View File

@@ -33,6 +33,7 @@
#include <QLibraryInfo>
#include <QSysInfo>
#include <QProcess>
#include <QAtomicInt>
#ifndef DISABLE_GUI
#include "gui/guiiconprovider.h"
@@ -48,7 +49,7 @@
#endif // Q_OS_MAC
#include "mainwindow.h"
#include "addnewtorrentdialog.h"
#include "shutdownconfirm.h"
#include "shutdownconfirmdlg.h"
#else // DISABLE_GUI
#include <iostream>
#endif // DISABLE_GUI
@@ -69,6 +70,7 @@
#include "base/net/smtp.h"
#include "base/net/downloadmanager.h"
#include "base/net/geoipmanager.h"
#include "base/net/proxyconfigurationmanager.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
@@ -96,9 +98,7 @@ namespace
Application::Application(const QString &id, int &argc, char **argv)
: BaseApplication(id, argc, argv)
, m_running(false)
#ifndef DISABLE_GUI
, m_shutdownAct(ShutdownAction::None)
#endif
, m_shutdownAct(ShutdownDialogAction::Exit)
{
Logger::initInstance();
SettingsStorage::initInstance();
@@ -132,6 +132,13 @@ Application::Application(const QString &id, int &argc, char **argv)
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
}
#ifndef DISABLE_GUI
QPointer<MainWindow> Application::mainWindow()
{
return m_window;
}
#endif
bool Application::isFileLoggerEnabled() const
{
return settings()->loadValue(KEY_FILELOGGER_ENABLED, true).toBool();
@@ -236,6 +243,50 @@ void Application::processMessage(const QString &message)
m_paramsQueue.append(params);
}
void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) const
{
QString program = Preferences::instance()->getAutoRunProgram();
program.replace("%N", torrent->name());
program.replace("%L", torrent->category());
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
program.replace("%C", QString::number(torrent->filesCount()));
program.replace("%Z", QString::number(torrent->totalSize()));
program.replace("%T", torrent->currentTracker());
program.replace("%I", torrent->hash());
Logger *logger = Logger::instance();
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name()).arg(program));
#if defined(Q_OS_UNIX)
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
#elif defined(Q_OS_WIN) // test cmd: `echo "%F" > "c:\ab ba.txt"`
program.prepend(QLatin1String("\"")).append(QLatin1String("\""));
program.prepend(Utils::Misc::windowsSystemPath() + QLatin1String("\\cmd.exe /C "));
const int cmdMaxLength = 32768; // max length (incl. terminate char) for `lpCommandLine` in `CreateProcessW()`
if ((program.size() + 1) > cmdMaxLength) {
logger->addMessage(tr("Torrent: %1, run external program command too long (length > %2), execution failed.").arg(torrent->name()).arg(cmdMaxLength), Log::CRITICAL);
return;
}
STARTUPINFOW si = {0};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {0};
WCHAR *arg = new WCHAR[program.size() + 1];
program.toWCharArray(arg);
arg[program.size()] = L'\0';
if (CreateProcessW(NULL, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
delete[] arg;
#else
QProcess::startDetached(program);
#endif
}
void Application::sendNotificationEmail(BitTorrent::TorrentHandle *const torrent)
{
// Prepare mail content
@@ -260,75 +311,58 @@ void Application::torrentFinished(BitTorrent::TorrentHandle *const torrent)
Preferences *const pref = Preferences::instance();
// AutoRun program
if (pref->isAutoRunEnabled()) {
QString program = pref->getAutoRunProgram();
program.replace("%N", torrent->name());
program.replace("%L", torrent->category());
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
program.replace("%C", QString::number(torrent->filesCount()));
program.replace("%Z", QString::number(torrent->totalSize()));
program.replace("%T", torrent->currentTracker());
program.replace("%I", torrent->hash());
QProcess::startDetached(program);
}
if (pref->isAutoRunEnabled())
runExternalProgram(torrent);
// Mail notification
if (pref->isMailNotificationEnabled())
if (pref->isMailNotificationEnabled()) {
Logger::instance()->addMessage(tr("Torrent: %1, sending mail notification").arg(torrent->name()));
sendNotificationEmail(torrent);
}
}
void Application::allTorrentsFinished()
{
#ifndef DISABLE_GUI
Preferences *const pref = Preferences::instance();
bool isExit = pref->shutdownqBTWhenDownloadsComplete();
bool isShutdown = pref->shutdownWhenDownloadsComplete();
bool isSuspend = pref->suspendWhenDownloadsComplete();
bool isHibernate = pref->hibernateWhenDownloadsComplete();
bool will_shutdown = (pref->shutdownWhenDownloadsComplete()
|| pref->shutdownqBTWhenDownloadsComplete()
|| pref->suspendWhenDownloadsComplete()
|| pref->hibernateWhenDownloadsComplete());
bool haveAction = isExit || isShutdown || isSuspend || isHibernate;
if (!haveAction) return;
// Auto-Shutdown
if (will_shutdown) {
bool suspend = pref->suspendWhenDownloadsComplete();
bool hibernate = pref->hibernateWhenDownloadsComplete();
bool shutdown = pref->shutdownWhenDownloadsComplete();
ShutdownDialogAction action = ShutdownDialogAction::Exit;
if (isSuspend)
action = ShutdownDialogAction::Suspend;
else if (isHibernate)
action = ShutdownDialogAction::Hibernate;
else if (isShutdown)
action = ShutdownDialogAction::Shutdown;
// Confirm shutdown
ShutdownAction action = ShutdownAction::None;
if (suspend)
action = ShutdownAction::Suspend;
else if (hibernate)
action = ShutdownAction::Hibernate;
else if (shutdown)
action = ShutdownAction::Shutdown;
if ((action == ShutdownAction::None) && (!pref->dontConfirmAutoExit())) {
if (!ShutdownConfirmDlg::askForConfirmation(action))
return;
}
else { //exit and shutdown
if (!ShutdownConfirmDlg::askForConfirmation(action))
return;
}
// Actually shut down
if (suspend || hibernate || shutdown) {
qDebug("Preparing for auto-shutdown because all downloads are complete!");
// Disabling it for next time
pref->setShutdownWhenDownloadsComplete(false);
pref->setSuspendWhenDownloadsComplete(false);
pref->setHibernateWhenDownloadsComplete(false);
// Make sure preferences are synced before exiting
m_shutdownAct = action;
}
qDebug("Exiting the application");
exit();
#ifndef DISABLE_GUI
// ask confirm
if ((action == ShutdownDialogAction::Exit) && (pref->dontConfirmAutoExit())) {
// do nothing & skip confirm
}
else {
if (!ShutdownConfirmDlg::askForConfirmation(action)) return;
}
#endif // DISABLE_GUI
// Actually shut down
if (action != ShutdownDialogAction::Exit) {
qDebug("Preparing for auto-shutdown because all downloads are complete!");
// Disabling it for next time
pref->setShutdownWhenDownloadsComplete(false);
pref->setSuspendWhenDownloadsComplete(false);
pref->setHibernateWhenDownloadsComplete(false);
// Make sure preferences are synced before exiting
m_shutdownAct = action;
}
qDebug("Exiting the application");
exit();
}
bool Application::sendParams(const QStringList &params)
@@ -362,6 +396,7 @@ void Application::processParams(const QStringList &params)
int Application::exec(const QStringList &params)
{
Net::ProxyConfigurationManager::initInstance();
Net::DownloadManager::initInstance();
#ifdef DISABLE_GUI
IconProvider::initInstance();
@@ -371,7 +406,7 @@ int Application::exec(const QStringList &params)
BitTorrent::Session::initInstance();
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()));
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()), Qt::QueuedConnection);
#ifndef DISABLE_COUNTRIES_RESOLUTION
Net::GeoIPManager::initInstance();
@@ -548,11 +583,9 @@ void Application::cleanup()
#ifndef DISABLE_GUI
#ifdef Q_OS_WIN
// cleanup() can be called multiple times during shutdown. We only need it once.
static bool alreadyDone = false;
if (alreadyDone)
static QAtomicInt alreadyDone;
if (!alreadyDone.testAndSetAcquire(0, 1))
return;
alreadyDone = true;
#endif // Q_OS_WIN
// Hide the window and not leave it on screen as
@@ -590,11 +623,13 @@ void Application::cleanup()
Net::GeoIPManager::freeInstance();
#endif
Net::DownloadManager::freeInstance();
Net::ProxyConfigurationManager::freeInstance();
Preferences::freeInstance();
SettingsStorage::freeInstance();
delete m_fileLogger;
Logger::freeInstance();
IconProvider::freeInstance();
#ifndef DISABLE_GUI
#ifdef Q_OS_WIN
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
@@ -604,9 +639,10 @@ void Application::cleanup()
shutdownBRDestroy((HWND)m_window->effectiveWinId());
#endif // Q_OS_WIN
delete m_window;
if (m_shutdownAct != ShutdownAction::None) {
#endif // DISABLE_GUI
if (m_shutdownAct != ShutdownDialogAction::Exit) {
qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
Utils::Misc::shutdownComputer(m_shutdownAct);
}
#endif
}

View File

@@ -76,6 +76,10 @@ public:
int exec(const QStringList &params);
bool sendParams(const QStringList &params);
#ifndef DISABLE_GUI
QPointer<MainWindow> mainWindow();
#endif
// FileLogger properties
bool isFileLoggerEnabled() const;
void setFileLoggerEnabled(bool value);
@@ -111,10 +115,10 @@ private slots:
private:
bool m_running;
ShutdownDialogAction m_shutdownAct;
#ifndef DISABLE_GUI
QPointer<MainWindow> m_window;
ShutdownAction m_shutdownAct;
#endif
#ifndef DISABLE_WEBUI
@@ -130,6 +134,7 @@ private:
void initializeTranslation();
void processParams(const QStringList &params);
void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
};

View File

@@ -135,6 +135,12 @@ int main(int argc, char *argv[])
// We must save it here because QApplication constructor may change it
bool isOneArg = (argc == 2);
#ifdef Q_OS_MAC
// On macOS 10.12 Sierra, Apple changed the behaviour of CFPreferencesSetValue() https://bugreports.qt.io/browse/QTBUG-56344
// Due to this, we have to move from native plist to IniFormat
macMigratePlists();
#endif
// Create Application
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
QScopedPointer<Application> app(new Application(appId, argc, argv));
@@ -230,6 +236,17 @@ int main(int argc, char *argv[])
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
#endif
#if defined(Q_OS_MAC)
{
// Since Apple made difficult for users to set PATH, we set here for convenience.
// Users are supposed to install Homebrew Python for search function.
// For more info see issue #5571.
QByteArray path = "/usr/local/bin:";
path += qgetenv("PATH");
qputenv("PATH", path.constData());
}
#endif
#ifndef DISABLE_GUI
if (!upgrade()) return EXIT_FAILURE;
#else

View File

@@ -1,3 +1,5 @@
project(qtsingleapplication)
set(QBT_QTSINGLEAPPLICATION_HEADERS
qtlocalpeer.h
)
@@ -15,6 +17,7 @@ else (GUI)
endif (GUI)
add_library(qtsingleapplication ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
if (QT4_FOUND)
target_link_libraries(qtsingleapplication Qt4::QtNetwork)
@@ -30,3 +33,4 @@ if (GUI)
endif(QT4_FOUND)
endif (GUI)
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)

View File

@@ -25,6 +25,7 @@
#include <dbghelp.h>
#include <stdio.h>
#include <QDir>
#include <QTextStream>
#ifdef __MINGW32__
#include <cxxabi.h>
@@ -41,6 +42,9 @@ namespace straceWin
#ifdef __MINGW32__
void demangle(QString& str);
#endif
QString getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr);
bool makeRelativePath(const QString& dir, QString& file);
}
#ifdef __MINGW32__
@@ -108,6 +112,65 @@ BOOL CALLBACK straceWin::EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVO
}
/**
* Cuts off leading 'dir' path from 'file' path, otherwise leaves it unchanged
* returns true if 'dir' is an ancestor of 'file', otherwise - false
*/
bool straceWin::makeRelativePath(const QString& dir, QString& file)
{
QString d = QDir::toNativeSeparators(QDir(dir).absolutePath());
QString f = QDir::toNativeSeparators(QFileInfo(file).absoluteFilePath());
// append separator at the end of dir
QChar separator = QDir::separator();
if (!d.isEmpty() && (d[d.length() - 1] != separator))
d += separator;
if (f.startsWith(d, Qt::CaseInsensitive)) {
f.remove(0, d.length());
file.swap(f);
return true;
}
return false;
}
QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
{
IMAGEHLP_LINE64 line = {0};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD dwDisplacement = 0;
if (SymGetLineFromAddr64(hProcess, addr, &dwDisplacement, &line)) {
QString path(line.FileName);
#if defined STACKTRACE_WIN_PROJECT_PATH || defined STACKTRACE_WIN_MAKEFILE_PATH
#define STACKTRACE_WIN_QUOTE(x) #x
#define STACKTRACE_WIN_STRING(x) STACKTRACE_WIN_QUOTE(x)
//prune leading project directory path or build target directory path
bool success = false;
#ifdef STACKTRACE_WIN_PROJECT_PATH
QString projectPath(STACKTRACE_WIN_STRING(STACKTRACE_WIN_PROJECT_PATH));
success = makeRelativePath(projectPath, path);
#endif
#ifdef STACKTRACE_WIN_MAKEFILE_PATH
if (!success) {
QString targetPath(STACKTRACE_WIN_STRING(STACKTRACE_WIN_MAKEFILE_PATH));
makeRelativePath(targetPath, path);
}
#endif
#endif
return QString("%1 : %2").arg(path).arg(line.LineNumber);
}
return QString();
}
#if defined( _M_IX86 ) && defined(Q_CC_MSVC)
// Disable global optimization and ignore /GS waning caused by
@@ -221,11 +284,16 @@ const QString straceWin::getBacktrace()
fileName = fileName.mid(slashPos + 1);
}
QString funcName;
QString sourceFile;
if(SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {
funcName = QString(pSymbol->Name);
#ifdef __MINGW32__
demangle(funcName);
#endif
// now ihsf.InstructionOffset points to the instruction that follows CALL instuction
// decrease the query address by one byte to point somewhere in the CALL instruction byte sequence
sourceFile = getSourcePathAndLineNumber(hProcess, ihsf.InstructionOffset - 1);
}
else {
funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
@@ -248,6 +316,9 @@ const QString straceWin::getBacktrace()
.arg(funcName)
#ifndef __MINGW32__
.arg(params.join(", "));
if (!sourceFile.isEmpty())
debugLine += QString("[ %1 ]").arg(sourceFile);
#else
;
#endif
@@ -262,6 +333,8 @@ const QString straceWin::getBacktrace()
//logStream << "\n\nList of linked Modules:\n";
//EnumModulesContext modulesContext(hProcess, logStream);
//SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
SymCleanup(hProcess);
logStream << "```";
return log;
}

View File

@@ -29,24 +29,31 @@
#ifndef UPGRADE_H
#define UPGRADE_H
#include <libtorrent/lazy_entry.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/version.hpp>
#if LIBTORRENT_VERSION_NUM >= 10100
#include <libtorrent/bdecode.hpp>
#endif
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#if LIBTORRENT_VERSION_NUM < 10100
#include <libtorrent/lazy_entry.hpp>
#endif
#include <QString>
#include <QDir>
#include <QFile>
#include <QRegExp>
#ifndef DISABLE_GUI
#include <QMessageBox>
#endif
#include <QRegExp>
#include <QString>
#include "base/logger.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "base/qinisettings.h"
#include "base/preferences.h"
#include "base/qinisettings.h"
bool userAcceptsUpgrade()
{
@@ -86,10 +93,16 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
QByteArray data = file1.readAll();
file1.close();
libtorrent::lazy_entry fastOld;
libtorrent::error_code ec;
libtorrent::lazy_bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
if (ec || (fastOld.type() != libtorrent::lazy_entry::dict_t)) return false;
#if LIBTORRENT_VERSION_NUM < 10100
libtorrent::lazy_entry fastOld;
libtorrent::lazy_bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
if (ec || (fastOld.type() != libtorrent::lazy_entry::dict_t)) return false;
#else
libtorrent::bdecode_node fastOld;
libtorrent::bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
if (ec || (fastOld.type() != libtorrent::bdecode_node::dict_t)) return false;
#endif
libtorrent::entry fastNew;
fastNew = fastOld;
@@ -143,7 +156,12 @@ bool upgrade(bool ask = true)
upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile));
// ****************************************************************************************
#ifdef Q_OS_MAC
// native .plist
QSettings *oldResumeSettings = new QSettings("qBittorrent", "qBittorrent-resume");
#else
QIniSettings *oldResumeSettings = new QIniSettings("qBittorrent", "qBittorrent-resume");
#endif
QString oldResumeFilename = oldResumeSettings->fileName();
QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash();
delete oldResumeSettings;
@@ -210,4 +228,34 @@ bool upgrade(bool ask = true)
return true;
}
#ifdef Q_OS_MAC
void migratePlistToIni(const QString &application)
{
QIniSettings iniFile("qBittorrent", application);
if (!iniFile.allKeys().isEmpty()) return; // We copy the contents of plist, only if inifile does not exist(is empty).
QSettings *plistFile = new QSettings("qBittorrent", application);
plistFile->setFallbacksEnabled(false);
const QStringList plist = plistFile->allKeys();
if (!plist.isEmpty()) {
foreach (const QString &key, plist)
iniFile.setValue(key, plistFile->value(key));
plistFile->clear();
}
QString plistPath = plistFile->fileName();
delete plistFile;
Utils::Fs::forceRemove(plistPath);
}
void macMigratePlists()
{
migratePlistToIni("qBittorrent-data");
migratePlistToIni("qBittorrent-rss");
migratePlistToIni("qBittorrent");
}
#endif // Q_OS_MAC
#endif // UPGRADE_H

View File

@@ -1,5 +1,4 @@
find_package(ZLIB REQUIRED)
include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS})
set(QBT_BASE_HEADERS
bittorrent/cachestatus.h
@@ -31,6 +30,7 @@ net/downloadmanager.h
net/geoipmanager.h
net/portforwarder.h
net/private/geoipdatabase.h
net/proxyconfigurationmanager.h
net/reverseresolution.h
net/smtp.h
rss/private/rssparser.h
@@ -47,12 +47,14 @@ utils/misc.h
utils/string.h
filesystemwatcher.h
iconprovider.h
indexrange.h
logger.h
preferences.h
qinisettings.h
scanfoldersmodel.h
searchengine.h
settingsstorage.h
torrentfileguard.h
torrentfilter.h
tristatebool.h
types.h
@@ -87,6 +89,7 @@ net/downloadmanager.cpp
net/geoipmanager.cpp
net/portforwarder.cpp
net/private/geoipdatabase.cpp
net/proxyconfigurationmanager.cpp
net/reverseresolution.cpp
net/smtp.cpp
rss/private/rssparser.cpp
@@ -108,12 +111,13 @@ preferences.cpp
scanfoldersmodel.cpp
searchengine.cpp
settingsstorage.cpp
torrentfileguard.cpp
torrentfilter.cpp
tristatebool.cpp
)
add_library(qbt_base STATIC ${QBT_BASE_HEADERS} ${QBT_BASE_SOURCES})
target_link_libraries(qbt_base ${ZLIB_LIBRARIES} ${LibtorrentRasterbar_LIBRARIES})
target_link_libraries(qbt_base ZLIB::ZLIB LibtorrentRasterbar::LibTorrent)
target_link_qt_components(qbt_base Core Network Xml)
if (QT4_FOUND)
if (GUI)
@@ -124,3 +128,13 @@ else (QT4_FOUND)
target_link_libraries(qbt_base Qt5::Gui Qt5::Widgets)
endif (GUI)
endif (QT4_FOUND)
if (DBUS)
target_link_qt_components(qbt_base DBus)
endif ()
if (APPLE)
find_library(IOKit_LIBRARY IOKit)
find_library(Carbon_LIBRARY Carbon)
target_link_libraries(qbt_base ${Carbon_LIBRARY} ${IOKit_LIBRARY})
endif (APPLE)

View File

@@ -5,7 +5,9 @@ HEADERS += \
$$PWD/qinisettings.h \
$$PWD/logger.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/preferences.h \
$$PWD/indexrange.h \
$$PWD/iconprovider.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/connection.h \
@@ -19,6 +21,7 @@ HEADERS += \
$$PWD/net/downloadhandler.h \
$$PWD/net/geoipmanager.h \
$$PWD/net/portforwarder.h \
$$PWD/net/proxyconfigurationmanager.h \
$$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \
$$PWD/net/private/geoipdatabase.h \
@@ -51,6 +54,7 @@ HEADERS += \
$$PWD/utils/misc.h \
$$PWD/utils/string.h \
$$PWD/unicodestrings.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \
$$PWD/scanfoldersmodel.h \
$$PWD/searchengine.h
@@ -72,6 +76,7 @@ SOURCES += \
$$PWD/net/downloadhandler.cpp \
$$PWD/net/geoipmanager.cpp \
$$PWD/net/portforwarder.cpp \
$$PWD/net/proxyconfigurationmanager.cpp \
$$PWD/net/reverseresolution.cpp \
$$PWD/net/smtp.cpp \
$$PWD/net/private/geoipdatabase.cpp \
@@ -103,6 +108,7 @@ SOURCES += \
$$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/string.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/searchengine.cpp

View File

@@ -68,7 +68,6 @@ bool InfoHash::isValid() const
return m_valid;
}
InfoHash::operator libtorrent::sha1_hash() const
{
return m_nativeHash;

View File

@@ -31,13 +31,13 @@
#include <QTime>
#include <QDateTime>
#include "base/bittorrent/session.h"
#include "base/preferences.h"
#include "bandwidthscheduler.h"
BandwidthScheduler::BandwidthScheduler(QObject *parent)
: QTimer(parent)
{
Q_ASSERT(Preferences::instance()->isSchedulerEnabled());
// Single shot, we call start() again manually
setSingleShot(true);
// Connect Signals/Slots
@@ -47,8 +47,7 @@ BandwidthScheduler::BandwidthScheduler(QObject *parent)
void BandwidthScheduler::start()
{
const Preferences* const pref = Preferences::instance();
Q_ASSERT(pref->isSchedulerEnabled());
bool alt_bw_enabled = pref->isAltBandwidthEnabled();
bool alt_bw_enabled = BitTorrent::Session::instance()->isAltGlobalSpeedLimitEnabled();
QTime start = pref->getSchedulerStartTime();
QTime end = pref->getSchedulerEndTime();

View File

@@ -55,10 +55,10 @@ FilterParserThread::~FilterParserThread()
}
// Parser for eMule ip filter in DAT format
int FilterParserThread::parseDATFilterFile(QString m_filePath, libt::ip_filter &filter)
int FilterParserThread::parseDATFilterFile(QString filePath, libt::ip_filter &filter)
{
int ruleCount = 0;
QFile file(m_filePath);
QFile file(filePath);
if (!file.exists()) return ruleCount;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -149,10 +149,10 @@ int FilterParserThread::parseDATFilterFile(QString m_filePath, libt::ip_filter &
}
// Parser for PeerGuardian ip filter in p2p format
int FilterParserThread::parseP2PFilterFile(QString m_filePath, libt::ip_filter &filter)
int FilterParserThread::parseP2PFilterFile(QString filePath, libt::ip_filter &filter)
{
int ruleCount = 0;
QFile file(m_filePath);
QFile file(filePath);
if (!file.exists()) return ruleCount;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -257,10 +257,10 @@ int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name,
}
// Parser for PeerGuardian ip filter in p2p format
int FilterParserThread::parseP2BFilterFile(QString m_filePath, libt::ip_filter &filter)
int FilterParserThread::parseP2BFilterFile(QString filePath, libt::ip_filter &filter)
{
int ruleCount = 0;
QFile file(m_filePath);
QFile file(filePath);
if (!file.exists()) return ruleCount;
if (!file.open(QIODevice::ReadOnly)) {
@@ -369,7 +369,7 @@ int FilterParserThread::parseP2BFilterFile(QString m_filePath, libt::ip_filter &
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
void FilterParserThread::processFilterFile(QString _filePath)
void FilterParserThread::processFilterFile(QString filePath)
{
if (isRunning()) {
// Already parsing a filter, m_abort first
@@ -378,30 +378,37 @@ void FilterParserThread::processFilterFile(QString _filePath)
}
m_abort = false;
m_filePath = _filePath;
m_filePath = filePath;
// Run it
start();
}
void FilterParserThread::processFilterList(libt::session *s, const QStringList &IPs)
{
// First, import current filter
libt::ip_filter filter = s->get_ip_filter();
foreach (const QString &ip, IPs) {
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData());
boost::system::error_code ec;
libt::address addr = libt::address::from_string(ip.toLocal8Bit().constData(), ec);
Q_ASSERT(!ec);
if (!ec)
filter.add_rule(addr, addr, libt::ip_filter::blocked);
}
s->set_ip_filter(filter);
}
QString FilterParserThread::cleanupIPAddress(QString _ip)
{
QHostAddress ip(_ip.trimmed());
_ip = _ip.trimmed();
// Emule .DAT files contain leading zeroes in IPv4 addresses
// eg 001.009.106.186
// We need to remove them because both QHostAddress and Boost.Asio fail to parse them.
QStringList octets = _ip.split('.', QString::SkipEmptyParts);
if (octets.size() == 4) {
QString octet; // it is faster to not recreate this object in the loop
for (int i = 0; i < 4; i++) {
octet = octets[i];
if ((octet[0] == QChar('0')) && (octet.count() > 1)) {
if ((octet[1] == QChar('0')) && (octet.count() > 2))
octet.remove(0, 2);
else
octet.remove(0, 1);
octets[i] = octet;
}
}
_ip = octets.join(".");
}
QHostAddress ip(_ip);
if (ip.isNull()) return QString();
return ip.toString();

View File

@@ -54,8 +54,7 @@ public:
int parseP2PFilterFile(QString filePath, libtorrent::ip_filter &filter);
int getlineInStream(QDataStream &stream, std::string &name, char delim);
int parseP2BFilterFile(QString filePath, libtorrent::ip_filter &filter);
void processFilterFile(QString _filePath);
static void processFilterList(libtorrent::session *s, const QStringList &IPs);
void processFilterFile(QString filePath);
signals:
void IPFilterParsed(int ruleCount);

View File

@@ -27,17 +27,21 @@
* exception statement from your version.
*/
#include <QList>
#include "speedmonitor.h"
SpeedMonitor::SpeedMonitor()
: m_speedSamples(MAX_SAMPLES)
{
}
void SpeedMonitor::addSample(const SpeedSample &sample)
{
if (m_speedSamples.size() >= MAX_SAMPLES) {
m_sum -= m_speedSamples.front();
}
m_speedSamples.push_back(sample);
m_sum += sample;
if (m_speedSamples.size() > MAX_SAMPLES) {
m_sum -= m_speedSamples.front();
m_speedSamples.pop_front();
}
}
SpeedSampleAvg SpeedMonitor::average() const

View File

@@ -30,7 +30,11 @@
#ifndef SPEEDMONITOR_H
#define SPEEDMONITOR_H
template<typename T> class QList;
#ifndef Q_MOC_RUN
#include <boost/circular_buffer.hpp>
#endif
#include <QtGlobal>
template<typename T>
struct Sample
@@ -71,13 +75,15 @@ typedef Sample<qreal> SpeedSampleAvg;
class SpeedMonitor
{
public:
SpeedMonitor();
void addSample(const SpeedSample &sample);
SpeedSampleAvg average() const;
void reset();
private:
static const int MAX_SAMPLES = 30;
QList<SpeedSample> m_speedSamples;
boost::circular_buffer<SpeedSample> m_speedSamples;
SpeedSample m_sum;
};

File diff suppressed because it is too large Load Diff

View File

@@ -30,17 +30,23 @@
#ifndef BITTORRENT_SESSION_H
#define BITTORRENT_SESSION_H
#include <vector>
#include <libtorrent/version.hpp>
#include <QFile>
#include <QHash>
#include <QMap>
#include <QPointer>
#include <QVector>
#if LIBTORRENT_VERSION_NUM < 10100
#include <QMutex>
#include <QWaitCondition>
#endif
#include <QNetworkConfigurationManager>
#include <QPointer>
#include <QReadWriteLock>
#include <QStringList>
#include <QVector>
#include <QWaitCondition>
#include <libtorrent/version.hpp>
#include "base/settingvalue.h"
#include "base/tristatebool.h"
#include "base/types.h"
#include "torrentinfo.h"
@@ -52,19 +58,12 @@ namespace libtorrent
class entry;
struct add_torrent_params;
struct pe_settings;
struct session_settings;
struct session_status;
#if LIBTORRENT_VERSION_NUM < 10100
struct proxy_settings;
struct session_settings;
#else
namespace aux
{
struct proxy_settings;
}
typedef aux::proxy_settings proxy_settings;
struct settings_pack;
#endif
struct session_status;
class alert;
struct torrent_alert;
@@ -111,7 +110,6 @@ class FilterParserThread;
class BandwidthScheduler;
class Statistics;
class ResumeDataSavingManager;
class SettingsStorage;
enum MaxRatioAction
{
@@ -172,19 +170,13 @@ namespace BitTorrent
static void freeInstance();
static Session *instance();
bool isDHTEnabled() const;
bool isLSDEnabled() const;
bool isPexEnabled() const;
bool isQueueingEnabled() const;
qreal globalMaxRatio() const;
bool isAppendExtensionEnabled() const;
QString defaultSavePath() const;
void setDefaultSavePath(QString path);
QString tempPath() const;
void setTempPath(QString path);
bool isTempPathEnabled() const;
void setTempPathEnabled(bool enabled);
QString torrentTempPath(const InfoHash &hash) const;
static bool isValidCategoryName(const QString &name);
// returns category itself and all top level categories
@@ -198,28 +190,144 @@ namespace BitTorrent
bool isSubcategoriesEnabled() const;
void setSubcategoriesEnabled(bool value);
// Advanced Saving Management subsystem (ASM)
// Torrent Management Mode subsystem (TMM)
//
// Each torrent can be either in Simple mode or in Advanced mode
// In Simple mode torrent has explicit save path
// In Advanced Mode torrent has implicit save path (based on Default
// save path and Category save path)
// In Advanced Mode torrent save path can be changed in following cases:
// Each torrent can be either in Manual mode or in Automatic mode
// In Manual Mode various torrent properties are set explicitly(eg save path)
// In Automatic Mode various torrent properties are set implicitly(eg save path)
// based on the associated category.
// In Automatic Mode torrent save path can be changed in following cases:
// 1. Default save path changed
// 2. Torrent category save path changed
// 3. Torrent category changed
// (unless otherwise is specified)
bool isASMDisabledByDefault() const;
void setASMDisabledByDefault(bool value);
bool isDisableASMWhenCategoryChanged() const;
void setDisableASMWhenCategoryChanged(bool value);
bool isDisableASMWhenDefaultSavePathChanged() const;
void setDisableASMWhenDefaultSavePathChanged(bool value);
bool isDisableASMWhenCategorySavePathChanged() const;
void setDisableASMWhenCategorySavePathChanged(bool value);
bool isAutoTMMDisabledByDefault() const;
void setAutoTMMDisabledByDefault(bool value);
bool isDisableAutoTMMWhenCategoryChanged() const;
void setDisableAutoTMMWhenCategoryChanged(bool value);
bool isDisableAutoTMMWhenDefaultSavePathChanged() const;
void setDisableAutoTMMWhenDefaultSavePathChanged(bool value);
bool isDisableAutoTMMWhenCategorySavePathChanged() const;
void setDisableAutoTMMWhenCategorySavePathChanged(bool value);
qreal globalMaxRatio() const;
void setGlobalMaxRatio(qreal ratio);
bool isDHTEnabled() const;
void setDHTEnabled(bool enabled);
bool isLSDEnabled() const;
void setLSDEnabled(bool enabled);
bool isPeXEnabled() const;
void setPeXEnabled(bool enabled);
bool isTrackerExchangeEnabled() const;
void setTrackerExchangeEnabled(bool enabled);
bool isAddTorrentPaused() const;
void setAddTorrentPaused(bool value);
bool isTrackerEnabled() const;
void setTrackerEnabled(bool enabled);
bool isAppendExtensionEnabled() const;
void setAppendExtensionEnabled(bool enabled);
uint refreshInterval() const;
void setRefreshInterval(uint value);
bool isPreallocationEnabled() const;
void setPreallocationEnabled(bool enabled);
QString torrentExportDirectory() const;
void setTorrentExportDirectory(QString path);
QString finishedTorrentExportDirectory() const;
void setFinishedTorrentExportDirectory(QString path);
int globalDownloadSpeedLimit() const;
void setGlobalDownloadSpeedLimit(int limit);
int globalUploadSpeedLimit() const;
void setGlobalUploadSpeedLimit(int limit);
int altGlobalDownloadSpeedLimit() const;
void setAltGlobalDownloadSpeedLimit(int limit);
int altGlobalUploadSpeedLimit() const;
void setAltGlobalUploadSpeedLimit(int limit);
int downloadSpeedLimit() const;
void setDownloadSpeedLimit(int limit);
int uploadSpeedLimit() const;
void setUploadSpeedLimit(int limit);
bool isAltGlobalSpeedLimitEnabled() const;
void setAltGlobalSpeedLimitEnabled(bool enabled);
bool isBandwidthSchedulerEnabled() const;
void setBandwidthSchedulerEnabled(bool enabled);
uint saveResumeDataInterval() const;
void setSaveResumeDataInterval(uint value);
int port() const;
void setPort(int port);
bool useRandomPort() const;
void setUseRandomPort(bool value);
QString networkInterface() const;
void setNetworkInterface(const QString &interface);
QString networkInterfaceName() const;
void setNetworkInterfaceName(const QString &name);
QString networkInterfaceAddress() const;
void setNetworkInterfaceAddress(const QString &address);
bool isIPv6Enabled() const;
void setIPv6Enabled(bool enabled);
int encryption() const;
void setEncryption(int state);
bool isForceProxyEnabled() const;
void setForceProxyEnabled(bool enabled);
bool isProxyPeerConnectionsEnabled() const;
void setProxyPeerConnectionsEnabled(bool enabled);
bool isAddTrackersEnabled() const;
void setAddTrackersEnabled(bool enabled);
QString additionalTrackers() const;
void setAdditionalTrackers(const QString &trackers);
bool isIPFilteringEnabled() const;
void setIPFilteringEnabled(bool enabled);
QString IPFilterFile() const;
void setIPFilterFile(QString path);
bool announceToAllTrackers() const;
void setAnnounceToAllTrackers(bool val);
uint diskCacheSize() const;
void setDiskCacheSize(uint size);
uint diskCacheTTL() const;
void setDiskCacheTTL(uint ttl);
bool useOSCache() const;
void setUseOSCache(bool use);
bool isAnonymousModeEnabled() const;
void setAnonymousModeEnabled(bool enabled);
bool isQueueingSystemEnabled() const;
void setQueueingSystemEnabled(bool enabled);
bool ignoreSlowTorrentsForQueueing() const;
void setIgnoreSlowTorrentsForQueueing(bool ignore);
uint outgoingPortsMin() const;
void setOutgoingPortsMin(uint min);
uint outgoingPortsMax() const;
void setOutgoingPortsMax(uint max);
bool ignoreLimitsOnLAN() const;
void setIgnoreLimitsOnLAN(bool ignore);
bool includeOverheadInLimits() const;
void setIncludeOverheadInLimits(bool include);
QString announceIP() const;
void setAnnounceIP(const QString &ip);
bool isSuperSeedingEnabled() const;
void setSuperSeedingEnabled(bool enabled);
int maxConnections() const;
void setMaxConnections(int max);
int maxHalfOpenConnections() const;
void setMaxHalfOpenConnections(int max);
int maxConnectionsPerTorrent() const;
void setMaxConnectionsPerTorrent(int max);
int maxUploads() const;
void setMaxUploads(int max);
int maxUploadsPerTorrent() const;
void setMaxUploadsPerTorrent(int max);
int maxActiveDownloads() const;
void setMaxActiveDownloads(int max);
int maxActiveUploads() const;
void setMaxActiveUploads(int max);
int maxActiveTorrents() const;
void setMaxActiveTorrents(int max);
bool isUTPEnabled() const;
void setUTPEnabled(bool enabled);
bool isUTPRateLimited() const;
void setUTPRateLimited(bool limited);
bool isTrackerFilteringEnabled() const;
void setTrackerFilteringEnabled(bool enabled);
TorrentHandle *findTorrent(const InfoHash &hash) const;
QHash<InfoHash, TorrentHandle *> torrents() const;
@@ -230,19 +338,11 @@ namespace BitTorrent
CacheStatus cacheStatus() const;
quint64 getAlltimeDL() const;
quint64 getAlltimeUL() const;
int downloadRateLimit() const;
int uploadRateLimit() const;
bool isListening() const;
MaxRatioAction maxRatioAction() const;
void setMaxRatioAction(MaxRatioAction act);
void changeSpeedLimitMode(bool alternative);
void setDownloadRateLimit(int rate);
void setUploadRateLimit(int rate);
void setGlobalMaxRatio(qreal ratio);
void enableIPFilter(const QString &filterPath, bool force = false);
void disableIPFilter();
void banIP(const QString &ip);
bool isKnownTorrent(const InfoHash &hash) const;
@@ -284,6 +384,7 @@ namespace BitTorrent
void torrentsUpdated();
void addTorrentFailed(const QString &error);
void torrentAdded(BitTorrent::TorrentHandle *const torrent);
void torrentNew(BitTorrent::TorrentHandle *const torrent);
void torrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent);
void torrentPaused(BitTorrent::TorrentHandle *const torrent);
void torrentResumed(BitTorrent::TorrentHandle *const torrent);
@@ -302,7 +403,7 @@ namespace BitTorrent
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent);
void speedLimitModeChanged(bool alternative);
void ipFilterParsed(bool error, int ruleCount);
void IPFilterParsed(bool error, int ruleCount);
void trackersAdded(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &trackers);
void trackersRemoved(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &trackers);
void trackersChanged(BitTorrent::TorrentHandle *const torrent);
@@ -314,7 +415,7 @@ namespace BitTorrent
void subcategoriesSupportChanged();
private slots:
void configure();
void configureDeferred();
void readAlerts();
void refresh();
void processBigRatios();
@@ -339,25 +440,30 @@ namespace BitTorrent
void initResumeFolder();
// Session configuration
void setSessionSettings();
void setProxySettings(libtorrent::proxy_settings proxySettings);
void adjustLimits();
Q_INVOKABLE void configure();
#if LIBTORRENT_VERSION_NUM < 10100
void configure(libtorrent::session_settings &sessionSettings);
void adjustLimits(libtorrent::session_settings &sessionSettings);
#else
void configure(libtorrent::settings_pack &settingsPack);
void adjustLimits(libtorrent::settings_pack &settingsPack);
#endif
void adjustLimits();
void processBannedIPs();
const QStringList getListeningIPs();
void setListeningPort();
void preAllocateAllFiles(bool b);
void setMaxConnectionsPerTorrent(int max);
void setMaxUploadsPerTorrent(int max);
void enableLSD(bool enable);
void enableDHT(bool enable);
void configureListeningInterface();
void changeSpeedLimitMode_impl(bool alternative);
void setAppendExtension(bool append);
void enableTracker(bool enable);
void enableBandwidthScheduler();
void populateAdditionalTrackers();
void enableIPFilter();
void disableIPFilter();
void startUpTorrents();
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
const TorrentInfo &torrentInfo = TorrentInfo(),
TorrentInfo torrentInfo = TorrentInfo(),
const QByteArray &fastresumeData = QByteArray());
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
void updateRatioTimer();
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
@@ -385,34 +491,100 @@ namespace BitTorrent
void saveResumeData();
void dispatchAlerts(std::auto_ptr<libtorrent::alert> alertPtr);
void getPendingAlerts(QVector<libtorrent::alert *> &out, ulong time = 0);
SettingsStorage *m_settings;
#if LIBTORRENT_VERSION_NUM < 10100
void dispatchAlerts(libtorrent::alert *alertPtr);
#endif
void getPendingAlerts(std::vector<libtorrent::alert *> &out, ulong time = 0);
// BitTorrent
libtorrent::session *m_nativeSession;
bool m_LSDEnabled;
bool m_DHTEnabled;
bool m_PeXEnabled;
bool m_queueingEnabled;
bool m_torrentExportEnabled;
bool m_finishedTorrentExportEnabled;
bool m_preAllocateAll;
qreal m_globalMaxRatio;
bool m_deferredConfigureScheduled;
bool m_IPFilteringChanged;
#if LIBTORRENT_VERSION_NUM >= 10100
bool m_listenInterfaceChanged; // optimization
#endif
CachedSettingValue<bool> m_isDHTEnabled;
CachedSettingValue<bool> m_isLSDEnabled;
CachedSettingValue<bool> m_isPeXEnabled;
CachedSettingValue<bool> m_isTrackerExchangeEnabled;
CachedSettingValue<bool> m_isIPFilteringEnabled;
CachedSettingValue<bool> m_isTrackerFilteringEnabled;
CachedSettingValue<QString> m_IPFilterFile;
CachedSettingValue<bool> m_announceToAllTrackers;
CachedSettingValue<uint> m_diskCacheSize;
CachedSettingValue<uint> m_diskCacheTTL;
CachedSettingValue<bool> m_useOSCache;
CachedSettingValue<bool> m_isAnonymousModeEnabled;
CachedSettingValue<bool> m_isQueueingEnabled;
CachedSettingValue<int> m_maxActiveDownloads;
CachedSettingValue<int> m_maxActiveUploads;
CachedSettingValue<int> m_maxActiveTorrents;
CachedSettingValue<bool> m_ignoreSlowTorrentsForQueueing;
CachedSettingValue<uint> m_outgoingPortsMin;
CachedSettingValue<uint> m_outgoingPortsMax;
CachedSettingValue<bool> m_ignoreLimitsOnLAN;
CachedSettingValue<bool> m_includeOverheadInLimits;
CachedSettingValue<QString> m_announceIP;
CachedSettingValue<bool> m_isSuperSeedingEnabled;
CachedSettingValue<int> m_maxConnections;
CachedSettingValue<int> m_maxHalfOpenConnections;
CachedSettingValue<int> m_maxUploads;
CachedSettingValue<int> m_maxConnectionsPerTorrent;
CachedSettingValue<int> m_maxUploadsPerTorrent;
CachedSettingValue<bool> m_isUTPEnabled;
CachedSettingValue<bool> m_isUTPRateLimited;
CachedSettingValue<bool> m_isAddTrackersEnabled;
CachedSettingValue<QString> m_additionalTrackers;
CachedSettingValue<qreal> m_globalMaxRatio;
CachedSettingValue<bool> m_isAddTorrentPaused;
CachedSettingValue<bool> m_isAppendExtensionEnabled;
CachedSettingValue<uint> m_refreshInterval;
CachedSettingValue<bool> m_isPreallocationEnabled;
CachedSettingValue<QString> m_torrentExportDirectory;
CachedSettingValue<QString> m_finishedTorrentExportDirectory;
CachedSettingValue<int> m_globalDownloadSpeedLimit;
CachedSettingValue<int> m_globalUploadSpeedLimit;
CachedSettingValue<int> m_altGlobalDownloadSpeedLimit;
CachedSettingValue<int> m_altGlobalUploadSpeedLimit;
CachedSettingValue<bool> m_isAltGlobalSpeedLimitEnabled;
CachedSettingValue<bool> m_isBandwidthSchedulerEnabled;
CachedSettingValue<uint> m_saveResumeDataInterval;
CachedSettingValue<int> m_port;
CachedSettingValue<bool> m_useRandomPort;
CachedSettingValue<QString> m_networkInterface;
CachedSettingValue<QString> m_networkInterfaceName;
CachedSettingValue<QString> m_networkInterfaceAddress;
CachedSettingValue<bool> m_isIPv6Enabled;
CachedSettingValue<int> m_encryption;
CachedSettingValue<bool> m_isForceProxyEnabled;
CachedSettingValue<bool> m_isProxyPeerConnectionsEnabled;
CachedSettingValue<QVariantMap> m_storedCategories;
CachedSettingValue<int> m_maxRatioAction;
CachedSettingValue<QString> m_defaultSavePath;
CachedSettingValue<QString> m_tempPath;
CachedSettingValue<bool> m_isSubcategoriesEnabled;
CachedSettingValue<bool> m_isTempPathEnabled;
CachedSettingValue<bool> m_isAutoTMMDisabledByDefault;
CachedSettingValue<bool> m_isDisableAutoTMMWhenCategoryChanged;
CachedSettingValue<bool> m_isDisableAutoTMMWhenDefaultSavePathChanged;
CachedSettingValue<bool> m_isDisableAutoTMMWhenCategorySavePathChanged;
CachedSettingValue<bool> m_isTrackerEnabled;
CachedSettingValue<QStringList> m_bannedIPs;
// Order is important. These need to be declared after their CachedSettingsValue
// counterparts, because they use them for initialization in the constructor
// initialization list.
const bool m_wasPexEnabled;
const bool m_wasTrackerExchangeEnabled;
int m_numResumeData;
int m_extraLimit;
bool m_appendExtension;
uint m_refreshInterval;
MaxRatioAction m_maxRatioAction;
QList<BitTorrent::TrackerEntry> m_additionalTrackers;
QString m_defaultSavePath;
QString m_tempPath;
QString m_filterPath;
QList<BitTorrent::TrackerEntry> m_additionalTrackerList;
QString m_resumeFolderPath;
QFile m_resumeFolderLock;
QHash<InfoHash, QString> m_savePathsToRemove;
bool m_useProxy;
QTimer *m_refreshTimer;
QTimer *m_bigRatioTimer;
@@ -434,12 +606,16 @@ namespace BitTorrent
TorrentStatusReport m_torrentStatusReport;
QStringMap m_categories;
#if LIBTORRENT_VERSION_NUM < 10100
QMutex m_alertsMutex;
QWaitCondition m_alertsWaitCondition;
QVector<libtorrent::alert *> m_alerts;
std::vector<libtorrent::alert *> m_alerts;
#endif
QNetworkConfigurationManager m_networkManager;
mutable QReadWriteLock m_lock;
static Session *m_instance;
};
}

View File

@@ -60,7 +60,7 @@
#include "trackerentry.h"
#include "torrenthandle.h"
static const char QB_EXT[] = ".!qB";
const QString QB_EXT {".!qB"};
namespace libt = libtorrent;
using namespace BitTorrent;
@@ -199,10 +199,10 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_nativeHandle(nativeHandle)
, m_state(TorrentState::Unknown)
, m_renameCount(0)
, m_useAutoTMM(data.savePath.isEmpty())
, m_name(data.name)
, m_savePath(Utils::Fs::toNativePath(data.savePath))
, m_category(data.category)
, m_useASM(data.savePath.isEmpty())
, m_hasSeedStatus(data.hasSeedStatus)
, m_ratioLimit(data.ratioLimit)
, m_tempPathDisabled(data.disableTempPath)
@@ -210,20 +210,14 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_pauseAfterRecheck(false)
, m_needSaveResumeData(false)
{
if (m_useASM)
if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
updateStatus();
m_hash = InfoHash(m_nativeStatus.info_hash);
adjustActualSavePath();
if (!data.resumed) {
if (!data.resumed)
setSequentialDownload(data.sequential);
if (hasMetadata()) {
if (m_session->isAppendExtensionEnabled())
appendExtensionsToIncompleteFiles();
}
}
}
TorrentHandle::~TorrentHandle() {}
@@ -332,19 +326,19 @@ QString TorrentHandle::contentPath(bool actual) const
return rootPath(actual);
}
bool TorrentHandle::isASMEnabled() const
bool TorrentHandle::isAutoTMMEnabled() const
{
return m_useASM;
return m_useAutoTMM;
}
void TorrentHandle::setASMEnabled(bool enabled)
void TorrentHandle::setAutoTMMEnabled(bool enabled)
{
if (m_useASM == enabled) return;
if (m_useAutoTMM == enabled) return;
m_useASM = enabled;
m_useAutoTMM = enabled;
m_session->handleTorrentSavingModeChanged(this);
if (m_useASM)
if (m_useAutoTMM)
move_impl(m_session->categorySavePath(m_category));
}
@@ -603,25 +597,6 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const
return res;
}
QPair<int, int> TorrentHandle::fileExtremityPieces(int index) const
{
if (!hasMetadata()) return qMakePair(-1, -1);
const int numPieces = piecesCount();
const qlonglong pieceSize = pieceLength();
// Determine the first and last piece of the file
int firstPiece = floor((m_torrentInfo.fileOffset(index) + 1) / (float) pieceSize);
Q_ASSERT((firstPiece >= 0) && (firstPiece < numPieces));
int numPiecesInFile = ceil(fileSize(index) / (float) pieceSize);
int lastPiece = firstPiece + numPiecesInFile - 1;
Q_ASSERT((lastPiece >= 0) && (lastPiece < numPieces));
Q_UNUSED(numPieces)
return qMakePair(firstPiece, lastPiece);
}
QVector<int> TorrentHandle::filePriorities() const
{
std::vector<int> fp;
@@ -739,13 +714,13 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
std::vector<int> fp;
SAFE_GET(fp, file_priorities);
QPair<int, int> extremities;
TorrentInfo::PieceRange extremities;
bool found = false;
int count = static_cast<int>(fp.size());
for (int i = 0; i < count; ++i) {
const QString ext = Utils::Fs::fileExtension(filePath(i));
if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) {
extremities = fileExtremityPieces(i);
extremities = info().filePieces(i);
found = true;
break;
}
@@ -755,8 +730,8 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
int first = 0;
int last = 0;
SAFE_GET(first, piece_priority, extremities.first);
SAFE_GET(last, piece_priority, extremities.second);
SAFE_GET(first, piece_priority, extremities.first());
SAFE_GET(last, piece_priority, extremities.last());
return ((first == 7) && (last == 7));
}
@@ -777,7 +752,7 @@ void TorrentHandle::updateState()
m_state = isSeed() ? TorrentState::PausedUploading : TorrentState::PausedDownloading;
}
else {
if (m_session->isQueueingEnabled() && isQueued() && !isChecking()) {
if (m_session->isQueueingSystemEnabled() && isQueued() && !isChecking()) {
m_state = isSeed() ? TorrentState::QueuedUploading : TorrentState::QueuedDownloading;
}
else {
@@ -1156,11 +1131,11 @@ bool TorrentHandle::setCategory(const QString &category)
m_needSaveResumeData = true;
m_session->handleTorrentCategoryChanged(this, oldCategory);
if (m_useASM) {
if (!m_session->isDisableASMWhenCategoryChanged())
if (m_useAutoTMM) {
if (!m_session->isDisableAutoTMMWhenCategoryChanged())
move_impl(m_session->categorySavePath(m_category));
else
setASMEnabled(false);
setAutoTMMEnabled(false);
}
}
@@ -1169,7 +1144,7 @@ bool TorrentHandle::setCategory(const QString &category)
void TorrentHandle::move(QString path)
{
m_useASM = false;
m_useAutoTMM = false;
m_session->handleTorrentSavingModeChanged(this);
path = Utils::Fs::fromNativePath(path.trimmed());
@@ -1251,13 +1226,13 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
// Determine the priority to set
int prio = b ? 7 : fp[index];
QPair<int, int> extremities = fileExtremityPieces(index);
TorrentInfo::PieceRange extremities = info().filePieces(index);
// worst case: AVI index = 1% of total file size (at the end of the file)
int nNumPieces = ceil(fileSize(index) * 0.01 / pieceLength());
for (int i = 0; i < nNumPieces; ++i) {
pp[extremities.first + i] = prio;
pp[extremities.second - i] = prio;
pp[extremities.first() + i] = prio;
pp[extremities.last() - i] = prio;
}
}
}
@@ -1309,10 +1284,6 @@ void TorrentHandle::moveStorage(const QString &newPath)
if (QDir(oldPath) == QDir(newPath)) return;
qDebug("move storage: %s to %s", qPrintable(oldPath), qPrintable(newPath));
// Create destination directory if necessary
// or move_storage() will fail...
QDir().mkpath(newPath);
try {
// Actually move the storage
m_nativeHandle.move_storage(newPath.toUtf8().constData());
@@ -1398,7 +1369,7 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
// Attempt to remove old folder if empty
QDir oldSaveDir(Utils::Fs::fromNativePath(m_oldPath));
if ((oldSaveDir != QDir(m_session->defaultSavePath())) && (oldSaveDir != QDir(m_session->tempPath()))) {
if (oldSaveDir != QDir(m_session->defaultSavePath())) {
qDebug("Attempting to remove %s", qPrintable(m_oldPath));
QDir().rmpath(m_oldPath);
}
@@ -1475,6 +1446,7 @@ void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert
m_hasSeedStatus = true;
adjustActualSavePath();
manageIncompleteFiles();
if (m_pauseAfterRecheck) {
m_pauseAfterRecheck = false;
@@ -1496,13 +1468,19 @@ void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_aler
m_hasSeedStatus = true;
adjustActualSavePath();
if (Preferences::instance()->recheckTorrentsOnCompletion())
forceRecheck();
manageIncompleteFiles();
if (isMoveInProgress() || m_renameCount > 0)
const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion();
if (isMoveInProgress() || m_renameCount > 0) {
if (recheckTorrentsOnCompletion)
m_moveFinishedTriggers.append(boost::bind(&TorrentHandle::forceRecheck, this));
m_moveFinishedTriggers.append(boost::bind(&Session::handleTorrentFinished, m_session, this));
else
}
else {
if (recheckTorrentsOnCompletion)
forceRecheck();
m_session->handleTorrentFinished(this);
}
}
void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p)
@@ -1530,7 +1508,7 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
resumeData["qBt-paused"] = isPaused();
resumeData["qBt-forced"] = isForced();
}
resumeData["qBt-savePath"] = m_useASM ? "" : Utils::String::toStdString(m_savePath);
resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Utils::String::toStdString(m_savePath);
resumeData["qBt-ratioLimit"] = Utils::String::toStdString(QString::number(m_ratioLimit));
resumeData["qBt-category"] = Utils::String::toStdString(m_category);
resumeData["qBt-name"] = Utils::String::toStdString(m_name);
@@ -1616,7 +1594,7 @@ void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p
QString name = filePath(p->index);
if (name.endsWith(QB_EXT)) {
const QString oldName = name;
name.chop(QString(QB_EXT).size());
name.chop(QB_EXT.size());
qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name));
renameFile(p->index, name);
}
@@ -1637,7 +1615,7 @@ void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p
qDebug("Metadata received for torrent %s.", qPrintable(name()));
updateStatus();
if (m_session->isAppendExtensionEnabled())
appendExtensionsToIncompleteFiles();
manageIncompleteFiles();
m_session->handleTorrentMetadataReceived(this);
if (isPaused()) {
@@ -1655,7 +1633,7 @@ void TorrentHandle::handleTempPathChanged()
void TorrentHandle::handleCategorySavePathChanged()
{
if (m_useASM)
if (m_useAutoTMM)
move_impl(m_session->categorySavePath(m_category));
}
@@ -1663,10 +1641,7 @@ void TorrentHandle::handleAppendExtensionToggled()
{
if (!hasMetadata()) return;
if (m_session->isAppendExtensionEnabled())
appendExtensionsToIncompleteFiles();
else
removeExtensionsFromIncompleteFiles();
manageIncompleteFiles();
}
void TorrentHandle::handleAlert(libtorrent::alert *a)
@@ -1723,30 +1698,26 @@ void TorrentHandle::handleAlert(libtorrent::alert *a)
}
}
void TorrentHandle::appendExtensionsToIncompleteFiles()
void TorrentHandle::manageIncompleteFiles()
{
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
QVector<qreal> fp = filesProgress();
for (int i = 0; i < filesCount(); ++i) {
if ((fileSize(i) > 0) && (fp[i] < 1)) {
const QString name = filePath(i);
QString name = filePath(i);
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1)) {
if (!name.endsWith(QB_EXT)) {
const QString newName = name + QB_EXT;
qDebug("Renaming %s to %s", qPrintable(name), qPrintable(newName));
qDebug() << "Renaming" << name << "to" << newName;
renameFile(i, newName);
}
}
}
}
void TorrentHandle::removeExtensionsFromIncompleteFiles()
{
for (int i = 0; i < filesCount(); ++i) {
QString name = filePath(i);
if (name.endsWith(QB_EXT)) {
const QString oldName = name;
name.chop(QString(QB_EXT).size());
qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name));
renameFile(i, name);
else {
if (name.endsWith(QB_EXT)) {
const QString oldName = name;
name.chop(QB_EXT.size());
qDebug() << "Renaming" << oldName << "to" << name;
renameFile(i, name);
}
}
}
}
@@ -1769,8 +1740,8 @@ void TorrentHandle::adjustActualSavePath_impl()
}
else {
// Moving all downloading torrents to temporary save path
path = m_session->tempPath();
qDebug("Moving torrent to its temp save path: %s", qPrintable(path));
path = m_session->torrentTempPath(hash());
qDebug() << "Moving torrent to its temp save path:" << path;
}
moveStorage(Utils::Fs::toNativePath(path));

View File

@@ -54,6 +54,8 @@ class QBitArray;
class QStringList;
template<typename T, typename U> struct QPair;
extern const QString QB_EXT;
namespace libtorrent
{
class alert;
@@ -227,8 +229,8 @@ namespace BitTorrent
QString rootPath(bool actual = false) const;
QString contentPath(bool actual = false) const;
bool isASMEnabled() const;
void setASMEnabled(bool enabled);
bool isAutoTMMEnabled() const;
void setAutoTMMEnabled(bool enabled);
QString category() const;
bool belongsToCategory(const QString &category) const;
bool setCategory(const QString &category);
@@ -245,7 +247,6 @@ namespace BitTorrent
qlonglong fileSize(int index) const;
QStringList absoluteFilePaths() const;
QStringList absoluteFilePathsUnwanted() const;
QPair<int, int> fileExtremityPieces(int index) const;
QVector<int> filePriorities() const;
TorrentInfo info() const;
@@ -386,8 +387,7 @@ namespace BitTorrent
void adjustActualSavePath_impl();
void move_impl(QString path);
void moveStorage(const QString &newPath);
void appendExtensionsToIncompleteFiles();
void removeExtensionsFromIncompleteFiles();
void manageIncompleteFiles();
bool addTracker(const TrackerEntry &tracker);
bool addUrlSeed(const QUrl &urlSeed);
bool removeUrlSeed(const QUrl &urlSeed);
@@ -411,7 +411,7 @@ namespace BitTorrent
QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount;
bool m_useASM;
bool m_useAutoTMM;
// Persistent data
QString m_name;

View File

@@ -26,6 +26,7 @@
* exception statement from your version.
*/
#include <QDebug>
#include <QString>
#include <QList>
#include <QUrl>
@@ -138,6 +139,12 @@ int TorrentInfo::pieceLength() const
return m_nativeInfo->piece_length();
}
int TorrentInfo::pieceLength(int index) const
{
if (!isValid()) return -1;
return m_nativeInfo->piece_size(index);
}
int TorrentInfo::piecesCount() const
{
if (!isValid()) return -1;
@@ -178,7 +185,7 @@ qlonglong TorrentInfo::fileSize(int index) const
qlonglong TorrentInfo::fileOffset(int index) const
{
if (!isValid()) return -1;
if (!isValid()) return -1;
return m_nativeInfo->file_at(index).offset;
}
@@ -213,24 +220,79 @@ QByteArray TorrentInfo::metadata() const
QStringList TorrentInfo::filesForPiece(int pieceIndex) const
{
if (pieceIndex < 0)
return QStringList();
// no checks here because fileIndicesForPiece() will return an empty list
QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
std::vector<libtorrent::file_slice> files(
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
QStringList res;
for (const libtorrent::file_slice& s: files) {
res.append(filePath(s.file_index));
}
res.reserve(fileIndices.size());
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res),
[this](int i) { return filePath(i); });
return res;
}
QVector<int> TorrentInfo::fileIndicesForPiece(int pieceIndex) const
{
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
return QVector<int>();
std::vector<libt::file_slice> files(
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
QVector<int> res;
res.reserve(int(files.size()));
std::transform(files.begin(), files.end(), std::back_inserter(res),
[](const libt::file_slice &s) { return s.file_index; });
return res;
}
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
{
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
return {};
int index = fileIndex(file);
if (index == -1) {
qDebug() << "Filename" << file << "was not found in torrent" << name();
return {};
}
return filePieces(index);
}
TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
{
if (!isValid())
return {};
if ((fileIndex < 0) || (fileIndex >= filesCount())) {
qDebug() << "File index (" << fileIndex << ") is out of range for torrent" << name();
return {};
}
const libt::file_storage &files = nativeInfo()->files();
const auto fileSize = files.file_size(fileIndex);
const auto firstOffset = files.file_offset(fileIndex);
return makeInterval(static_cast<int>(firstOffset / pieceLength()),
static_cast<int>((firstOffset + fileSize - 1) / pieceLength()));
}
void TorrentInfo::renameFile(uint index, const QString &newPath)
{
if (!isValid()) return;
nativeInfo()->rename_file(index, Utils::String::toStdString(newPath));
}
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
{
// the check whether the object valid is not needed here
// because filesCount() returns -1 in that case and the loop exits immediately
for (int i = 0; i < filesCount(); ++i)
if (fileName == filePath(i))
return i;
return -1;
}
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
{
return m_nativeInfo;

View File

@@ -34,12 +34,15 @@
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/version.hpp>
#include "base/indexrange.h"
class QString;
class QUrl;
class QDateTime;
class QStringList;
class QByteArray;
template<typename T> class QList;
template<typename T> class QVector;
namespace BitTorrent
{
@@ -75,6 +78,7 @@ namespace BitTorrent
qlonglong totalSize() const;
int filesCount() const;
int pieceLength() const;
int pieceLength(int index) const;
int piecesCount() const;
QString filePath(int index) const;
QStringList filePaths() const;
@@ -86,12 +90,21 @@ namespace BitTorrent
QList<QUrl> urlSeeds() const;
QByteArray metadata() const;
QStringList filesForPiece(int pieceIndex) const;
QVector<int> fileIndicesForPiece(int pieceIndex) const;
using PieceRange = IndexRange<int>;
// returns pair of the first and the last pieces into which
// the given file extends (maybe partially).
PieceRange filePieces(const QString &file) const;
PieceRange filePieces(int fileIndex) const;
void renameFile(uint index, const QString &newPath);
NativePtr nativeInfo() const;
private:
// returns file index or -1 if fileName is not found
int fileIndex(const QString &fileName) const;
NativePtr m_nativeInfo;
};
}

View File

@@ -91,6 +91,7 @@ void Server::incomingConnection(int socketDescriptor)
#else
static_cast<QSslSocket*>(serverSocket)->setLocalCertificate(m_certificates.first());
#endif
static_cast<QSslSocket*>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket*>(serverSocket)->startServerEncryption();
}
#endif

130
src/base/indexrange.h Normal file
View File

@@ -0,0 +1,130 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin
*
* 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.
*/
#ifndef QBT_INDEXRANGE_H
#define QBT_INDEXRANGE_H
#include <QtGlobal>
// Interval is defined via [first;last]
template <typename Index>
class IndexInterval
{
public:
using IndexType = Index;
IndexInterval(IndexType first, IndexType last)
: m_first {first}
, m_last {last}
{
Q_ASSERT(first <= last);
}
IndexType first() const
{
return m_first;
}
IndexType last() const
{
return m_last;
}
private:
IndexType m_first;
IndexType m_last;
};
template <typename T>
inline IndexInterval<T> makeInterval(T first, T last)
{
return {first, last};
}
// range is defined via first index and size
template <typename Index, typename IndexDiff = Index>
class IndexRange
{
public:
using IndexType = Index;
using IndexDiffType = IndexDiff;
constexpr IndexRange()
: m_first {0}
, m_size {0}
{
}
constexpr IndexRange(IndexType first, IndexDiffType size)
: m_first {first}
, m_size {size}
{
}
constexpr IndexRange(const IndexInterval<IndexType> &interval)
: m_first {interval.first()}
, m_size {interval.last() - interval.first() + 1}
{
}
constexpr IndexType begin() const
{
return m_first;
}
constexpr IndexType end() const
{
return m_first + m_size;
}
constexpr IndexDiffType size() const
{
return m_size;
}
constexpr IndexType first() const
{
return m_first;
}
constexpr IndexType last() const
{
return m_first + m_size - 1;
}
constexpr bool isEmpty() const
{
return m_size == 0;
}
private:
IndexType m_first;
IndexDiffType m_size;
};
#endif // QBT_INDEXRANGE_H

View File

@@ -27,20 +27,21 @@
* exception statement from your version.
*/
#include "downloadmanager.h"
#include <QDateTime>
#include <QNetworkRequest>
#include <QNetworkProxy>
#include <QNetworkCookieJar>
#include <QNetworkReply>
#include <QDebug>
#include <QNetworkCookie>
#include <QNetworkCookieJar>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QSslError>
#include <QUrl>
#include <QDebug>
#include "base/preferences.h"
#include "downloadhandler.h"
#include "downloadmanager.h"
#include "proxyconfigurationmanager.h"
// Spoof Firefox 38 user agent to avoid web server banning
const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0";
@@ -75,6 +76,9 @@ namespace
Preferences::instance()->setNetworkCookies(cookies);
}
using QNetworkCookieJar::allCookies;
using QNetworkCookieJar::setAllCookies;
#ifndef QBT_USES_QT5
virtual bool deleteCookie(const QNetworkCookie &cookie)
{
@@ -188,6 +192,16 @@ bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
}
QList<QNetworkCookie> DownloadManager::allCookies() const
{
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
}
void DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
{
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
}
bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
{
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
@@ -195,16 +209,16 @@ bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
void DownloadManager::applyProxySettings()
{
auto proxyManager = ProxyConfigurationManager::instance();
ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
QNetworkProxy proxy;
const Preferences* const pref = Preferences::instance();
if (pref->isProxyEnabled() && !pref->isProxyOnlyForTorrents()) {
if (!proxyManager->isProxyOnlyForTorrents() && (proxyConfig.type != ProxyType::None)) {
// Proxy enabled
proxy.setHostName(pref->getProxyIp());
proxy.setPort(pref->getProxyPort());
proxy.setHostName(proxyConfig.ip);
proxy.setPort(proxyConfig.port);
// Default proxy type is HTTP, we must change if it is SOCKS5
const int proxyType = pref->getProxyType();
if ((proxyType == Proxy::SOCKS5) || (proxyType == Proxy::SOCKS5_PW)) {
if ((proxyConfig.type == ProxyType::SOCKS5) || (proxyConfig.type == ProxyType::SOCKS5_PW)) {
qDebug() << Q_FUNC_INFO << "using SOCKS proxy";
proxy.setType(QNetworkProxy::Socks5Proxy);
}
@@ -213,10 +227,10 @@ void DownloadManager::applyProxySettings()
proxy.setType(QNetworkProxy::HttpProxy);
}
// Authentication?
if (pref->isProxyAuthEnabled()) {
if (proxyManager->isAuthenticationRequired()) {
qDebug("Proxy requires authentication, authenticating");
proxy.setUser(pref->getProxyUsername());
proxy.setPassword(pref->getProxyPassword());
proxy.setUser(proxyConfig.username);
proxy.setPassword(proxyConfig.password);
}
}
else {

View File

@@ -54,6 +54,8 @@ namespace Net
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false, const QString &userAgent = "");
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
QList<QNetworkCookie> allCookies() const;
void setAllCookies(const QList<QNetworkCookie> &cookieList);
bool deleteCookie(const QNetworkCookie &cookie);
private slots:

View File

@@ -45,7 +45,6 @@
static const char DATABASE_URL[] = "https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz";
static const char GEOIP_FOLDER[] = "GeoIP";
static const char GEOIP_FILENAME[] = "GeoLite2-Country.mmdb";
static const int CACHE_SIZE = 1000;
static const int UPDATE_INTERVAL = 30; // Days between database updates
using namespace Net;

View File

@@ -26,13 +26,17 @@
* exception statement from your version.
*/
#include "portforwarder.h"
#include <QDebug>
#include <libtorrent/session.hpp>
#include <libtorrent/version.hpp>
#include "base/logger.h"
#include "base/preferences.h"
#include "portforwarder.h"
#include "base/settingsstorage.h"
static const QString KEY_ENABLED = QLatin1String("Network/PortForwardingEnabled");
namespace libt = libtorrent;
using namespace Net;
@@ -42,8 +46,8 @@ PortForwarder::PortForwarder(libtorrent::session *provider, QObject *parent)
, m_active(false)
, m_provider(provider)
{
configure();
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
if (SettingsStorage::instance()->loadValue(KEY_ENABLED, true).toBool())
start();
}
PortForwarder::~PortForwarder()
@@ -70,7 +74,24 @@ PortForwarder *PortForwarder::instance()
return m_instance;
}
void PortForwarder::addPort(qint16 port)
bool PortForwarder::isEnabled() const
{
return m_active;
}
void PortForwarder::setEnabled(bool enabled)
{
if (m_active != enabled) {
if (enabled)
start();
else
stop();
SettingsStorage::instance()->storeValue(KEY_ENABLED, enabled);
}
}
void PortForwarder::addPort(quint16 port)
{
if (!m_mappedPorts.contains(port)) {
m_mappedPorts.insert(port, 0);
@@ -79,7 +100,7 @@ void PortForwarder::addPort(qint16 port)
}
}
void PortForwarder::deletePort(qint16 port)
void PortForwarder::deletePort(quint16 port)
{
if (m_mappedPorts.contains(port)) {
if (m_active)
@@ -88,23 +109,19 @@ void PortForwarder::deletePort(qint16 port)
}
}
void PortForwarder::configure()
{
bool enable = Preferences::instance()->isUPnPEnabled();
if (m_active != enable) {
if (enable)
start();
else
stop();
}
}
void PortForwarder::start()
{
qDebug("Enabling UPnP / NAT-PMP");
#if LIBTORRENT_VERSION_NUM < 10100
m_provider->start_upnp();
m_provider->start_natpmp();
foreach (qint16 port, m_mappedPorts.keys())
#else
libt::settings_pack settingsPack = m_provider->get_settings();
settingsPack.set_bool(libt::settings_pack::enable_upnp, true);
settingsPack.set_bool(libt::settings_pack::enable_natpmp, true);
m_provider->apply_settings(settingsPack);
#endif
foreach (quint16 port, m_mappedPorts.keys())
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
m_active = true;
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO);
@@ -113,8 +130,15 @@ void PortForwarder::start()
void PortForwarder::stop()
{
qDebug("Disabling UPnP / NAT-PMP");
#if LIBTORRENT_VERSION_NUM < 10100
m_provider->stop_upnp();
m_provider->stop_natpmp();
#else
libt::settings_pack settingsPack = m_provider->get_settings();
settingsPack.set_bool(libt::settings_pack::enable_upnp, false);
settingsPack.set_bool(libt::settings_pack::enable_natpmp, false);
m_provider->apply_settings(settingsPack);
#endif
m_active = false;
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [OFF]"), Log::INFO);
}

View File

@@ -49,11 +49,11 @@ namespace Net
static void freeInstance();
static PortForwarder *instance();
void addPort(qint16 port);
void deletePort(qint16 port);
bool isEnabled() const;
void setEnabled(bool enabled);
private slots:
void configure();
void addPort(quint16 port);
void deletePort(quint16 port);
private:
explicit PortForwarder(libtorrent::session *const provider, QObject *parent = 0);
@@ -64,7 +64,7 @@ namespace Net
bool m_active;
libtorrent::session *m_provider;
QHash<qint16, int> m_mappedPorts;
QHash<quint16, int> m_mappedPorts;
static PortForwarder *m_instance;
};

View File

@@ -40,7 +40,7 @@ namespace
{
const quint32 __ENDIAN_TEST__ = 0x00000001;
const bool __IS_LITTLE_ENDIAN__ = (reinterpret_cast<const uchar *>(&__ENDIAN_TEST__)[0] == 0x01);
const int MAX_FILE_SIZE = 10485760; // 10MB
const qint32 MAX_FILE_SIZE = 67108864; // 64MB
const char DB_TYPE[] = "GeoLite2-Country";
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";

View File

@@ -0,0 +1,163 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "proxyconfigurationmanager.h"
#include "base/settingsstorage.h"
#define SETTINGS_KEY(name) "Network/Proxy/" name
const QString KEY_ONLY_FOR_TORRENTS = SETTINGS_KEY("OnlyForTorrents");
const QString KEY_TYPE = SETTINGS_KEY("Type");
const QString KEY_IP = SETTINGS_KEY("IP");
const QString KEY_PORT = SETTINGS_KEY("Port");
const QString KEY_USERNAME = SETTINGS_KEY("Username");
const QString KEY_PASSWORD = SETTINGS_KEY("Password");
namespace
{
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
inline bool isSameConfig(const Net::ProxyConfiguration &conf1, const Net::ProxyConfiguration &conf2)
{
return conf1.type == conf2.type
&& conf1.ip == conf2.ip
&& conf1.port == conf2.port
&& conf1.username == conf2.username
&& conf1.password == conf2.password;
}
}
using namespace Net;
ProxyConfigurationManager *ProxyConfigurationManager::m_instance = nullptr;
ProxyConfigurationManager::ProxyConfigurationManager(QObject *parent)
: QObject(parent)
{
m_isProxyOnlyForTorrents = settings()->loadValue(KEY_ONLY_FOR_TORRENTS, false).toBool();
m_config.type = static_cast<ProxyType>(
settings()->loadValue(KEY_TYPE, static_cast<int>(ProxyType::None)).toInt());
if ((m_config.type < ProxyType::None) || (m_config.type > ProxyType::SOCKS4))
m_config.type = ProxyType::None;
m_config.ip = settings()->loadValue(KEY_IP, "0.0.0.0").toString();
m_config.port = static_cast<ushort>(settings()->loadValue(KEY_PORT, 8080).toUInt());
m_config.username = settings()->loadValue(KEY_USERNAME).toString();
m_config.password = settings()->loadValue(KEY_PASSWORD).toString();
configureProxy();
}
void ProxyConfigurationManager::initInstance()
{
if (!m_instance)
m_instance = new ProxyConfigurationManager;
}
void ProxyConfigurationManager::freeInstance()
{
if (m_instance) {
delete m_instance;
m_instance = 0;
}
}
ProxyConfigurationManager *ProxyConfigurationManager::instance()
{
return m_instance;
}
ProxyConfiguration ProxyConfigurationManager::proxyConfiguration() const
{
return m_config;
}
void ProxyConfigurationManager::setProxyConfiguration(const ProxyConfiguration &config)
{
if (!isSameConfig(config, m_config)) {
m_config = config;
settings()->storeValue(KEY_TYPE, static_cast<int>(config.type));
settings()->storeValue(KEY_IP, config.ip);
settings()->storeValue(KEY_PORT, config.port);
settings()->storeValue(KEY_USERNAME, config.username);
settings()->storeValue(KEY_PASSWORD, config.password);
configureProxy();
emit proxyConfigurationChanged();
}
}
bool ProxyConfigurationManager::isProxyOnlyForTorrents() const
{
return m_isProxyOnlyForTorrents || (m_config.type == ProxyType::SOCKS4);
}
void ProxyConfigurationManager::setProxyOnlyForTorrents(bool onlyForTorrents)
{
if (m_isProxyOnlyForTorrents != onlyForTorrents) {
settings()->storeValue(KEY_ONLY_FOR_TORRENTS, onlyForTorrents);
m_isProxyOnlyForTorrents = onlyForTorrents;
}
}
bool ProxyConfigurationManager::isAuthenticationRequired() const
{
return m_config.type == ProxyType::SOCKS5_PW
|| m_config.type == ProxyType::HTTP_PW;
}
void ProxyConfigurationManager::configureProxy()
{
// Define environment variables for urllib in search engine plugins
QString proxyStrHTTP, proxyStrSOCK;
if (!m_isProxyOnlyForTorrents) {
switch (m_config.type) {
case ProxyType::HTTP_PW:
proxyStrHTTP = QString("http://%1:%2@%3:%4").arg(m_config.username)
.arg(m_config.password).arg(m_config.ip).arg(m_config.port);
break;
case ProxyType::HTTP:
proxyStrHTTP = QString("http://%1:%2").arg(m_config.ip).arg(m_config.port);
break;
case ProxyType::SOCKS5:
proxyStrSOCK = QString("%1:%2").arg(m_config.ip).arg(m_config.port);
break;
case ProxyType::SOCKS5_PW:
proxyStrSOCK = QString("%1:%2@%3:%4").arg(m_config.username)
.arg(m_config.password).arg(m_config.ip).arg(m_config.port);
break;
default:
qDebug("Disabling HTTP communications proxy");
}
qDebug("HTTP communications proxy string: %s"
, qPrintable((m_config.type == ProxyType::SOCKS5) || (m_config.type == ProxyType::SOCKS5_PW)
? proxyStrSOCK : proxyStrHTTP));
}
qputenv("http_proxy", proxyStrHTTP.toLocal8Bit());
qputenv("https_proxy", proxyStrHTTP.toLocal8Bit());
qputenv("sock_proxy", proxyStrSOCK.toLocal8Bit());
}

View File

@@ -0,0 +1,87 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#ifndef NET_PROXYCONFIGURATIONMANAGER_H
#define NET_PROXYCONFIGURATIONMANAGER_H
#include <QObject>
namespace Net
{
enum class ProxyType
{
None = 0,
HTTP = 1,
SOCKS5 = 2,
HTTP_PW = 3,
SOCKS5_PW = 4,
SOCKS4 = 5
};
struct ProxyConfiguration
{
ProxyType type = ProxyType::None;
QString ip = "0.0.0.0";
ushort port = 8080;
QString username;
QString password;
};
class ProxyConfigurationManager: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ProxyConfigurationManager)
explicit ProxyConfigurationManager(QObject *parent = nullptr);
~ProxyConfigurationManager() = default;
public:
static void initInstance();
static void freeInstance();
static ProxyConfigurationManager *instance();
ProxyConfiguration proxyConfiguration() const;
void setProxyConfiguration(const ProxyConfiguration &config);
bool isProxyOnlyForTorrents() const;
void setProxyOnlyForTorrents(bool onlyForTorrents);
bool isAuthenticationRequired() const;
signals:
void proxyConfigurationChanged();
private:
void configureProxy();
static ProxyConfigurationManager *m_instance;
ProxyConfiguration m_config;
bool m_isProxyOnlyForTorrents;
};
}
#endif // NET_PROXYCONFIGURATIONMANAGER_H

View File

@@ -53,7 +53,9 @@
namespace
{
const short DEFAULT_PORT = 25;
#ifndef QT_NO_OPENSSL
const short DEFAULT_PORT_SSL = 465;
#endif
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
{
@@ -97,6 +99,13 @@ Smtp::Smtp(QObject *parent)
, m_useSsl(false)
, m_authType(AuthPlain)
{
static bool needToRegisterMetaType = true;
if (needToRegisterMetaType) {
qRegisterMetaType<QAbstractSocket::SocketError>();
needToRegisterMetaType = false;
}
#ifndef QT_NO_OPENSSL
m_socket = new QSslSocket(this);
#else
@@ -105,6 +114,7 @@ Smtp::Smtp(QObject *parent)
connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(error(QAbstractSocket::SocketError)));
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
@@ -525,3 +535,11 @@ QString Smtp::getCurrentDateTime() const
QString ret = weekDayStr + ", " + dayStr + " " + monthStr + " " + yearStr + " " + timeStr + " " + timeOffsetStr;
return ret;
}
void Smtp::error(QAbstractSocket::SocketError socketError)
{
// Getting a remote host closed error is apparently normal, even when successfully sending
// an email
if (socketError != QAbstractSocket::RemoteHostClosedError)
logError(m_socket->errorString());
}

View File

@@ -39,6 +39,8 @@
#include <QObject>
#include <QByteArray>
#include <QHash>
#include <QAbstractSocket>
#include <QMetaType>
QT_BEGIN_NAMESPACE
class QTextStream;
@@ -64,6 +66,7 @@ namespace Net
private slots:
void readyRead();
void error(QAbstractSocket::SocketError socketError);
private:
enum States
@@ -123,4 +126,8 @@ namespace Net
};
}
#ifndef QBT_USES_QT5
Q_DECLARE_METATYPE(QAbstractSocket::SocketError)
#endif
#endif

View File

@@ -50,8 +50,6 @@
#include <CoreServices/CoreServices.h>
#endif
#include <cstdlib>
#include "utils/fs.h"
#include "utils/misc.h"
#include "settingsstorage.h"
@@ -60,10 +58,7 @@
Preferences* Preferences::m_instance = 0;
Preferences::Preferences()
: m_randomPort(rand() % 64512 + 1024)
{
}
Preferences::Preferences() {}
Preferences *Preferences::instance()
{
@@ -105,16 +100,6 @@ void Preferences::setLocale(const QString &locale)
setValue("Preferences/General/Locale", locale);
}
bool Preferences::useProgramNotification() const
{
return value("Preferences/General/ProgramNotification", true).toBool();
}
void Preferences::useProgramNotification(bool use)
{
setValue("Preferences/General/ProgramNotification", use);
}
bool Preferences::deleteTorrentFilesAsDefault() const
{
return value("Preferences/General/DeleteTorrentsFilesAsDefault", false).toBool();
@@ -175,16 +160,6 @@ void Preferences::setHideZeroComboValues(int n)
setValue("Preferences/General/HideZeroComboValues", n);
}
bool Preferences::useRandomPort() const
{
return value("Preferences/General/UseRandomPort", false).toBool();
}
void Preferences::setRandomPort(bool b)
{
setValue("Preferences/General/UseRandomPort", b);
}
bool Preferences::systrayIntegration() const
{
return value("Preferences/General/SystrayEnabled", true).toBool();
@@ -277,16 +252,6 @@ void Preferences::setWinStartup(bool b)
#endif
// Downloads
bool Preferences::useIncompleteFilesExtension() const
{
return value("Preferences/Downloads/UseIncompleteExtension", false).toBool();
}
void Preferences::useIncompleteFilesExtension(bool enabled)
{
setValue("Preferences/Downloads/UseIncompleteExtension", enabled);
}
QString Preferences::lastLocationPath() const
{
return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
@@ -297,16 +262,6 @@ void Preferences::setLastLocationPath(const QString &path)
setValue("Preferences/Downloads/LastLocationPath", Utils::Fs::fromNativePath(path));
}
bool Preferences::preAllocateAllFiles() const
{
return value("Preferences/Downloads/PreAllocation", false).toBool();
}
void Preferences::preAllocateAllFiles(bool enabled)
{
return setValue("Preferences/Downloads/PreAllocation", enabled);
}
QVariantHash Preferences::getScanDirs() const
{
return value("Preferences/Downloads/ScanDirsV2").toHash();
@@ -328,36 +283,6 @@ void Preferences::setScanDirsLastPath(const QString &path)
setValue("Preferences/Downloads/ScanDirsLastPath", Utils::Fs::fromNativePath(path));
}
bool Preferences::isTorrentExportEnabled() const
{
return !value("Preferences/Downloads/TorrentExportDir").toString().isEmpty();
}
QString Preferences::getTorrentExportDir() const
{
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TorrentExportDir").toString());
}
void Preferences::setTorrentExportDir(QString path)
{
setValue("Preferences/Downloads/TorrentExportDir", Utils::Fs::fromNativePath(path.trimmed()));
}
bool Preferences::isFinishedTorrentExportEnabled() const
{
return !value("Preferences/Downloads/FinishedTorrentExportDir").toString().isEmpty();
}
QString Preferences::getFinishedTorrentExportDir() const
{
return Utils::Fs::fromNativePath(value("Preferences/Downloads/FinishedTorrentExportDir").toString());
}
void Preferences::setFinishedTorrentExportDir(QString path)
{
setValue("Preferences/Downloads/FinishedTorrentExportDir", Utils::Fs::fromNativePath(path.trimmed()));
}
bool Preferences::isMailNotificationEnabled() const
{
return value("Preferences/MailNotification/enabled", false).toBool();
@@ -448,97 +373,6 @@ void Preferences::setActionOnDblClOnTorrentFn(int act)
setValue("Preferences/Downloads/DblClOnTorFn", act);
}
// Connection options
int Preferences::getSessionPort() const
{
if (useRandomPort())
return m_randomPort;
return value("Preferences/Connection/PortRangeMin", 8999).toInt();
}
void Preferences::setSessionPort(int port)
{
setValue("Preferences/Connection/PortRangeMin", port);
}
bool Preferences::isUPnPEnabled() const
{
return value("Preferences/Connection/UPnP", true).toBool();
}
void Preferences::setUPnPEnabled(bool enabled)
{
setValue("Preferences/Connection/UPnP", enabled);
}
int Preferences::getGlobalDownloadLimit() const
{
return value("Preferences/Connection/GlobalDLLimit", -1).toInt();
}
void Preferences::setGlobalDownloadLimit(int limit)
{
if (limit <= 0)
limit = -1;
setValue("Preferences/Connection/GlobalDLLimit", limit);
}
int Preferences::getGlobalUploadLimit() const
{
return value("Preferences/Connection/GlobalUPLimit", -1).toInt();
}
void Preferences::setGlobalUploadLimit(int limit)
{
if (limit <= 0)
limit = -1;
setValue("Preferences/Connection/GlobalUPLimit", limit);
}
int Preferences::getAltGlobalDownloadLimit() const
{
return value("Preferences/Connection/GlobalDLLimitAlt", 10).toInt();
}
void Preferences::setAltGlobalDownloadLimit(int limit)
{
if (limit <= 0)
limit = -1;
setValue("Preferences/Connection/GlobalDLLimitAlt", limit);
}
int Preferences::getAltGlobalUploadLimit() const
{
return value("Preferences/Connection/GlobalUPLimitAlt", 10).toInt();
}
void Preferences::setAltGlobalUploadLimit(int limit)
{
if (limit <= 0)
limit = -1;
setValue("Preferences/Connection/GlobalUPLimitAlt", limit);
}
bool Preferences::isAltBandwidthEnabled() const
{
return value("Preferences/Connection/alt_speeds_on", false).toBool();
}
void Preferences::setAltBandwidthEnabled(bool enabled)
{
setValue("Preferences/Connection/alt_speeds_on", enabled);
}
bool Preferences::isSchedulerEnabled() const
{
return value("Preferences/Scheduler/Enabled", false).toBool();
}
void Preferences::setSchedulerEnabled(bool enabled)
{
setValue("Preferences/Scheduler/Enabled", enabled);
}
QTime Preferences::getSchedulerStartTime() const
{
return value("Preferences/Scheduler/start_time", QTime(8,0)).toTime();
@@ -569,286 +403,6 @@ void Preferences::setSchedulerDays(scheduler_days days)
setValue("Preferences/Scheduler/days", (int)days);
}
// Proxy options
bool Preferences::isProxyEnabled() const
{
return getProxyType() > 0;
}
bool Preferences::isProxyAuthEnabled() const
{
return value("Preferences/Connection/Proxy/Authentication", false).toBool();
}
void Preferences::setProxyAuthEnabled(bool enabled)
{
setValue("Preferences/Connection/Proxy/Authentication", enabled);
}
QString Preferences::getProxyIp() const
{
return value("Preferences/Connection/Proxy/IP", "0.0.0.0").toString();
}
void Preferences::setProxyIp(const QString &ip)
{
setValue("Preferences/Connection/Proxy/IP", ip);
}
unsigned short Preferences::getProxyPort() const
{
return value("Preferences/Connection/Proxy/Port", 8080).toInt();
}
void Preferences::setProxyPort(unsigned short port)
{
setValue("Preferences/Connection/Proxy/Port", port);
}
QString Preferences::getProxyUsername() const
{
return value("Preferences/Connection/Proxy/Username").toString();
}
void Preferences::setProxyUsername(const QString &username)
{
setValue("Preferences/Connection/Proxy/Username", username);
}
QString Preferences::getProxyPassword() const
{
return value("Preferences/Connection/Proxy/Password").toString();
}
void Preferences::setProxyPassword(const QString &password)
{
setValue("Preferences/Connection/Proxy/Password", password);
}
int Preferences::getProxyType() const
{
return value("Preferences/Connection/ProxyType", 0).toInt();
}
void Preferences::setProxyType(int type)
{
setValue("Preferences/Connection/ProxyType", type);
}
bool Preferences::proxyPeerConnections() const
{
return value("Preferences/Connection/ProxyPeerConnections", false).toBool();
}
void Preferences::setProxyPeerConnections(bool enabled)
{
setValue("Preferences/Connection/ProxyPeerConnections", enabled);
}
bool Preferences::getForceProxy() const
{
return value("Preferences/Connection/ProxyForce", true).toBool();
}
void Preferences::setForceProxy(bool enabled)
{
setValue("Preferences/Connection/ProxyForce", enabled);
}
void Preferences::setProxyOnlyForTorrents(bool enabled)
{
setValue("Preferences/Connection/ProxyOnlyForTorrents", enabled);
}
bool Preferences::isProxyOnlyForTorrents() const
{
return value("Preferences/Connection/ProxyOnlyForTorrents", false).toBool();
}
// Bittorrent options
int Preferences::getMaxConnecs() const
{
return value("Preferences/Bittorrent/MaxConnecs", 500).toInt();
}
void Preferences::setMaxConnecs(int val)
{
if (val <= 0)
val = -1;
setValue("Preferences/Bittorrent/MaxConnecs", val);
}
int Preferences::getMaxConnecsPerTorrent() const
{
return value("Preferences/Bittorrent/MaxConnecsPerTorrent", 100).toInt();
}
void Preferences::setMaxConnecsPerTorrent(int val)
{
if (val <= 0)
val = -1;
setValue("Preferences/Bittorrent/MaxConnecsPerTorrent", val);
}
int Preferences::getMaxUploads() const
{
return value("Preferences/Bittorrent/MaxUploads", -1).toInt();
}
void Preferences::setMaxUploads(int val)
{
if (val <= 0)
val = -1;
setValue("Preferences/Bittorrent/MaxUploads", val);
}
int Preferences::getMaxUploadsPerTorrent() const
{
return value("Preferences/Bittorrent/MaxUploadsPerTorrent", -1).toInt();
}
void Preferences::setMaxUploadsPerTorrent(int val)
{
if (val <= 0)
val = -1;
setValue("Preferences/Bittorrent/MaxUploadsPerTorrent", val);
}
bool Preferences::isuTPEnabled() const
{
return value("Preferences/Bittorrent/uTP", true).toBool();
}
void Preferences::setuTPEnabled(bool enabled)
{
setValue("Preferences/Bittorrent/uTP", enabled);
}
bool Preferences::isuTPRateLimited() const
{
return value("Preferences/Bittorrent/uTP_rate_limited", true).toBool();
}
void Preferences::setuTPRateLimited(bool enabled)
{
setValue("Preferences/Bittorrent/uTP_rate_limited", enabled);
}
bool Preferences::isDHTEnabled() const
{
return value("Preferences/Bittorrent/DHT", true).toBool();
}
void Preferences::setDHTEnabled(bool enabled)
{
setValue("Preferences/Bittorrent/DHT", enabled);
}
bool Preferences::isPeXEnabled() const
{
return value("Preferences/Bittorrent/PeX", true).toBool();
}
void Preferences::setPeXEnabled(bool enabled)
{
setValue("Preferences/Bittorrent/PeX", enabled);
}
bool Preferences::isLSDEnabled() const
{
return value("Preferences/Bittorrent/LSD", true).toBool();
}
void Preferences::setLSDEnabled(bool enabled)
{
setValue("Preferences/Bittorrent/LSD", enabled);
}
int Preferences::getEncryptionSetting() const
{
return value("Preferences/Bittorrent/Encryption", 0).toInt();
}
void Preferences::setEncryptionSetting(int val)
{
setValue("Preferences/Bittorrent/Encryption", val);
}
bool Preferences::isAddTrackersEnabled() const
{
return value("Preferences/Bittorrent/AddTrackers", false).toBool();
}
void Preferences::setAddTrackersEnabled(bool enabled)
{
setValue("Preferences/Bittorrent/AddTrackers", enabled);
}
QString Preferences::getTrackersList() const
{
return value("Preferences/Bittorrent/TrackersList").toString();
}
void Preferences::setTrackersList(const QString &val)
{
setValue("Preferences/Bittorrent/TrackersList", val);
}
qreal Preferences::getGlobalMaxRatio() const
{
return value("Preferences/Bittorrent/MaxRatio", -1).toReal();
}
void Preferences::setGlobalMaxRatio(qreal ratio)
{
setValue("Preferences/Bittorrent/MaxRatio", ratio);
}
// IP Filter
bool Preferences::isFilteringEnabled() const
{
return value("Preferences/IPFilter/Enabled", false).toBool();
}
void Preferences::setFilteringEnabled(bool enabled)
{
setValue("Preferences/IPFilter/Enabled", enabled);
}
bool Preferences::isFilteringTrackerEnabled() const
{
return value("Preferences/IPFilter/FilterTracker", false).toBool();
}
void Preferences::setFilteringTrackerEnabled(bool enabled)
{
setValue("Preferences/IPFilter/FilterTracker", enabled);
}
QString Preferences::getFilter() const
{
return Utils::Fs::fromNativePath(value("Preferences/IPFilter/File").toString());
}
void Preferences::setFilter(const QString &path)
{
setValue("Preferences/IPFilter/File", Utils::Fs::fromNativePath(path));
}
QStringList Preferences::bannedIPs() const
{
return value("Preferences/IPFilter/BannedIPs").toStringList();
}
void Preferences::banIP(const QString &ip)
{
QStringList banned_ips = value("Preferences/IPFilter/BannedIPs").toStringList();
if (!banned_ips.contains(ip)) {
banned_ips << ip;
setValue("Preferences/IPFilter/BannedIPs", banned_ips);
}
}
// Search
bool Preferences::isSearchEnabled() const
{
@@ -860,63 +414,6 @@ void Preferences::setSearchEnabled(bool enabled)
setValue("Preferences/Search/SearchEnabled", enabled);
}
// Queueing system
bool Preferences::isQueueingSystemEnabled() const
{
return value("Preferences/Queueing/QueueingEnabled", true).toBool();
}
void Preferences::setQueueingSystemEnabled(bool enabled)
{
setValue("Preferences/Queueing/QueueingEnabled", enabled);
}
int Preferences::getMaxActiveDownloads() const
{
return value("Preferences/Queueing/MaxActiveDownloads", 3).toInt();
}
void Preferences::setMaxActiveDownloads(int val)
{
if (val < 0)
val = -1;
setValue("Preferences/Queueing/MaxActiveDownloads", val);
}
int Preferences::getMaxActiveUploads() const
{
return value("Preferences/Queueing/MaxActiveUploads", 3).toInt();
}
void Preferences::setMaxActiveUploads(int val)
{
if (val < 0)
val = -1;
setValue("Preferences/Queueing/MaxActiveUploads", val);
}
int Preferences::getMaxActiveTorrents() const
{
return value("Preferences/Queueing/MaxActiveTorrents", 5).toInt();
}
void Preferences::setMaxActiveTorrents(int val)
{
if (val < 0)
val = -1;
setValue("Preferences/Queueing/MaxActiveTorrents", val);
}
bool Preferences::ignoreSlowTorrentsForQueueing() const
{
return value("Preferences/Queueing/IgnoreSlowTorrents", false).toBool();
}
void Preferences::setIgnoreSlowTorrentsForQueueing(bool ignore)
{
setValue("Preferences/Queueing/IgnoreSlowTorrents", ignore);
}
bool Preferences::isWebUiEnabled() const
{
#ifdef DISABLE_GUI
@@ -1174,111 +671,6 @@ void Preferences::setDontConfirmAutoExit(bool dontConfirmAutoExit)
setValue("ShutdownConfirmDlg/DontConfirmAutoExit", dontConfirmAutoExit);
}
uint Preferences::diskCacheSize() const
{
uint size = value("Preferences/Downloads/DiskWriteCacheSize", 0).toUInt();
// These macros may not be available on compilers other than MSVC and GCC
#if defined(__x86_64__) || defined(_M_X64)
size = qMin(size, (uint) 4096); // 4GiB
#else
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
size = qMin(size, (uint) 1536);
#endif
return size;
}
void Preferences::setDiskCacheSize(uint size)
{
#if defined(__x86_64__) || defined(_M_X64)
size = qMin(size, (uint) 4096); // 4GiB
#else
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
size = qMin(size, (uint) 1536);
#endif
setValue("Preferences/Downloads/DiskWriteCacheSize", size);
}
uint Preferences::diskCacheTTL() const
{
return value("Preferences/Downloads/DiskWriteCacheTTL", 60).toUInt();
}
void Preferences::setDiskCacheTTL(uint ttl)
{
setValue("Preferences/Downloads/DiskWriteCacheTTL", ttl);
}
bool Preferences::osCache() const
{
return value("Preferences/Advanced/osCache", true).toBool();
}
void Preferences::setOsCache(bool enable)
{
setValue("Preferences/Advanced/osCache", enable);
}
uint Preferences::saveResumeDataInterval() const
{
return value("Preferences/Downloads/SaveResumeDataInterval", 3).toUInt();
}
void Preferences::setSaveResumeDataInterval(uint m)
{
setValue("Preferences/Downloads/SaveResumeDataInterval", m);
}
uint Preferences::outgoingPortsMin() const
{
return value("Preferences/Advanced/OutgoingPortsMin", 0).toUInt();
}
void Preferences::setOutgoingPortsMin(uint val)
{
setValue("Preferences/Advanced/OutgoingPortsMin", val);
}
uint Preferences::outgoingPortsMax() const
{
return value("Preferences/Advanced/OutgoingPortsMax", 0).toUInt();
}
void Preferences::setOutgoingPortsMax(uint val)
{
setValue("Preferences/Advanced/OutgoingPortsMax", val);
}
bool Preferences::getIgnoreLimitsOnLAN() const
{
return value("Preferences/Advanced/IgnoreLimitsLAN", true).toBool();
}
void Preferences::setIgnoreLimitsOnLAN(bool ignore)
{
setValue("Preferences/Advanced/IgnoreLimitsLAN", ignore);
}
bool Preferences::includeOverheadInLimits() const
{
return value("Preferences/Advanced/IncludeOverhead", false).toBool();
}
void Preferences::includeOverheadInLimits(bool include)
{
setValue("Preferences/Advanced/IncludeOverhead", include);
}
bool Preferences::trackerExchangeEnabled() const
{
return value("Preferences/Advanced/LtTrackerExchange", false).toBool();
}
void Preferences::setTrackerExchangeEnabled(bool enable)
{
setValue("Preferences/Advanced/LtTrackerExchange", enable);
}
bool Preferences::recheckTorrentsOnCompletion() const
{
return value("Preferences/Advanced/RecheckOnCompletion", false).toBool();
@@ -1289,16 +681,6 @@ void Preferences::recheckTorrentsOnCompletion(bool recheck)
setValue("Preferences/Advanced/RecheckOnCompletion", recheck);
}
unsigned int Preferences::getRefreshInterval() const
{
return value("Preferences/General/RefreshInterval", 1500).toUInt();
}
void Preferences::setRefreshInterval(uint interval)
{
setValue("Preferences/General/RefreshInterval", interval);
}
bool Preferences::resolvePeerCountries() const
{
return value("Preferences/Connection/ResolvePeerCountries", true).toBool();
@@ -1319,91 +701,6 @@ void Preferences::resolvePeerHostNames(bool resolve)
setValue("Preferences/Connection/ResolvePeerHostNames", resolve);
}
int Preferences::getMaxHalfOpenConnections() const
{
const int val = value("Preferences/Connection/MaxHalfOpenConnec", 20).toInt();
if (val <= 0)
return -1;
return val;
}
void Preferences::setMaxHalfOpenConnections(int value)
{
if (value <= 0)
value = -1;
setValue("Preferences/Connection/MaxHalfOpenConnec", value);
}
QString Preferences::getNetworkInterface() const
{
return value("Preferences/Connection/Interface").toString();
}
void Preferences::setNetworkInterface(const QString& iface)
{
setValue("Preferences/Connection/Interface", iface);
}
QString Preferences::getNetworkInterfaceName() const
{
return value("Preferences/Connection/InterfaceName").toString();
}
void Preferences::setNetworkInterfaceName(const QString& iface)
{
setValue("Preferences/Connection/InterfaceName", iface);
}
bool Preferences::getListenIPv6() const
{
return value("Preferences/Connection/InterfaceListenIPv6", false).toBool();
}
void Preferences::setListenIPv6(bool enable)
{
setValue("Preferences/Connection/InterfaceListenIPv6", enable);
}
QString Preferences::getNetworkAddress() const
{
return value("Preferences/Connection/InetAddress").toString();
}
void Preferences::setNetworkAddress(const QString& addr)
{
setValue("Preferences/Connection/InetAddress", addr);
}
bool Preferences::isAnonymousModeEnabled() const
{
return value("Preferences/Advanced/AnonymousMode", false).toBool();
}
void Preferences::enableAnonymousMode(bool enabled)
{
setValue("Preferences/Advanced/AnonymousMode", enabled);
}
bool Preferences::isSuperSeedingEnabled() const
{
return value("Preferences/Advanced/SuperSeeding", false).toBool();
}
void Preferences::enableSuperSeeding(bool enabled)
{
setValue("Preferences/Advanced/SuperSeeding", enabled);
}
bool Preferences::announceToAllTrackers() const
{
return value("Preferences/Advanced/AnnounceToAllTrackers", true).toBool();
}
void Preferences::setAnnounceToAllTrackers(bool enabled)
{
setValue("Preferences/Advanced/AnnounceToAllTrackers", enabled);
}
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
bool Preferences::useSystemIconTheme() const
{
@@ -1710,16 +1007,6 @@ void Preferences::setMagnetLinkAssoc()
}
#endif
bool Preferences::isTrackerEnabled() const
{
return value("Preferences/Advanced/trackerEnabled", false).toBool();
}
void Preferences::setTrackerEnabled(bool enabled)
{
setValue("Preferences/Advanced/trackerEnabled", enabled);
}
int Preferences::getTrackerPort() const
{
return value("Preferences/Advanced/trackerPort", 9000).toInt();
@@ -1997,7 +1284,7 @@ void Preferences::setRssOpenFolders(const QStringList &folders)
setValue("Rss/open_folders", folders);
}
QByteArray Preferences::getRssHSplitterState() const
QByteArray Preferences::getRssSideSplitterState() const
{
#ifdef QBT_USES_QT5
return value("Rss/qt5/splitter_h").toByteArray();
@@ -2006,7 +1293,7 @@ QByteArray Preferences::getRssHSplitterState() const
#endif
}
void Preferences::setRssHSplitterState(const QByteArray &state)
void Preferences::setRssSideSplitterState(const QByteArray &state)
{
#ifdef QBT_USES_QT5
setValue("Rss/qt5/splitter_h", state);
@@ -2015,21 +1302,21 @@ void Preferences::setRssHSplitterState(const QByteArray &state)
#endif
}
QByteArray Preferences::getRssVSplitterState() const
QByteArray Preferences::getRssMainSplitterState() const
{
#ifdef QBT_USES_QT5
return value("Rss/qt5/splitter_v").toByteArray();
return value("Rss/qt5/splitterMain").toByteArray();
#else
return value("Rss/splitter_v").toByteArray();
return value("Rss/splitterMain").toByteArray();
#endif
}
void Preferences::setRssVSplitterState(const QByteArray &state)
void Preferences::setRssMainSplitterState(const QByteArray &state)
{
#ifdef QBT_USES_QT5
setValue("Rss/qt5/splitter_v", state);
setValue("Rss/qt5/splitterMain", state);
#else
setValue("Rss/splitter_v", state);
setValue("Rss/splitterMain", state);
#endif
}
@@ -2194,7 +1481,7 @@ void Preferences::setRSSEnabled(const bool enabled)
uint Preferences::getRSSRefreshInterval() const
{
return value("Preferences/RSS/RSSRefresh", 5).toUInt();
return value("Preferences/RSS/RSSRefresh", 30).toUInt();
}
void Preferences::setRSSRefreshInterval(const uint &interval)

View File

@@ -57,18 +57,6 @@ enum scheduler_days
SUN
};
namespace Proxy
{
enum ProxyType
{
HTTP = 1,
SOCKS5 = 2,
HTTP_PW = 3,
SOCKS5_PW = 4,
SOCKS4 = 5
};
}
namespace TrayIcon
{
enum Style
@@ -102,7 +90,6 @@ class Preferences: public QObject
void setValue(const QString &key, const QVariant &value);
static Preferences* m_instance;
int m_randomPort;
signals:
void changed();
@@ -115,8 +102,6 @@ public:
// General options
QString getLocale() const;
void setLocale(const QString &locale);
bool useProgramNotification() const;
void useProgramNotification(bool use);
bool deleteTorrentFilesAsDefault() const;
void setDeleteTorrentFilesAsDefault(bool del);
bool confirmOnExit() const;
@@ -129,8 +114,6 @@ public:
void setHideZeroValues(bool b);
int getHideZeroComboValues() const;
void setHideZeroComboValues(int n);
bool useRandomPort() const;
void setRandomPort(bool b);
bool systrayIntegration() const;
void setSystrayIntegration(bool enabled);
bool isToolbarDisplayed() const;
@@ -151,22 +134,12 @@ public:
#endif
// Downloads
bool useIncompleteFilesExtension() const;
void useIncompleteFilesExtension(bool enabled);
QString lastLocationPath() const;
void setLastLocationPath(const QString &path);
bool preAllocateAllFiles() const;
void preAllocateAllFiles(bool enabled);
QVariantHash getScanDirs() const;
void setScanDirs(const QVariantHash &dirs);
QString getScanDirsLastPath() const;
void setScanDirsLastPath(const QString &path);
bool isTorrentExportEnabled() const;
QString getTorrentExportDir() const;
void setTorrentExportDir(QString path);
bool isFinishedTorrentExportEnabled() const;
QString getFinishedTorrentExportDir() const;
void setFinishedTorrentExportDir(QString path);
bool isMailNotificationEnabled() const;
void setMailNotificationEnabled(bool enabled);
QString getMailNotificationEmail() const;
@@ -187,22 +160,6 @@ public:
void setActionOnDblClOnTorrentFn(int act);
// Connection options
int getSessionPort() const;
void setSessionPort(int port);
bool isUPnPEnabled() const;
void setUPnPEnabled(bool enabled);
int getGlobalDownloadLimit() const;
void setGlobalDownloadLimit(int limit);
int getGlobalUploadLimit() const;
void setGlobalUploadLimit(int limit);
int getAltGlobalDownloadLimit() const;
void setAltGlobalDownloadLimit(int limit);
int getAltGlobalUploadLimit() const;
void setAltGlobalUploadLimit(int limit);
bool isAltBandwidthEnabled() const;
void setAltBandwidthEnabled(bool enabled);
bool isSchedulerEnabled() const;
void setSchedulerEnabled(bool enabled);
QTime getSchedulerStartTime() const;
void setSchedulerStartTime(const QTime &time);
QTime getSchedulerEndTime() const;
@@ -210,80 +167,10 @@ public:
scheduler_days getSchedulerDays() const;
void setSchedulerDays(scheduler_days days);
// Proxy options
bool isProxyEnabled() const;
bool isProxyAuthEnabled() const;
void setProxyAuthEnabled(bool enabled);
QString getProxyIp() const;
void setProxyIp(const QString &ip);
unsigned short getProxyPort() const;
void setProxyPort(unsigned short port);
QString getProxyUsername() const;
void setProxyUsername(const QString &username);
QString getProxyPassword() const;
void setProxyPassword(const QString &password);
int getProxyType() const;
void setProxyType(int type);
bool proxyPeerConnections() const;
void setProxyPeerConnections(bool enabled);
bool getForceProxy() const;
void setForceProxy(bool enabled);
void setProxyOnlyForTorrents(bool enabled);
bool isProxyOnlyForTorrents() const;
// Bittorrent options
int getMaxConnecs() const;
void setMaxConnecs(int val);
int getMaxConnecsPerTorrent() const;
void setMaxConnecsPerTorrent(int val);
int getMaxUploads() const;
void setMaxUploads(int val);
int getMaxUploadsPerTorrent() const;
void setMaxUploadsPerTorrent(int val);
bool isuTPEnabled() const;
void setuTPEnabled(bool enabled);
bool isuTPRateLimited() const;
void setuTPRateLimited(bool enabled);
bool isDHTEnabled() const;
void setDHTEnabled(bool enabled);
bool isPeXEnabled() const;
void setPeXEnabled(bool enabled);
bool isLSDEnabled() const;
void setLSDEnabled(bool enabled);
int getEncryptionSetting() const;
void setEncryptionSetting(int val);
bool isAddTrackersEnabled() const;
void setAddTrackersEnabled(bool enabled);
QString getTrackersList() const;
void setTrackersList(const QString &val);
qreal getGlobalMaxRatio() const;
void setGlobalMaxRatio(qreal ratio);
// IP Filter
bool isFilteringEnabled() const;
void setFilteringEnabled(bool enabled);
bool isFilteringTrackerEnabled() const;
void setFilteringTrackerEnabled(bool enabled);
QString getFilter() const;
void setFilter(const QString &path);
QStringList bannedIPs() const;
void banIP(const QString &ip);
// Search
bool isSearchEnabled() const;
void setSearchEnabled(bool enabled);
// Queueing system
bool isQueueingSystemEnabled() const;
void setQueueingSystemEnabled(bool enabled);
int getMaxActiveDownloads() const;
void setMaxActiveDownloads(int val);
int getMaxActiveUploads() const;
void setMaxActiveUploads(int val);
int getMaxActiveTorrents() const;
void setMaxActiveTorrents(int val);
bool ignoreSlowTorrentsForQueueing() const;
void setIgnoreSlowTorrentsForQueueing(bool ignore);
bool isWebUiEnabled() const;
void setWebUiEnabled(bool enabled);
bool isWebUiLocalAuthEnabled() const;
@@ -333,48 +220,12 @@ public:
void setShutdownqBTWhenDownloadsComplete(bool shutdown);
bool dontConfirmAutoExit() const;
void setDontConfirmAutoExit(bool dontConfirmAutoExit);
uint diskCacheSize() const;
void setDiskCacheSize(uint size);
uint diskCacheTTL() const;
void setDiskCacheTTL(uint ttl);
bool osCache() const;
void setOsCache(bool enable);
uint saveResumeDataInterval() const;
void setSaveResumeDataInterval(uint m);
uint outgoingPortsMin() const;
void setOutgoingPortsMin(uint val);
uint outgoingPortsMax() const;
void setOutgoingPortsMax(uint val);
bool getIgnoreLimitsOnLAN() const;
void setIgnoreLimitsOnLAN(bool ignore);
bool includeOverheadInLimits() const;
void includeOverheadInLimits(bool include);
bool trackerExchangeEnabled() const;
void setTrackerExchangeEnabled(bool enable);
bool recheckTorrentsOnCompletion() const;
void recheckTorrentsOnCompletion(bool recheck);
unsigned int getRefreshInterval() const;
void setRefreshInterval(uint interval);
bool resolvePeerCountries() const;
void resolvePeerCountries(bool resolve);
bool resolvePeerHostNames() const;
void resolvePeerHostNames(bool resolve);
int getMaxHalfOpenConnections() const;
void setMaxHalfOpenConnections(int value);
QString getNetworkInterface() const;
void setNetworkInterface(const QString& iface);
QString getNetworkInterfaceName() const;
void setNetworkInterfaceName(const QString& iface);
bool getListenIPv6() const;
void setListenIPv6(bool enable);
QString getNetworkAddress() const;
void setNetworkAddress(const QString& addr);
bool isAnonymousModeEnabled() const;
void enableAnonymousMode(bool enabled);
bool isSuperSeedingEnabled() const;
void enableSuperSeeding(bool enabled);
bool announceToAllTrackers() const;
void setAnnounceToAllTrackers(bool enabled);
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
bool useSystemIconTheme() const;
void useSystemIconTheme(bool enabled);
@@ -396,8 +247,6 @@ public:
static void setTorrentFileAssoc();
static void setMagnetLinkAssoc();
#endif
bool isTrackerEnabled() const;
void setTrackerEnabled(bool enabled);
int getTrackerPort() const;
void setTrackerPort(int port);
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
@@ -451,10 +300,10 @@ public:
void setRssHSplitterSizes(const QByteArray &sizes);
QStringList getRssOpenFolders() const;
void setRssOpenFolders(const QStringList &folders);
QByteArray getRssHSplitterState() const;
void setRssHSplitterState(const QByteArray &state);
QByteArray getRssVSplitterState() const;
void setRssVSplitterState(const QByteArray &state);
QByteArray getRssSideSplitterState() const;
void setRssSideSplitterState(const QByteArray &state);
QByteArray getRssMainSplitterState() const;
void setRssMainSplitterState(const QByteArray &state);
QString getSearchColsWidth() const;
void setSearchColsWidth(const QString &width);
QStringList getSearchEngDisabled() const;

View File

@@ -39,7 +39,7 @@ class QIniSettings : public QSettings {
public:
QIniSettings(const QString &organization = "qBittorrent", const QString &application = "qBittorrent", QObject *parent = 0 ):
#ifdef Q_OS_WIN
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
QSettings(QSettings::IniFormat, QSettings::UserScope, organization, application, parent)
#else
QSettings(organization, application, parent)

View File

@@ -259,8 +259,13 @@ bool Feed::hasCustomIcon() const
void Feed::setIconPath(const QString &path)
{
if (!path.isEmpty() && QFile::exists(path))
m_icon = path;
QString nativePath = Utils::Fs::fromNativePath(path);
if (nativePath == m_icon || nativePath.isEmpty() || !QFile::exists(nativePath)) return;
if (!m_icon.startsWith(":/") && QFile::exists(m_icon))
Utils::Fs::forceRemove(m_icon);
m_icon = nativePath;
}
ArticlePtr Feed::getItem(const QString &guid) const
@@ -322,7 +327,7 @@ QString Feed::iconUrl() const
void Feed::handleIconDownloadFinished(const QString &url, const QString &filePath)
{
Q_UNUSED(url);
m_icon = filePath;
setIconPath(filePath);
qDebug() << Q_FUNC_INFO << "icon path:" << m_icon;
m_manager->forwardFeedIconChanged(m_url, m_icon);
}

View File

@@ -28,20 +28,17 @@
* Contact : chris@qbittorrent.org
*/
#include "scanfoldersmodel.h"
#include <QDir>
#include <QFileInfo>
#include <QString>
#include <QStringList>
#include <QTemporaryFile>
#include <QTextStream>
#include "utils/misc.h"
#include "utils/fs.h"
#include "preferences.h"
#include "logger.h"
#include "filesystemwatcher.h"
#include "bittorrent/session.h"
#include "scanfoldersmodel.h"
#include "filesystemwatcher.h"
#include "preferences.h"
#include "utils/fs.h"
struct ScanFoldersModel::PathData
{
@@ -128,10 +125,8 @@ QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const
else if (role == Qt::DisplayRole) {
switch (pathData->downloadType) {
case DOWNLOAD_IN_WATCH_FOLDER:
value = tr("Watch Folder");
break;
case DEFAULT_LOCATION:
value = tr("Default Folder");
value = pathTypeDisplayName(pathData->downloadType);
break;
case CUSTOM_LOCATION:
value = pathData->downloadPath;
@@ -153,10 +148,10 @@ QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation,
switch (section) {
case WATCH:
title = tr("Watched Folder");
title = tr("Monitored Folder");
break;
case DOWNLOAD:
title = tr("Save Files to");
title = tr("Override Save Location");
break;
}
@@ -392,3 +387,18 @@ void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
}
}
}
QString ScanFoldersModel::pathTypeDisplayName(const PathType type)
{
switch(type) {
case DOWNLOAD_IN_WATCH_FOLDER:
return tr("Monitored folder");
case DEFAULT_LOCATION:
return tr("Default save location");
case CUSTOM_LOCATION:
return tr("Browse...");
default:
qDebug("Invalid PathType: %d", type);
};
return QString();
}

View File

@@ -34,13 +34,10 @@
#include <QAbstractListModel>
#include <QList>
QT_BEGIN_NAMESPACE
class QStringList;
QT_END_NAMESPACE
class FileSystemWatcher;
class ScanFoldersModel : public QAbstractListModel
class ScanFoldersModel: public QAbstractListModel
{
Q_OBJECT
Q_DISABLE_COPY(ScanFoldersModel)
@@ -71,7 +68,9 @@ public:
static bool initInstance(QObject *parent = 0);
static void freeInstance();
static ScanFoldersModel *instance();
static ScanFoldersModel* instance();
static QString pathTypeDisplayName(const PathType type);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
@@ -81,8 +80,8 @@ public:
// TODO: removePaths(); singular version becomes private helper functions;
// also: remove functions should take modelindexes
PathStatus addPath(const QString &watchPath, const PathType& downloadType, const QString &downloadPath, bool addToFSWatcher = true);
PathStatus updatePath(const QString &watchPath, const PathType& downloadType, const QString &downloadPath);
PathStatus addPath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath, bool addToFSWatcher = true);
PathStatus updatePath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath);
// PRECONDITION: The paths must have been added with addPath() first.
void addToFSWatcher(const QStringList &watchPaths);
void removePath(int row, bool removeFromFSWatcher = true);

View File

@@ -62,7 +62,7 @@ static inline void removePythonScriptIfExists(const QString &scriptPath)
const QHash<QString, QString> SearchEngine::m_categoryNames = SearchEngine::initializeCategoryNames();
SearchEngine::SearchEngine()
: m_updateUrl(QString("https://raw.github.com/qbittorrent/qBittorrent/master/src/searchengine/%1/engines/").arg(Utils::Misc::pythonVersion() >= 3 ? "nova3" : "nova"))
: m_updateUrl(QString("http://searchplugins.qbittorrent.org/%1/engines/").arg(Utils::Misc::pythonVersion() >= 3 ? "nova3" : "nova"))
, m_searchStopped(false)
{
updateNova();
@@ -266,6 +266,21 @@ void SearchEngine::cancelSearch()
}
}
void SearchEngine::downloadTorrent(const QString &siteUrl, const QString &url)
{
QProcess *downloadProcess = new QProcess(this);
downloadProcess->setEnvironment(QProcess::systemEnvironment());
connect(downloadProcess, SIGNAL(finished(int)), this, SLOT(torrentFileDownloadFinished(int)));
m_downloaders << downloadProcess;
QStringList params {
Utils::Fs::toNativePath(engineLocation() + "/nova2dl.py"),
siteUrl,
url
};
// Launch search
downloadProcess->start(Utils::Misc::pythonExecutable(), params, QIODevice::ReadOnly);
}
void SearchEngine::startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins)
{
// Search process already running or
@@ -295,6 +310,11 @@ QString SearchEngine::categoryFullName(const QString &categoryName)
return tr(m_categoryNames.value(categoryName).toUtf8().constData());
}
QString SearchEngine::pluginFullName(const QString &pluginName)
{
return pluginInfo(pluginName) ? pluginInfo(pluginName)->fullName : QString();
}
QString SearchEngine::pluginsLocation()
{
return QString("%1/engines").arg(engineLocation());
@@ -357,6 +377,21 @@ void SearchEngine::pluginDownloadFailed(const QString &url, const QString &reaso
emit pluginInstallationFailed(pluginName, tr("Failed to download the plugin file. %1").arg(reason));
}
void SearchEngine::torrentFileDownloadFinished(int exitcode)
{
QProcess *downloadProcess = static_cast<QProcess*>(sender());
if (exitcode == 0) {
QString line = QString::fromUtf8(downloadProcess->readAllStandardOutput()).trimmed();
QStringList parts = line.split(' ');
if (parts.size() == 2)
emit torrentFileDownloaded(parts[0]);
}
qDebug() << "Deleting downloadProcess";
m_downloaders.removeOne(downloadProcess);
downloadProcess->deleteLater();
}
// Update nova.py search plugin if necessary
void SearchEngine::updateNova()
{
@@ -383,6 +418,12 @@ void SearchEngine::updateNova()
QFile::copy(":/" + novaFolder + "/nova2.py", filePath);
}
filePath = searchDir.absoluteFilePath("nova2dl.py");
if (getPluginVersion(":/" + novaFolder + "/nova2dl.py") > getPluginVersion(filePath)) {
removePythonScriptIfExists(filePath);
QFile::copy(":/" + novaFolder + "/nova2dl.py", filePath);
}
filePath = searchDir.absoluteFilePath("fix_encoding.py");
QFile::copy(":/" + novaFolder + "/fix_encoding.py", filePath);

View File

@@ -84,8 +84,11 @@ public:
void startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
void cancelSearch();
void downloadTorrent(const QString &siteUrl, const QString &url);
static qreal getPluginVersion(QString filePath);
static QString categoryFullName(const QString &categoryName);
QString pluginFullName(const QString &pluginName);
static QString pluginsLocation();
signals:
@@ -102,6 +105,8 @@ signals:
void checkForUpdatesFinished(const QHash<QString, qreal> &updateInfo);
void checkForUpdatesFailed(const QString &reason);
void torrentFileDownloaded(const QString &path);
private slots:
void onTimeout();
void readSearchOutput();
@@ -110,6 +115,7 @@ private slots:
void versionInfoDownloadFailed(const QString &url, const QString &reason);
void pluginDownloaded(const QString &url, QString filePath);
void pluginDownloadFailed(const QString &url, const QString &reason);
void torrentFileDownloadFinished(int exitcode);
private:
void update();
@@ -132,6 +138,7 @@ private:
bool m_searchStopped;
QTimer *m_searchTimeout;
QByteArray m_searchResultLineTruncated;
QList<QProcess*> m_downloaders;
};
#endif // SEARCHENGINE_H

View File

@@ -27,6 +27,9 @@
* exception statement from your version.
*/
#include "settingsstorage.h"
#include <memory>
#include <QFile>
#include <QHash>
#include <QStringList>
@@ -34,18 +37,43 @@
#include "logger.h"
#include "utils/fs.h"
#include "settingsstorage.h"
namespace
{
inline QSettings *createSettings(const QString &name)
// Encapsulates serialization of settings in "atomic" way.
// write() does not leave half-written files,
// read() has a workaround for a case of power loss during a previous serialization
class TransactionalSettings
{
#ifdef Q_OS_WIN
return new QSettings(QSettings::IniFormat, QSettings::UserScope, "qBittorrent", name);
public:
TransactionalSettings(const QString &name)
: m_name(name)
{
}
QVariantHash read();
bool write(const QVariantHash &data);
private:
// we return actual file names used by QSettings because
// there is no other way to get that name except
// actually create a QSettings object.
// if serialization operation was not successful we return empty string
QString deserialize(const QString &name, QVariantHash &data);
QString serialize(const QString &name, const QVariantHash &data);
using SettingsPtr = std::unique_ptr<QSettings>;
SettingsPtr createSettings(const QString &name)
{
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
return SettingsPtr(new QSettings(QSettings::IniFormat, QSettings::UserScope, "qBittorrent", name));
#else
return new QSettings("qBittorrent", name);
return SettingsPtr(new QSettings("qBittorrent", name));
#endif
}
}
QString m_name;
};
#ifdef QBT_USES_QT5
typedef QHash<QString, QString> MappingTable;
@@ -66,23 +94,87 @@ namespace
{
static const MappingTable keyMapping = {
{ "BitTorrent/Session/MaxRatioAction", "Preferences/Bittorrent/MaxRatioAction" },
{ "BitTorrent/Session/DefaultSavePath", "Preferences/Downloads/SavePath" },
{ "BitTorrent/Session/TempPath", "Preferences/Downloads/TempPath" },
{ "BitTorrent/Session/TempPathEnabled", "Preferences/Downloads/TempPathEnabled" },
{ "BitTorrent/Session/AddTorrentPaused", "Preferences/Downloads/StartInPause" },
{"BitTorrent/Session/MaxRatioAction", "Preferences/Bittorrent/MaxRatioAction"},
{"BitTorrent/Session/DefaultSavePath", "Preferences/Downloads/SavePath"},
{"BitTorrent/Session/TempPath", "Preferences/Downloads/TempPath"},
{"BitTorrent/Session/TempPathEnabled", "Preferences/Downloads/TempPathEnabled"},
{"BitTorrent/Session/AddTorrentPaused", "Preferences/Downloads/StartInPause"},
{"BitTorrent/Session/RefreshInterval", "Preferences/General/RefreshInterval"},
{"BitTorrent/Session/Preallocation", "Preferences/Downloads/PreAllocation"},
{"BitTorrent/Session/AddExtensionToIncompleteFiles", "Preferences/Downloads/UseIncompleteExtension"},
{"BitTorrent/Session/TorrentExportDirectory", "Preferences/Downloads/TorrentExportDir"},
{"BitTorrent/Session/FinishedTorrentExportDirectory", "Preferences/Downloads/FinishedTorrentExportDir"},
{"BitTorrent/Session/GlobalUPSpeedLimit", "Preferences/Connection/GlobalUPLimit"},
{"BitTorrent/Session/GlobalDLSpeedLimit", "Preferences/Connection/GlobalDLLimit"},
{"BitTorrent/Session/AlternativeGlobalUPSpeedLimit", "Preferences/Connection/GlobalUPLimitAlt"},
{"BitTorrent/Session/AlternativeGlobalDLSpeedLimit", "Preferences/Connection/GlobalDLLimitAlt"},
{"BitTorrent/Session/UseAlternativeGlobalSpeedLimit", "Preferences/Connection/alt_speeds_on"},
{"BitTorrent/Session/BandwidthSchedulerEnabled", "Preferences/Scheduler/Enabled"},
{"BitTorrent/Session/Port", "Preferences/Connection/PortRangeMin"},
{"BitTorrent/Session/UseRandomPort", "Preferences/General/UseRandomPort"},
{"BitTorrent/Session/IPv6Enabled", "Preferences/Connection/InterfaceListenIPv6"},
{"BitTorrent/Session/Interface", "Preferences/Connection/Interface"},
{"BitTorrent/Session/InterfaceName", "Preferences/Connection/InterfaceName"},
{"BitTorrent/Session/InterfaceAddress", "Preferences/Connection/InterfaceAddress"},
{"BitTorrent/Session/SaveResumeDataInterval", "Preferences/Downloads/SaveResumeDataInterval"},
{"BitTorrent/Session/Encryption", "Preferences/Bittorrent/Encryption"},
{"BitTorrent/Session/ForceProxy", "Preferences/Connection/ProxyForce"},
{"BitTorrent/Session/ProxyPeerConnections", "Preferences/Connection/ProxyPeerConnections"},
{"BitTorrent/Session/MaxConnections", "Preferences/Bittorrent/MaxConnecs"},
{"BitTorrent/Session/MaxUploads", "Preferences/Bittorrent/MaxUploads"},
{"BitTorrent/Session/MaxConnectionsPerTorrent", "Preferences/Bittorrent/MaxConnecsPerTorrent"},
{"BitTorrent/Session/MaxUploadsPerTorrent", "Preferences/Bittorrent/MaxUploadsPerTorrent"},
{"BitTorrent/Session/DHTEnabled", "Preferences/Bittorrent/DHT"},
{"BitTorrent/Session/LSDEnabled", "Preferences/Bittorrent/LSD"},
{"BitTorrent/Session/PeXEnabled", "Preferences/Bittorrent/PeX"},
{"BitTorrent/Session/TrackerExchangeEnabled", "Preferences/Advanced/LtTrackerExchange"},
{"BitTorrent/Session/AddTrackersEnabled", "Preferences/Bittorrent/AddTrackers"},
{"BitTorrent/Session/AdditionalTrackers", "Preferences/Bittorrent/TrackersList"},
{"BitTorrent/Session/IPFilteringEnabled", "Preferences/IPFilter/Enabled"},
{"BitTorrent/Session/TrackerFilteringEnabled", "Preferences/IPFilter/FilterTracker"},
{"BitTorrent/Session/IPFilter", "Preferences/IPFilter/File"},
{"BitTorrent/Session/GlobalMaxRatio", "Preferences/Bittorrent/MaxRatio"},
{"BitTorrent/Session/AnnounceToAllTrackers", "Preferences/Advanced/AnnounceToAllTrackers"},
{"BitTorrent/Session/DiskCacheSize", "Preferences/Downloads/DiskWriteCacheSize"},
{"BitTorrent/Session/DiskCacheTTL", "Preferences/Downloads/DiskWriteCacheTTL"},
{"BitTorrent/Session/UseOSCache", "Preferences/Advanced/osCache"},
{"BitTorrent/Session/AnonymousModeEnabled", "Preferences/Advanced/AnonymousMode"},
{"BitTorrent/Session/QueueingSystemEnabled", "Preferences/Queueing/QueueingEnabled"},
{"BitTorrent/Session/MaxActiveDownloads", "Preferences/Queueing/MaxActiveDownloads"},
{"BitTorrent/Session/MaxActiveUploads", "Preferences/Queueing/MaxActiveUploads"},
{"BitTorrent/Session/MaxActiveTorrents", "Preferences/Queueing/MaxActiveTorrents"},
{"BitTorrent/Session/IgnoreSlowTorrentsForQueueing", "Preferences/Queueing/IgnoreSlowTorrents"},
{"BitTorrent/Session/OutgoingPortsMin", "Preferences/Advanced/OutgoingPortsMin"},
{"BitTorrent/Session/OutgoingPortsMax", "Preferences/Advanced/OutgoingPortsMax"},
{"BitTorrent/Session/IgnoreLimitsOnLAN", "Preferences/Advanced/IgnoreLimitsLAN"},
{"BitTorrent/Session/IncludeOverheadInLimits", "Preferences/Advanced/IncludeOverhead"},
{"BitTorrent/Session/AnnounceIP", "Preferences/Connection/InetAddress"},
{"BitTorrent/Session/SuperSeedingEnabled", "Preferences/Advanced/SuperSeeding"},
{"BitTorrent/Session/MaxHalfOpenConnections", "Preferences/Connection/MaxHalfOpenConnec"},
{"BitTorrent/Session/uTPEnabled", "Preferences/Bittorrent/uTP"},
{"BitTorrent/Session/uTPRateLimited", "Preferences/Bittorrent/uTP_rate_limited"},
{"BitTorrent/TrackerEnabled", "Preferences/Advanced/trackerEnabled"},
{"Network/Proxy/OnlyForTorrents", "Preferences/Connection/ProxyOnlyForTorrents"},
{"Network/Proxy/Type", "Preferences/Connection/ProxyType"},
{"Network/Proxy/Authentication", "Preferences/Connection/Proxy/Authentication"},
{"Network/Proxy/Username", "Preferences/Connection/Proxy/Username"},
{"Network/Proxy/Password", "Preferences/Connection/Proxy/Password"},
{"Network/Proxy/IP", "Preferences/Connection/Proxy/IP"},
{"Network/Proxy/Port", "Preferences/Connection/Proxy/Port"},
{"Network/PortForwardingEnabled", "Preferences/Connection/UPnP"},
#ifdef QBT_USES_QT5
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/qt5/treeHeaderState" },
{"AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/qt5/treeHeaderState"},
#else
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/treeHeaderState" },
{"AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/treeHeaderState"},
#endif
{ "AddNewTorrentDialog/Width", "AddNewTorrentDialog/width" },
{ "AddNewTorrentDialog/Position", "AddNewTorrentDialog/y" },
{ "AddNewTorrentDialog/Expanded", "AddNewTorrentDialog/expanded" },
{ "AddNewTorrentDialog/SavePathHistory", "TorrentAdditionDlg/save_path_history" },
{ "AddNewTorrentDialog/Enabled", "Preferences/Downloads/NewAdditionDialog" },
{ "AddNewTorrentDialog/TopLevel", "Preferences/Downloads/NewAdditionDialogFront" },
{ "ExecutionLog/Enabled", "Preferences/ExecutionLog/enabled" }
{"AddNewTorrentDialog/Width", "AddNewTorrentDialog/width"},
{"AddNewTorrentDialog/Position", "AddNewTorrentDialog/y"},
{"AddNewTorrentDialog/Expanded", "AddNewTorrentDialog/expanded"},
{"AddNewTorrentDialog/SavePathHistory", "TorrentAdditionDlg/save_path_history"},
{"AddNewTorrentDialog/Enabled", "Preferences/Downloads/NewAdditionDialog"},
{"AddNewTorrentDialog/TopLevel", "Preferences/Downloads/NewAdditionDialogFront"},
{"State/BannedIPs", "Preferences/IPFilter/BannedIPs"}
};
@@ -93,48 +185,10 @@ namespace
SettingsStorage *SettingsStorage::m_instance = nullptr;
SettingsStorage::SettingsStorage()
: m_dirty(false)
: m_data{TransactionalSettings(QLatin1String("qBittorrent")).read()}
, m_dirty(false)
, m_lock(QReadWriteLock::Recursive)
{
QSettings *settings;
#ifdef Q_OS_MAC
settings = createSettings("qBittorrent");
#else
settings = createSettings("qBittorrent_new");
QString newPath = settings->fileName();
// This means that the PC closed either due to power outage
// or because the disk was full. In any case the settings weren't transfered
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
// contains the most recent settings.
if (!settings->allKeys().isEmpty()) {
Logger::instance()->addMessage(tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING);
m_dirty = true;
}
else {
delete settings;
settings = createSettings("qBittorrent");
}
#endif
QStringList keys = settings->allKeys();
// Copy everything into memory. This means even keys inserted in the file manually
// or that we don't touch directly in this code(eg disabled by ifdef). This ensures
// that they will be copied over when save our settings to disk.
foreach (const QString &key, keys)
m_data[key] = settings->value(key);
//Ensures sync to disk before we attempt to manipulate the files from save().
delete settings;
#ifndef Q_OS_MAC
Utils::Fs::forceRemove(newPath);
if (m_dirty)
save();
#endif
m_timer.setSingleShot(true);
m_timer.setInterval(5 * 1000);
connect(&m_timer, SIGNAL(timeout()), SLOT(save()));
@@ -164,54 +218,17 @@ SettingsStorage *SettingsStorage::instance()
bool SettingsStorage::save()
{
if (!m_dirty) return false; // Obtaining the lock is expensive, let's check early
QWriteLocker locker(&m_lock);
if (!m_dirty) return false; // something might have changed while we were getting the lock
if (!m_dirty) return false;
#ifndef Q_OS_MAC
// QSettings delete the file before writing it out. This can result in problems
// if the disk is full or a power outage occurs. Those events might occur
// between deleting the file and recreating it. This is a safety measure.
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
// replace qBittorrent_new.ini/qBittorrent.conf with it.
QSettings *settings = createSettings("qBittorrent_new");
#else
QSettings *settings = createSettings("qBittorrent");
#endif
foreach (const QString &key, m_data.keys())
settings->setValue(key, m_data[key]);
m_dirty = false;
locker.unlock();
#ifndef Q_OS_MAC
settings->sync(); // Important to get error status
QSettings::Status status = settings->status();
QString newPath = settings->fileName();
if (status != QSettings::NoError) {
if (status == QSettings::AccessError)
Logger::instance()->addMessage(tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
else
Logger::instance()->addMessage(tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL);
delete settings;
Utils::Fs::forceRemove(newPath);
return false;
TransactionalSettings settings(QLatin1String("qBittorrent"));
if (settings.write(m_data)) {
m_dirty = false;
return true;
}
delete settings;
QString finalPath = newPath;
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
finalPath.remove(index, 4);
Utils::Fs::forceRemove(finalPath);
QFile::rename(newPath, finalPath);
#else
delete settings;
#endif
return true;
return false;
}
QVariant SettingsStorage::loadValue(const QString &key, const QVariant &defaultValue) const
@@ -226,8 +243,8 @@ void SettingsStorage::storeValue(const QString &key, const QVariant &value)
QWriteLocker locker(&m_lock);
if (m_data.value(realKey) != value) {
m_dirty = true;
m_timer.start();
m_data.insert(realKey, value);
m_timer.start();
}
}
@@ -237,7 +254,88 @@ void SettingsStorage::removeValue(const QString &key)
QWriteLocker locker(&m_lock);
if (m_data.contains(realKey)) {
m_dirty = true;
m_timer.start();
m_data.remove(realKey);
m_timer.start();
}
}
QVariantHash TransactionalSettings::read()
{
QVariantHash res;
bool writeBackNeeded = false;
QString newPath = deserialize(m_name + QLatin1String("_new"), res);
if (!newPath.isEmpty()) { // "_new" file is NOT empty
// This means that the PC closed either due to power outage
// or because the disk was full. In any case the settings weren't transfered
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
// contains the most recent settings.
Logger::instance()->addMessage(QObject::tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING);
writeBackNeeded = true;
}
else {
deserialize(m_name, res);
}
Utils::Fs::forceRemove(newPath);
if (writeBackNeeded)
write(res);
return res;
}
bool TransactionalSettings::write(const QVariantHash &data)
{
// QSettings delete the file before writing it out. This can result in problems
// if the disk is full or a power outage occurs. Those events might occur
// between deleting the file and recreating it. This is a safety measure.
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
// replace qBittorrent.ini/qBittorrent.conf with it.
QString newPath = serialize(m_name + QLatin1String("_new"), data);
if (newPath.isEmpty()) {
Utils::Fs::forceRemove(newPath);
return false;
}
QString finalPath = newPath;
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
finalPath.remove(index, 4);
Utils::Fs::forceRemove(finalPath);
QFile::rename(newPath, finalPath);
return true;
}
QString TransactionalSettings::deserialize(const QString &name, QVariantHash &data)
{
SettingsPtr settings = createSettings(name);
if (settings->allKeys().isEmpty())
return QString();
// Copy everything into memory. This means even keys inserted in the file manually
// or that we don't touch directly in this code (eg disabled by ifdef). This ensures
// that they will be copied over when save our settings to disk.
foreach (const QString &key, settings->allKeys())
data.insert(key, settings->value(key));
return settings->fileName();
}
QString TransactionalSettings::serialize(const QString &name, const QVariantHash &data)
{
SettingsPtr settings = createSettings(name);
for (auto i = data.begin(); i != data.end(); ++i)
settings->setValue(i.key(), i.value());
settings->sync(); // Important to get error status
QSettings::Status status = settings->status();
if (status != QSettings::NoError) {
if (status == QSettings::AccessError)
Logger::instance()->addMessage(QObject::tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
else
Logger::instance()->addMessage(QObject::tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL);
return QString();
}
return settings->fileName();
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,63 +24,50 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef TORRENTIMPORTDLG_H
#define TORRENTIMPORTDLG_H
#ifndef SETTINGVALUE_H
#define SETTINGVALUE_H
#include <QDialog>
#include <QStringList>
#include <functional>
#include <QString>
#include "base/bittorrent/torrentinfo.h"
#include "settingsstorage.h"
namespace Ui
template <typename T>
class CachedSettingValue
{
class TorrentImportDlg;
}
class TorrentImportDlg: public QDialog
{
Q_OBJECT
using ProxyFunc = std::function<T (const T&)>;
public:
explicit TorrentImportDlg(QWidget *parent = 0);
~TorrentImportDlg();
explicit CachedSettingValue(const char *keyName, const T &defaultValue = T()
, ProxyFunc proxyFunc = [](const T &value) { return value; })
: m_keyName(QLatin1String(keyName))
, m_value(proxyFunc(SettingsStorage::instance()->loadValue(
m_keyName, defaultValue).template value<T>()))
{
}
static void importTorrent();
T value() const
{
return m_value;
}
QString getTorrentPath() const;
QString getContentPath() const;
bool fileRenamed() const;
BitTorrent::TorrentInfo torrent() const;
bool skipFileChecking() const;
CachedSettingValue<T> &operator=(const T &newValue)
{
m_value = newValue;
SettingsStorage::instance()->storeValue(m_keyName, m_value);
return *this;
}
protected slots:
void loadTorrent(const QString &torrentPath);
void initializeFilesPath();
private slots:
void on_browseTorrentBtn_clicked();
void on_browseContentBtn_clicked();
void on_importBtn_clicked();
protected:
void closeEvent(QCloseEvent *event);
operator T() const
{
return value();
}
private:
void loadSettings();
void saveSettings();
private:
Ui::TorrentImportDlg *ui;
BitTorrent::TorrentInfo m_torrentInfo;
// NOTE: Where do we use it?
QStringList m_filesPath;
QString m_contentPath;
QString m_torrentPath;
bool m_fileRenamed;
const QString m_keyName;
T m_value;
};
#endif // TORRENTIMPORTDLG_H
#endif // SETTINGVALUE_H

View File

@@ -0,0 +1,100 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@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 "torrentfileguard.h"
#include <QMetaEnum>
#include "settingsstorage.h"
#include "utils/fs.h"
namespace
{
const QLatin1String KEY_AUTO_DELETE_ENABLED ("Core/AutoDeleteAddedTorrentFile");
}
FileGuard::FileGuard(const QString &path)
: m_path {path}
, m_remove {true}
{
}
void FileGuard::setAutoRemove(bool remove) noexcept
{
m_remove = remove;
}
FileGuard::~FileGuard()
{
if (m_remove && !m_path.isEmpty())
Utils::Fs::forceRemove(m_path); // forceRemove() checks for file existence
}
TorrentFileGuard::TorrentFileGuard(const QString &path)
: m_mode {autoDeleteMode()}
, m_wasAdded {false}
, m_guard {m_mode != Never ? path : QString()}
{
}
TorrentFileGuard::~TorrentFileGuard()
{
if (!m_wasAdded && (m_mode != Always))
m_guard.setAutoRemove(false);
}
void TorrentFileGuard::markAsAddedToSession()
{
m_wasAdded = true;
}
void TorrentFileGuard::setAutoRemove(bool remove)
{
m_guard.setAutoRemove(remove);
}
TorrentFileGuard::AutoDeleteMode TorrentFileGuard::autoDeleteMode()
{
QMetaEnum meta {modeMetaEnum()};
return static_cast<AutoDeleteMode>(meta.keyToValue(SettingsStorage::instance()->loadValue(
KEY_AUTO_DELETE_ENABLED, meta.valueToKey(Never)).toByteArray()));
}
void TorrentFileGuard::setAutoDeleteMode(TorrentFileGuard::AutoDeleteMode mode)
{
QMetaEnum meta {modeMetaEnum()};
SettingsStorage::instance()->storeValue(KEY_AUTO_DELETE_ENABLED, meta.valueToKey(mode));
}
QMetaEnum TorrentFileGuard::modeMetaEnum()
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
return QMetaEnum::fromType<AutoDeleteMode>();
#else
return staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AutoDeleteMode"));
#endif
}

View File

@@ -0,0 +1,95 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@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 <QString>
#include <QMetaType>
class QMetaEnum;
/// Utility class to defer file deletion
class FileGuard
{
public:
FileGuard(const QString &path = QString());
~FileGuard();
/// Cancels or re-enables deferred file deletion
void setAutoRemove(bool remove) noexcept;
private:
QString m_path;
bool m_remove;
};
/// Reads settings for .torrent files from preferences
/// and sets the file guard up accordingly
class TorrentFileGuard
{
Q_GADGET
public:
TorrentFileGuard(const QString &path = QString());
~TorrentFileGuard();
/// marks the torrent file as loaded (added) into the BitTorrent::Session
void markAsAddedToSession();
void setAutoRemove(bool remove);
enum AutoDeleteMode // do not change these names: they are stored in config file
{
Never,
IfAdded,
Always
};
// static interface to get/set preferences
static AutoDeleteMode autoDeleteMode();
static void setAutoDeleteMode(AutoDeleteMode mode);
private:
static QMetaEnum modeMetaEnum();
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
Q_ENUMS(AutoDeleteMode)
#else
Q_ENUM(AutoDeleteMode)
#endif
AutoDeleteMode m_mode;
bool m_wasAdded;
// Qt 4 moc has troubles with Q_GADGET: if Q_GADGET is present in a class, moc unconditionally
// references in the generated code the statiMetaObject from the class ancestor.
// Moreover, if the ancestor class has Q_GADGET but does not have other
// Q_ declarations, moc does not generate staticMetaObject for it. These results
// in referencing the non existent staticMetaObject and such code fails to compile.
// This problem is NOT present in Qt 5.7.0 and maybe in some older Qt 5 versions too
// Qt 4.8.7 has it.
// Therefore, we can't inherit FileGuard :(
FileGuard m_guard;
};
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
Q_DECLARE_METATYPE(TorrentFileGuard::AutoDeleteMode)
#endif

View File

@@ -33,9 +33,9 @@
const qlonglong MAX_ETA = 8640000;
enum class ShutdownAction
enum class ShutdownDialogAction
{
None,
Exit,
Shutdown,
Suspend,
Hibernate

View File

@@ -46,16 +46,20 @@ const char C_LOCALE_ESPERANTO[] = "Esperanto";
const char C_LOCALE_FRENCH[] = "Français";
const char C_LOCALE_GERMAN[] = "Deutsch";
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_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_SLOVAK[] = "Slovenčina";
const char C_LOCALE_SLOVENIAN[] = "Slovenščina";
@@ -71,6 +75,7 @@ const char C_LOCALE_NORWEGIAN[] = "Norsk";
const char C_LOCALE_DANISH[] = "Dansk";
const char C_LOCALE_BULGARIAN[] = "Български";
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[] = "עברית";

View File

@@ -269,67 +269,43 @@ bool Utils::Fs::isValidFileSystemName(const QString &name, bool allowSeparators)
return !name.contains(regex);
}
qlonglong Utils::Fs::freeDiskSpaceOnPath(QString path)
qulonglong Utils::Fs::freeDiskSpaceOnPath(const QString &path)
{
if (path.isEmpty()) return -1;
QDir dir_path(path);
if (!dir_path.exists()) {
QStringList parts = path.split("/");
while (parts.size() > 1 && !QDir(parts.join("/")).exists()) {
parts.removeLast();
}
dir_path = QDir(parts.join("/"));
if (!dir_path.exists()) return -1;
}
Q_ASSERT(dir_path.exists());
if (path.isEmpty()) return 0;
#ifndef Q_OS_WIN
unsigned long long available;
#ifdef Q_OS_HAIKU
const QString statfs_path = dir_path.path() + "/.";
dev_t device = dev_for_path (qPrintable(statfs_path));
if (device >= 0) {
fs_info info;
if (fs_stat_dev(device, &info) == B_OK) {
available = ((unsigned long long)(info.free_blocks * info.block_size));
return available;
}
QDir dirPath(path);
if (!dirPath.exists()) {
QStringList parts = path.split("/");
while (parts.size() > 1 && !QDir(parts.join("/")).exists())
parts.removeLast();
dirPath = QDir(parts.join("/"));
if (!dirPath.exists()) return 0;
}
return -1;
Q_ASSERT(dirPath.exists());
#if defined(Q_OS_WIN)
ULARGE_INTEGER bytesFree;
LPCWSTR nativePath = reinterpret_cast<LPCWSTR>((toNativePath(dirPath.path())).utf16());
if (GetDiskFreeSpaceExW(nativePath, &bytesFree, NULL, NULL) == 0)
return 0;
return bytesFree.QuadPart;
#elif defined(Q_OS_HAIKU)
const QString statfsPath = dirPath.path() + "/.";
dev_t device = dev_for_path(qPrintable(statfsPath));
if (device < 0)
return 0;
fs_info info;
if (fs_stat_dev(device, &info) != B_OK)
return 0;
return ((qulonglong) info.free_blocks * (qulonglong) info.block_size);
#else
struct statfs stats;
const QString statfs_path = dir_path.path() + "/.";
const int ret = statfs(qPrintable(statfs_path), &stats);
if (ret == 0) {
available = ((unsigned long long)stats.f_bavail)
* ((unsigned long long)stats.f_bsize);
return available;
}
else {
return -1;
}
#endif
#else
typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR,
PULARGE_INTEGER,
PULARGE_INTEGER,
PULARGE_INTEGER);
GetDiskFreeSpaceEx_t pGetDiskFreeSpaceEx =
(GetDiskFreeSpaceEx_t)::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "GetDiskFreeSpaceExW");
if (pGetDiskFreeSpaceEx) {
ULARGE_INTEGER bytesFree, bytesTotal;
unsigned long long *ret;
if (pGetDiskFreeSpaceEx((LPCTSTR)(toNativePath(dir_path.path())).utf16(), &bytesFree, &bytesTotal, NULL)) {
ret = (unsigned long long*)&bytesFree;
return *ret;
}
else {
return -1;
}
}
else {
return -1;
}
const QString statfsPath = dirPath.path() + "/.";
const int ret = statfs(qPrintable(statfsPath), &stats);
if (ret != 0)
return 0;
return ((qulonglong) stats.f_bavail * (qulonglong) stats.f_bsize);
#endif
}
@@ -378,19 +354,17 @@ QString Utils::Fs::expandPathAbs(const QString& path)
QString Utils::Fs::QDesktopServicesDataLocation()
{
QString result;
#ifdef Q_OS_WIN
LPWSTR path=new WCHAR[256];
if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE))
#if defined(Q_OS_WIN)
wchar_t path[MAX_PATH + 1] = {L'\0'};
if (SHGetSpecialFolderPathW(0, path, CSIDL_LOCAL_APPDATA, FALSE))
result = fromNativePath(QString::fromWCharArray(path));
if (!QCoreApplication::applicationName().isEmpty())
result += QLatin1String("/") + qApp->applicationName();
#else
#ifdef Q_OS_MAC
#elif defined(Q_OS_MAC)
FSRef ref;
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref);
if (err)
return QString();
QString path;
QByteArray ba(2048, 0);
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
@@ -402,7 +376,6 @@ QString Utils::Fs::QDesktopServicesDataLocation()
xdgDataHome += QLatin1String("/data/")
+ qApp->applicationName();
result = xdgDataHome;
#endif
#endif
if (!result.endsWith("/"))
result += "/";

View File

@@ -50,7 +50,7 @@ namespace Utils
bool sameFiles(const QString& path1, const QString& path2);
QString toValidFileSystemName(const QString &name, bool allowSeparators = false);
bool isValidFileSystemName(const QString& name, bool allowSeparators = false);
qlonglong freeDiskSpaceOnPath(QString path);
qulonglong freeDiskSpaceOnPath(const QString &path);
QString branchPath(const QString& file_path, QString* removed = 0);
bool sameFileNames(const QString& first, const QString& second);
QString expandPath(const QString& path);

View File

@@ -52,6 +52,7 @@
#ifdef Q_OS_WIN
#include <windows.h>
#include <powrprof.h>
#include <Shlobj.h>
const int UNLEN = 256;
#else
#include <unistd.h>
@@ -86,20 +87,21 @@ static struct { const char *source; const char *comment; } units[] = {
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)")
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)")
};
#ifndef DISABLE_GUI
void Utils::Misc::shutdownComputer(ShutdownAction action)
void Utils::Misc::shutdownComputer(const ShutdownDialogAction &action)
{
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
// Use dbus to power off / suspend the system
if (action != ShutdownAction::Shutdown) {
if (action != ShutdownDialogAction::Shutdown) {
// Some recent systems use systemd's logind
QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", QDBusConnection::systemBus());
if (login1Iface.isValid()) {
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
login1Iface.call("Suspend", false);
else
login1Iface.call("Hibernate", false);
@@ -109,7 +111,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
QDBusInterface upowerIface("org.freedesktop.UPower", "/org/freedesktop/UPower",
"org.freedesktop.UPower", QDBusConnection::systemBus());
if (upowerIface.isValid()) {
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
upowerIface.call("Suspend");
else
upowerIface.call("Hibernate");
@@ -119,7 +121,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer",
"org.freedesktop.Hal.Device.SystemPowerManagement",
QDBusConnection::systemBus());
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
halIface.call("Suspend", 5);
else
halIface.call("Hibernate");
@@ -148,7 +150,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
#endif
#ifdef Q_OS_MAC
AEEventID EventToSend;
if (action != ShutdownAction::Shutdown)
if (action != ShutdownDialogAction::Shutdown)
EventToSend = kAESleep;
else
EventToSend = kAEShutDown;
@@ -158,7 +160,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
AppleEvent appleEventToSend = {typeNull, NULL};
OSStatus error = AECreateDesc(typeProcessSerialNumber, &kPSNOfSystemProcess,
sizeof(kPSNOfSystemProcess), &targetDesc);
sizeof(kPSNOfSystemProcess), &targetDesc);
if (error != noErr)
return;
@@ -201,9 +203,9 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
if (GetLastError() != ERROR_SUCCESS)
return;
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
SetSuspendState(false, false, false);
else if (action == ShutdownAction::Hibernate)
else if (action == ShutdownDialogAction::Hibernate)
SetSuspendState(true, false, false);
else
InitiateSystemShutdownA(0, QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.").toLocal8Bit().data(), 10, true, false);
@@ -214,7 +216,6 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
(PTOKEN_PRIVILEGES) NULL, 0);
#endif
}
#endif // DISABLE_GUI
#ifndef DISABLE_GUI
// Get screen center
@@ -233,6 +234,7 @@ QPoint Utils::Misc::screenCenter(QWidget *win)
QRect desk(QApplication::desktop()->availableGeometry(scrn));
return QPoint((desk.width() - win->frameGeometry().width()) / 2, (desk.height() - win->frameGeometry().height()) / 2);
}
#endif
/**
@@ -267,12 +269,12 @@ QString Utils::Misc::pythonExecutable()
* http://legacy.python.org/dev/peps/pep-0394/
*/
pythonProc.start("python3", QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0) {
if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0)) {
executable = "python3";
return executable;
}
pythonProc.start("python2", QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0) {
if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0)) {
executable = "python2";
return executable;
}
@@ -280,7 +282,7 @@ QString Utils::Misc::pythonExecutable()
// Look for "python" in Windows and in UNIX if "python2" and "python3" are
// not detected.
pythonProc.start("python", QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0)
if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0))
executable = "python";
else
Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python not detected"), Log::INFO);
@@ -293,14 +295,15 @@ QString Utils::Misc::pythonExecutable()
* eg 2.7.9
* Make sure to have setup python first
*/
QString Utils::Misc::pythonVersionComplete() {
QString Utils::Misc::pythonVersionComplete()
{
static QString version;
if (version.isEmpty()) {
if (pythonExecutable().isEmpty())
return version;
QProcess pythonProc;
pythonProc.start(pythonExecutable(), QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0) {
if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0)) {
QByteArray output = pythonProc.readAllStandardOutput();
if (output.isEmpty())
output = pythonProc.readAllStandardError();
@@ -318,31 +321,57 @@ QString Utils::Misc::pythonVersionComplete() {
return version;
}
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
QString Utils::Misc::unitString(Utils::Misc::SizeUnit unit)
{
return QCoreApplication::translate("misc",
units[static_cast<int>(unit)].source, units[static_cast<int>(unit)].comment);
}
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB, ...)
// use Binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
// value must be given in bytes
// to send numbers instead of strings with suffixes
QString Utils::Misc::friendlyUnit(qreal val, bool is_speed)
bool Utils::Misc::friendlyUnit(qint64 sizeInBytes, qreal &val, Utils::Misc::SizeUnit &unit)
{
if (val < 0)
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
if (sizeInBytes < 0) return false;
int i = 0;
while(val >= 1024. && i < 4) {
val /= 1024.;
qreal rawVal = static_cast<qreal>(sizeInBytes);
while ((rawVal >= 1024.) && (i <= static_cast<int>(SizeUnit::ExbiByte))) {
rawVal /= 1024.;
++i;
}
val = rawVal;
unit = static_cast<SizeUnit>(i);
return true;
}
QString Utils::Misc::friendlyUnit(qint64 bytesValue, bool isSpeed)
{
SizeUnit unit;
qreal friendlyVal;
if (!friendlyUnit(bytesValue, friendlyVal, unit))
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
QString ret;
if (i == 0)
ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment);
if (unit == SizeUnit::Byte)
ret = QString::number(bytesValue) + " " + unitString(unit);
else
ret = Utils::String::fromDouble(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment);
if (is_speed)
ret = Utils::String::fromDouble(friendlyVal, 1) + " " + unitString(unit);
if (isSpeed)
ret += QCoreApplication::translate("misc", "/s", "per second");
return ret;
}
bool Utils::Misc::isPreviewable(const QString& extension)
qlonglong Utils::Misc::sizeInBytes(qreal size, Utils::Misc::SizeUnit unit)
{
for (int i = 0; i < static_cast<int>(unit); ++i)
size *= 1024;
return size;
}
bool Utils::Misc::isPreviewable(const QString &extension)
{
static QSet<QString> multimedia_extensions;
if (multimedia_extensions.empty()) {
@@ -399,7 +428,7 @@ bool Utils::Misc::isPreviewable(const QString& extension)
// time duration like "1d 2h 10m".
QString Utils::Misc::userFriendlyDuration(qlonglong seconds)
{
if (seconds < 0 || seconds >= MAX_ETA)
if ((seconds < 0) || (seconds >= MAX_ETA))
return QString::fromUtf8(C_INFINITY);
if (seconds == 0)
return "0";
@@ -407,7 +436,7 @@ QString Utils::Misc::userFriendlyDuration(qlonglong seconds)
return QCoreApplication::translate("misc", "< 1m", "< 1 minute");
int minutes = seconds / 60;
if (minutes < 60)
return QCoreApplication::translate("misc", "%1m","e.g: 10minutes").arg(QString::number(minutes));
return QCoreApplication::translate("misc", "%1m", "e.g: 10minutes").arg(QString::number(minutes));
int hours = minutes / 60;
minutes = minutes - hours * 60;
if (hours < 24)
@@ -424,7 +453,7 @@ QString Utils::Misc::getUserIDString()
QString uid = "0";
#ifdef Q_OS_WIN
WCHAR buffer[UNLEN + 1] = {0};
DWORD buffer_len = sizeof(buffer)/sizeof(*buffer);
DWORD buffer_len = sizeof(buffer) / sizeof(*buffer);
if (GetUserNameW(buffer, &buffer_len))
uid = QString::fromWCharArray(buffer);
#else
@@ -468,18 +497,18 @@ QString Utils::Misc::parseHtmlLinks(const QString &raw_text)
{
QString result = raw_text;
static QRegExp reURL(
"(\\s|^)" //start with whitespace or beginning of line
"(\\s|^)" // start with whitespace or beginning of line
"("
"(" //case 1 -- URL with scheme
"(http(s?))\\://" //start with scheme
"(" // case 1 -- URL with scheme
"(http(s?))\\://" // start with scheme
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
"([a-zA-Z0-9\\?%=&/_\\.:#;-]+)" // everything to 1st non-URI char, must be at least one char after the previous dot (cannot use ".*" because it can be too greedy)
")"
"|"
"(" //case 2a -- no scheme, contains common TLD example.com
"(" // case 2a -- no scheme, contains common TLD example.com
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
"(?=" // must be followed by TLD
"AERO|aero|" //N.B. assertions are non-capturing
"AERO|aero|" // N.B. assertions are non-capturing
"ARPA|arpa|"
"ASIA|asia|"
"BIZ|biz|"
@@ -507,8 +536,8 @@ QString Utils::Misc::parseHtmlLinks(const QString &raw_text)
")"
"|"
"(" // case 2b no scheme, no TLD, must have at least 2 alphanum strings plus uncommon TLD string --> del.icio.us
"([a-zA-Z0-9_-]+\\.) {2,}" //2 or more domainpart. --> del.icio.
"[a-zA-Z]{2,}" //one ab (2 char or longer) --> us
"([a-zA-Z0-9_-]+\\.) {2,}" // 2 or more domainpart. --> del.icio.
"[a-zA-Z]{2,}" // one ab (2 char or longer) --> us
"([a-zA-Z0-9\\?%=&/_\\.:#;-]*)" // everything to 1st non-URI char, maybe nothing in case of del.icio.us/path
")"
")"
@@ -529,7 +558,7 @@ QString Utils::Misc::parseHtmlLinks(const QString &raw_text)
#ifndef DISABLE_GUI
// Open the given path with an appropriate application
void Utils::Misc::openPath(const QString& absolutePath)
void Utils::Misc::openPath(const QString &absolutePath)
{
const QString path = Utils::Fs::fromNativePath(absolutePath);
// Hack to access samba shares with QDesktopServices::openUrl
@@ -541,79 +570,51 @@ void Utils::Misc::openPath(const QString& absolutePath)
// Open the parent directory of the given path with a file manager and select
// (if possible) the item at the given path
void Utils::Misc::openFolderSelect(const QString& absolutePath)
void Utils::Misc::openFolderSelect(const QString &absolutePath)
{
const QString path = Utils::Fs::fromNativePath(absolutePath);
// If the item to select doesn't exist, try to open its parent
if (!QFileInfo(path).exists()) {
openPath(path.left(path.lastIndexOf("/")));
return;
}
#ifdef Q_OS_WIN
if (QFileInfo(path).exists()) {
// Syntax is: explorer /select, "C:\Folder1\Folder2\file_to_select"
// Dir separators MUST be win-style slashes
// QProcess::startDetached() has an obscure bug. If the path has
// no spaces and a comma(and maybe other special characters) it doesn't
// get wrapped in quotes. So explorer.exe can't find the correct path and
// displays the default one. If we wrap the path in quotes and pass it to
// QProcess::startDetached() explorer.exe still shows the default path. In
// this case QProcess::startDetached() probably puts its own quotes around ours.
STARTUPINFO startupInfo;
::ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInfo;
::ZeroMemory(&processInfo, sizeof(processInfo));
QString cmd = QString("explorer.exe /select,\"%1\"").arg(Utils::Fs::toNativePath(absolutePath));
LPWSTR lpCmd = new WCHAR[cmd.size() + 1];
cmd.toWCharArray(lpCmd);
lpCmd[cmd.size()] = 0;
bool ret = ::CreateProcessW(NULL, lpCmd, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
delete [] lpCmd;
if (ret) {
::CloseHandle(processInfo.hProcess);
::CloseHandle(processInfo.hThread);
}
}
else {
// If the item to select doesn't exist, try to open its parent
openPath(path.left(path.lastIndexOf("/")));
HRESULT hresult = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
PIDLIST_ABSOLUTE pidl = ::ILCreateFromPathW(reinterpret_cast<PCTSTR>(Utils::Fs::toNativePath(path).utf16()));
if (pidl) {
::SHOpenFolderAndSelectItems(pidl, 0, nullptr, 0);
::ILFree(pidl);
}
if ((hresult == S_OK) || (hresult == S_FALSE))
::CoUninitialize();
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (QFileInfo(path).exists()) {
QProcess proc;
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
proc.waitForFinished();
QString output = proc.readLine().simplified();
if (output == "dolphin.desktop" || output == "org.kde.dolphin.desktop")
proc.startDetached("dolphin", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else if (output == "nautilus.desktop" || output == "org.gnome.Nautilus.desktop"
|| output == "nautilus-folder-handler.desktop")
proc.startDetached("nautilus", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "nemo.desktop")
proc.startDetached("nemo", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "konqueror.desktop" || output == "kfmclient_dir.desktop")
proc.startDetached("konqueror", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else {
// "caja" manager can't pinpoint the file, see: https://github.com/qbittorrent/qBittorrent/issues/5003
openPath(path.left(path.lastIndexOf("/")));
}
}
else {
// If the item to select doesn't exist, try to open its parent
QProcess proc;
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
proc.waitForFinished();
QString output = proc.readLine().simplified();
if ((output == "dolphin.desktop") || (output == "org.kde.dolphin.desktop"))
proc.startDetached("dolphin", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else if ((output == "nautilus.desktop") || (output == "org.gnome.Nautilus.desktop")
|| (output == "nautilus-folder-handler.desktop"))
proc.startDetached("nautilus", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "nemo.desktop")
proc.startDetached("nemo", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if ((output == "konqueror.desktop") || (output == "kfmclient_dir.desktop"))
proc.startDetached("konqueror", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else
// "caja" manager can't pinpoint the file, see: https://github.com/qbittorrent/qBittorrent/issues/5003
openPath(path.left(path.lastIndexOf("/")));
}
#else
openPath(path.left(path.lastIndexOf("/")));
#endif
}
#endif // DISABLE_GUI
namespace
{
// Trick to get a portable sleep() function
class SleeperThread : public QThread
class SleeperThread: public QThread
{
public:
static void msleep(unsigned long msecs)
@@ -635,6 +636,7 @@ QSize Utils::Misc::smallIconSize()
int s = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
return QSize(s, s);
}
#endif
QString Utils::Misc::osName()
@@ -642,12 +644,12 @@ QString Utils::Misc::osName()
// static initialization for usage in signal handler
static const QString name =
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
QString("%1 %2 %3")
.arg(QSysInfo::prettyProductName())
.arg(QSysInfo::kernelVersion())
.arg(QSysInfo::currentCpuArchitecture());
QString("%1 %2 %3")
.arg(QSysInfo::prettyProductName())
.arg(QSysInfo::kernelVersion())
.arg(QSysInfo::currentCpuArchitecture());
#else
"<Input OS name here>";
"<Input OS name here>";
#endif
return name;
}
@@ -656,9 +658,9 @@ QString Utils::Misc::boostVersionString()
{
// static initialization for usage in signal handler
static const QString ver = QString("%1.%2.%3")
.arg(BOOST_VERSION / 100000)
.arg((BOOST_VERSION / 100) % 1000)
.arg(BOOST_VERSION % 100);
.arg(BOOST_VERSION / 100000)
.arg((BOOST_VERSION / 100) % 1000)
.arg(BOOST_VERSION % 100);
return ver;
}
@@ -668,3 +670,15 @@ QString Utils::Misc::libtorrentVersionString()
static const QString ver = LIBTORRENT_VERSION;
return ver;
}
#ifdef Q_OS_WIN
QString Utils::Misc::windowsSystemPath()
{
static const QString path = []() -> QString {
WCHAR systemPath[64] = {0};
GetSystemDirectoryW(systemPath, sizeof(systemPath) / sizeof(WCHAR));
return QString::fromWCharArray(systemPath);
}();
return path;
}
#endif

View File

@@ -48,11 +48,28 @@ namespace Utils
{
namespace Misc
{
// use binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
enum class SizeUnit
{
Byte, // 1024^0,
KibiByte, // 1024^1,
MebiByte, // 1024^2,
GibiByte, // 1024^3,
TebiByte, // 1024^4,
PebiByte, // 1024^5,
ExbiByte // 1024^6,
// int64 is used for sizes and thus the next units can not be handled
// ZebiByte, // 1024^7,
// YobiByte, // 1024^8
};
QString parseHtmlLinks(const QString &raw_text);
bool isUrl(const QString &s);
void shutdownComputer(const ShutdownDialogAction &action);
#ifndef DISABLE_GUI
void shutdownComputer(ShutdownAction action);
// Get screen center
QPoint screenCenter(QWidget *win);
QSize smallIconSize();
@@ -64,11 +81,15 @@ namespace Utils
int pythonVersion();
QString pythonExecutable();
QString pythonVersionComplete();
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
// use Binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
QString unitString(SizeUnit unit);
// return best user friendly storage unit (B, KiB, MiB, GiB, TiB)
// value must be given in bytes
QString friendlyUnit(qreal val, bool is_speed = false);
bool friendlyUnit(qint64 sizeInBytes, qreal& val, SizeUnit& unit);
QString friendlyUnit(qint64 bytesValue, bool isSpeed = false);
qint64 sizeInBytes(qreal size, SizeUnit unit);
bool isPreviewable(const QString& extension);
// Take a number of seconds and return an user-friendly
@@ -81,12 +102,16 @@ namespace Utils
QList<int> intListfromStringList(const QStringList &l);
QList<bool> boolListfromStringList(const QStringList &l);
void msleep(unsigned long msecs);
#ifndef DISABLE_GUI
void openPath(const QString& absolutePath);
void openFolderSelect(const QString& absolutePath);
#endif
void msleep(unsigned long msecs);
#ifdef Q_OS_WIN
QString windowsSystemPath();
#endif
}
}

View File

@@ -27,12 +27,149 @@
* exception statement from your version.
*/
#include <QByteArray>
#include <QString>
#include <QLocale>
#include <cmath>
#include "string.h"
#include <cmath>
#include <QByteArray>
#include <QtGlobal>
#include <QLocale>
#ifdef QBT_USES_QT5
#include <QCollator>
#endif
#ifdef Q_OS_MAC
#include <QThreadStorage>
#endif
namespace
{
class NaturalCompare
{
public:
explicit NaturalCompare(const bool caseSensitive = true)
: m_caseSensitive(caseSensitive)
{
#ifdef QBT_USES_QT5
#if defined(Q_OS_WIN)
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
// sorts older versions of μTorrent differently than the newer ones because the
// 'μ' character is encoded differently and the native API can't cope with that.
// So default to using our custom natural sorting algorithm instead.
// See #5238 and #5240
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return;
#endif
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
#endif
}
bool operator()(const QString &left, const QString &right) const
{
#ifdef QBT_USES_QT5
#if defined(Q_OS_WIN)
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
// sorts older versions of μTorrent differently than the newer ones because the
// 'μ' character is encoded differently and the native API can't cope with that.
// So default to using our custom natural sorting algorithm instead.
// See #5238 and #5240
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return lessThan(left, right);
#endif
return (m_collator.compare(left, right) < 0);
#else
return lessThan(left, right);
#endif
}
bool lessThan(const QString &left, const QString &right) const
{
// Return value `false` indicates `right` should go before `left`, otherwise, after
int posL = 0;
int posR = 0;
while (true) {
while (true) {
if ((posL == left.size()) || (posR == right.size()))
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
QChar leftChar = m_caseSensitive ? left[posL] : left[posL].toLower();
QChar rightChar = m_caseSensitive ? right[posR] : right[posR].toLower();
if (leftChar == rightChar)
; // compare next character
else if (leftChar.isDigit() && rightChar.isDigit())
break; // Both are digits, break this loop and compare numbers
else
return leftChar < rightChar;
++posL;
++posR;
}
int startL = posL;
while ((posL < left.size()) && left[posL].isDigit())
++posL;
#ifdef QBT_USES_QT5
int numL = left.midRef(startL, posL - startL).toInt();
#else
int numL = left.mid(startL, posL - startL).toInt();
#endif
int startR = posR;
while ((posR < right.size()) && right[posR].isDigit())
++posR;
#ifdef QBT_USES_QT5
int numR = right.midRef(startR, posR - startR).toInt();
#else
int numR = right.mid(startR, posR - startR).toInt();
#endif
if (numL != numR)
return (numL < numR);
// Strings + digits do match and we haven't hit string end
// Do another round
}
return false;
}
private:
#ifdef QBT_USES_QT5
QCollator m_collator;
#endif
const bool m_caseSensitive;
};
}
bool Utils::String::naturalCompareCaseSensitive(const QString &left, const QString &right)
{
// provide a single `NaturalCompare` instance for easy use
// https://doc.qt.io/qt-5/threads-reentrancy.html
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(true));
return (nCmp.localData())(left, right);
#else
thread_local NaturalCompare nCmp(true);
return nCmp(left, right);
#endif
}
bool Utils::String::naturalCompareCaseInsensitive(const QString &left, const QString &right)
{
// provide a single `NaturalCompare` instance for easy use
// https://doc.qt.io/qt-5/threads-reentrancy.html
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(false));
return (nCmp.localData())(left, right);
#else
thread_local NaturalCompare nCmp(false);
return nCmp(left, right);
#endif
}
QString Utils::String::fromStdString(const std::string &str)
{
return QString::fromUtf8(str.c_str());
@@ -44,145 +181,6 @@ std::string Utils::String::toStdString(const QString &str)
return std::string(utf8.constData(), utf8.length());
}
// uses lessThan comparison
bool Utils::String::naturalSort(const QString &left, const QString &right, bool &result)
{
// Return value indicates if functions was successful
// result argument will contain actual comparison result if function was successful
int posL = 0;
int posR = 0;
do {
forever {
if (posL == left.size() || posR == right.size())
return false; // No data
QChar leftChar = left.at(posL);
QChar rightChar = right.at(posR);
bool leftCharIsDigit = leftChar.isDigit();
bool rightCharIsDigit = rightChar.isDigit();
if (leftCharIsDigit != rightCharIsDigit)
return false; // Digit positions mismatch
if (leftCharIsDigit)
break; // Both are digit, break this loop and compare numbers
if (leftChar != rightChar)
return false; // Strings' subsets before digit do not match
++posL;
++posR;
}
QString temp;
while (posL < left.size()) {
if (left.at(posL).isDigit())
temp += left.at(posL);
else
break;
posL++;
}
int numL = temp.toInt();
temp.clear();
while (posR < right.size()) {
if (right.at(posR).isDigit())
temp += right.at(posR);
else
break;
posR++;
}
int numR = temp.toInt();
if (numL != numR) {
result = (numL < numR);
return true;
}
// Strings + digits do match and we haven't hit string end
// Do another round
} while (true);
return false;
}
Utils::String::NaturalCompare::NaturalCompare()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return;
#endif
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(Qt::CaseInsensitive);
#endif
}
bool Utils::String::NaturalCompare::operator()(const QString &l, const QString &r)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return lessThan(l, r);
#endif
return (m_collator.compare(l, r) < 0);
#else
return lessThan(l, r);
#endif
}
bool Utils::String::NaturalCompare::lessThan(const QString &left, const QString &right)
{
// Return value `false` indicates `right` should go before `left`, otherwise, after
int posL = 0;
int posR = 0;
while (true) {
while (true) {
if (posL == left.size() || posR == right.size())
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
QChar leftChar = left[posL].toLower();
QChar rightChar = right[posR].toLower();
if (leftChar == rightChar)
; // compare next character
else if (leftChar.isDigit() && rightChar.isDigit())
break; // Both are digits, break this loop and compare numbers
else
return leftChar < rightChar;
++posL;
++posR;
}
int startL = posL;
while ((posL < left.size()) && left[posL].isDigit())
++posL;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numL = left.midRef(startL, posL - startL).toInt();
#else
int numL = left.mid(startL, posL - startL).toInt();
#endif
int startR = posR;
while ((posR < right.size()) && right[posR].isDigit())
++posR;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numR = right.midRef(startR, posR - startR).toInt();
#else
int numR = right.mid(startR, posR - startR).toInt();
#endif
if (numL != numR)
return (numL < numR);
// Strings + digits do match and we haven't hit string end
// Do another round
}
return false;
}
// to send numbers instead of strings with suffixes
QString Utils::String::fromDouble(double n, int precision)
{

View File

@@ -31,13 +31,9 @@
#define UTILS_STRING_H
#include <string>
#include <QtGlobal>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#include <QCollator>
#endif
class QString;
class QByteArray;
class QString;
namespace Utils
{
@@ -51,19 +47,8 @@ namespace Utils
// Taken from https://crackstation.net/hashing-security.htm
bool slowEquals(const QByteArray &a, const QByteArray &b);
bool naturalSort(const QString &left, const QString &right, bool &result);
class NaturalCompare
{
public:
NaturalCompare();
bool operator()(const QString &l, const QString &r);
bool lessThan(const QString &left, const QString &right);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
private:
QCollator m_collator;
#endif
};
bool naturalCompareCaseSensitive(const QString &left, const QString &right);
bool naturalCompareCaseInsensitive(const QString &left, const QString &right);
}
}

View File

@@ -8,6 +8,7 @@ add_subdirectory(properties)
add_subdirectory(powermanagement)
add_subdirectory(rss)
add_subdirectory(search)
if (UNIX AND NOT APPLE AND DBUS)
add_subdirectory(qtnotify)
include_directories(qtnotify)
@@ -31,6 +32,8 @@ addnewtorrentdialog.h
advancedsettings.h
advancedsettings.h
autoexpandabledialog.h
cookiesdialog.h
cookiesmodel.h
deletionconfirmationdlg.h
downloadfromurldlg.h
executionlog.h
@@ -40,11 +43,11 @@ ico.h
loglistwidget.h
mainwindow.h
messageboxraised.h
options_imp.h
optionsdlg.h
previewlistdelegate.h
previewselect.h
scanfoldersdelegate.h
shutdownconfirm.h
shutdownconfirmdlg.h
speedlimitdlg.h
statsdialog.h
statusbar.h
@@ -55,7 +58,6 @@ torrentcontentmodelfolder.h
torrentcontentmodelitem.h
torrentcontenttreeview.h
torrentcreatordlg.h
torrentimportdlg.h
torrentmodel.h
trackerlogin.h
transferlistdelegate.h
@@ -69,16 +71,18 @@ set(QBT_GUI_SOURCES
addnewtorrentdialog.cpp
advancedsettings.cpp
autoexpandabledialog.cpp
cookiesdialog.cpp
cookiesmodel.cpp
executionlog.cpp
guiiconprovider.cpp
ico.cpp
loglistwidget.cpp
mainwindow.cpp
messageboxraised.cpp
options_imp.cpp
optionsdlg.cpp
previewselect.cpp
scanfoldersdelegate.cpp
shutdownconfirm.cpp
shutdownconfirmdlg.cpp
speedlimitdlg.cpp
statsdialog.cpp
statusbar.cpp
@@ -89,7 +93,6 @@ torrentcontentmodelfolder.cpp
torrentcontentmodelitem.cpp
torrentcontenttreeview.cpp
torrentcreatordlg.cpp
torrentimportdlg.cpp
torrentmodel.cpp
trackerlogin.cpp
transferlistdelegate.cpp
@@ -107,22 +110,26 @@ endif (WIN32 OR APPLE)
set(QBT_GUI_FORMS
mainwindow.ui
about.ui
cookiesdialog.ui
preview.ui
login.ui
downloadfromurldlg.ui
bandwidth_limit.ui
updownratiodlg.ui
confirmdeletiondlg.ui
torrentimportdlg.ui
executionlog.ui
addnewtorrentdialog.ui
autoexpandabledialog.ui
statsdialog.ui
options.ui
optionsdlg.ui
torrentcreatordlg.ui
shutdownconfirmdlg.ui
)
set(QBT_GUI_RESOURCES about.qrc)
qbt_target_sources(about.qrc)
add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES} ${QBT_GUI_RESOURCES})
target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine ${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base)
add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES})
target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine
${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base
QtSingleApplication::QtSingleApplication
)

View File

@@ -71,11 +71,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>

View File

@@ -45,6 +45,7 @@
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "base/torrentfileguard.h"
#include "base/unicodestrings.h"
#include "guiiconprovider.h"
#include "autoexpandabledialog.h"
@@ -57,7 +58,6 @@
#define SETTINGS_KEY(name) "AddNewTorrentDialog/" name
const QString KEY_ENABLED = SETTINGS_KEY("Enabled");
const QString KEY_DEFAULTSAVEPATH = SETTINGS_KEY("DefaultSavePath");
const QString KEY_DEFAULTCATEGORY = SETTINGS_KEY("DefaultCategory");
const QString KEY_TREEHEADERSTATE = SETTINGS_KEY("TreeHeaderState");
const QString KEY_WIDTH = SETTINGS_KEY("Width");
@@ -88,15 +88,19 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
auto session = BitTorrent::Session::instance();
ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused());
(session->isASMDisabledByDefault() ? ui->simpleModeRadioButton : ui->advancedModeRadioButton)->setChecked(true);
ui->comboTTM->blockSignals(true); //the TreeView size isn't correct if the slot does it job at this point
ui->comboTTM->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
ui->comboTTM->blockSignals(false);
populateSavePathComboBox();
connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
connect(ui->browseButton, SIGNAL(clicked()), SLOT(browseButton_clicked()));
ui->defaultSavePathCheckBox->setVisible(false); // Default path is selected by default
ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
// Load categories
QStringList categories = session->categories();
std::sort(categories.begin(), categories.end(), Utils::String::NaturalCompare());
std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString();
if (!defaultCategory.isEmpty())
@@ -112,6 +116,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
loadState();
// Signal / slots
connect(ui->adv_button, SIGNAL(clicked(bool)), SLOT(showAdvancedSettings(bool)));
connect(ui->doNotDeleteTorrentCheckBox, SIGNAL(clicked(bool)), SLOT(doNotDeleteTorrentClicked(bool)));
editHotkey = new QShortcut(QKeySequence("F2"), ui->contentTreeView, 0, 0, Qt::WidgetShortcut);
connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedFile()));
connect(ui->contentTreeView, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedFile()));
@@ -203,13 +208,13 @@ bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
m_filePath = torrentPath;
if (!QFile::exists(m_filePath)) {
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist."));
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file '%1' does not exist.").arg(Utils::Fs::toNativePath(m_filePath)));
return false;
}
QFileInfo fileinfo(m_filePath);
if (!fileinfo.isReadable()) {
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file cannot be read from the disk. Probably you don't have enough permissions."));
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file '%1' cannot be read from the disk. Probably you don't have enough permissions.").arg(Utils::Fs::toNativePath(m_filePath)));
return false;
}
@@ -217,10 +222,11 @@ bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
QString error;
m_torrentInfo = BitTorrent::TorrentInfo::loadFromFile(m_filePath, error);
if (!m_torrentInfo.isValid()) {
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(error));
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1.\nError: %2", "Don't remove the '\n' characters. They insert a newline.").arg(Utils::Fs::toNativePath(m_filePath)).arg(error));
return false;
}
m_torrentGuard.reset(new TorrentFileGuard(m_filePath));
m_hash = m_torrentInfo.hash();
// Prevent showing the dialog if download is already present
@@ -244,6 +250,7 @@ bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
ui->lblhash->setText(m_hash);
setupTreeview();
TMMChanged(ui->comboTTM->currentIndex());
return true;
}
@@ -254,6 +261,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
return false;
}
m_torrentGuard.reset(new TorrentFileGuard(QString()));
m_hash = magnetUri.hash();
// Prevent showing the dialog if download is already present
if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) {
@@ -281,6 +289,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name);
setupTreeview();
TMMChanged(ui->comboTTM->currentIndex());
// Set dialog position
setdialogPosition();
@@ -384,7 +393,7 @@ void AddNewTorrentDialog::onSavePathChanged(int index)
ui->defaultSavePathCheckBox->setChecked(false);
ui->defaultSavePathCheckBox->setVisible(
QDir(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString())
!= QDir(defaultSavePath()));
!= QDir(BitTorrent::Session::instance()->defaultSavePath()));
// Remember index
m_oldIndex = index;
@@ -396,7 +405,7 @@ void AddNewTorrentDialog::categoryChanged(int index)
{
Q_UNUSED(index);
if (ui->advancedModeRadioButton->isChecked()) {
if (ui->comboTTM->currentIndex() == 1) {
QString savePath = BitTorrent::Session::instance()->categorySavePath(ui->categoryComboBox->currentText());
ui->savePathComboBox->setItemText(0, Utils::Fs::toNativePath(savePath));
ui->savePathComboBox->setItemData(0, savePath);
@@ -557,7 +566,7 @@ void AddNewTorrentDialog::setdialogPosition()
void AddNewTorrentDialog::populateSavePathComboBox()
{
QString defSavePath = defaultSavePath();
QString defSavePath = BitTorrent::Session::instance()->defaultSavePath();
ui->savePathComboBox->clear();
ui->savePathComboBox->addItem(Utils::Fs::toNativePath(defSavePath), defSavePath);
@@ -632,11 +641,11 @@ void AddNewTorrentDialog::accept()
params.addPaused = !ui->startTorrentCheckBox->isChecked();
QString savePath = ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString();
if (ui->simpleModeRadioButton->isChecked()) {
if (ui->comboTTM->currentIndex() != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
params.savePath = savePath;
saveSavePathHistory();
if (ui->defaultSavePathCheckBox->isChecked())
settings()->storeValue(KEY_DEFAULTSAVEPATH, savePath);
BitTorrent::Session::instance()->setDefaultSavePath(savePath);
}
setEnabled(!ui->never_show_cb->isChecked());
@@ -647,6 +656,7 @@ void AddNewTorrentDialog::accept()
else
BitTorrent::Session::instance()->addTorrent(m_torrentInfo, params);
m_torrentGuard->markAsAddedToSession();
QDialog::accept();
}
@@ -732,13 +742,6 @@ void AddNewTorrentDialog::setupTreeview()
setdialogPosition();
}
QString AddNewTorrentDialog::defaultSavePath() const
{
return Utils::Fs::fromNativePath(
settings()->loadValue(KEY_DEFAULTSAVEPATH,
BitTorrent::Session::instance()->defaultSavePath()).toString());
}
void AddNewTorrentDialog::handleDownloadFailed(const QString &url, const QString &reason)
{
MessageBoxRaised::critical(0, tr("Download Error"), QString("Cannot download '%1': %2").arg(url).arg(reason));
@@ -763,25 +766,25 @@ void AddNewTorrentDialog::handleDownloadFinished(const QString &url, const QStri
this->deleteLater();
}
void AddNewTorrentDialog::savingModeChanged(bool enabled)
void AddNewTorrentDialog::TMMChanged(int index)
{
if (!enabled) return;
if (ui->simpleModeRadioButton->isChecked()) {
if (index != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
populateSavePathComboBox();
ui->savePathComboBox->setEnabled(true);
ui->browseButton->setEnabled(true);
ui->groupBoxSavePath->setEnabled(true);
ui->savePathComboBox->blockSignals(false);
ui->savePathComboBox->setCurrentIndex(m_oldIndex < ui->savePathComboBox->count() ? m_oldIndex : ui->savePathComboBox->count() - 1);
ui->adv_button->setEnabled(true);
}
else {
ui->groupBoxSavePath->setEnabled(false);
ui->savePathComboBox->blockSignals(true);
ui->savePathComboBox->clear();
QString savePath = BitTorrent::Session::instance()->categorySavePath(ui->categoryComboBox->currentText());
ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath);
ui->savePathComboBox->setEnabled(false);
ui->browseButton->setEnabled(false);
ui->defaultSavePathCheckBox->setVisible(false);
ui->adv_button->setChecked(true);
ui->adv_button->setEnabled(false);
showAdvancedSettings(true);
}
}
@@ -795,3 +798,8 @@ void AddNewTorrentDialog::setCommentText(const QString &str) const
int height = lineHeight * lines;
ui->scrollArea->setMaximumHeight(height);
}
void AddNewTorrentDialog::doNotDeleteTorrentClicked(bool checked)
{
m_torrentGuard->setAutoRemove(!checked);
}

View File

@@ -31,8 +31,9 @@
#ifndef ADDNEWTORRENTDIALOG_H
#define ADDNEWTORRENTDIALOG_H
#include <QShortcut>
#include <QDialog>
#include <QScopedPointer>
#include <QShortcut>
#include <QUrl>
#include "base/bittorrent/infohash.h"
@@ -49,6 +50,7 @@ namespace Ui
}
class TorrentContentFilterModel;
class TorrentFileGuard;
class PropListDelegate;
class AddNewTorrentDialog: public QDialog
@@ -77,8 +79,9 @@ private slots:
void handleDownloadFailed(const QString &url, const QString &reason);
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
void handleDownloadFinished(const QString &url, const QString &filePath);
void savingModeChanged(bool enabled);
void TMMChanged(int index);
void categoryChanged(int index);
void doNotDeleteTorrentClicked(bool checked);
void accept() override;
void reject() override;
@@ -94,7 +97,6 @@ private:
void saveState();
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString());
void setupTreeview();
QString defaultSavePath() const;
void setCommentText(const QString &str) const;
void showEvent(QShowEvent *event) override;
@@ -109,6 +111,7 @@ private:
QShortcut *editHotkey;
QByteArray m_headerState;
int m_oldIndex;
QScopedPointer<TorrentFileGuard> m_torrentGuard;
};
#endif // ADDNEWTORRENTDIALOG_H

View File

@@ -7,60 +7,57 @@
<x>0</x>
<y>0</y>
<width>414</width>
<height>590</height>
<height>661</height>
</rect>
</property>
<layout class="QVBoxLayout" name="AddNewTorrentDialogLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelTorrentManagementMode">
<property name="text">
<string>Torrent Management Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboTTM">
<property name="toolTip">
<string>Automatic mode means that various torrent properties(eg save path) will be decided by the associated category</string>
</property>
<item>
<property name="text">
<string>Manual</string>
</property>
</item>
<item>
<property name="text">
<string>Automatic</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSavePath">
<property name="title">
<string>Save at</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>20</number>
</property>
<item>
<widget class="QLabel" name="savingModeLabel">
<property name="text">
<string>Saving Management:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="simpleModeRadioButton">
<property name="text">
<string>Simple</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advancedModeRadioButton">
<property name="text">
<string>Advanced</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
@@ -95,6 +92,16 @@
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="doNotDeleteTorrentCheckBox">
<property name="toolTip">
<string>When checked, the .torrent file will not be deleted despite the settings at the &quot;Download&quot; page of the options dialog</string>
</property>
<property name="text">
<string>Do not delete .torrent file</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="never_show_cb">
<property name="text">
@@ -129,12 +136,6 @@
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Category:</string>
</property>
@@ -142,11 +143,11 @@
</item>
<item>
<widget class="QComboBox" name="categoryComboBox">
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
@@ -175,27 +176,17 @@
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<width>35</width>
<height>0</height>
</size>
</property>
@@ -209,109 +200,28 @@
<property name="title">
<string>Torrent information</string>
</property>
<layout class="QVBoxLayout" name="infoGroupLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="size_lbl"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="date_lbl"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Hash:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblhash">
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Comment:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>299</width>
<height>73</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="commentLabel">
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QLabel" name="lblhash">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Hash:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="TorrentContentTreeView" name="contentTreeView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
@@ -324,6 +234,100 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="date_lbl">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="size_lbl">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Comment:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>308</width>
<height>74</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="commentLabel">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
@@ -395,15 +399,14 @@
<tabstops>
<tabstop>savePathComboBox</tabstop>
<tabstop>browseButton</tabstop>
<tabstop>simpleModeRadioButton</tabstop>
<tabstop>advancedModeRadioButton</tabstop>
<tabstop>defaultSavePathCheckBox</tabstop>
<tabstop>never_show_cb</tabstop>
<tabstop>adv_button</tabstop>
<tabstop>startTorrentCheckBox</tabstop>
<tabstop>skip_check_cb</tabstop>
<tabstop>categoryComboBox</tabstop>
<tabstop>defaultCategoryCheckbox</tabstop>
<tabstop>skip_check_cb</tabstop>
<tabstop>scrollArea</tabstop>
<tabstop>contentTreeView</tabstop>
</tabstops>
<resources/>
@@ -440,38 +443,6 @@
</hint>
</hints>
</connection>
<connection>
<sender>simpleModeRadioButton</sender>
<signal>toggled(bool)</signal>
<receiver>AddNewTorrentDialog</receiver>
<slot>savingModeChanged(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>154</x>
<y>39</y>
</hint>
<hint type="destinationlabel">
<x>122</x>
<y>6</y>
</hint>
</hints>
</connection>
<connection>
<sender>advancedModeRadioButton</sender>
<signal>toggled(bool)</signal>
<receiver>AddNewTorrentDialog</receiver>
<slot>savingModeChanged(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>218</x>
<y>44</y>
</hint>
<hint type="destinationlabel">
<x>209</x>
<y>7</y>
</hint>
</hints>
</connection>
<connection>
<sender>categoryComboBox</sender>
<signal>currentIndexChanged(int)</signal>
@@ -488,9 +459,26 @@
</hint>
</hints>
</connection>
<connection>
<sender>comboTTM</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>AddNewTorrentDialog</receiver>
<slot>TMMChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>200</x>
<y>19</y>
</hint>
<hint type="destinationlabel">
<x>206</x>
<y>294</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>savingModeChanged(bool)</slot>
<slot>categoryChanged(int)</slot>
<slot>TMMChanged(int)</slot>
</slots>
</ui>

View File

@@ -27,11 +27,16 @@
*/
#include "advancedsettings.h"
#include <QFont>
#include <QHeaderView>
#include <QHostAddress>
#include <QNetworkInterface>
#include "app/application.h"
#include "base/bittorrent/session.h"
#include "base/preferences.h"
#include "gui/mainwindow.h"
enum AdvSettingsCols
{
@@ -45,6 +50,8 @@ enum AdvSettingsRows
QBITTORRENT_HEADER,
// network interface
NETWORK_IFACE,
//Optional network address
NETWORK_IFACE_ADDRESS,
NETWORK_LISTEN_IPV6,
// behavior
SAVE_RESUME_DATA_INTERVAL,
@@ -58,6 +65,8 @@ enum AdvSettingsRows
RESOLVE_HOSTS,
RESOLVE_COUNTRIES,
PROGRAM_NOTIFICATIONS,
TORRENT_ADDED_NOTIFICATIONS,
DOWNLOAD_TRACKER_FAVICON,
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
USE_ICON_THEME,
#endif
@@ -80,7 +89,7 @@ enum AdvSettingsRows
// tracker
TRACKER_EXCHANGE,
ANNOUNCE_ALL_TRACKERS,
NETWORK_ADDRESS,
ANNOUNCE_IP,
ROW_COUNT
};
@@ -101,6 +110,7 @@ AdvancedSettings::AdvancedSettings(QWidget *parent)
setEditTriggers(QAbstractItemView::NoEditTriggers);
// Signals
connect(&spin_cache, SIGNAL(valueChanged(int)), SLOT(updateCacheSpinSuffix(int)));
connect(&combo_iface, SIGNAL(currentIndexChanged(int)), SLOT(updateInterfaceAddressCombo()));
// Load settings
loadAdvancedSettings();
resizeColumnToContents(0);
@@ -110,49 +120,63 @@ AdvancedSettings::AdvancedSettings(QWidget *parent)
void AdvancedSettings::saveAdvancedSettings()
{
Preferences* const pref = Preferences::instance();
BitTorrent::Session *const session = BitTorrent::Session::instance();
// Disk write cache
pref->setDiskCacheSize(spin_cache.value());
pref->setDiskCacheTTL(spin_cache_ttl.value());
session->setDiskCacheSize(spin_cache.value());
session->setDiskCacheTTL(spin_cache_ttl.value());
// Enable OS cache
pref->setOsCache(cb_os_cache.isChecked());
session->setUseOSCache(cb_os_cache.isChecked());
// Save resume data interval
pref->setSaveResumeDataInterval(spin_save_resume_data_interval.value());
session->setSaveResumeDataInterval(spin_save_resume_data_interval.value());
// Outgoing ports
pref->setOutgoingPortsMin(outgoing_ports_min.value());
pref->setOutgoingPortsMax(outgoing_ports_max.value());
session->setOutgoingPortsMin(outgoing_ports_min.value());
session->setOutgoingPortsMax(outgoing_ports_max.value());
// Recheck torrents on completion
pref->recheckTorrentsOnCompletion(cb_recheck_completed.isChecked());
// Transfer list refresh interval
pref->setRefreshInterval(spin_list_refresh.value());
session->setRefreshInterval(spin_list_refresh.value());
// Peer resolution
pref->resolvePeerCountries(cb_resolve_countries.isChecked());
pref->resolvePeerHostNames(cb_resolve_hosts.isChecked());
// Max Half-Open connections
pref->setMaxHalfOpenConnections(spin_maxhalfopen.value());
session->setMaxHalfOpenConnections(spin_maxhalfopen.value());
// Super seeding
pref->enableSuperSeeding(cb_super_seeding.isChecked());
session->setSuperSeedingEnabled(cb_super_seeding.isChecked());
// Network interface
if (combo_iface.currentIndex() == 0) {
// All interfaces (default)
pref->setNetworkInterface(QString::null);
pref->setNetworkInterfaceName(QString::null);
session->setNetworkInterface(QString());
session->setNetworkInterfaceName(QString());
}
else {
pref->setNetworkInterface(combo_iface.itemData(combo_iface.currentIndex()).toString());
pref->setNetworkInterfaceName(combo_iface.currentText());
session->setNetworkInterface(combo_iface.itemData(combo_iface.currentIndex()).toString());
session->setNetworkInterfaceName(combo_iface.currentText());
}
// Listen on IPv6 address
pref->setListenIPv6(cb_listen_ipv6.isChecked());
// Network address
QHostAddress addr(txt_network_address.text().trimmed());
if (addr.isNull())
pref->setNetworkAddress("");
else
pref->setNetworkAddress(addr.toString());
// Interface address
if (combo_iface_address.currentIndex() == 0) {
// All addresses (default)
session->setNetworkInterfaceAddress(QString::null);
}
else {
QHostAddress ifaceAddr(combo_iface_address.currentText().trimmed());
ifaceAddr.isNull() ? session->setNetworkInterfaceAddress(QString::null) : session->setNetworkInterfaceAddress(ifaceAddr.toString());
}
session->setIPv6Enabled(cb_listen_ipv6.isChecked());
// Announce IP
QHostAddress addr(txtAnnounceIP.text().trimmed());
session->setAnnounceIP(addr.isNull() ? "" : addr.toString());
// Program notification
pref->useProgramNotification(cb_program_notifications.isChecked());
MainWindow * const mainWindow = static_cast<Application*>(QCoreApplication::instance())->mainWindow();
mainWindow->setNotificationsEnabled(cb_program_notifications.isChecked());
mainWindow->setTorrentAddedNotificationsEnabled(cb_torrent_added_notifications.isChecked());
// Misc GUI properties
mainWindow->setDownloadTrackerFavicon(cb_tracker_favicon.isChecked());
// Tracker
pref->setTrackerEnabled(cb_tracker_status.isChecked());
session->setTrackerEnabled(cb_tracker_status.isChecked());
pref->setTrackerPort(spin_tracker_port.value());
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
pref->setUpdateCheckEnabled(cb_update_check.isChecked());
@@ -163,8 +187,8 @@ void AdvancedSettings::saveAdvancedSettings()
#endif
pref->setConfirmTorrentRecheck(cb_confirm_torrent_recheck.isChecked());
// Tracker exchange
pref->setTrackerExchangeEnabled(cb_enable_tracker_ext.isChecked());
pref->setAnnounceToAllTrackers(cb_announce_all_trackers.isChecked());
session->setTrackerExchangeEnabled(cb_enable_tracker_ext.isChecked());
session->setAnnounceToAllTrackers(cb_announce_all_trackers.isChecked());
}
void AdvancedSettings::updateCacheSpinSuffix(int value)
@@ -175,9 +199,48 @@ void AdvancedSettings::updateCacheSpinSuffix(int value)
spin_cache.setSuffix(tr(" MiB"));
}
void AdvancedSettings::updateInterfaceAddressCombo()
{
// Try to get the currently selected interface name
const QString ifaceName = combo_iface.itemData(combo_iface.currentIndex()).toString(); // Empty string for the first element
const QString currentAddress = BitTorrent::Session::instance()->networkInterfaceAddress();
//Clear all items and reinsert them, default to all
combo_iface_address.clear();
combo_iface_address.addItem(tr("All addresses"));
combo_iface_address.setCurrentIndex(0);
auto populateCombo = [this, &currentAddress](const QString &ip, const QAbstractSocket::NetworkLayerProtocol &protocol)
{
Q_ASSERT(protocol == QAbstractSocket::IPv4Protocol || protocol == QAbstractSocket::IPv6Protocol);
//Only take ipv4 for now?
if (protocol != QAbstractSocket::IPv4Protocol && protocol != QAbstractSocket::IPv6Protocol)
return;
combo_iface_address.addItem(ip);
//Try to select the last added one
if (ip == currentAddress)
combo_iface_address.setCurrentIndex(combo_iface_address.count() - 1);
};
if (ifaceName.isEmpty()) {
foreach (const QHostAddress &ip, QNetworkInterface::allAddresses())
populateCombo(ip.toString(), ip.protocol());
}
else {
const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
const QList<QNetworkAddressEntry> addresses = iface.addressEntries();
foreach (const QNetworkAddressEntry &entry, addresses) {
const QHostAddress ip = entry.ip();
populateCombo(ip.toString(), ip.protocol());
}
}
}
void AdvancedSettings::loadAdvancedSettings()
{
const Preferences* const pref = Preferences::instance();
const BitTorrent::Session *const session = BitTorrent::Session::instance();
// add section headers
QFont boldFont;
boldFont.setBold(true);
@@ -200,33 +263,33 @@ void AdvancedSettings::loadAdvancedSettings()
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
spin_cache.setMaximum(1536);
#endif
spin_cache.setValue(pref->diskCacheSize());
spin_cache.setValue(session->diskCacheSize());
updateCacheSpinSuffix(spin_cache.value());
addRow(DISK_CACHE, tr("Disk write cache size"), &spin_cache);
// Disk cache expiry
spin_cache_ttl.setMinimum(15);
spin_cache_ttl.setMaximum(600);
spin_cache_ttl.setValue(pref->diskCacheTTL());
spin_cache_ttl.setValue(session->diskCacheTTL());
spin_cache_ttl.setSuffix(tr(" s", " seconds"));
addRow(DISK_CACHE_TTL, tr("Disk cache expiry interval"), &spin_cache_ttl);
// Enable OS cache
cb_os_cache.setChecked(pref->osCache());
cb_os_cache.setChecked(session->useOSCache());
addRow(OS_CACHE, tr("Enable OS cache"), &cb_os_cache);
// Save resume data interval
spin_save_resume_data_interval.setMinimum(1);
spin_save_resume_data_interval.setMaximum(1440);
spin_save_resume_data_interval.setValue(pref->saveResumeDataInterval());
spin_save_resume_data_interval.setValue(session->saveResumeDataInterval());
spin_save_resume_data_interval.setSuffix(tr(" m", " minutes"));
addRow(SAVE_RESUME_DATA_INTERVAL, tr("Save resume data interval", "How often the fastresume file is saved."), &spin_save_resume_data_interval);
// Outgoing port Min
outgoing_ports_min.setMinimum(0);
outgoing_ports_min.setMaximum(65535);
outgoing_ports_min.setValue(pref->outgoingPortsMin());
outgoing_ports_min.setValue(session->outgoingPortsMin());
addRow(OUTGOING_PORT_MIN, tr("Outgoing ports (Min) [0: Disabled]"), &outgoing_ports_min);
// Outgoing port Min
outgoing_ports_max.setMinimum(0);
outgoing_ports_max.setMaximum(65535);
outgoing_ports_max.setValue(pref->outgoingPortsMax());
outgoing_ports_max.setValue(session->outgoingPortsMax());
addRow(OUTGOING_PORT_MAX, tr("Outgoing ports (Max) [0: Disabled]"), &outgoing_ports_max);
// Recheck completed torrents
cb_recheck_completed.setChecked(pref->recheckTorrentsOnCompletion());
@@ -234,7 +297,7 @@ void AdvancedSettings::loadAdvancedSettings()
// Transfer list refresh interval
spin_list_refresh.setMinimum(30);
spin_list_refresh.setMaximum(99999);
spin_list_refresh.setValue(pref->getRefreshInterval());
spin_list_refresh.setValue(session->refreshInterval());
spin_list_refresh.setSuffix(tr(" ms", " milliseconds"));
addRow(LIST_REFRESH, tr("Transfer list refresh interval"), &spin_list_refresh);
// Resolve Peer countries
@@ -246,17 +309,23 @@ void AdvancedSettings::loadAdvancedSettings()
// Max Half Open connections
spin_maxhalfopen.setMinimum(0);
spin_maxhalfopen.setMaximum(99999);
spin_maxhalfopen.setValue(pref->getMaxHalfOpenConnections());
spin_maxhalfopen.setValue(session->maxHalfOpenConnections());
addRow(MAX_HALF_OPEN, tr("Maximum number of half-open connections [0: Unlimited]"), &spin_maxhalfopen);
// Super seeding
cb_super_seeding.setChecked(pref->isSuperSeedingEnabled());
cb_super_seeding.setChecked(session->isSuperSeedingEnabled());
addRow(SUPER_SEEDING, tr("Strict super seeding"), &cb_super_seeding);
// Network interface
combo_iface.addItem(tr("Any interface", "i.e. Any network interface"));
const QString current_iface = pref->getNetworkInterface();
const QString current_iface = session->networkInterface();
bool interface_exists = current_iface.isEmpty();
int i = 1;
foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
// This line fixes a Qt bug => https://bugreports.qt.io/browse/QTBUG-52633
// Tested in Qt 5.6.0. For more info see:
// https://github.com/qbittorrent/qBittorrent/issues/5131
// https://github.com/qbittorrent/qBittorrent/pull/5135
if (iface.addressEntries().isEmpty()) continue;
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
combo_iface.addItem(iface.humanReadableName(), iface.name());
if (!current_iface.isEmpty() && (iface.name() == current_iface)) {
@@ -267,21 +336,33 @@ void AdvancedSettings::loadAdvancedSettings()
}
// Saved interface does not exist, show it anyway
if (!interface_exists) {
combo_iface.addItem(pref->getNetworkInterfaceName(), current_iface);
combo_iface.addItem(session->networkInterfaceName(), current_iface);
combo_iface.setCurrentIndex(i);
}
addRow(NETWORK_IFACE, tr("Network Interface (requires restart)"), &combo_iface);
// Network interface address
updateInterfaceAddressCombo();
addRow(NETWORK_IFACE_ADDRESS, tr("Optional IP Address to bind to (requires restart)"), &combo_iface_address);
// Listen on IPv6 address
cb_listen_ipv6.setChecked(pref->getListenIPv6());
cb_listen_ipv6.setChecked(session->isIPv6Enabled());
addRow(NETWORK_LISTEN_IPV6, tr("Listen on IPv6 address (requires restart)"), &cb_listen_ipv6);
// Network address
txt_network_address.setText(pref->getNetworkAddress());
addRow(NETWORK_ADDRESS, tr("IP Address to report to trackers (requires restart)"), &txt_network_address);
// Announce IP
txtAnnounceIP.setText(session->announceIP());
addRow(ANNOUNCE_IP, tr("IP Address to report to trackers (requires restart)"), &txtAnnounceIP);
// Program notifications
cb_program_notifications.setChecked(pref->useProgramNotification());
addRow(PROGRAM_NOTIFICATIONS, tr("Display program on-screen notifications"), &cb_program_notifications);
const MainWindow * const mainWindow = static_cast<Application*>(QCoreApplication::instance())->mainWindow();
cb_program_notifications.setChecked(mainWindow->isNotificationsEnabled());
addRow(PROGRAM_NOTIFICATIONS, tr("Display notifications"), &cb_program_notifications);
// Torrent added notifications
cb_torrent_added_notifications.setChecked(mainWindow->isTorrentAddedNotificationsEnabled());
addRow(TORRENT_ADDED_NOTIFICATIONS, tr("Display notifications for added torrents"), &cb_torrent_added_notifications);
// Download tracker's favicon
cb_tracker_favicon.setChecked(mainWindow->isDownloadTrackerFavicon());
addRow(DOWNLOAD_TRACKER_FAVICON, tr("Download tracker's favicon"), &cb_tracker_favicon);
// Tracker State
cb_tracker_status.setChecked(pref->isTrackerEnabled());
cb_tracker_status.setChecked(session->isTrackerEnabled());
addRow(TRACKER_STATUS, tr("Enable embedded tracker"), &cb_tracker_status);
// Tracker port
spin_tracker_port.setMinimum(1);
@@ -300,10 +381,10 @@ void AdvancedSettings::loadAdvancedSettings()
cb_confirm_torrent_recheck.setChecked(pref->confirmTorrentRecheck());
addRow(CONFIRM_RECHECK_TORRENT, tr("Confirm torrent recheck"), &cb_confirm_torrent_recheck);
// Tracker exchange
cb_enable_tracker_ext.setChecked(pref->trackerExchangeEnabled());
cb_enable_tracker_ext.setChecked(session->isTrackerExchangeEnabled());
addRow(TRACKER_EXCHANGE, tr("Exchange trackers with other peers"), &cb_enable_tracker_ext);
// Announce to all trackers
cb_announce_all_trackers.setChecked(pref->announceToAllTrackers());
cb_announce_all_trackers.setChecked(session->announceToAllTrackers());
addRow(ANNOUNCE_ALL_TRACKERS, tr("Always announce to all trackers"), &cb_announce_all_trackers);
}

View File

@@ -52,6 +52,7 @@ signals:
private slots:
void updateCacheSpinSuffix(int value);
void updateInterfaceAddressCombo();
private:
void loadAdvancedSettings();
@@ -59,11 +60,11 @@ private:
QLabel labelQbtLink, labelLibtorrentLink;
QSpinBox spin_cache, spin_save_resume_data_interval, outgoing_ports_min, outgoing_ports_max, spin_list_refresh, spin_maxhalfopen, spin_tracker_port, spin_cache_ttl;
QCheckBox cb_os_cache, cb_recheck_completed, cb_resolve_countries, cb_resolve_hosts,
cb_super_seeding, cb_program_notifications, cb_tracker_status,
QCheckBox cb_os_cache, cb_recheck_completed, cb_resolve_countries, cb_resolve_hosts, cb_super_seeding,
cb_program_notifications, cb_torrent_added_notifications, cb_tracker_favicon, cb_tracker_status,
cb_confirm_torrent_recheck, cb_enable_tracker_ext, cb_listen_ipv6, cb_announce_all_trackers;
QComboBox combo_iface;
QLineEdit txt_network_address;
QComboBox combo_iface, combo_iface_address;
QLineEdit txtAnnounceIP;
// OS dependent settings
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)

89
src/gui/cookiesdialog.cpp Normal file
View File

@@ -0,0 +1,89 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "cookiesdialog.h"
#include "base/settingsstorage.h"
#include "base/net/downloadmanager.h"
#include "guiiconprovider.h"
#include "cookiesmodel.h"
#include "ui_cookiesdialog.h"
#define SETTINGS_KEY(name) "CookiesDialog/" name
const QString KEY_GEOMETRY = SETTINGS_KEY("Geometry");
const QString KEY_COOKIESVIEWSTATE = SETTINGS_KEY("CookiesViewState");
CookiesDialog::CookiesDialog(QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::CookiesDialog)
, m_cookiesModel(new CookiesModel(Net::DownloadManager::instance()->allCookies(), this))
{
m_ui->setupUi(this);
setWindowIcon(GuiIconProvider::instance()->getIcon("preferences-web-browser-cookies"));
m_ui->buttonAdd->setIcon(GuiIconProvider::instance()->getIcon("list-add"));
m_ui->buttonDelete->setIcon(GuiIconProvider::instance()->getIcon("list-remove"));
m_ui->treeView->setModel(m_cookiesModel);
if (m_cookiesModel->rowCount() > 0)
m_ui->treeView->selectionModel()->setCurrentIndex(
m_cookiesModel->index(0, 0),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
restoreGeometry(SettingsStorage::instance()->loadValue(KEY_GEOMETRY).toByteArray());
m_ui->treeView->header()->restoreState(
SettingsStorage::instance()->loadValue(KEY_COOKIESVIEWSTATE).toByteArray());
}
CookiesDialog::~CookiesDialog()
{
SettingsStorage::instance()->storeValue(KEY_GEOMETRY, saveGeometry());
SettingsStorage::instance()->storeValue(
KEY_COOKIESVIEWSTATE, m_ui->treeView->header()->saveState());
delete m_ui;
}
void CookiesDialog::accept()
{
Net::DownloadManager::instance()->setAllCookies(m_cookiesModel->cookies());
QDialog::accept();
}
void CookiesDialog::onButtonAddClicked()
{
int row = m_ui->treeView->selectionModel()->currentIndex().row() + 1;
m_cookiesModel->insertRow(row);
m_ui->treeView->selectionModel()->setCurrentIndex(
m_cookiesModel->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
void CookiesDialog::onButtonDeleteClicked()
{
m_cookiesModel->removeRow(m_ui->treeView->selectionModel()->currentIndex().row());
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,39 +24,38 @@
* 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.
*
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
*/
#ifndef COOKIESDLG_H
#define COOKIESDLG_H
#ifndef COOKIESDIALOG_H
#define COOKIESDIALOG_H
#include <QDialog>
#include <QList>
class QNetworkCookie;
class QUrl;
namespace Ui {
class CookiesDlg;
namespace Ui
{
class CookiesDialog;
}
class CookiesDlg : public QDialog
class CookiesModel;
class CookiesDialog : public QDialog
{
Q_OBJECT
public:
explicit CookiesDlg(const QUrl &url, QWidget *parent = 0);
~CookiesDlg();
QList<QNetworkCookie> getCookies() const;
static bool askForCookies(QWidget *parent, const QUrl &url, QList<QNetworkCookie> &out);
explicit CookiesDialog(QWidget *parent = 0);
~CookiesDialog();
protected slots:
void on_add_btn_clicked();
void on_del_btn_clicked();
public slots:
void accept() override;
private slots:
void onButtonAddClicked();
void onButtonDeleteClicked();
private:
Ui::CookiesDlg *ui;
Ui::CookiesDialog *m_ui;
CookiesModel *m_cookiesModel;
};
#endif // COOKIESDLG_H
#endif // COOKIESDIALOG_H

179
src/gui/cookiesdialog.ui Normal file
View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CookiesDialog</class>
<widget class="QDialog" name="CookiesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>618</width>
<height>369</height>
</rect>
</property>
<property name="windowTitle">
<string>Manage Cookies</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="treeView"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="buttonAdd">
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="buttonDelete">
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CookiesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>406</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CookiesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>406</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonAdd</sender>
<signal>clicked()</signal>
<receiver>CookiesDialog</receiver>
<slot>onButtonAddClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>484</x>
<y>174</y>
</hint>
<hint type="destinationlabel">
<x>486</x>
<y>93</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonDelete</sender>
<signal>clicked()</signal>
<receiver>CookiesDialog</receiver>
<slot>onButtonDeleteClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>483</x>
<y>226</y>
</hint>
<hint type="destinationlabel">
<x>485</x>
<y>296</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>onButtonAddClicked()</slot>
<slot>onButtonDeleteClicked()</slot>
</slots>
</ui>

178
src/gui/cookiesmodel.cpp Normal file
View File

@@ -0,0 +1,178 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include <QDateTime>
#include "cookiesmodel.h"
CookiesModel::CookiesModel(const QList<QNetworkCookie> &cookies, QObject *parent)
: QAbstractItemModel(parent)
, m_cookies(cookies)
{
}
QList<QNetworkCookie> CookiesModel::cookies() const
{
return m_cookies;
}
QVariant CookiesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal)) {
switch (section)
{
case COL_DOMAIN:
return tr("Domain");
case COL_PATH:
return tr("Path");
case COL_NAME:
return tr("Name");
case COL_VALUE:
return tr("Value");
case COL_EXPDATE:
return tr("Expiration Date");
}
}
return QVariant();
}
QModelIndex CookiesModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() // no items with valid parent
|| (row < 0) || (row >= m_cookies.size())
|| (column < 0) || (column >= NB_COLUMNS))
return QModelIndex();
return createIndex(row, column, &m_cookies[row]);
}
QModelIndex CookiesModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index);
return QModelIndex();
}
int CookiesModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) return 0;
return m_cookies.size();
}
int CookiesModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return NB_COLUMNS;
}
QVariant CookiesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || (index.row() >= m_cookies.size())
|| ((role != Qt::DisplayRole) && (role != Qt::EditRole)))
return QVariant();
switch (index.column()) {
case COL_DOMAIN:
return m_cookies[index.row()].domain();
case COL_PATH:
return m_cookies[index.row()].path();
case COL_NAME:
return QString::fromLatin1(m_cookies[index.row()].name());
case COL_VALUE:
return QString::fromLatin1(m_cookies[index.row()].value());
case COL_EXPDATE:
return m_cookies[index.row()].expirationDate();
}
return QVariant();
}
bool CookiesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole) return false;
switch (index.column()) {
case COL_DOMAIN:
m_cookies[index.row()].setDomain(value.toString());
break;
case COL_PATH:
m_cookies[index.row()].setPath(value.toString());
break;
case COL_NAME:
m_cookies[index.row()].setName(value.toString().toLatin1());
break;
case COL_VALUE:
m_cookies[index.row()].setValue(value.toString().toLatin1());
break;
case COL_EXPDATE:
m_cookies[index.row()].setExpirationDate(value.toDateTime());
break;
default:
return false;
}
emit dataChanged(index, index);
return true;
}
bool CookiesModel::insertRows(int row, int count, const QModelIndex &parent)
{
if ((row < 0) || (row > m_cookies.size())) return false;
QNetworkCookie newCookie;
newCookie.setExpirationDate(QDateTime::currentDateTime().addYears(99));
beginInsertRows(parent, row, row + count - 1);
while (count-- > 0)
m_cookies.insert(row, newCookie);
endInsertRows();
return true;
}
bool CookiesModel::removeRows(int row, int count, const QModelIndex &parent)
{
if ((m_cookies.size() == 0)
|| (row >= m_cookies.size())
|| ((row + count) > m_cookies.size()))
return false;
beginRemoveRows(parent, row, row + count - 1);
while (count-- > 0)
m_cookies.removeAt(row);
endRemoveRows();
return true;
}
Qt::ItemFlags CookiesModel::flags(const QModelIndex &index) const
{
if (!index.isValid()) return 0;
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
}

74
src/gui/cookiesmodel.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#ifndef COOKIESMODEL_H
#define COOKIESMODEL_H
#include <QAbstractItemModel>
#include <QList>
#include <QNetworkCookie>
class CookiesModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum Column
{
COL_DOMAIN,
COL_PATH,
COL_NAME,
COL_VALUE,
COL_EXPDATE,
NB_COLUMNS
};
explicit CookiesModel(const QList<QNetworkCookie> &cookies, QObject *parent = 0);
QList<QNetworkCookie> cookies() const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
private:
mutable QList<QNetworkCookie> m_cookies;
};
#endif // COOKIESMODEL_H

View File

@@ -42,7 +42,7 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
Q_OBJECT
public:
DeletionConfirmationDlg(QWidget *parent, const int &size, const QString &name): QDialog(parent) {
DeletionConfirmationDlg(QWidget *parent, const int &size, const QString &name, bool defaultDeleteFiles): QDialog(parent) {
setupUi(this);
if (size == 1)
label->setText(tr("Are you sure you want to delete '%1' from the transfer list?", "Are you sure you want to delete 'ubuntu-linux-iso' from the transfer list?").arg(name));
@@ -54,7 +54,7 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
rememberBtn->setIcon(GuiIconProvider::instance()->getIcon("object-locked"));
move(Utils::Misc::screenCenter(this));
checkPermDelete->setChecked(Preferences::instance()->deleteTorrentFilesAsDefault());
checkPermDelete->setChecked(defaultDeleteFiles || Preferences::instance()->deleteTorrentFilesAsDefault());
connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState()));
buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
}
@@ -63,10 +63,10 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
return checkPermDelete->isChecked();
}
static bool askForDeletionConfirmation(bool& delete_local_files, const int& size, const QString& name) {
DeletionConfirmationDlg dlg(NULL, size, name);
static bool askForDeletionConfirmation(bool& deleteLocalFiles, const int& size, const QString& name) {
DeletionConfirmationDlg dlg(NULL, size, name, deleteLocalFiles);
if (dlg.exec() == QDialog::Accepted) {
delete_local_files = dlg.shouldDeleteLocalFiles();
deleteLocalFiles = dlg.shouldDeleteLocalFiles();
return true;
}
return false;

View File

@@ -96,9 +96,9 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL{
QMessageBox::warning(0, tr("No URL entered"), tr("Please type at least one URL."));
return;
}
close();
emit urlsReadyToBeDownloaded(url_list_cleaned);
qDebug("Emitted urlsReadytobedownloaded signal");
close();
}
void on_cancelButton_clicked() {

View File

@@ -14,6 +14,18 @@
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabConsole">
<property name="tabPosition">
@@ -26,13 +38,13 @@
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QVBoxLayout"/>
<layout class="QVBoxLayout" name="tabGeneralLayout"/>
</widget>
<widget class="QWidget" name="tabBan">
<attribute name="title">
<string>Blocked IPs</string>
</attribute>
<layout class="QVBoxLayout" name="_2"/>
<layout class="QVBoxLayout" name="tabBanLayout"/>
</widget>
</widget>
</item>

View File

@@ -28,7 +28,6 @@ HEADERS += \
$$PWD/downloadfromurldlg.h \
$$PWD/trackerlogin.h \
$$PWD/hidabletabwidget.h \
$$PWD/torrentimportdlg.h \
$$PWD/executionlog.h \
$$PWD/guiiconprovider.h \
$$PWD/updownratiodlg.h \
@@ -37,9 +36,9 @@ HEADERS += \
$$PWD/autoexpandabledialog.h \
$$PWD/statsdialog.h \
$$PWD/messageboxraised.h \
$$PWD/options_imp.h \
$$PWD/optionsdlg.h \
$$PWD/advancedsettings.h \
$$PWD/shutdownconfirm.h \
$$PWD/shutdownconfirmdlg.h \
$$PWD/torrentmodel.h \
$$PWD/torrentcreatordlg.h \
$$PWD/scanfoldersdelegate.h \
@@ -48,7 +47,9 @@ HEADERS += \
$$PWD/search/pluginselectdlg.h \
$$PWD/search/pluginsourcedlg.h \
$$PWD/search/searchlistdelegate.h \
$$PWD/search/searchsortmodel.h
$$PWD/search/searchsortmodel.h \
$$PWD/cookiesmodel.h \
$$PWD/cookiesdialog.h
SOURCES += \
$$PWD/mainwindow.cpp \
@@ -63,7 +64,6 @@ SOURCES += \
$$PWD/torrentcontentmodelfile.cpp \
$$PWD/torrentcontentfiltermodel.cpp \
$$PWD/torrentcontenttreeview.cpp \
$$PWD/torrentimportdlg.cpp \
$$PWD/executionlog.cpp \
$$PWD/speedlimitdlg.cpp \
$$PWD/previewselect.cpp \
@@ -77,8 +77,8 @@ SOURCES += \
$$PWD/statusbar.cpp \
$$PWD/advancedsettings.cpp \
$$PWD/trackerlogin.cpp \
$$PWD/options_imp.cpp \
$$PWD/shutdownconfirm.cpp \
$$PWD/optionsdlg.cpp \
$$PWD/shutdownconfirmdlg.cpp \
$$PWD/torrentmodel.cpp \
$$PWD/torrentcreatordlg.cpp \
$$PWD/scanfoldersdelegate.cpp \
@@ -87,7 +87,9 @@ SOURCES += \
$$PWD/search/pluginselectdlg.cpp \
$$PWD/search/pluginsourcedlg.cpp \
$$PWD/search/searchlistdelegate.cpp \
$$PWD/search/searchsortmodel.cpp
$$PWD/search/searchsortmodel.cpp \
$$PWD/cookiesmodel.cpp \
$$PWD/cookiesdialog.cpp
win32|macx {
HEADERS += $$PWD/programupdater.h
@@ -103,16 +105,17 @@ FORMS += \
$$PWD/bandwidth_limit.ui \
$$PWD/updownratiodlg.ui \
$$PWD/confirmdeletiondlg.ui \
$$PWD/confirmshutdowndlg.ui \
$$PWD/torrentimportdlg.ui \
$$PWD/shutdownconfirmdlg.ui \
$$PWD/executionlog.ui \
$$PWD/addnewtorrentdialog.ui \
$$PWD/autoexpandabledialog.ui \
$$PWD/statsdialog.ui \
$$PWD/options.ui \
$$PWD/optionsdlg.ui \
$$PWD/torrentcreatordlg.ui \
$$PWD/search/searchwidget.ui \
$$PWD/search/pluginselectdlg.ui \
$$PWD/search/pluginsourcedlg.ui
$$PWD/search/pluginsourcedlg.ui \
$$PWD/search/searchtab.ui \
$$PWD/cookiesdialog.ui
RESOURCES += $$PWD/about.qrc

View File

@@ -10,9 +10,11 @@ set(QBT_LINEEDIT_RESOURCES
resources/lineeditimages.qrc
)
add_library(qbt_lineedit STATIC ${QBT_LINEEDIT_SOURCES} ${QBT_LINEEDIT_HEADERS} ${QBT_LINEEDIT_RESOURCES})
add_library(qbt_lineedit STATIC ${QBT_LINEEDIT_SOURCES} ${QBT_LINEEDIT_HEADERS})
if (QT4_FOUND)
target_link_libraries(qbt_lineedit Qt4::QtGui)
else (QT4_FOUND)
target_link_libraries(qbt_lineedit Qt5::Widgets)
endif (QT4_FOUND)
qbt_target_sources(${QBT_LINEEDIT_RESOURCES})

View File

@@ -16,17 +16,15 @@
LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent)
{
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
QPixmap pixmap1(":/lineeditimages/search.png");
searchButton = new QToolButton(this);
searchButton->setIcon(QIcon(pixmap1));
searchButton->setIconSize(pixmap1.size());
searchButton->setCursor(Qt::ArrowCursor);
searchButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
QSize searchButtonHint = searchButton->sizeHint();
int clearButtonSizeHintWidth = 0;
int clearButtonSizeHintHeight = 0;
QSize clearButtonHint(0, 0);
#ifndef QBT_USES_QT5
QPixmap pixmap2(":/lineeditimages/clear_left.png");
clearButton = new QToolButton(this);
@@ -39,17 +37,17 @@ LineEdit::LineEdit(QWidget *parent)
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(updateCloseButton(const QString &)));
clearButtonSizeHintWidth = clearButton->sizeHint().width();
clearButtonSizeHintHeight = clearButton->sizeHint().height();
setStyleSheet(QString("QLineEdit { padding-left: %1px; padding-right: %2px; }").arg(searchButton->sizeHint().width()).arg(clearButtonSizeHintWidth));
clearButtonHint = clearButton->sizeHint();
setStyleSheet(QString("QLineEdit { padding-left: %1px; padding-right: %2px; }").arg(searchButtonHint.width()).arg(clearButtonHint.width()));
#else
setClearButtonEnabled(true);
setStyleSheet(QString("QLineEdit { padding-left: %1px; }").arg(searchButton->sizeHint().width())); // padding between text and widget borders
setStyleSheet(QString("QLineEdit { padding-left: %1px; }").arg(searchButtonHint.width())); // padding between text and widget borders
#endif
QSize msz = sizeHint();
setMinimumSize(qMax(msz.width(), searchButton->sizeHint().width() + clearButtonSizeHintWidth),
std::max({ msz.height(), searchButton->sizeHint().height(), clearButtonSizeHintHeight }) + frameWidth * 2);
QSize widgetHint = sizeHint();
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setMaximumHeight(std::max({ widgetHint.height(), searchButtonHint.height(), clearButtonHint.height() }) + frameWidth * 2);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
}
void LineEdit::resizeEvent(QResizeEvent *e)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -28,20 +28,25 @@
* Contact : chris@qbittorrent.org
*/
#ifndef GUI_H
#define GUI_H
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QProcess>
#include <QMainWindow>
#include <QSystemTrayIcon>
#include <QPointer>
#include "ui_mainwindow.h"
#include "statsdialog.h"
class QCloseEvent;
class QFileSystemWatcher;
class QShortcut;
class QSplitter;
class QTabWidget;
class QTimer;
class downloadFromURL;
class SearchWidget;
class RSSImp;
class about;
class options_imp;
class OptionsDialog;
class TransferListWidget;
class TransferListFiltersWidget;
class PropertiesWidget;
@@ -52,34 +57,30 @@ class downloadFromURL;
class LineEdit;
class ExecutionLog;
class PowerManagement;
QT_BEGIN_NAMESPACE
class QCloseEvent;
class QFileSystemWatcher;
class QShortcut;
class QSplitter;
class QTabWidget;
class QTimer;
QT_END_NAMESPACE
class StatsDialog;
namespace BitTorrent
{
class TorrentHandle;
}
class MainWindow: public QMainWindow, private Ui::MainWindow
namespace Ui
{
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
// Construct / Destruct
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// Methods
QWidget* getCurrentTabWidget() const;
TransferListWidget* getTransferList() const { return transferList; }
QMenu* getTrayIconMenu();
PropertiesWidget *getProperties() const { return properties; }
~MainWindow() override;
QWidget* currentTabWidget() const;
TransferListWidget* transferListWidget() const;
PropertiesWidget *propertiesWidget() const;
QMenu* trayIconMenu();
// ExecutionLog properties
bool isExecutionLogEnabled() const;
@@ -87,32 +88,32 @@ public:
int executionLogMsgTypes() const;
void setExecutionLogMsgTypes(const int value);
public slots:
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
void setTabText(int index, QString text) const;
void showNotificationBaloon(QString title, QString msg) const;
void downloadFromURLList(const QStringList& urls);
void updateAltSpeedsBtn(bool alternative);
void updateNbTorrents();
// Notifications properties
bool isNotificationsEnabled() const;
void setNotificationsEnabled(bool value);
bool isTorrentAddedNotificationsEnabled() const;
void setTorrentAddedNotificationsEnabled(bool value);
// Misc properties
bool isDownloadTrackerFavicon() const;
void setDownloadTrackerFavicon(bool value);
void activate();
void cleanup();
protected slots:
// GUI related slots
void showNotificationBaloon(QString title, QString msg) const;
private slots:
void toggleVisibility(QSystemTrayIcon::ActivationReason e = QSystemTrayIcon::Trigger);
void on_actionAbout_triggered();
void on_actionStatistics_triggered();
void on_actionCreate_torrent_triggered();
void balloonClicked();
void writeSettings();
void readSettings();
void on_actionExit_triggered();
void createTrayIcon();
void fullDiskError(BitTorrent::TorrentHandle *const torrent, QString msg) const;
void handleDownloadFromUrlFailure(QString, QString) const;
void createSystrayDelayed();
void tab_changed(int);
void on_actionLock_qBittorrent_triggered();
void tabChanged(int newTab);
void defineUILockPassword();
void clearUILockPassword();
bool unlockUI();
@@ -125,110 +126,56 @@ protected slots:
void displayTransferTab() const;
void displaySearchTab() const;
void displayRSSTab() const;
// Torrent actions
void on_actionSet_global_upload_limit_triggered();
void on_actionSet_global_download_limit_triggered();
void on_actionDocumentation_triggered() const;
void on_actionOpen_triggered();
void updateGUI();
void loadPreferences(bool configure_session = true);
void loadPreferences(bool configureSession = true);
void addUnauthenticatedTracker(const QPair<BitTorrent::TorrentHandle*, QString> &tracker);
void addTorrentFailed(const QString &error) const;
void torrentNew(BitTorrent::TorrentHandle *const torrent) const;
void finishedTorrent(BitTorrent::TorrentHandle *const torrent) const;
void askRecursiveTorrentDownloadConfirmation(BitTorrent::TorrentHandle *const torrent);
// Options slots
void on_actionOptions_triggered();
void optionsSaved();
// HTTP slots
void on_actionDownload_from_URL_triggered();
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
void handleUpdateCheckFinished(bool update_available, QString new_version, bool invokedByUser);
void handleUpdateCheckFinished(bool updateAvailable, QString newVersion, bool invokedByUser);
#endif
void updateRSSTabLabel(int count);
protected:
void dropEvent(QDropEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void closeEvent(QCloseEvent *);
void showEvent(QShowEvent *);
bool event(QEvent * event);
void displayRSSTab(bool enable);
void displaySearchTab(bool enable);
private:
QIcon getSystrayIcon() const;
#ifdef Q_OS_WIN
bool addPythonPathToEnv();
void installPython();
private slots:
void pythonDownloadSuccess(const QString &url, const QString &filePath);
void pythonDownloadFailure(const QString &url, const QString &error);
#endif
void addToolbarContextMenu();
void manageCookies();
private:
QFileSystemWatcher *executable_watcher;
// Bittorrent
QList<QPair<BitTorrent::TorrentHandle*, QString>> unauthenticated_trackers; // Still needed?
// GUI related
bool m_posInitialized;
QTabWidget *tabs;
StatusBar *status_bar;
QPointer<options_imp> options;
QPointer<about> aboutDlg;
QPointer<StatsDialog> statsDlg;
QPointer<TorrentCreatorDlg> createTorrentDlg;
QPointer<downloadFromURL> downloadFromURLDialog;
QPointer<QSystemTrayIcon> systrayIcon;
QPointer<QTimer> systrayCreator;
QPointer<QMenu> myTrayIconMenu;
TransferListWidget *transferList;
TransferListFiltersWidget *transferListFilters;
PropertiesWidget *properties;
bool displaySpeedInTitle;
bool force_exit;
bool ui_locked;
bool unlockDlgShowing;
LineEdit *search_filter;
QAction *searchFilterAct;
// Widgets
QAction *prioSeparator;
QAction *prioSeparatorMenu;
QSplitter *hSplitter;
QSplitter *vSplitter;
// Search
QPointer<SearchWidget> searchEngine;
// RSS
QPointer<RSSImp> rssWidget;
// Execution Log
QPointer<ExecutionLog> m_executionLog;
// Power Management
PowerManagement *m_pwr;
QTimer *preventTimer;
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
QTimer programUpdateTimer;
bool m_wasUpdateCheckEnabled;
#endif
bool has_python;
QMenu* toolbarMenu;
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
void downloadFromURLList(const QStringList &urlList);
void updateAltSpeedsBtn(bool alternative);
void updateNbTorrents();
private slots:
void on_actionSearch_engine_triggered();
void on_actionRSS_Reader_triggered();
void on_actionSpeed_in_title_bar_triggered();
void on_actionTop_tool_bar_triggered();
void on_action_Import_Torrent_triggered();
void on_actionDonate_money_triggered();
void on_actionSearchWidget_triggered();
void on_actionRSSReader_triggered();
void on_actionSpeedInTitleBar_triggered();
void on_actionTopToolBar_triggered();
void on_actionDonateMoney_triggered();
void on_actionExecutionLogs_triggered(bool checked);
void on_actionNormalMessages_triggered(bool checked);
void on_actionInformationMessages_triggered(bool checked);
void on_actionWarningMessages_triggered(bool checked);
void on_actionCriticalMessages_triggered(bool checked);
void on_actionAutoExit_qBittorrent_toggled(bool );
void on_actionAutoSuspend_system_toggled(bool );
void on_actionAutoHibernate_system_toggled(bool );
void on_actionAutoShutdown_system_toggled(bool );
void on_actionAutoExit_toggled(bool);
void on_actionAutoSuspend_toggled(bool);
void on_actionAutoHibernate_toggled(bool);
void on_actionAutoShutdown_toggled(bool);
void on_actionAbout_triggered();
void on_actionStatistics_triggered();
void on_actionCreateTorrent_triggered();
void on_actionOptions_triggered();
void on_actionSetGlobalUploadLimit_triggered();
void on_actionSetGlobalDownloadLimit_triggered();
void on_actionDocumentation_triggered() const;
void on_actionOpen_triggered();
void on_actionDownloadFromURL_triggered();
void on_actionExit_triggered();
void on_actionLock_triggered();
// Check for active torrents and set preventing from suspend state
void checkForActiveTorrents();
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
@@ -240,6 +187,64 @@ private slots:
void toolbarTextBeside();
void toolbarTextUnder();
void toolbarFollowSystem();
private:
QIcon getSystrayIcon() const;
#ifdef Q_OS_WIN
bool addPythonPathToEnv();
void installPython();
#endif
void dropEvent(QDropEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void closeEvent(QCloseEvent *) override;
void showEvent(QShowEvent *) override;
bool event(QEvent * event) override;
void displayRSSTab(bool enable);
void displaySearchTab(bool enable);
Ui::MainWindow *m_ui;
QFileSystemWatcher *m_executableWatcher;
// Bittorrent
QList<QPair<BitTorrent::TorrentHandle*, QString>> m_unauthenticatedTrackers; // Still needed?
// GUI related
bool m_posInitialized;
QTabWidget *m_tabs;
StatusBar *m_statusBar;
QPointer<OptionsDialog> m_options;
QPointer<about> m_aboutDlg;
QPointer<StatsDialog> m_statsDlg;
QPointer<TorrentCreatorDlg> m_createTorrentDlg;
QPointer<downloadFromURL> m_downloadFromURLDialog;
QPointer<QSystemTrayIcon> m_systrayIcon;
QPointer<QTimer> m_systrayCreator;
QPointer<QMenu> m_trayIconMenu;
TransferListWidget *m_transferListWidget;
TransferListFiltersWidget *m_transferListFiltersWidget;
PropertiesWidget *m_propertiesWidget;
bool m_displaySpeedInTitle;
bool m_forceExit;
bool m_uiLocked;
bool m_unlockDlgShowing;
LineEdit *m_searchFilter;
QAction *m_searchFilterAction;
// Widgets
QAction *m_prioSeparator;
QAction *m_prioSeparatorMenu;
QSplitter *m_splitter;
QPointer<SearchWidget> m_searchWidget;
QPointer<RSSImp> m_rssWidget;
QPointer<ExecutionLog> m_executionLog;
// Power Management
PowerManagement *m_pwr;
QTimer *m_preventTimer;
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
QTimer *m_programUpdateTimer;
bool m_wasUpdateCheckEnabled;
#endif
bool m_hasPython;
QMenu *m_toolbarMenu;
};
#endif
#endif // MAINWINDOW_H

View File

@@ -35,15 +35,17 @@
<x>0</x>
<y>0</y>
<width>914</width>
<height>21</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menu_Edit">
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>&amp;Edit</string>
</property>
<addaction name="actionStart"/>
<addaction name="actionPause"/>
<addaction name="actionStartAll"/>
<addaction name="actionPauseAll"/>
<addaction name="separator"/>
<addaction name="actionDelete"/>
<addaction name="actionTopPriority"/>
@@ -51,43 +53,43 @@
<addaction name="actionDecreasePriority"/>
<addaction name="actionBottomPriority"/>
</widget>
<widget class="QMenu" name="menu_Help">
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="actionDocumentation"/>
<addaction name="actionCheck_for_updates"/>
<addaction name="actionCheckForUpdates"/>
<addaction name="separator"/>
<addaction name="actionDonate_money"/>
<addaction name="actionDonateMoney"/>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menu_Options">
<widget class="QMenu" name="menuOptions">
<property name="title">
<string>&amp;Tools</string>
</property>
<widget class="QMenu" name="menuAuto_Shutdown_on_downloads_completion">
<widget class="QMenu" name="menuAutoShutdownOnDownloadsCompletion">
<property name="title">
<string>On Downloads &amp;Done</string>
</property>
<addaction name="actionAutoShutdown_Disabled"/>
<addaction name="actionAutoExit_qBittorrent"/>
<addaction name="actionAutoSuspend_system"/>
<addaction name="actionAutoHibernate_system"/>
<addaction name="actionAutoShutdown_system"/>
<addaction name="actionAutoShutdownDisabled"/>
<addaction name="actionAutoExit"/>
<addaction name="actionAutoSuspend"/>
<addaction name="actionAutoHibernate"/>
<addaction name="actionAutoShutdown"/>
</widget>
<addaction name="actionCreate_torrent"/>
<addaction name="actionCreateTorrent"/>
<addaction name="separator"/>
<addaction name="actionManageCookies"/>
<addaction name="actionOptions"/>
<addaction name="separator"/>
<addaction name="menuAuto_Shutdown_on_downloads_completion"/>
<addaction name="menuAutoShutdownOnDownloadsCompletion"/>
</widget>
<widget class="QMenu" name="menu_File">
<widget class="QMenu" name="menuFile">
<property name="title">
<string>&amp;File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionDownload_from_URL"/>
<addaction name="action_Import_Torrent"/>
<addaction name="actionDownloadFromURL"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
@@ -106,22 +108,23 @@
<addaction name="actionWarningMessages"/>
<addaction name="actionCriticalMessages"/>
</widget>
<addaction name="actionTop_tool_bar"/>
<addaction name="actionSpeed_in_title_bar"/>
<addaction name="separator"/>
<addaction name="actionSearch_engine"/>
<addaction name="actionRSS_Reader"/>
<addaction name="actionTopToolBar"/>
<addaction name="actionSpeedInTitleBar"/>
<addaction name="separator"/>
<addaction name="actionSearchWidget"/>
<addaction name="actionRSSReader"/>
<addaction name="menuLog"/>
<addaction name="separator"/>
<addaction name="actionStatistics"/>
<addaction name="separator"/>
<addaction name="actionLock_qBittorrent"/>
<addaction name="actionLock"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Edit"/>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
<addaction name="menu_Options"/>
<addaction name="menu_Help"/>
<addaction name="menuOptions"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="movable">
@@ -142,7 +145,7 @@
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionDownload_from_URL"/>
<addaction name="actionDownloadFromURL"/>
<addaction name="actionOpen"/>
<addaction name="actionDelete"/>
<addaction name="separator"/>
@@ -154,7 +157,7 @@
<addaction name="actionBottomPriority"/>
<addaction name="separator"/>
<addaction name="actionOptions"/>
<addaction name="actionLock_qBittorrent"/>
<addaction name="actionLock"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionOpen">
@@ -190,12 +193,22 @@
<string>&amp;Pause</string>
</property>
</action>
<action name="actionStartAll">
<property name="text">
<string>R&amp;esume All</string>
</property>
</action>
<action name="actionPauseAll">
<property name="text">
<string>P&amp;ause All</string>
</property>
</action>
<action name="actionDelete">
<property name="text">
<string>&amp;Delete</string>
</property>
</action>
<action name="actionDownload_from_URL">
<action name="actionDownloadFromURL">
<property name="text">
<string>Add Torrent &amp;Link...</string>
</property>
@@ -203,17 +216,17 @@
<string>Open URL</string>
</property>
</action>
<action name="actionCreate_torrent">
<action name="actionCreateTorrent">
<property name="text">
<string>Torrent &amp;Creator</string>
</property>
</action>
<action name="actionSet_upload_limit">
<action name="actionSetUploadLimit">
<property name="text">
<string>Set Upload Limit...</string>
</property>
</action>
<action name="actionSet_download_limit">
<action name="actionSetDownloadLimit">
<property name="text">
<string>Set Download Limit...</string>
</property>
@@ -223,12 +236,12 @@
<string>&amp;Documentation</string>
</property>
</action>
<action name="actionSet_global_download_limit">
<action name="actionSetGlobalDownloadLimit">
<property name="text">
<string>Set Global Download Limit...</string>
</property>
</action>
<action name="actionSet_global_upload_limit">
<action name="actionSetGlobalUploadLimit">
<property name="text">
<string>Set Global Upload Limit...</string>
</property>
@@ -265,7 +278,7 @@
<bool>true</bool>
</property>
</action>
<action name="actionUse_alternative_speed_limits">
<action name="actionUseAlternativeSpeedLimits">
<property name="checkable">
<bool>true</bool>
</property>
@@ -276,7 +289,7 @@
<string>Alternative Speed Limits</string>
</property>
</action>
<action name="actionTop_tool_bar">
<action name="actionTopToolBar">
<property name="checkable">
<bool>true</bool>
</property>
@@ -287,7 +300,7 @@
<string>Display Top Toolbar</string>
</property>
</action>
<action name="actionSpeed_in_title_bar">
<action name="actionSpeedInTitleBar">
<property name="checkable">
<bool>true</bool>
</property>
@@ -298,7 +311,7 @@
<string>Show Transfer Speed in Title Bar</string>
</property>
</action>
<action name="actionRSS_Reader">
<action name="actionRSSReader">
<property name="checkable">
<bool>true</bool>
</property>
@@ -306,7 +319,7 @@
<string>&amp;RSS Reader</string>
</property>
</action>
<action name="actionSearch_engine">
<action name="actionSearchWidget">
<property name="checkable">
<bool>true</bool>
</property>
@@ -314,7 +327,7 @@
<string>Search &amp;Engine</string>
</property>
</action>
<action name="actionLock_qBittorrent">
<action name="actionLock">
<property name="text">
<string>L&amp;ock qBittorrent</string>
</property>
@@ -325,15 +338,7 @@
<string notr="true">Ctrl+L</string>
</property>
</action>
<action name="action_Import_Torrent">
<property name="text">
<string>&amp;Import Existing Torrent...</string>
</property>
<property name="toolTip">
<string>Import Torrent...</string>
</property>
</action>
<action name="actionDonate_money">
<action name="actionDonateMoney">
<property name="text">
<string>Do&amp;nate!</string>
</property>
@@ -341,17 +346,7 @@
<string>If you like qBittorrent, please donate!</string>
</property>
</action>
<action name="actionStart_All">
<property name="text">
<string>R&amp;esume All</string>
</property>
</action>
<action name="actionPause_All">
<property name="text">
<string>P&amp;ause All</string>
</property>
</action>
<action name="actionAutoExit_qBittorrent">
<action name="actionAutoExit">
<property name="checkable">
<bool>true</bool>
</property>
@@ -359,7 +354,7 @@
<string>&amp;Exit qBittorrent</string>
</property>
</action>
<action name="actionAutoSuspend_system">
<action name="actionAutoSuspend">
<property name="checkable">
<bool>true</bool>
</property>
@@ -367,7 +362,7 @@
<string>&amp;Suspend System</string>
</property>
</action>
<action name="actionAutoHibernate_system">
<action name="actionAutoHibernate">
<property name="checkable">
<bool>true</bool>
</property>
@@ -375,7 +370,7 @@
<string>&amp;Hibernate System</string>
</property>
</action>
<action name="actionAutoShutdown_system">
<action name="actionAutoShutdown">
<property name="checkable">
<bool>true</bool>
</property>
@@ -383,7 +378,7 @@
<string>S&amp;hutdown System</string>
</property>
</action>
<action name="actionAutoShutdown_Disabled">
<action name="actionAutoShutdownDisabled">
<property name="checkable">
<bool>true</bool>
</property>
@@ -406,7 +401,7 @@
<string>&amp;Statistics</string>
</property>
</action>
<action name="actionCheck_for_updates">
<action name="actionCheckForUpdates">
<property name="text">
<string>Check for Updates</string>
</property>
@@ -414,6 +409,14 @@
<string>Check for Program Updates</string>
</property>
</action>
<action name="actionManageCookies">
<property name="text">
<string>Manage Cookies...</string>
</property>
<property name="toolTip">
<string>Manage stored network cookies</string>
</property>
</action>
<action name="actionExecutionLogs">
<property name="checkable">
<bool>true</bool>

File diff suppressed because it is too large Load Diff

1694
src/gui/optionsdlg.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -28,10 +28,16 @@
* Contact : chris@qbittorrent.org
*/
#ifndef OPTIONS_IMP_H
#define OPTIONS_IMP_H
#ifndef OPTIONSDLG_H
#define OPTIONSDLG_H
#include "ui_options.h"
#include <QButtonGroup>
#include <QDialog>
class QAbstractButton;
class QCloseEvent;
class QListWidgetItem;
class AdvancedSettings;
// actions on double-click on torrents
enum DoubleClickAction
@@ -41,13 +47,17 @@ enum DoubleClickAction
NO_ACTION
};
class AdvancedSettings;
namespace Net
{
enum class ProxyType;
}
QT_BEGIN_NAMESPACE
class QCloseEvent;
QT_END_NAMESPACE
namespace Ui
{
class OptionsDialog;
}
class options_imp: public QDialog, private Ui_Preferences
class OptionsDialog: public QDialog
{
Q_OBJECT
private:
@@ -64,8 +74,8 @@ private:
public:
// Constructor / Destructor
options_imp(QWidget *parent = 0);
~options_imp();
OptionsDialog(QWidget *parent = 0);
~OptionsDialog();
public slots:
void showConnectionTab();
@@ -144,9 +154,9 @@ private:
unsigned short getProxyPort() const;
QString getProxyUsername() const;
QString getProxyPassword() const;
int getProxyType() const;
Net::ProxyType getProxyType() const;
// IP Filter
bool isFilteringEnabled() const;
bool isIPFilteringEnabled() const;
QString getFilter() const;
bool m_refreshingIpFilter;
// Queueing system
@@ -167,6 +177,7 @@ private:
bool webUIAuthenticationOk();
private:
Ui::OptionsDialog *m_ui;
QButtonGroup choiceLanguage;
QAbstractButton *applyButton;
AdvancedSettings *advancedSettings;

File diff suppressed because it is too large Load Diff

View File

@@ -32,8 +32,8 @@
#define PREVIEWLISTDELEGATE_H
#include <QItemDelegate>
#include <QStyleOptionProgressBarV2>
#include <QStyleOptionViewItemV2>
#include <QStyleOptionProgressBar>
#include <QStyleOptionViewItem>
#include <QModelIndex>
#include <QPainter>
#include <QApplication>
@@ -59,7 +59,7 @@ class PreviewListDelegate: public QItemDelegate {
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
painter->save();
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
switch(index.column()) {
case PreviewSelect::SIZE:
@@ -67,7 +67,7 @@ class PreviewListDelegate: public QItemDelegate {
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
break;
case PreviewSelect::PROGRESS:{
QStyleOptionProgressBarV2 newopt;
QStyleOptionProgressBar newopt;
qreal progress = index.data().toDouble()*100.;
newopt.rect = opt.rect;
newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%");

View File

@@ -45,6 +45,8 @@ namespace
#ifdef Q_OS_MAC
const QString OS_TYPE("Mac OS X");
#elif defined(Q_OS_WIN) && (defined(__x86_64__) || defined(_M_X64))
const QString OS_TYPE("Windows x64");
#else
const QString OS_TYPE("Windows");
#endif

View File

@@ -16,6 +16,7 @@ peerlistwidget.h
proplistdelegate.h
trackerlist.h
downloadedpiecesbar.h
piecesbar.h
peerlistdelegate.h
peerlistsortmodel.h
peersadditiondlg.h
@@ -33,6 +34,7 @@ peerlistwidget.cpp
trackerlist.cpp
peersadditiondlg.cpp
downloadedpiecesbar.cpp
piecesbar.cpp
trackersadditiondlg.cpp
pieceavailabilitybar.cpp
proptabbar.cpp

View File

@@ -28,216 +28,153 @@
* Contact : chris@qbittorrent.org
*/
#include <cmath>
#include <QDebug>
#include "downloadedpiecesbar.h"
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent): QWidget(parent)
#include <cmath>
#include <QDebug>
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent)
: base {parent}
, m_dlPieceColor {0, 0xd0, 0}
{
setToolTip(QString("%1\n%2\n%3").arg(tr("White: Missing pieces")).arg(tr("Green: Partial pieces")).arg(tr("Blue: Completed pieces")));
m_bgColor = 0xffffff;
m_borderColor = palette().color(QPalette::Dark).rgb();
m_pieceColor = 0x0000ff;
m_dlPieceColor = 0x00d000;
updatePieceColors();
}
QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin, int reqSize)
{
QVector<float> result(reqSize, 0.0);
if (vecin.isEmpty()) return result;
QVector<float> result(reqSize, 0.0);
if (vecin.isEmpty()) return result;
const float ratio = vecin.size() / (float)reqSize;
const float ratio = vecin.size() / static_cast<float>(reqSize);
// simple linear transformation algorithm
// for example:
// image.x(0) = pieces.x(0.0 >= x < 1.7)
// image.x(1) = pieces.x(1.7 >= x < 3.4)
// simple linear transformation algorithm
// for example:
// image.x(0) = pieces.x(0.0 >= x < 1.7)
// image.x(1) = pieces.x(1.7 >= x < 3.4)
for (int x = 0; x < reqSize; ++x) {
// R - real
const float fromR = x * ratio;
const float toR = (x + 1) * ratio;
for (int x = 0; x < reqSize; ++x) {
// R - real
const float fromR = x * ratio;
const float toR = (x + 1) * ratio;
// C - integer
int fromC = fromR;// std::floor not needed
int toC = std::ceil(toR);
if (toC > vecin.size())
--toC;
// C - integer
int fromC = fromR; // std::floor not needed
int toC = std::ceil(toR);
if (toC > vecin.size())
--toC;
// position in pieces table
int x2 = fromC;
// position in pieces table
int x2 = fromC;
// little speed up for really big pieces table, 10K+ size
const int toCMinusOne = toC - 1;
// little speed up for really big pieces table, 10K+ size
const int toCMinusOne = toC - 1;
// value in returned vector
float value = 0;
// value in returned vector
float value = 0;
// case when calculated range is (15.2 >= x < 15.7)
if (x2 == toCMinusOne) {
if (vecin[x2]) {
value += ratio;
}
++x2;
}
// case when (15.2 >= x < 17.8)
else {
// subcase (15.2 >= x < 16)
if (x2 != fromR) {
if (vecin[x2]) {
value += 1.0 - (fromR - fromC);
// case when calculated range is (15.2 >= x < 15.7)
if (x2 == toCMinusOne) {
if (vecin[x2])
value += ratio;
++x2;
}
++x2;
}
// case when (15.2 >= x < 17.8)
else {
// subcase (15.2 >= x < 16)
if (x2 != fromR) {
if (vecin[x2])
value += 1.0 - (fromR - fromC);
++x2;
}
// subcase (16 >= x < 17)
for (; x2 < toCMinusOne; ++x2) {
if (vecin[x2]) {
value += 1.0;
}
}
// subcase (16 >= x < 17)
for (; x2 < toCMinusOne; ++x2)
if (vecin[x2])
value += 1.0;
// subcase (17 >= x < 17.8)
if (x2 == toCMinusOne) {
if (vecin[x2]) {
value += 1.0 - (toC - toR);
// subcase (17 >= x < 17.8)
if (x2 == toCMinusOne) {
if (vecin[x2])
value += 1.0 - (toC - toR);
++x2;
}
}
++x2;
}
// normalization <0, 1>
value /= ratio;
// float precision sometimes gives > 1, because in not possible to store irrational numbers
value = qMin(value, 1.0f);
result[x] = value;
}
// normalization <0, 1>
value /= ratio;
// float precision sometimes gives > 1, because in not possible to store irrational numbers
value = qMin(value, (float)1.0);
result[x] = value;
}
return result;
return result;
}
int DownloadedPiecesBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
bool DownloadedPiecesBar::updateImage(QImage &image)
{
int r1 = qRed(rgb1);
int g1 = qGreen(rgb1);
int b1 = qBlue(rgb1);
int r2 = qRed(rgb2);
int g2 = qGreen(rgb2);
int b2 = qBlue(rgb2);
float ratio_n = 1.0 - ratio;
int r = (r1 * ratio_n) + (r2 * ratio);
int g = (g1 * ratio_n) + (g2 * ratio);
int b = (b1 * ratio_n) + (b2 * ratio);
return qRgb(r, g, b);
}
void DownloadedPiecesBar::updateImage()
{
// qDebug() << "updateImage";
QImage image2(width() - 2, 1, QImage::Format_RGB888);
if (image2.isNull()) {
qDebug() << "QImage image2() allocation failed, width():" << width();
return;
}
if (m_pieces.isEmpty()) {
image2.fill(0xffffff);
m_image = image2;
update();
return;
}
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x)
{
float pieces2_val = scaled_pieces.at(x);
float pieces2_val_dl = scaled_pieces_dl.at(x);
if (pieces2_val_dl != 0)
{
float fill_ratio = pieces2_val + pieces2_val_dl;
float ratio = pieces2_val_dl / fill_ratio;
int mixedColor = mixTwoColors(m_pieceColor, m_dlPieceColor, ratio);
mixedColor = mixTwoColors(m_bgColor, mixedColor, fill_ratio);
image2.setPixel(x, 0, mixedColor);
// qDebug() << "updateImage";
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
if (image2.isNull()) {
qDebug() << "QImage image2() allocation failed, width():" << width();
return false;
}
else
{
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
if (m_pieces.isEmpty()) {
image2.fill(Qt::white);
image = image2;
return true;
}
}
m_image = image2;
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x) {
float pieces2_val = scaled_pieces.at(x);
float pieces2_val_dl = scaled_pieces_dl.at(x);
if (pieces2_val_dl != 0) {
float fill_ratio = pieces2_val + pieces2_val_dl;
float ratio = pieces2_val_dl / fill_ratio;
QRgb mixedColor = mixTwoColors(pieceColor().rgb(), m_dlPieceColor.rgb(), ratio);
mixedColor = mixTwoColors(backgroundColor().rgb(), mixedColor, fill_ratio);
image2.setPixel(x, 0, mixedColor);
}
else {
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
}
}
image = image2;
return true;
}
void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces)
{
m_pieces = pieces;
m_downloadedPieces = downloadedPieces;
m_pieces = pieces;
m_downloadedPieces = downloadedPieces;
updateImage();
update();
requestImageUpdate();
}
void DownloadedPiecesBar::updatePieceColors()
void DownloadedPiecesBar::setColors(const QColor &background, const QColor &border, const QColor &complete, const QColor &incomplete)
{
m_pieceColors = QVector<int>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
}
m_dlPieceColor = incomplete;
base::setColors(background, border, complete);
}
void DownloadedPiecesBar::clear()
{
m_image = QImage();
update();
m_pieces.clear();
m_downloadedPieces.clear();
base::clear();
}
void DownloadedPiecesBar::paintEvent(QPaintEvent *)
QString DownloadedPiecesBar::simpleToolTipText() const
{
QPainter painter(this);
QRect imageRect(1, 1, width() - 2, height() - 2);
if (m_image.isNull())
{
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else
{
if (m_image.width() != imageRect.width())
updateImage();
painter.drawImage(imageRect, m_image);
}
QPainterPath border;
border.addRect(0, 0, width() - 1, height() - 1);
painter.setPen(m_borderColor);
painter.drawPath(border);
return tr("White: Missing pieces") + '\n'
+ tr("Green: Partial pieces") + '\n'
+ tr("Blue: Completed pieces") + '\n';
}
void DownloadedPiecesBar::setColors(int background, int border, int complete, int incomplete)
{
m_bgColor = background;
m_borderColor = border;
m_pieceColor = complete;
m_dlPieceColor = incomplete;
updatePieceColors();
updateImage();
update();
}

View File

@@ -32,54 +32,39 @@
#define DOWNLOADEDPIECESBAR_H
#include <QWidget>
#include <QPainter>
#include <QImage>
#include <QBitArray>
#include <QVector>
class DownloadedPiecesBar: public QWidget {
Q_OBJECT
Q_DISABLE_COPY(DownloadedPiecesBar)
#include "piecesbar.h"
private:
QImage m_image;
// I used values, because it should be possible to change colors in runtime
// background color
int m_bgColor;
// border color
int m_borderColor;
// complete piece color
int m_pieceColor;
// incomplete piece color
int m_dlPieceColor;
// buffered 256 levels gradient from bg_color to piece_color
QVector<int> m_pieceColors;
// last used bitfields, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
QBitArray m_pieces;
QBitArray m_downloadedPieces;
// scale bitfield vector to float vector
QVector<float> bitfieldToFloatVector(const QBitArray &vecin, int reqSize);
// mix two colors by light model, ratio <0, 1>
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
// draw new image and replace actual image
void updateImage();
class DownloadedPiecesBar: public PiecesBar
{
using base = PiecesBar;
Q_OBJECT
Q_DISABLE_COPY(DownloadedPiecesBar)
public:
DownloadedPiecesBar(QWidget *parent);
DownloadedPiecesBar(QWidget *parent);
void setProgress(const QBitArray &m_pieces, const QBitArray &downloadedPieces);
void updatePieceColors();
void clear();
void setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces);
void setColors(int background, int border, int complete, int incomplete);
void setColors(const QColor &background, const QColor &border, const QColor &complete, const QColor &incomplete);
protected:
void paintEvent(QPaintEvent *);
// PiecesBar interface
void clear() override;
private:
// scale bitfield vector to float vector
QVector<float> bitfieldToFloatVector(const QBitArray &vecin, int reqSize);
virtual bool updateImage(QImage &image) override;
QString simpleToolTipText() const override;
// incomplete piece color
QColor m_dlPieceColor;
// last used bitfields, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
QBitArray m_pieces;
QBitArray m_downloadedPieces;
};
#endif // DOWNLOADEDPIECESBAR_H

View File

@@ -50,7 +50,7 @@ public:
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
painter->save();
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
switch(index.column()) {
case TOT_DOWN:
case TOT_UP:

View File

@@ -43,21 +43,16 @@ public:
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const {
if (sortColumn() == PeerListDelegate::IP || sortColumn() == PeerListDelegate::CLIENT) {
QVariant vL = sourceModel()->data(left);
QVariant vR = sourceModel()->data(right);
if (!(vL.isValid() && vR.isValid()))
return QSortFilterProxyModel::lessThan(left, right);
Q_ASSERT(vL.isValid());
Q_ASSERT(vR.isValid());
switch (sortColumn()) {
case PeerListDelegate::IP:
case PeerListDelegate::CLIENT: {
QString vL = left.data().toString();
QString vR = right.data().toString();
return Utils::String::naturalCompareCaseInsensitive(vL, vR);
}
};
bool res = false;
if (Utils::String::naturalSort(vL.toString(), vR.toString(), res))
return res;
return QSortFilterProxyModel::lessThan(left, right);
}
return QSortFilterProxyModel::lessThan(left, right);
return QSortFilterProxyModel::lessThan(left, right);
}
};

View File

@@ -35,6 +35,7 @@
#include <QMenu>
#include <QClipboard>
#include <QMessageBox>
#include <QWheelEvent>
#ifdef QBT_USES_QT5
#include <QTableView>
#endif
@@ -57,7 +58,6 @@
PeerListWidget::PeerListWidget(PropertiesWidget *parent)
: QTreeView(parent)
, m_properties(parent)
, m_resolveCountries(false)
{
// Load settings
loadSettings();
@@ -91,9 +91,9 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
setModel(m_proxyModel);
hideColumn(PeerListDelegate::IP_HIDDEN);
hideColumn(PeerListDelegate::COL_COUNT);
if (!Preferences::instance()->resolvePeerCountries())
m_resolveCountries = Preferences::instance()->resolvePeerCountries();
if (!m_resolveCountries)
hideColumn(PeerListDelegate::COUNTRY);
m_wasCountryColHidden = isColumnHidden(PeerListDelegate::COUNTRY);
//Ensure that at least one column is visible at all times
bool atLeastOne = false;
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++) {
@@ -108,7 +108,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
//its size is 0, because explicitly 'showing' the column isn't enough
//in the above scenario.
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++)
if (!columnWidth(i))
if ((columnWidth(i) <= 0) && !isColumnHidden(i))
resizeColumnToContents(i);
// Context menu
setContextMenuPolicy(Qt::CustomContextMenu);
@@ -125,8 +125,8 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
connect(header(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayToggleColumnsMenu(const QPoint &)));
connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int)));
handleSortColumnChanged(header()->sortIndicatorSection());
m_copyHotkey = new QShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_C), this, SLOT(copySelectedPeers()), 0, Qt::WidgetShortcut);
m_copyHotkey = new QShortcut(QKeySequence::Copy, this, SLOT(copySelectedPeers()), 0, Qt::WidgetShortcut);
#ifdef QBT_USES_QT5
// This hack fixes reordering of first column with Qt5.
// https://github.com/qtproject/qtbase/commit/e0fc088c0c8bc61dbcaf5928b24986cd61a22777
@@ -208,14 +208,12 @@ void PeerListWidget::updatePeerCountryResolutionState()
m_resolveCountries = !m_resolveCountries;
if (m_resolveCountries) {
loadPeers(m_properties->getCurrentTorrent());
if (!m_wasCountryColHidden) {
showColumn(PeerListDelegate::COUNTRY);
showColumn(PeerListDelegate::COUNTRY);
if (columnWidth(PeerListDelegate::COUNTRY) <= 0)
resizeColumnToContents(PeerListDelegate::COUNTRY);
}
}
else {
hideColumn(PeerListDelegate::COUNTRY);
m_wasCountryColHidden = false; // to forcefully enable that column if the user decides to resolve countries again
}
}
}
@@ -236,7 +234,7 @@ void PeerListWidget::showPeerListMenu(const QPoint&)
QAction *banAct = 0;
QAction *copyPeerAct = 0;
if (!selectionModel()->selectedRows().isEmpty()) {
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy selected"));
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy IP:port"));
menu.addSeparator();
banAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently"));
emptyMenu = false;
@@ -458,3 +456,16 @@ void PeerListWidget::handleSortColumnChanged(int col)
}
}
void PeerListWidget::wheelEvent(QWheelEvent *event)
{
event->accept();
if(event->modifiers() & Qt::ShiftModifier) {
// Shift + scroll = horizontal scroll
QWheelEvent scrollHEvent(event->pos(), event->globalPos(), event->delta(), event->buttons(), event->modifiers(), Qt::Horizontal);
QTreeView::wheelEvent(&scrollHEvent);
return;
}
QTreeView::wheelEvent(event); // event delegated to base class
}

View File

@@ -85,6 +85,8 @@ private slots:
void handleResolved(const QString &ip, const QString &hostname);
private:
void wheelEvent(QWheelEvent *event) override;
QStandardItemModel *m_listModel;
PeerListDelegate *m_listDelegate;
PeerListSortModel *m_proxyModel;
@@ -94,7 +96,6 @@ private:
QPointer<Net::ReverseResolution> m_resolver;
PropertiesWidget *m_properties;
bool m_resolveCountries;
bool m_wasCountryColHidden;
QShortcut *m_copyHotkey;
};

View File

@@ -28,21 +28,15 @@
* Contact : chris@qbittorrent.org
*/
#include <cmath>
#include <QDebug>
#include "pieceavailabilitybar.h"
#include <cmath>
#include <QDebug>
PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent)
: QWidget(parent)
: base {parent}
{
setToolTip(QString("%1\n%2").arg(tr("White: Unavailable pieces")).arg(tr("Blue: Available pieces")));
m_bgColor = 0xffffff;
m_borderColor = palette().color(QPalette::Dark).rgb();
m_pieceColor = 0x0000ff;
updatePieceColors();
}
QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin, int reqSize)
@@ -126,37 +120,18 @@ QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin,
return result;
}
int PieceAvailabilityBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
bool PieceAvailabilityBar::updateImage(QImage &image)
{
int r1 = qRed(rgb1);
int g1 = qGreen(rgb1);
int b1 = qBlue(rgb1);
int r2 = qRed(rgb2);
int g2 = qGreen(rgb2);
int b2 = qBlue(rgb2);
float ratio_n = 1.0 - ratio;
int r = (r1 * ratio_n) + (r2 * ratio);
int g = (g1 * ratio_n) + (g2 * ratio);
int b = (b1 * ratio_n) + (b2 * ratio);
return qRgb(r, g, b);
}
void PieceAvailabilityBar::updateImage()
{
QImage image2(width() - 2, 1, QImage::Format_RGB888);
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
if (image2.isNull()) {
qDebug() << "QImage image2() allocation failed, width():" << width();
return;
return false;
}
if (m_pieces.empty()) {
image2.fill(0xffffff);
m_image = image2;
update();
return;
image2.fill(Qt::white);
image = image2;
return true;
}
QVector<float> scaled_pieces = intToFloatVector(m_pieces, image2.width());
@@ -164,61 +139,32 @@ void PieceAvailabilityBar::updateImage()
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x) {
float pieces2_val = scaled_pieces.at(x);
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
}
m_image = image2;
image = image2;
return true;
}
void PieceAvailabilityBar::setAvailability(const QVector<int> &avail)
{
m_pieces = avail;
updateImage();
update();
}
void PieceAvailabilityBar::updatePieceColors()
{
m_pieceColors = QVector<int>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
}
requestImageUpdate();
}
void PieceAvailabilityBar::clear()
{
m_image = QImage();
update();
m_pieces.clear();
base::clear();
}
void PieceAvailabilityBar::paintEvent(QPaintEvent *)
QString PieceAvailabilityBar::simpleToolTipText() const
{
QPainter painter(this);
QRect imageRect(1, 1, width() - 2, height() - 2);
if (m_image.isNull()) {
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else {
if (m_image.width() != imageRect.width())
updateImage();
painter.drawImage(imageRect, m_image);
}
QPainterPath border;
border.addRect(0, 0, width() - 1, height() - 1);
painter.setPen(m_borderColor);
painter.drawPath(border);
return tr("White: Unavailable pieces") + '\n'
+ tr("Blue: Available pieces") + '\n';
}
void PieceAvailabilityBar::setColors(int background, int border, int available)
bool PieceAvailabilityBar::isFileNameCorrectionNeeded() const
{
m_bgColor = background;
m_borderColor = border;
m_pieceColor = available;
updatePieceColors();
updateImage();
update();
return true;
}

View File

@@ -31,28 +31,26 @@
#ifndef PIECEAVAILABILITYBAR_H
#define PIECEAVAILABILITYBAR_H
#include <QWidget>
#include <QPainter>
#include <QImage>
#include "piecesbar.h"
class PieceAvailabilityBar: public QWidget
class PieceAvailabilityBar: public PiecesBar
{
using base = PiecesBar;
Q_OBJECT
Q_DISABLE_COPY(PieceAvailabilityBar)
public:
PieceAvailabilityBar(QWidget *parent);
void setAvailability(const QVector<int> &avail);
// PiecesBar interface
void clear() override;
private:
QImage m_image;
// I used values, because it should be possible to change colors in runtime
// background color
int m_bgColor;
// border color
int m_borderColor;
// complete piece color
int m_pieceColor;
// buffered 256 levels gradient from bg_color to piece_color
QVector<int> m_pieceColors;
bool updateImage(QImage &image) override;
QString simpleToolTipText() const override;
bool isFileNameCorrectionNeeded() const override;
// last used int vector, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
@@ -60,23 +58,6 @@ private:
// scale int vector to float vector
QVector<float> intToFloatVector(const QVector<int> &vecin, int reqSize);
// mix two colors by light model, ratio <0, 1>
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
// draw new image and replace actual image
void updateImage();
public:
PieceAvailabilityBar(QWidget *parent);
void setAvailability(const QVector<int> &avail);
void updatePieceColors();
void clear();
void setColors(int background, int border, int available);
protected:
void paintEvent(QPaintEvent *);
};
#endif // PIECEAVAILABILITYBAR_H

View File

@@ -0,0 +1,334 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin
* Copyright (C) 2006 Christophe Dumez
*
* 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 "piecesbar.h"
#include <QApplication>
#include <QDebug>
#include <QHelpEvent>
#include <QPainter>
#include <QTextStream>
#include <QToolTip>
#include "base/bittorrent/torrenthandle.h"
#include "base/utils/misc.h"
namespace
{
using ImageRange = IndexRange<int>;
// Computes approximate mapping from image scale (measured in pixels) onto the torrent contents scale (in pieces)
// However, taking the size of a screen to be ~ 1000 px and the torrent size larger than 10 MiB, the pointing error
// is well below 0.5 px and thus is negligible.
class PieceIndexToImagePos
{
public:
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
: m_bytesPerPixel {image.width() > 0 ? torrentInfo.totalSize() / image.width() : -1}
, m_torrentInfo {torrentInfo}
{
if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
qDebug() << "PieceIndexToImagePos: torrent size is too small for correct computaions."
<< "Torrent size =" << torrentInfo.totalSize() << "Image width = " << image.width();
}
ImageRange imagePos(const BitTorrent::TorrentInfo::PieceRange &pieces) const
{
if (m_bytesPerPixel < 0)
return {0, 0};
// the type conversion is used to prevent integer overflow with torrents of 2+ GiB size
const qlonglong pieceLength = m_torrentInfo.pieceLength();
return makeInterval<ImageRange::IndexType>(
(pieces.first() * pieceLength) / m_bytesPerPixel,
(pieces.last() * pieceLength + m_torrentInfo.pieceLength(pieces.last()) - 1) / m_bytesPerPixel);
}
int pieceIndex(int imagePos) const
{
return m_bytesPerPixel < 0 ? 0 : (imagePos * m_bytesPerPixel + m_bytesPerPixel / 2) / m_torrentInfo.pieceLength();
}
private:
const qlonglong m_bytesPerPixel; // how many bytes of the torrent are squeezed into a bar's pixel
const BitTorrent::TorrentInfo m_torrentInfo;
};
class DetailedTooltipRenderer
{
public:
DetailedTooltipRenderer(QTextStream &stream, const QString &header)
: m_stream(stream)
{
m_stream << header
<< R"(<table style="width:100%; padding: 3px; vertical-align: middle;">)";
}
~DetailedTooltipRenderer()
{
m_stream << "</table>";
}
void operator()(const QString &size, const QString &path)
{
m_stream << R"(<tr><td style="white-space:nowrap">)" << size << "</td><td>" << path << "</td></tr>";
}
private:
QTextStream &m_stream;
};
}
PiecesBar::PiecesBar(QWidget *parent)
: QWidget {parent}
, m_torrent {nullptr}
, m_borderColor {palette().color(QPalette::Dark)}
, m_bgColor {Qt::white}
, m_pieceColor {Qt::blue}
, m_hovered {false}
{
updatePieceColors();
setMouseTracking(true);
}
void PiecesBar::setTorrent(BitTorrent::TorrentHandle *torrent)
{
m_torrent = torrent;
if (!m_torrent)
clear();
}
void PiecesBar::clear()
{
m_image = QImage();
update();
}
void PiecesBar::setColors(const QColor &background, const QColor &border, const QColor &complete)
{
m_bgColor = background;
m_borderColor = border;
m_pieceColor = complete;
updatePieceColors();
requestImageUpdate();
}
bool PiecesBar::event(QEvent *e)
{
if (e->type() == QEvent::ToolTip) {
showToolTip(static_cast<QHelpEvent *>(e));
return true;
}
else {
return base::event(e);
}
}
void PiecesBar::enterEvent(QEvent *e)
{
m_hovered = true;
base::enterEvent(e);
}
void PiecesBar::leaveEvent(QEvent *e)
{
m_hovered = false;
m_highlitedRegion = QRect();
requestImageUpdate();
base::leaveEvent(e);
}
void PiecesBar::mouseMoveEvent(QMouseEvent *e)
{
// if user pointed to a piece which is a part of a single large file,
// we highlight the space, occupied by this file
highlightFile(e->pos().x() - borderWidth);
base::mouseMoveEvent(e);
}
void PiecesBar::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QRect imageRect(borderWidth, borderWidth, width() - 2 * borderWidth, height() - 2 * borderWidth);
if (m_image.isNull()) {
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else {
if (m_image.width() != imageRect.width())
updateImage(m_image);
painter.drawImage(imageRect, m_image);
}
if (!m_highlitedRegion.isNull()) {
QColor highlightColor {this->palette().color(QPalette::Active, QPalette::Highlight)};
highlightColor.setAlphaF(0.35);
QRect targetHighlightRect {m_highlitedRegion.adjusted(borderWidth, borderWidth, borderWidth, height() - 2 * borderWidth)};
painter.fillRect(targetHighlightRect, highlightColor);
}
QPainterPath border;
border.addRect(0, 0, width(), height());
painter.setPen(m_borderColor);
painter.drawPath(border);
}
void PiecesBar::requestImageUpdate()
{
if (updateImage(m_image))
update();
}
QColor PiecesBar::backgroundColor() const
{
return m_bgColor;
}
QColor PiecesBar::borderColor() const
{
return m_borderColor;
}
QColor PiecesBar::pieceColor() const
{
return m_pieceColor;
}
const QVector<QRgb> &PiecesBar::pieceColors() const
{
return m_pieceColors;
}
QRgb PiecesBar::mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio)
{
int r1 = qRed(rgb1);
int g1 = qGreen(rgb1);
int b1 = qBlue(rgb1);
int r2 = qRed(rgb2);
int g2 = qGreen(rgb2);
int b2 = qBlue(rgb2);
float ratioN = 1.0f - ratio;
int r = (r1 * ratioN) + (r2 * ratio);
int g = (g1 * ratioN) + (g2 * ratio);
int b = (b1 * ratioN) + (b2 * ratio);
return qRgb(r, g, b);
}
void PiecesBar::showToolTip(const QHelpEvent *e)
{
if (!m_torrent)
return;
QString toolTipText;
QTextStream stream(&toolTipText, QIODevice::WriteOnly);
bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
if (showDetailedInformation) {
const int imagePos = e->pos().x() - borderWidth;
if ((imagePos >=0) && (imagePos < m_image.width())) {
stream << "<html><body>";
PieceIndexToImagePos transform {m_torrent->info(), m_image};
int pieceIndex = transform.pieceIndex(imagePos);
QVector<int> files {m_torrent->info().fileIndicesForPiece(pieceIndex)};
QString tooltipTitle;
if (files.count() > 1) {
tooltipTitle = tr("Files in this piece:");
}
else {
if (m_torrent->info().fileSize(files.front()) == m_torrent->info().pieceLength(pieceIndex))
tooltipTitle = tr("File in this piece");
else
tooltipTitle = tr("File in these pieces");
}
DetailedTooltipRenderer renderer(stream, tooltipTitle);
const bool isFileNameCorrectionNeeded = this->isFileNameCorrectionNeeded();
for (int f: files) {
QString filePath {m_torrent->info().filePath(f)};
if (isFileNameCorrectionNeeded)
filePath.replace(QLatin1String("/.unwanted"), QString());
renderer(Utils::Misc::friendlyUnit(m_torrent->info().fileSize(f)), filePath);
}
stream << "</body></html>";
}
}
else {
stream << simpleToolTipText();
stream << '\n' << tr("Hold Shift key for detailed information");
}
stream.flush();
QToolTip::showText(e->globalPos(), toolTipText, this);
}
void PiecesBar::highlightFile(int imagePos)
{
if (!m_torrent || (imagePos < 0) || (imagePos >= m_image.width()))
return;
PieceIndexToImagePos transform {m_torrent->info(), m_image};
int pieceIndex = transform.pieceIndex(imagePos);
QVector<int> fileIndices {m_torrent->info().fileIndicesForPiece(pieceIndex)};
if (fileIndices.count() == 1) {
BitTorrent::TorrentInfo::PieceRange filePieces = m_torrent->info().filePieces(fileIndices.first());
ImageRange imageRange = transform.imagePos(filePieces);
QRect newHighlitedRegion {imageRange.first(), 0, imageRange.size(), m_image.height()};
if (newHighlitedRegion != m_highlitedRegion) {
m_highlitedRegion = newHighlitedRegion;
update();
}
}
else if (!m_highlitedRegion.isEmpty()) {
m_highlitedRegion = QRect();
update();
}
}
void PiecesBar::updatePieceColors()
{
m_pieceColors = QVector<QRgb>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
m_pieceColors[i] = mixTwoColors(backgroundColor().rgb(), m_pieceColor.rgb(), ratio);
}
}
bool PiecesBar::isFileNameCorrectionNeeded() const
{
return false;
}

View File

@@ -0,0 +1,109 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin
* Copyright (C) 2006 Christophe Dumez
*
* 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.
*/
#ifndef PIECESBAR_H
#define PIECESBAR_H
#include <QColor>
#include <QImage>
#include <QWidget>
class QHelpEvent;
namespace BitTorrent
{
class TorrentHandle;
}
class PiecesBar: public QWidget
{
using base = QWidget;
Q_OBJECT
Q_DISABLE_COPY(PiecesBar)
public:
explicit PiecesBar(QWidget *parent = nullptr);
void setTorrent(BitTorrent::TorrentHandle *torrent);
void setColors(const QColor &background, const QColor &border, const QColor &complete);
virtual void clear();
// QObject interface
virtual bool event(QEvent*) override;
protected:
// QWidget interface
void enterEvent(QEvent*) override;
void leaveEvent(QEvent*) override;
void mouseMoveEvent(QMouseEvent*) override;
void paintEvent(QPaintEvent*) override;
void requestImageUpdate();
QColor backgroundColor() const;
QColor borderColor() const;
QColor pieceColor() const;
const QVector<QRgb> &pieceColors() const;
// mix two colors by light model, ratio <0, 1>
static QRgb mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio);
static constexpr int borderWidth = 1;
private:
void showToolTip(const QHelpEvent*);
void highlightFile(int imagePos);
virtual QString simpleToolTipText() const = 0;
/// whether to perform removing of ".unwanted" directory from paths
virtual bool isFileNameCorrectionNeeded() const;
// draw new image to replace the actual image
// returns true if image was successfully updated
virtual bool updateImage(QImage &image) = 0;
void updatePieceColors();
const BitTorrent::TorrentHandle *m_torrent;
QImage m_image;
// I used values, because it should be possible to change colors at run time
// border color
QColor m_borderColor;
// background color
QColor m_bgColor;
// complete piece color
QColor m_pieceColor;
// buffered 256 levels gradient from bg_color to piece_color
QVector<QRgb> m_pieceColors;
bool m_hovered;
QRect m_highlitedRegion; //!< part of the bar can be highlighted; this rectangle is in the same frame as m_image
};
#endif // PIECESBAR_H

View File

@@ -16,7 +16,8 @@ HEADERS += $$PWD/propertieswidget.h \
$$PWD/pieceavailabilitybar.h \
$$PWD/proptabbar.h \
$$PWD/speedwidget.h \
$$PWD/speedplotview.h
$$PWD/speedplotview.h \
$$PWD/piecesbar.h
SOURCES += $$PWD/propertieswidget.cpp \
$$PWD/proplistdelegate.cpp \
@@ -28,4 +29,5 @@ SOURCES += $$PWD/propertieswidget.cpp \
$$PWD/pieceavailabilitybar.cpp \
$$PWD/proptabbar.cpp \
$$PWD/speedwidget.cpp \
$$PWD/speedplotview.cpp
$$PWD/speedplotview.cpp \
$$PWD/piecesbar.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -55,80 +55,81 @@ class QAction;
class QTimer;
QT_END_NAMESPACE
class PropertiesWidget : public QWidget, private Ui::PropertiesWidget {
Q_OBJECT
Q_DISABLE_COPY(PropertiesWidget)
class PropertiesWidget: public QWidget, private Ui::PropertiesWidget
{
Q_OBJECT
Q_DISABLE_COPY(PropertiesWidget)
public:
enum SlideState {REDUCED, VISIBLE};
enum SlideState {REDUCED, VISIBLE};
public:
PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList);
~PropertiesWidget();
BitTorrent::TorrentHandle *getCurrentTorrent() const;
TrackerList* getTrackerList() const { return trackerList; }
PeerListWidget* getPeerList() const { return peersList; }
QTreeView* getFilesList() const { return filesList; }
SpeedWidget* getSpeedWidget() const { return speedWidget; }
PropertiesWidget(QWidget *parent, MainWindow *main_window, TransferListWidget *transferList);
~PropertiesWidget();
BitTorrent::TorrentHandle *getCurrentTorrent() const;
TrackerList *getTrackerList() const { return trackerList; }
PeerListWidget *getPeerList() const { return peersList; }
QTreeView *getFilesList() const { return filesList; }
SpeedWidget *getSpeedWidget() const { return speedWidget; }
protected:
QPushButton* getButtonFromIndex(int index);
bool applyPriorities();
QPushButton *getButtonFromIndex(int index);
bool applyPriorities();
protected slots:
void loadTorrentInfos(BitTorrent::TorrentHandle *const torrent);
void updateTorrentInfos(BitTorrent::TorrentHandle *const torrent);
void loadUrlSeeds();
void askWebSeed();
void deleteSelectedUrlSeeds();
void copySelectedWebSeedsToClipboard() const;
void editWebSeed();
void displayFilesListMenu(const QPoint& pos);
void displayWebSeedListMenu(const QPoint& pos);
void filteredFilesChanged();
void showPiecesDownloaded(bool show);
void showPiecesAvailability(bool show);
void renameSelectedFile();
void openSelectedFile();
void loadTorrentInfos(BitTorrent::TorrentHandle *const torrent);
void updateTorrentInfos(BitTorrent::TorrentHandle *const torrent);
void loadUrlSeeds();
void askWebSeed();
void deleteSelectedUrlSeeds();
void copySelectedWebSeedsToClipboard() const;
void editWebSeed();
void displayFilesListMenu(const QPoint &pos);
void displayWebSeedListMenu(const QPoint &pos);
void filteredFilesChanged();
void showPiecesDownloaded(bool show);
void showPiecesAvailability(bool show);
void renameSelectedFile();
void openSelectedFile();
public slots:
void setVisibility(bool visible);
void loadDynamicData();
void clear();
void readSettings();
void saveSettings();
void reloadPreferences();
void openDoubleClickedFile(const QModelIndex &);
void loadTrackers(BitTorrent::TorrentHandle *const torrent);
void setVisibility(bool visible);
void loadDynamicData();
void clear();
void readSettings();
void saveSettings();
void reloadPreferences();
void openDoubleClickedFile(const QModelIndex &);
void loadTrackers(BitTorrent::TorrentHandle *const torrent);
private:
void openFile(const QModelIndex &index);
void openFolder(const QModelIndex &index, bool containing_folder);
void openFile(const QModelIndex &index);
void openFolder(const QModelIndex &index, bool containing_folder);
private:
TransferListWidget *transferList;
MainWindow *main_window;
BitTorrent::TorrentHandle *m_torrent;
QTimer *refreshTimer;
SlideState state;
TorrentContentFilterModel *PropListModel;
PropListDelegate *PropDelegate;
PeerListWidget *peersList;
TrackerList *trackerList;
SpeedWidget *speedWidget;
QList<int> slideSizes;
DownloadedPiecesBar *downloaded_pieces;
PieceAvailabilityBar *pieces_availability;
PropTabBar *m_tabBar;
LineEdit *m_contentFilterLine;
QShortcut *editHotkeyFile;
QShortcut *editHotkeyWeb;
QShortcut *deleteHotkeyWeb;
QShortcut *openHotkeyFile;
TransferListWidget *transferList;
MainWindow *main_window;
BitTorrent::TorrentHandle *m_torrent;
QTimer *refreshTimer;
SlideState state;
TorrentContentFilterModel *PropListModel;
PropListDelegate *PropDelegate;
PeerListWidget *peersList;
TrackerList *trackerList;
SpeedWidget *speedWidget;
QList<int> slideSizes;
DownloadedPiecesBar *downloaded_pieces;
PieceAvailabilityBar *pieces_availability;
PropTabBar *m_tabBar;
LineEdit *m_contentFilterLine;
QShortcut *editHotkeyFile;
QShortcut *editHotkeyWeb;
QShortcut *deleteHotkeyWeb;
QShortcut *openHotkeyFile;
private slots:
void filterText(const QString& filter);
void updateSavePath(BitTorrent::TorrentHandle *const torrent);
void filterText(const QString &filter);
void updateSavePath(BitTorrent::TorrentHandle *const torrent);
};
#endif // PROPERTIESWIDGET_H

View File

@@ -28,14 +28,12 @@
* Contact : chris@qbittorrent.org
*/
#include <QStyleOptionProgressBarV2>
#include <QStyleOptionViewItemV2>
#include <QStyleOptionComboBox>
#include <QComboBox>
#include <QModelIndex>
#include <QPainter>
#include <QPalette>
#include <QProgressBar>
#include <QApplication>
#include <QStyleOptionProgressBar>
#ifdef Q_OS_WIN
#ifndef QBT_USES_QT5
@@ -51,6 +49,23 @@
#include "proplistdelegate.h"
#include "torrentcontentmodelitem.h"
namespace {
QPalette progressBarDisabledPalette()
{
auto getPalette = []()
{
QProgressBar bar;
bar.setEnabled(false);
QStyleOptionProgressBar opt;
opt.initFrom(&bar);
return opt.palette;
};
static QPalette palette = getPalette();
return palette;
}
}
PropListDelegate::PropListDelegate(PropertiesWidget *properties, QObject *parent)
: QItemDelegate(parent)
, m_properties(properties)
@@ -60,7 +75,7 @@ PropListDelegate::PropListDelegate(PropertiesWidget *properties, QObject *parent
void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
painter->save();
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
switch(index.column()) {
case PCSIZE:
@@ -69,24 +84,24 @@ void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
break;
case REMAINING:
QItemDelegate::drawBackground(painter, opt, index);
if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) {
QItemDelegate::drawDisplay(painter, opt, option.rect, tr("N/A"));
}
else {
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
}
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
break;
case PROGRESS:
if (index.data().toDouble() >= 0) {
QStyleOptionProgressBarV2 newopt;
QStyleOptionProgressBar newopt;
qreal progress = index.data().toDouble() * 100.;
newopt.rect = opt.rect;
newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%");
newopt.progress = (int)progress;
newopt.maximum = 100;
newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = true;
if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) {
newopt.state &= ~QStyle::State_Enabled;
newopt.palette = progressBarDisabledPalette();
}
else
newopt.state |= QStyle::State_Enabled;
#ifndef Q_OS_WIN
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
#else
@@ -139,14 +154,17 @@ void PropListDelegate::setEditorData(QWidget *editor, const QModelIndex &index)
QComboBox *combobox = static_cast<QComboBox*>(editor);
// Set combobox index
switch(index.data().toInt()) {
case prio::HIGH:
combobox->setCurrentIndex(1);
case prio::IGNORED:
combobox->setCurrentIndex(0);
break;
case prio::MAXIMUM:
case prio::HIGH:
combobox->setCurrentIndex(2);
break;
case prio::MAXIMUM:
combobox->setCurrentIndex(3);
break;
default:
combobox->setCurrentIndex(0);
combobox->setCurrentIndex(1);
break;
}
}
@@ -161,13 +179,12 @@ QWidget *PropListDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
return 0;
}
if (index.data().toInt() <= 0) {
// IGNORED or MIXED
if (index.data().toInt() == prio::MIXED)
return 0;
}
QComboBox* editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus);
editor->addItem(tr("Do not download", "Do not download (priority)"));
editor->addItem(tr("Normal", "Normal (priority)"));
editor->addItem(tr("High", "High (priority)"));
editor->addItem(tr("Maximum", "Maximum (priority)"));
@@ -181,10 +198,13 @@ void PropListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
qDebug("PropListDelegate: setModelData(%d)", value);
switch(value) {
case 1:
model->setData(index, prio::HIGH); // HIGH
case 0:
model->setData(index, prio::IGNORED); // IGNORED
break;
case 2:
model->setData(index, prio::HIGH); // HIGH
break;
case 3:
model->setData(index, prio::MAXIMUM); // MAX
break;
default:

View File

@@ -34,8 +34,12 @@
SpeedPlotView::SpeedPlotView(QWidget *parent)
: QGraphicsView(parent)
, m_data5Min(MIN5_BUF_SIZE)
, m_data30Min(MIN30_BUF_SIZE)
, m_data6Hour(HOUR6_BUF_SIZE)
, m_viewablePointsCount(MIN5_SEC)
, m_maxCapacity(HOUR6_SEC)
, m_counter30Min(-1)
, m_counter6Hour(-1)
{
QPen greenPen;
greenPen.setWidthF(1.5);
@@ -70,64 +74,94 @@ SpeedPlotView::SpeedPlotView(QWidget *parent)
void SpeedPlotView::setGraphEnable(GraphID id, bool enable)
{
m_properties[id].m_enable = enable;
m_properties[id].enable = enable;
viewport()->update();
}
void SpeedPlotView::pushXPoint(double x)
void SpeedPlotView::pushPoint(SpeedPlotView::PointData point)
{
while (m_xData.size() >= m_maxCapacity)
m_xData.pop_front();
m_counter30Min = (m_counter30Min + 1) % 3;
m_counter6Hour = (m_counter6Hour + 1) % 18;
m_xData.append(x);
}
m_data5Min.push_back(point);
void SpeedPlotView::pushYPoint(GraphID id, double y)
{
while (m_yData[id].size() >= m_maxCapacity)
m_yData[id].pop_front();
if (m_counter30Min == 0) {
m_data30Min.push_back(point);
}
else {
m_data30Min.back().x = (m_data30Min.back().x * m_counter30Min + point.x) / (m_counter30Min + 1);
for (int id = UP; id < NB_GRAPHS; ++id)
m_data30Min.back().y[id] = (m_data30Min.back().y[id] * m_counter30Min + point.y[id]) / (m_counter30Min + 1);
}
m_yData[id].append(y);
if (m_counter6Hour == 0) {
m_data6Hour.push_back(point);
}
else {
m_data6Hour.back().x = (m_data6Hour.back().x * m_counter6Hour + point.x) / (m_counter6Hour + 1);
for (int id = UP; id < NB_GRAPHS; ++id)
m_data6Hour.back().y[id] = (m_data6Hour.back().y[id] * m_counter6Hour + point.y[id]) / (m_counter6Hour + 1);
}
}
void SpeedPlotView::setViewableLastPoints(TimePeriod period)
{
m_period = period;
switch (period) {
case SpeedPlotView::MIN1:
m_viewablePointsCount = SpeedPlotView::MIN1_SEC;
m_viewablePointsCount = MIN1_SEC;
break;
case SpeedPlotView::MIN5:
m_viewablePointsCount = SpeedPlotView::MIN5_SEC;
m_viewablePointsCount = MIN5_SEC;
break;
case SpeedPlotView::MIN30:
m_viewablePointsCount = SpeedPlotView::MIN30_SEC;
m_viewablePointsCount = MIN30_BUF_SIZE;
break;
case SpeedPlotView::HOUR6:
m_viewablePointsCount = SpeedPlotView::HOUR6_SEC;
break;
default:
m_viewablePointsCount = HOUR6_BUF_SIZE;
break;
}
viewport()->update();
}
void SpeedPlotView::replot()
{
this->viewport()->update();
if ((m_period == MIN1)
|| (m_period == MIN5)
|| ((m_period == MIN30) && (m_counter30Min == 2))
|| ((m_period == HOUR6) && (m_counter6Hour == 17)))
viewport()->update();
}
double SpeedPlotView::maxYValue()
boost::circular_buffer<SpeedPlotView::PointData> &SpeedPlotView::getCurrentData()
{
double maxYValue = 0;
for (QMap<GraphID, QQueue<double> >::const_iterator it = m_yData.begin(); it != m_yData.end(); ++it) {
switch (m_period) {
case SpeedPlotView::MIN1:
case SpeedPlotView::MIN5:
default:
return m_data5Min;
case SpeedPlotView::MIN30:
return m_data30Min;
case SpeedPlotView::HOUR6:
return m_data6Hour;
}
}
if (!m_properties[it.key()].m_enable)
int SpeedPlotView::maxYValue()
{
boost::circular_buffer<PointData> &queue = getCurrentData();
int maxYValue = 0;
for (int id = UP; id < NB_GRAPHS; ++id) {
if (!m_properties[static_cast<GraphID>(id)].enable)
continue;
QQueue<double> &queue = m_yData[it.key()];
for (int i = queue.size() - 1, j = 0; i >= 0 && j <= m_viewablePointsCount; --i, ++j) {
if (queue.at(i) > maxYValue)
maxYValue = queue.at(i);
}
for (int i = int(queue.size()) - 1, j = 0; i >= 0 && j <= m_viewablePointsCount; --i, ++j)
if (queue[i].y[id] > maxYValue)
maxYValue = queue[i].y[id];
}
return maxYValue;
@@ -135,58 +169,59 @@ double SpeedPlotView::maxYValue()
void SpeedPlotView::paintEvent(QPaintEvent *)
{
QPainter painter(this->viewport());
QPainter painter(viewport());
QRect full_rect = this->viewport()->rect();
QRect rect = this->viewport()->rect();
QFontMetrics font_metrics = painter.fontMetrics();
QRect fullRect = viewport()->rect();
QRect rect = viewport()->rect();
QFontMetrics fontMetrics = painter.fontMetrics();
rect.adjust(4, 4, 0, -4); // Add padding
double max_y = maxYValue();
int maxY = maxYValue();
rect.adjust(0, font_metrics.height(), 0, 0); // Add top padding for top speed text
rect.adjust(0, fontMetrics.height(), 0, 0); // Add top padding for top speed text
// draw Y axis speed labels
QVector<QString> speed_labels(QVector<QString>() <<
Utils::Misc::friendlyUnit(max_y, true) <<
Utils::Misc::friendlyUnit(0.75 * max_y, true) <<
Utils::Misc::friendlyUnit(0.5 * max_y, true) <<
Utils::Misc::friendlyUnit(0.25 * max_y, true) <<
Utils::Misc::friendlyUnit(0, true));
QVector<QString> speedLabels = {
Utils::Misc::friendlyUnit(maxY, true),
Utils::Misc::friendlyUnit(0.75 * maxY, true),
Utils::Misc::friendlyUnit(0.5 * maxY, true),
Utils::Misc::friendlyUnit(0.25 * maxY, true),
Utils::Misc::friendlyUnit(0, true)
};
int y_axe_width = 0;
for (int i = 0; i < speed_labels.size(); ++i) {
if (font_metrics.width(speed_labels[i]) > y_axe_width)
y_axe_width = font_metrics.width(speed_labels[i]);
}
int yAxeWidth = 0;
for (const QString &label : speedLabels)
if (fontMetrics.width(label) > yAxeWidth)
yAxeWidth = fontMetrics.width(label);
for (int i = 0; i < speed_labels.size(); ++i) {
QRectF label_rect(rect.topLeft() + QPointF(-y_axe_width, i * 0.25 * rect.height() - font_metrics.height()),
QSizeF(2 * y_axe_width, font_metrics.height()));
painter.drawText(label_rect, speed_labels[i], QTextOption((Qt::AlignRight) | (Qt::AlignTop)));
int i = 0;
for (const QString &label : speedLabels) {
QRectF labelRect(rect.topLeft() + QPointF(-yAxeWidth, (i++) * 0.25 * rect.height() - fontMetrics.height()),
QSizeF(2 * yAxeWidth, fontMetrics.height()));
painter.drawText(labelRect, label, Qt::AlignRight | Qt::AlignTop);
}
// draw grid lines
rect.adjust(y_axe_width + 4, 0, 0, 0);
rect.adjust(yAxeWidth + 4, 0, 0, 0);
QPen grid_pen;
grid_pen.setStyle(Qt::DashLine);
grid_pen.setWidthF(1);
grid_pen.setColor(QColor(128, 128, 128, 128));
painter.setPen(grid_pen);
QPen gridPen;
gridPen.setStyle(Qt::DashLine);
gridPen.setWidthF(1);
gridPen.setColor(QColor(128, 128, 128, 128));
painter.setPen(gridPen);
painter.drawLine(full_rect.left(), rect.top(), rect.right(), rect.top());
painter.drawLine(full_rect.left(), rect.top() + 0.25 * rect.height(), rect.right(), rect.top() + 0.25 * rect.height());
painter.drawLine(full_rect.left(), rect.top() + 0.50 * rect.height(), rect.right(), rect.top() + 0.50 * rect.height());
painter.drawLine(full_rect.left(), rect.top() + 0.75 * rect.height(), rect.right(), rect.top() + 0.75 * rect.height());
painter.drawLine(full_rect.left(), rect.bottom(), rect.right(), rect.bottom());
painter.drawLine(fullRect.left(), rect.top(), rect.right(), rect.top());
painter.drawLine(fullRect.left(), rect.top() + 0.25 * rect.height(), rect.right(), rect.top() + 0.25 * rect.height());
painter.drawLine(fullRect.left(), rect.top() + 0.50 * rect.height(), rect.right(), rect.top() + 0.50 * rect.height());
painter.drawLine(fullRect.left(), rect.top() + 0.75 * rect.height(), rect.right(), rect.top() + 0.75 * rect.height());
painter.drawLine(fullRect.left(), rect.bottom(), rect.right(), rect.bottom());
painter.drawLine(rect.left(), full_rect.top(), rect.left(), full_rect.bottom());
painter.drawLine(rect.left() + 0.2 * rect.width(), full_rect.top(), rect.left() + 0.2 * rect.width(), full_rect.bottom());
painter.drawLine(rect.left() + 0.4 * rect.width(), full_rect.top(), rect.left() + 0.4 * rect.width(), full_rect.bottom());
painter.drawLine(rect.left() + 0.6 * rect.width(), full_rect.top(), rect.left() + 0.6 * rect.width(), full_rect.bottom());
painter.drawLine(rect.left() + 0.8 * rect.width(), full_rect.top(), rect.left() + 0.8 * rect.width(), full_rect.bottom());
painter.drawLine(rect.left(), fullRect.top(), rect.left(), fullRect.bottom());
painter.drawLine(rect.left() + 0.2 * rect.width(), fullRect.top(), rect.left() + 0.2 * rect.width(), fullRect.bottom());
painter.drawLine(rect.left() + 0.4 * rect.width(), fullRect.top(), rect.left() + 0.4 * rect.width(), fullRect.bottom());
painter.drawLine(rect.left() + 0.6 * rect.width(), fullRect.top(), rect.left() + 0.6 * rect.width(), fullRect.bottom());
painter.drawLine(rect.left() + 0.8 * rect.width(), fullRect.top(), rect.left() + 0.8 * rect.width(), fullRect.bottom());
// Set antialiasing for graphs
painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing);
@@ -194,73 +229,73 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
// draw graphs
rect.adjust(3, 0, 0, 0); // Need, else graphs cross left gridline
double y_multiplier = (max_y == 0.0) ? 0.0 : rect.height() / max_y;
double x_tick_size = double(rect.width()) / m_viewablePointsCount;
double yMultiplier = (maxY == 0) ? 0.0 : static_cast<double>(rect.height()) / maxY;
double xTickSize = static_cast<double>(rect.width()) / m_viewablePointsCount;
for (QMap<GraphID, QQueue<double> >::const_iterator it = m_yData.begin(); it != m_yData.end(); ++it) {
boost::circular_buffer<PointData> &queue = getCurrentData();
if (!m_properties[it.key()].m_enable)
for (int id = UP; id < NB_GRAPHS; ++id) {
if (!m_properties[static_cast<GraphID>(id)].enable)
continue;
QQueue<double> &queue = m_yData[it.key()];
QVector<QPointF> points;
QVector<QPoint> points;
for (int i = queue.size() - 1, j = 0; i >= 0 && j <= m_viewablePointsCount; --i, ++j) {
points.push_back(QPointF(rect.right() - j * x_tick_size,
rect.bottom() - queue.at(i) * y_multiplier));
for (int i = int(queue.size()) - 1, j = 0; i >= 0 && j <= m_viewablePointsCount; --i, ++j) {
int new_x = rect.right() - j * xTickSize;
int new_y = rect.bottom() - queue[i].y[id] * yMultiplier;
points.push_back(QPoint(new_x, new_y));
}
painter.setPen(m_properties[it.key()].m_pen);
painter.setPen(m_properties[static_cast<GraphID>(id)].pen);
painter.drawPolyline(points.data(), points.size());
}
// draw legend
QPoint legend_top_left(rect.left() + 4, full_rect.top() + 4);
QPoint legendTopLeft(rect.left() + 4, fullRect.top() + 4);
double legend_height = 0;
int legend_width = 0;
for (QMap<GraphID, GraphProperties>::const_iterator it = m_properties.begin(); it != m_properties.end(); ++it) {
double legendHeight = 0;
int legendWidth = 0;
for (const auto &property : m_properties) {
if (!it.value().m_enable)
if (!property.enable)
continue;
if (font_metrics.width(it.value().m_name) > legend_width)
legend_width = font_metrics.width(it.value().m_name);
legend_height += 1.5 * font_metrics.height();
if (fontMetrics.width(property.name) > legendWidth)
legendWidth = fontMetrics.width(property.name);
legendHeight += 1.5 * fontMetrics.height();
}
QRectF legend_background_rect(legend_top_left, QSizeF(legend_width, legend_height));
QRectF legendBackgroundRect(QPoint(legendTopLeft.x() - 4, legendTopLeft.y() - 4), QSizeF(legendWidth + 8, legendHeight + 8));
QColor legendBackgroundColor = QWidget::palette().color(QWidget::backgroundRole());
legendBackgroundColor.setAlpha(128); // 50% transparent
painter.fillRect(legend_background_rect, legendBackgroundColor);
painter.fillRect(legendBackgroundRect, legendBackgroundColor);
int i = 0;
for (QMap<GraphID, GraphProperties>::const_iterator it = m_properties.begin(); it != m_properties.end(); ++it) {
i = 0;
for (const auto &property : m_properties) {
if (!it.value().m_enable)
if (!property.enable)
continue;
int name_size = font_metrics.width(it.value().m_name);
double indent = 1.5 * i * font_metrics.height();
int nameSize = fontMetrics.width(property.name);
double indent = 1.5 * (i++) * fontMetrics.height();
painter.setPen(it.value().m_pen);
painter.drawLine(legend_top_left + QPointF(0, indent + font_metrics.height()),
legend_top_left + QPointF(name_size, indent + font_metrics.height()));
painter.drawText(QRectF(legend_top_left + QPointF(0, indent), QSizeF(2 * name_size, font_metrics.height())),
it.value().m_name, QTextOption(Qt::AlignVCenter));
++i;
painter.setPen(property.pen);
painter.drawLine(legendTopLeft + QPointF(0, indent + fontMetrics.height()),
legendTopLeft + QPointF(nameSize, indent + fontMetrics.height()));
painter.drawText(QRectF(legendTopLeft + QPointF(0, indent), QSizeF(2 * nameSize, fontMetrics.height())),
property.name, QTextOption(Qt::AlignVCenter));
}
}
SpeedPlotView::GraphProperties::GraphProperties()
: m_enable(false)
{
}
: enable(false)
{}
SpeedPlotView::GraphProperties::GraphProperties(const QString &name, const QPen &pen, bool enable)
: m_name(name)
, m_pen(pen)
, m_enable(enable)
{
}
: name(name)
, pen(pen)
, enable(enable)
{}

View File

@@ -29,14 +29,18 @@
#ifndef SPEEDPLOTVIEW_H
#define SPEEDPLOTVIEW_H
#ifndef Q_MOC_RUN
#include <boost/circular_buffer.hpp>
#endif
#include <QGraphicsView>
#include <QMap>
#include <QQueue>
class QPen;
class SpeedPlotView : public QGraphicsView
class SpeedPlotView: public QGraphicsView
{
Q_OBJECT
public:
enum GraphID
{
@@ -61,15 +65,19 @@ public:
HOUR6
};
struct PointData
{
uint x;
int y[NB_GRAPHS];
};
explicit SpeedPlotView(QWidget *parent = 0);
void setGraphEnable(GraphID id, bool enable);
void pushXPoint(double x);
void pushYPoint(GraphID id, double y);
void setViewableLastPoints(TimePeriod period);
void pushPoint(PointData point);
void replot();
protected:
@@ -84,24 +92,37 @@ private:
HOUR6_SEC = 6 * 60 * 60
};
enum PointsToSave
{
MIN5_BUF_SIZE = 5 * 60,
MIN30_BUF_SIZE = 10 * 60,
HOUR6_BUF_SIZE = 20 * 60
};
struct GraphProperties
{
GraphProperties();
GraphProperties(const QString &name, const QPen &pen, bool enable = false);
QString m_name;
QPen m_pen;
bool m_enable;
QString name;
QPen pen;
bool enable;
};
QQueue<double> m_xData;
QMap<GraphID, QQueue<double> > m_yData;
boost::circular_buffer<PointData> m_data5Min;
boost::circular_buffer<PointData> m_data30Min;
boost::circular_buffer<PointData> m_data6Hour;
QMap<GraphID, GraphProperties> m_properties;
PeriodInSeconds m_viewablePointsCount;
PeriodInSeconds m_maxCapacity;
TimePeriod m_period;
int m_viewablePointsCount;
double maxYValue();
int m_counter30Min;
int m_counter6Hour;
int maxYValue();
boost::circular_buffer<PointData> &getCurrentData();
};
#endif // SPEEDPLOTVIEW_H

View File

@@ -45,8 +45,7 @@
ComboBoxMenuButton::ComboBoxMenuButton(QWidget *parent, QMenu *menu)
: QComboBox(parent)
, m_menu(menu)
{
}
{}
void ComboBoxMenuButton::showPopup()
{
@@ -55,7 +54,6 @@ void ComboBoxMenuButton::showPopup()
QComboBox::hidePopup();
}
SpeedWidget::SpeedWidget(PropertiesWidget *parent)
: QWidget(parent)
{
@@ -75,7 +73,7 @@ SpeedWidget::SpeedWidget(PropertiesWidget *parent)
connect(m_periodCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(onPeriodChange(int)));
m_graphsMenu = new QMenu();
m_graphsMenu = new QMenu(this);
m_graphsMenu->addAction(tr("Total Upload"));
m_graphsMenu->addAction(tr("Total Download"));
m_graphsMenu->addAction(tr("Payload Upload"));
@@ -138,17 +136,20 @@ void SpeedWidget::update()
BitTorrent::SessionStatus btStatus = BitTorrent::Session::instance()->status();
m_plot->pushXPoint(QDateTime::currentDateTime().toTime_t());
m_plot->pushYPoint(SpeedPlotView::UP, btStatus.uploadRate());
m_plot->pushYPoint(SpeedPlotView::DOWN, btStatus.downloadRate());
m_plot->pushYPoint(SpeedPlotView::PAYLOAD_UP, btStatus.payloadUploadRate());
m_plot->pushYPoint(SpeedPlotView::PAYLOAD_DOWN, btStatus.payloadDownloadRate());
m_plot->pushYPoint(SpeedPlotView::OVERHEAD_UP, btStatus.ipOverheadUploadRate());
m_plot->pushYPoint(SpeedPlotView::OVERHEAD_DOWN, btStatus.ipOverheadDownloadRate());
m_plot->pushYPoint(SpeedPlotView::DHT_UP, btStatus.dhtUploadRate());
m_plot->pushYPoint(SpeedPlotView::DHT_DOWN, btStatus.dhtDownloadRate());
m_plot->pushYPoint(SpeedPlotView::TRACKER_UP, btStatus.trackerUploadRate());
m_plot->pushYPoint(SpeedPlotView::TRACKER_DOWN, btStatus.trackerDownloadRate());
SpeedPlotView::PointData point;
point.x = QDateTime::currentDateTime().toTime_t();
point.y[SpeedPlotView::UP] = btStatus.uploadRate();
point.y[SpeedPlotView::DOWN] = btStatus.downloadRate();
point.y[SpeedPlotView::PAYLOAD_UP] = btStatus.payloadUploadRate();
point.y[SpeedPlotView::PAYLOAD_DOWN] = btStatus.payloadDownloadRate();
point.y[SpeedPlotView::OVERHEAD_UP] = btStatus.ipOverheadUploadRate();
point.y[SpeedPlotView::OVERHEAD_DOWN] = btStatus.ipOverheadDownloadRate();
point.y[SpeedPlotView::DHT_UP] = btStatus.dhtUploadRate();
point.y[SpeedPlotView::DHT_DOWN] = btStatus.dhtDownloadRate();
point.y[SpeedPlotView::TRACKER_UP] = btStatus.trackerUploadRate();
point.y[SpeedPlotView::TRACKER_DOWN] = btStatus.trackerDownloadRate();
m_plot->pushPoint(point);
QMetaObject::invokeMethod(this, "graphUpdate", Qt::QueuedConnection);
Utils::Misc::msleep(1000);
@@ -163,15 +164,12 @@ void SpeedWidget::graphUpdate()
void SpeedWidget::onPeriodChange(int period)
{
m_plot->setViewableLastPoints(static_cast<SpeedPlotView::TimePeriod>(period));
graphUpdate();
}
void SpeedWidget::onGraphChange(int id)
{
QAction *action = m_graphsMenuActions.at(id);
m_plot->setGraphEnable(static_cast<SpeedPlotView::GraphID>(id), action->isChecked());
graphUpdate();
}
void SpeedWidget::loadSettings()
@@ -202,4 +200,3 @@ void SpeedWidget::saveSettings() const
preferences->setSpeedWidgetGraphEnable(id, action->isChecked());
}
}

View File

@@ -83,7 +83,7 @@ TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), propertie
editHotkey = new QShortcut(QKeySequence("F2"), this, SLOT(editSelectedTracker()), 0, Qt::WidgetShortcut);
connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(editSelectedTracker()));
deleteHotkey = new QShortcut(QKeySequence(QKeySequence::Delete), this, SLOT(deleteSelectedTrackers()), 0, Qt::WidgetShortcut);
copyHotkey = new QShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_C), this, SLOT(copyTrackerUrl()), 0, Qt::WidgetShortcut);
copyHotkey = new QShortcut(QKeySequence::Copy, this, SLOT(copyTrackerUrl()), 0, Qt::WidgetShortcut);
#ifdef QBT_USES_QT5
// This hack fixes reordering of first column with Qt5.
@@ -224,7 +224,7 @@ void TrackerList::loadStickyItems(BitTorrent::TorrentHandle *const torrent) {
dht_item->setText(COL_STATUS, disabled);
// Load PeX Information
if (BitTorrent::Session::instance()->isPexEnabled() && !torrent->isPrivate())
if (BitTorrent::Session::instance()->isPeXEnabled() && !torrent->isPrivate())
pex_item->setText(COL_STATUS, working);
else
pex_item->setText(COL_STATUS, disabled);

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