mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-10 09:24:59 -06:00
Merge branch 'master' of https://github.com/qbittorrent/qBittorrent
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ¶ms)
|
||||
@@ -362,6 +396,7 @@ void Application::processParams(const QStringList ¶ms)
|
||||
|
||||
int Application::exec(const QStringList ¶ms)
|
||||
{
|
||||
Net::ProxyConfigurationManager::initInstance();
|
||||
Net::DownloadManager::initInstance();
|
||||
#ifdef DISABLE_GUI
|
||||
IconProvider::initInstance();
|
||||
@@ -371,7 +406,7 @@ int Application::exec(const QStringList ¶ms)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -76,6 +76,10 @@ public:
|
||||
int exec(const QStringList ¶ms);
|
||||
bool sendParams(const QStringList ¶ms);
|
||||
|
||||
#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 ¶ms);
|
||||
void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
|
||||
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -68,7 +68,6 @@ bool InfoHash::isValid() const
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
|
||||
InfoHash::operator libtorrent::sha1_hash() const
|
||||
{
|
||||
return m_nativeHash;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
130
src/base/indexrange.h
Normal 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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
163
src/base/net/proxyconfigurationmanager.cpp
Normal file
163
src/base/net/proxyconfigurationmanager.cpp
Normal 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());
|
||||
}
|
||||
87
src/base/net/proxyconfigurationmanager.h
Normal file
87
src/base/net/proxyconfigurationmanager.h
Normal 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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
100
src/base/torrentfileguard.cpp
Normal file
100
src/base/torrentfileguard.cpp
Normal 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
|
||||
}
|
||||
95
src/base/torrentfileguard.h
Normal file
95
src/base/torrentfileguard.h
Normal 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
|
||||
@@ -33,9 +33,9 @@
|
||||
|
||||
const qlonglong MAX_ETA = 8640000;
|
||||
|
||||
enum class ShutdownAction
|
||||
enum class ShutdownDialogAction
|
||||
{
|
||||
None,
|
||||
Exit,
|
||||
Shutdown,
|
||||
Suspend,
|
||||
Hibernate
|
||||
|
||||
@@ -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[] = "עברית";
|
||||
|
||||
@@ -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 += "/";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "Download" 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>
|
||||
|
||||
@@ -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, ¤tAddress](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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
89
src/gui/cookiesdialog.cpp
Normal 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());
|
||||
}
|
||||
@@ -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
179
src/gui/cookiesdialog.ui
Normal 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
178
src/gui/cookiesmodel.cpp
Normal 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
74
src/gui/cookiesmodel.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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>&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>&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>&Tools</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuAuto_Shutdown_on_downloads_completion">
|
||||
<widget class="QMenu" name="menuAutoShutdownOnDownloadsCompletion">
|
||||
<property name="title">
|
||||
<string>On Downloads &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>&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>&Pause</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStartAll">
|
||||
<property name="text">
|
||||
<string>R&esume All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPauseAll">
|
||||
<property name="text">
|
||||
<string>P&ause All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="text">
|
||||
<string>&Delete</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDownload_from_URL">
|
||||
<action name="actionDownloadFromURL">
|
||||
<property name="text">
|
||||
<string>Add Torrent &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 &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>&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>&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 &Engine</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLock_qBittorrent">
|
||||
<action name="actionLock">
|
||||
<property name="text">
|
||||
<string>L&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>&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&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&esume All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPause_All">
|
||||
<property name="text">
|
||||
<string>P&ause All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoExit_qBittorrent">
|
||||
<action name="actionAutoExit">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -359,7 +354,7 @@
|
||||
<string>&Exit qBittorrent</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoSuspend_system">
|
||||
<action name="actionAutoSuspend">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -367,7 +362,7 @@
|
||||
<string>&Suspend System</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoHibernate_system">
|
||||
<action name="actionAutoHibernate">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -375,7 +370,7 @@
|
||||
<string>&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&hutdown System</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAutoShutdown_Disabled">
|
||||
<action name="actionAutoShutdownDisabled">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -406,7 +401,7 @@
|
||||
<string>&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
1694
src/gui/optionsdlg.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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) + "%");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
334
src/gui/properties/piecesbar.cpp
Normal file
334
src/gui/properties/piecesbar.cpp
Normal 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;
|
||||
}
|
||||
109
src/gui/properties/piecesbar.h
Normal file
109
src/gui/properties/piecesbar.h
Normal 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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user