mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-05 23:22:31 -06:00
Improve coding style
This commit is contained in:
committed by
sledgehammer999
parent
d3f46452a9
commit
1728c16580
@@ -70,21 +70,24 @@ AboutDialog::AboutDialog(QWidget *parent)
|
||||
|
||||
// Thanks
|
||||
QFile thanksfile(":/thanks.html");
|
||||
if (thanksfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
if (thanksfile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
m_ui->textBrowserThanks->setHtml(QString::fromUtf8(thanksfile.readAll().constData()));
|
||||
thanksfile.close();
|
||||
}
|
||||
|
||||
// Translation
|
||||
QFile translatorsfile(":/translators.html");
|
||||
if (translatorsfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
if (translatorsfile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
m_ui->textBrowserTranslation->setHtml(QString::fromUtf8(translatorsfile.readAll().constData()));
|
||||
translatorsfile.close();
|
||||
}
|
||||
|
||||
// License
|
||||
QFile licensefile(":/gpl.html");
|
||||
if (licensefile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
if (licensefile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
m_ui->textBrowserLicense->setHtml(QString::fromUtf8(licensefile.readAll().constData()));
|
||||
licensefile.close();
|
||||
}
|
||||
|
||||
@@ -230,7 +230,8 @@ void AddNewTorrentDialog::show(const QString &source, const BitTorrent::AddTorre
|
||||
{
|
||||
auto *dlg = new AddNewTorrentDialog(inParams, parent);
|
||||
|
||||
if (Net::DownloadManager::hasSupportedScheme(source)) {
|
||||
if (Net::DownloadManager::hasSupportedScheme(source))
|
||||
{
|
||||
// Launch downloader
|
||||
Net::DownloadManager::instance()->download(
|
||||
Net::DownloadRequest(source).limit(MAX_TORRENT_SIZE)
|
||||
@@ -262,7 +263,8 @@ bool AddNewTorrentDialog::loadTorrentFile(const QString &torrentPath)
|
||||
|
||||
QString error;
|
||||
m_torrentInfo = BitTorrent::TorrentInfo::loadFromFile(decodedPath, &error);
|
||||
if (!m_torrentInfo.isValid()) {
|
||||
if (!m_torrentInfo.isValid())
|
||||
{
|
||||
RaisedMessageBox::critical(this, 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(decodedPath), error));
|
||||
@@ -280,19 +282,24 @@ bool AddNewTorrentDialog::loadTorrentImpl()
|
||||
const BitTorrent::InfoHash infoHash = m_torrentInfo.hash();
|
||||
|
||||
// Prevent showing the dialog if download is already present
|
||||
if (BitTorrent::Session::instance()->isKnownTorrent(infoHash)) {
|
||||
if (BitTorrent::Session::instance()->isKnownTorrent(infoHash))
|
||||
{
|
||||
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(infoHash);
|
||||
if (torrent) {
|
||||
if (torrent->isPrivate() || m_torrentInfo.isPrivate()) {
|
||||
if (torrent)
|
||||
{
|
||||
if (torrent->isPrivate() || m_torrentInfo.isPrivate())
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("Torrent is already present"), tr("Torrent '%1' is already in the transfer list. Trackers haven't been merged because it is a private torrent.").arg(torrent->name()), QMessageBox::Ok);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
torrent->addTrackers(m_torrentInfo.trackers());
|
||||
torrent->addUrlSeeds(m_torrentInfo.urlSeeds());
|
||||
RaisedMessageBox::information(this, tr("Torrent is already present"), tr("Torrent '%1' is already in the transfer list. Trackers have been merged.").arg(torrent->name()), QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
RaisedMessageBox::information(this, tr("Torrent is already present"), tr("Torrent is already queued for processing."), QMessageBox::Ok);
|
||||
}
|
||||
return false;
|
||||
@@ -307,7 +314,8 @@ bool AddNewTorrentDialog::loadTorrentImpl()
|
||||
|
||||
bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
|
||||
{
|
||||
if (!magnetUri.isValid()) {
|
||||
if (!magnetUri.isValid())
|
||||
{
|
||||
RaisedMessageBox::critical(this, tr("Invalid magnet link"), tr("This magnet link was not recognized"));
|
||||
return false;
|
||||
}
|
||||
@@ -316,19 +324,24 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
|
||||
|
||||
const BitTorrent::InfoHash infoHash = magnetUri.hash();
|
||||
// Prevent showing the dialog if download is already present
|
||||
if (BitTorrent::Session::instance()->isKnownTorrent(infoHash)) {
|
||||
if (BitTorrent::Session::instance()->isKnownTorrent(infoHash))
|
||||
{
|
||||
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(infoHash);
|
||||
if (torrent) {
|
||||
if (torrent->isPrivate()) {
|
||||
if (torrent)
|
||||
{
|
||||
if (torrent->isPrivate())
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("Torrent is already present"), tr("Torrent '%1' is already in the transfer list. Trackers haven't been merged because it is a private torrent.").arg(torrent->name()), QMessageBox::Ok);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
torrent->addTrackers(magnetUri.trackers());
|
||||
torrent->addUrlSeeds(magnetUri.urlSeeds());
|
||||
RaisedMessageBox::information(this, tr("Torrent is already present"), tr("Magnet link '%1' is already in the transfer list. Trackers have been merged.").arg(torrent->name()), QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
RaisedMessageBox::information(this, tr("Torrent is already present"), tr("Magnet link is already queued for processing."), QMessageBox::Ok);
|
||||
}
|
||||
return false;
|
||||
@@ -395,15 +408,18 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
|
||||
// Determine torrent size
|
||||
qlonglong torrentSize = 0;
|
||||
|
||||
if (m_hasMetadata) {
|
||||
if (m_contentModel) {
|
||||
if (m_hasMetadata)
|
||||
{
|
||||
if (m_contentModel)
|
||||
{
|
||||
const QVector<BitTorrent::DownloadPriority> priorities = m_contentModel->model()->getFilePriorities();
|
||||
Q_ASSERT(priorities.size() == m_torrentInfo.filesCount());
|
||||
for (int i = 0; i < priorities.size(); ++i)
|
||||
if (priorities[i] > BitTorrent::DownloadPriority::Ignored)
|
||||
torrentSize += m_torrentInfo.fileSize(i);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
torrentSize = m_torrentInfo.totalSize();
|
||||
}
|
||||
}
|
||||
@@ -426,7 +442,8 @@ void AddNewTorrentDialog::categoryChanged(int index)
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
|
||||
if (m_ui->comboTTM->currentIndex() == 1) {
|
||||
if (m_ui->comboTTM->currentIndex() == 1)
|
||||
{
|
||||
QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
|
||||
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
||||
updateDiskSpaceLabel();
|
||||
@@ -436,7 +453,8 @@ void AddNewTorrentDialog::categoryChanged(int index)
|
||||
void AddNewTorrentDialog::setSavePath(const QString &newPath)
|
||||
{
|
||||
int existingIndex = indexOfSavePath(newPath);
|
||||
if (existingIndex < 0) {
|
||||
if (existingIndex < 0)
|
||||
{
|
||||
// New path, prepend to combo box
|
||||
m_ui->savePath->insertItem(0, newPath);
|
||||
existingIndex = 0;
|
||||
@@ -461,10 +479,12 @@ void AddNewTorrentDialog::saveTorrentFile()
|
||||
if (!path.endsWith(torrentFileExtension, Qt::CaseInsensitive))
|
||||
path += torrentFileExtension;
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
m_torrentInfo.saveToFile(path);
|
||||
}
|
||||
catch (const RuntimeError &err) {
|
||||
catch (const RuntimeError &err)
|
||||
{
|
||||
QMessageBox::critical(this, tr("I/O Error"), err.message());
|
||||
}
|
||||
}
|
||||
@@ -494,7 +514,8 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
|
||||
|
||||
const auto applyPriorities = [this, selectedRows](const BitTorrent::DownloadPriority prio)
|
||||
{
|
||||
for (const QModelIndex &index : selectedRows) {
|
||||
for (const QModelIndex &index : selectedRows)
|
||||
{
|
||||
m_contentModel->setData(
|
||||
m_contentModel->index(index.row(), PRIORITY, index.parent())
|
||||
, static_cast<int>(prio));
|
||||
@@ -504,7 +525,8 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (selectedRows.size() == 1) {
|
||||
if (selectedRows.size() == 1)
|
||||
{
|
||||
QAction *actRename = menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename..."));
|
||||
connect(actRename, &QAction::triggered, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
|
||||
|
||||
@@ -563,12 +585,14 @@ void AddNewTorrentDialog::accept()
|
||||
m_torrentParams.firstLastPiecePriority = m_ui->firstLastCheckBox->isChecked();
|
||||
|
||||
QString savePath = m_ui->savePath->selectedPath();
|
||||
if (m_ui->comboTTM->currentIndex() != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||
if (m_ui->comboTTM->currentIndex() != 1)
|
||||
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||
m_torrentParams.useAutoTMM = TriStateBool::False;
|
||||
m_torrentParams.savePath = savePath;
|
||||
saveSavePathHistory();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_torrentParams.useAutoTMM = TriStateBool::True;
|
||||
}
|
||||
|
||||
@@ -586,7 +610,8 @@ void AddNewTorrentDialog::accept()
|
||||
|
||||
void AddNewTorrentDialog::reject()
|
||||
{
|
||||
if (!m_hasMetadata) {
|
||||
if (!m_hasMetadata)
|
||||
{
|
||||
setMetadataProgressIndicator(false);
|
||||
BitTorrent::Session::instance()->cancelLoadMetadata(m_magnetURI.hash());
|
||||
}
|
||||
@@ -600,7 +625,8 @@ void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &metadata
|
||||
|
||||
disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::metadataLoaded, this, &AddNewTorrentDialog::updateMetadata);
|
||||
|
||||
if (!metadata.isValid()) {
|
||||
if (!metadata.isValid())
|
||||
{
|
||||
RaisedMessageBox::critical(this, tr("I/O Error"), ("Invalid metadata."));
|
||||
setMetadataProgressIndicator(false, tr("Invalid metadata"));
|
||||
return;
|
||||
@@ -628,11 +654,13 @@ void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, co
|
||||
|
||||
void AddNewTorrentDialog::setupTreeview()
|
||||
{
|
||||
if (!m_hasMetadata) {
|
||||
if (!m_hasMetadata)
|
||||
{
|
||||
m_ui->labelCommentData->setText(tr("Not Available", "This comment is unavailable"));
|
||||
m_ui->labelDateData->setText(tr("Not Available", "This date is unavailable"));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Set dialog title
|
||||
setWindowTitle(m_torrentInfo.name());
|
||||
|
||||
@@ -662,7 +690,8 @@ void AddNewTorrentDialog::setupTreeview()
|
||||
|
||||
// Expand single-item folders recursively
|
||||
QModelIndex currentIndex;
|
||||
while (m_contentModel->rowCount(currentIndex) == 1) {
|
||||
while (m_contentModel->rowCount(currentIndex) == 1)
|
||||
{
|
||||
currentIndex = m_contentModel->index(0, 0, currentIndex);
|
||||
m_ui->contentTreeView->setExpanded(currentIndex, true);
|
||||
}
|
||||
@@ -674,10 +703,12 @@ void AddNewTorrentDialog::setupTreeview()
|
||||
void AddNewTorrentDialog::handleDownloadFinished(const Net::DownloadResult &result)
|
||||
{
|
||||
QString error;
|
||||
switch (result.status) {
|
||||
switch (result.status)
|
||||
{
|
||||
case Net::DownloadStatus::Success:
|
||||
m_torrentInfo = BitTorrent::TorrentInfo::load(result.data, &error);
|
||||
if (!m_torrentInfo.isValid()) {
|
||||
if (!m_torrentInfo.isValid())
|
||||
{
|
||||
RaisedMessageBox::critical(this, tr("Invalid torrent"), tr("Failed to load from URL: %1.\nError: %2")
|
||||
.arg(result.url, error));
|
||||
return;
|
||||
@@ -705,13 +736,15 @@ void AddNewTorrentDialog::handleDownloadFinished(const Net::DownloadResult &resu
|
||||
|
||||
void AddNewTorrentDialog::TMMChanged(int index)
|
||||
{
|
||||
if (index != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||
if (index != 1)
|
||||
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||
populateSavePathComboBox();
|
||||
m_ui->groupBoxSavePath->setEnabled(true);
|
||||
m_ui->savePath->blockSignals(false);
|
||||
m_ui->savePath->setCurrentIndex(m_oldIndex < m_ui->savePath->count() ? m_oldIndex : m_ui->savePath->count() - 1);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->groupBoxSavePath->setEnabled(false);
|
||||
m_ui->savePath->blockSignals(true);
|
||||
m_ui->savePath->clear();
|
||||
|
||||
@@ -168,7 +168,8 @@ void AdvancedSettings::saveAdvancedSettings()
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
BitTorrent::OSMemoryPriority prio = BitTorrent::OSMemoryPriority::Normal;
|
||||
switch (m_comboBoxOSMemoryPriority.currentIndex()) {
|
||||
switch (m_comboBoxOSMemoryPriority.currentIndex())
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
prio = BitTorrent::OSMemoryPriority::Normal;
|
||||
@@ -248,12 +249,14 @@ void AdvancedSettings::saveAdvancedSettings()
|
||||
pref->resolvePeerCountries(m_checkBoxResolveCountries.isChecked());
|
||||
pref->resolvePeerHostNames(m_checkBoxResolveHosts.isChecked());
|
||||
// Network interface
|
||||
if (m_comboBoxInterface.currentIndex() == 0) {
|
||||
if (m_comboBoxInterface.currentIndex() == 0)
|
||||
{
|
||||
// All interfaces (default)
|
||||
session->setNetworkInterface(QString());
|
||||
session->setNetworkInterfaceName(QString());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
session->setNetworkInterface(m_comboBoxInterface.itemData(m_comboBoxInterface.currentIndex()).toString());
|
||||
session->setNetworkInterfaceName(m_comboBoxInterface.currentText());
|
||||
}
|
||||
@@ -336,21 +339,25 @@ void AdvancedSettings::updateInterfaceAddressCombo()
|
||||
|
||||
const auto populateCombo = [this](const QHostAddress &addr)
|
||||
{
|
||||
if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
if (addr.protocol() == QAbstractSocket::IPv4Protocol)
|
||||
{
|
||||
const QString str = addr.toString();
|
||||
m_comboBoxInterfaceAddress.addItem(str, str);
|
||||
}
|
||||
else if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
else if (addr.protocol() == QAbstractSocket::IPv6Protocol)
|
||||
{
|
||||
const QString str = Utils::Net::canonicalIPv6Addr(addr).toString();
|
||||
m_comboBoxInterfaceAddress.addItem(str, str);
|
||||
}
|
||||
};
|
||||
|
||||
if (ifaceName.isEmpty()) {
|
||||
if (ifaceName.isEmpty())
|
||||
{
|
||||
for (const QHostAddress &addr : asConst(QNetworkInterface::allAddresses()))
|
||||
populateCombo(addr);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
|
||||
const QList<QNetworkAddressEntry> addresses = iface.addressEntries();
|
||||
for (const QNetworkAddressEntry &entry : addresses)
|
||||
@@ -386,7 +393,8 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
#if defined(Q_OS_WIN)
|
||||
m_comboBoxOSMemoryPriority.addItems({tr("Normal"), tr("Below normal"), tr("Medium"), tr("Low"), tr("Very low")});
|
||||
int OSMemoryPriorityIndex = 0;
|
||||
switch (session->getOSMemoryPriority()) {
|
||||
switch (session->getOSMemoryPriority())
|
||||
{
|
||||
default:
|
||||
case BitTorrent::OSMemoryPriority::Normal:
|
||||
OSMemoryPriorityIndex = 0;
|
||||
@@ -580,16 +588,19 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
const QString currentInterface = session->networkInterface();
|
||||
bool interfaceExists = currentInterface.isEmpty();
|
||||
int i = 1;
|
||||
for (const QNetworkInterface &iface : asConst(QNetworkInterface::allInterfaces())) {
|
||||
for (const QNetworkInterface &iface : asConst(QNetworkInterface::allInterfaces()))
|
||||
{
|
||||
m_comboBoxInterface.addItem(iface.humanReadableName(), iface.name());
|
||||
if (!currentInterface.isEmpty() && (iface.name() == currentInterface)) {
|
||||
if (!currentInterface.isEmpty() && (iface.name() == currentInterface))
|
||||
{
|
||||
m_comboBoxInterface.setCurrentIndex(i);
|
||||
interfaceExists = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// Saved interface does not exist, show it anyway
|
||||
if (!interfaceExists) {
|
||||
if (!interfaceExists)
|
||||
{
|
||||
m_comboBoxInterface.addItem(session->networkInterfaceName(), currentInterface);
|
||||
m_comboBoxInterface.setCurrentIndex(i);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,8 @@ QString AutoExpandableDialog::getText(QWidget *parent, const QString &title, con
|
||||
d.m_ui->textEdit->setInputMethodHints(inputMethodHints);
|
||||
|
||||
d.m_ui->textEdit->selectAll();
|
||||
if (excludeExtension) {
|
||||
if (excludeExtension)
|
||||
{
|
||||
int lastDotIndex = text.lastIndexOf('.');
|
||||
if ((lastDotIndex > 3) && (text.mid(lastDotIndex - 4, 4).toLower() == ".tar"))
|
||||
lastDotIndex -= 4;
|
||||
@@ -86,7 +87,8 @@ void AutoExpandableDialog::showEvent(QShowEvent *e)
|
||||
int wd = m_ui->textEdit->fontMetrics().width(m_ui->textEdit->text()) + 4;
|
||||
#endif
|
||||
|
||||
if (!windowTitle().isEmpty()) {
|
||||
if (!windowTitle().isEmpty())
|
||||
{
|
||||
// not really the font metrics in window title, so we enlarge it a bit,
|
||||
// including the small icon and close button width
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||
@@ -97,7 +99,8 @@ void AutoExpandableDialog::showEvent(QShowEvent *e)
|
||||
wd = std::max(wd, w);
|
||||
}
|
||||
|
||||
if (!m_ui->textLabel->text().isEmpty()) {
|
||||
if (!m_ui->textLabel->text().isEmpty())
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||
int w = m_ui->textLabel->fontMetrics().horizontalAdvance(m_ui->textLabel->text());
|
||||
#else
|
||||
@@ -109,7 +112,8 @@ void AutoExpandableDialog::showEvent(QShowEvent *e)
|
||||
// Now resize the dialog to fit the contents
|
||||
// max width of text from either of: label, title, textedit
|
||||
// If the value is less than dialog default size, default size is used
|
||||
if (wd > width()) {
|
||||
if (wd > width())
|
||||
{
|
||||
QSize size = {width() - m_ui->verticalLayout->sizeHint().width() + wd, height()};
|
||||
Utils::Gui::resize(this, size);
|
||||
}
|
||||
|
||||
@@ -64,18 +64,21 @@ BanListOptionsDialog::~BanListOptionsDialog()
|
||||
|
||||
void BanListOptionsDialog::on_buttonBox_accepted()
|
||||
{
|
||||
if (m_modified) {
|
||||
if (m_modified)
|
||||
{
|
||||
// save to session
|
||||
QStringList IPList;
|
||||
// Operate on the m_sortFilter to grab the strings in sorted order
|
||||
for (int i = 0; i < m_sortFilter->rowCount(); ++i) {
|
||||
for (int i = 0; i < m_sortFilter->rowCount(); ++i)
|
||||
{
|
||||
QModelIndex index = m_sortFilter->index(i, 0);
|
||||
IPList << index.data().toString();
|
||||
}
|
||||
BitTorrent::Session::instance()->setBannedIPs(IPList);
|
||||
QDialog::accept();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QDialog::reject();
|
||||
}
|
||||
}
|
||||
@@ -83,7 +86,8 @@ void BanListOptionsDialog::on_buttonBox_accepted()
|
||||
void BanListOptionsDialog::on_buttonBanIP_clicked()
|
||||
{
|
||||
QString ip = m_ui->txtIP->text();
|
||||
if (!Utils::Net::isValidIP(ip)) {
|
||||
if (!Utils::Net::isValidIP(ip))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Warning"), tr("The entered IP address is invalid."));
|
||||
return;
|
||||
}
|
||||
@@ -91,9 +95,11 @@ void BanListOptionsDialog::on_buttonBanIP_clicked()
|
||||
// QHostAddress::toString() result format follows RFC5952;
|
||||
// thus we avoid duplicate entries pointing to the same address
|
||||
ip = QHostAddress(ip).toString();
|
||||
for (int i = 0; i < m_sortFilter->rowCount(); ++i) {
|
||||
for (int i = 0; i < m_sortFilter->rowCount(); ++i)
|
||||
{
|
||||
QModelIndex index = m_sortFilter->index(i, 0);
|
||||
if (ip == index.data().toString()) {
|
||||
if (ip == index.data().toString())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Warning"), tr("The entered IP is already banned."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ public:
|
||||
~CategoryModelItem()
|
||||
{
|
||||
clear();
|
||||
if (m_parent) {
|
||||
if (m_parent)
|
||||
{
|
||||
m_parent->m_torrentsCount -= m_torrentsCount;
|
||||
const QString uid = m_parent->m_children.key(this);
|
||||
m_parent->m_children.remove(uid);
|
||||
@@ -211,16 +212,19 @@ QVariant CategoryFilterModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
auto item = static_cast<const CategoryModelItem *>(index.internalPointer());
|
||||
|
||||
if ((index.column() == 0) && (role == Qt::DecorationRole)) {
|
||||
if ((index.column() == 0) && (role == Qt::DecorationRole))
|
||||
{
|
||||
return UIThemeManager::instance()->getIcon("inode-directory");
|
||||
}
|
||||
|
||||
if ((index.column() == 0) && (role == Qt::DisplayRole)) {
|
||||
if ((index.column() == 0) && (role == Qt::DisplayRole))
|
||||
{
|
||||
return QString(QStringLiteral("%1 (%2)"))
|
||||
.arg(item->name()).arg(item->torrentsCount());
|
||||
}
|
||||
|
||||
if ((index.column() == 0) && (role == Qt::UserRole)) {
|
||||
if ((index.column() == 0) && (role == Qt::UserRole))
|
||||
{
|
||||
return item->torrentsCount();
|
||||
}
|
||||
|
||||
@@ -306,7 +310,8 @@ void CategoryFilterModel::categoryAdded(const QString &categoryName)
|
||||
{
|
||||
CategoryModelItem *parent = m_rootItem;
|
||||
|
||||
if (m_isSubcategoriesEnabled) {
|
||||
if (m_isSubcategoriesEnabled)
|
||||
{
|
||||
QStringList expanded = BitTorrent::Session::expandCategory(categoryName);
|
||||
if (expanded.count() > 1)
|
||||
parent = findItem(expanded[expanded.count() - 2]);
|
||||
@@ -322,7 +327,8 @@ void CategoryFilterModel::categoryAdded(const QString &categoryName)
|
||||
void CategoryFilterModel::categoryRemoved(const QString &categoryName)
|
||||
{
|
||||
auto item = findItem(categoryName);
|
||||
if (item) {
|
||||
if (item)
|
||||
{
|
||||
QModelIndex i = index(item);
|
||||
beginRemoveRows(i.parent(), i.row(), i.row());
|
||||
delete item;
|
||||
@@ -357,7 +363,8 @@ void CategoryFilterModel::torrentCategoryChanged(BitTorrent::TorrentHandle *cons
|
||||
|
||||
item->decreaseTorrentsCount();
|
||||
i = index(item);
|
||||
while (i.isValid()) {
|
||||
while (i.isValid())
|
||||
{
|
||||
emit dataChanged(i, i);
|
||||
i = parent(i);
|
||||
}
|
||||
@@ -367,7 +374,8 @@ void CategoryFilterModel::torrentCategoryChanged(BitTorrent::TorrentHandle *cons
|
||||
|
||||
item->increaseTorrentsCount();
|
||||
i = index(item);
|
||||
while (i.isValid()) {
|
||||
while (i.isValid())
|
||||
{
|
||||
emit dataChanged(i, i);
|
||||
i = parent(i);
|
||||
}
|
||||
@@ -404,13 +412,17 @@ void CategoryFilterModel::populate()
|
||||
, [](Torrent *torrent) { return torrent->category().isEmpty(); })));
|
||||
|
||||
using Torrent = BitTorrent::TorrentHandle;
|
||||
for (auto i = session->categories().cbegin(); i != session->categories().cend(); ++i) {
|
||||
for (auto i = session->categories().cbegin(); i != session->categories().cend(); ++i)
|
||||
{
|
||||
const QString &category = i.key();
|
||||
if (m_isSubcategoriesEnabled) {
|
||||
if (m_isSubcategoriesEnabled)
|
||||
{
|
||||
CategoryModelItem *parent = m_rootItem;
|
||||
for (const QString &subcat : asConst(session->expandCategory(category))) {
|
||||
for (const QString &subcat : asConst(session->expandCategory(category)))
|
||||
{
|
||||
const QString subcatName = shortName(subcat);
|
||||
if (!parent->hasChild(subcatName)) {
|
||||
if (!parent->hasChild(subcatName))
|
||||
{
|
||||
new CategoryModelItem(
|
||||
parent, subcatName
|
||||
, std::count_if(torrents.cbegin(), torrents.cend()
|
||||
@@ -419,7 +431,8 @@ void CategoryFilterModel::populate()
|
||||
parent = parent->child(subcatName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
new CategoryModelItem(
|
||||
m_rootItem, category
|
||||
, std::count_if(torrents.begin(), torrents.end()
|
||||
@@ -437,7 +450,8 @@ CategoryModelItem *CategoryFilterModel::findItem(const QString &fullName) const
|
||||
return m_rootItem->child(fullName);
|
||||
|
||||
CategoryModelItem *item = m_rootItem;
|
||||
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(fullName))) {
|
||||
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(fullName)))
|
||||
{
|
||||
const QString subcatName = shortName(subcat);
|
||||
if (!item->hasChild(subcatName)) return nullptr;
|
||||
item = item->child(subcatName);
|
||||
|
||||
@@ -44,7 +44,8 @@ namespace
|
||||
QString getCategoryFilter(const CategoryFilterProxyModel *const model, const QModelIndex &index)
|
||||
{
|
||||
QString categoryFilter; // Defaults to All
|
||||
if (index.isValid()) {
|
||||
if (index.isValid())
|
||||
{
|
||||
if (!index.parent().isValid() && (index.row() == 1))
|
||||
categoryFilter = ""; // Uncategorized
|
||||
else if (index.parent().isValid() || (index.row() > 1))
|
||||
@@ -115,8 +116,10 @@ void CategoryFilterWidget::showMenu(const QPoint &)
|
||||
connect(addAct, &QAction::triggered, this, &CategoryFilterWidget::addCategory);
|
||||
|
||||
const auto selectedRows = selectionModel()->selectedRows();
|
||||
if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) {
|
||||
if (BitTorrent::Session::instance()->isSubcategoriesEnabled()) {
|
||||
if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first()))
|
||||
{
|
||||
if (BitTorrent::Session::instance()->isSubcategoriesEnabled())
|
||||
{
|
||||
const QAction *addSubAct = menu->addAction(
|
||||
UIThemeManager::instance()->getIcon("list-add")
|
||||
, tr("Add subcategory..."));
|
||||
@@ -175,7 +178,8 @@ QSize CategoryFilterWidget::sizeHint() const
|
||||
// otherwise widget will not correctly adjust the
|
||||
// size when subcategories are used.
|
||||
const QSize viewportSize {viewportSizeHint()};
|
||||
return {
|
||||
return
|
||||
{
|
||||
viewportSize.width(),
|
||||
viewportSize.height() + static_cast<int>(0.5 * sizeHintForRow(0))
|
||||
};
|
||||
@@ -194,7 +198,8 @@ void CategoryFilterWidget::rowsInserted(const QModelIndex &parent, int start, in
|
||||
|
||||
// Expand all parents if the parent(s) of the node are not expanded.
|
||||
QModelIndex p = parent;
|
||||
while (p.isValid()) {
|
||||
while (p.isValid())
|
||||
{
|
||||
if (!isExpanded(p))
|
||||
expand(p);
|
||||
p = model()->parent(p);
|
||||
@@ -221,7 +226,8 @@ void CategoryFilterWidget::editCategory()
|
||||
void CategoryFilterWidget::removeCategory()
|
||||
{
|
||||
const auto selectedRows = selectionModel()->selectedRows();
|
||||
if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) {
|
||||
if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first()))
|
||||
{
|
||||
BitTorrent::Session::instance()->removeCategory(
|
||||
static_cast<CategoryFilterProxyModel *>(model())->categoryName(selectedRows.first()));
|
||||
updateGeometry();
|
||||
@@ -231,7 +237,8 @@ void CategoryFilterWidget::removeCategory()
|
||||
void CategoryFilterWidget::removeUnusedCategories()
|
||||
{
|
||||
auto session = BitTorrent::Session::instance();
|
||||
for (const QString &category : asConst(session->categories().keys())) {
|
||||
for (const QString &category : asConst(session->categories().keys()))
|
||||
{
|
||||
if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
|
||||
session->removeCategory(category);
|
||||
}
|
||||
|
||||
@@ -43,8 +43,10 @@ QList<QNetworkCookie> CookiesModel::cookies() const
|
||||
|
||||
QVariant CookiesModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal)) {
|
||||
switch (section) {
|
||||
if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal))
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case COL_DOMAIN:
|
||||
return tr("Domain");
|
||||
case COL_PATH:
|
||||
@@ -96,7 +98,8 @@ QVariant CookiesModel::data(const QModelIndex &index, int role) const
|
||||
|| ((role != Qt::DisplayRole) && (role != Qt::EditRole)))
|
||||
return {};
|
||||
|
||||
switch (index.column()) {
|
||||
switch (index.column())
|
||||
{
|
||||
case COL_DOMAIN:
|
||||
return m_cookies[index.row()].domain();
|
||||
case COL_PATH:
|
||||
@@ -116,7 +119,8 @@ bool CookiesModel::setData(const QModelIndex &index, const QVariant &value, int
|
||||
{
|
||||
if (role != Qt::EditRole) return false;
|
||||
|
||||
switch (index.column()) {
|
||||
switch (index.column())
|
||||
{
|
||||
case COL_DOMAIN:
|
||||
m_cookies[index.row()].setDomain(value.toString());
|
||||
break;
|
||||
|
||||
@@ -71,7 +71,8 @@ DownloadFromURLDialog::DownloadFromURLDialog(QWidget *parent)
|
||||
const QVector<QStringRef> clipboardList = clipboardText.splitRef('\n');
|
||||
|
||||
QSet<QString> uniqueURLs;
|
||||
for (QStringRef strRef : clipboardList) {
|
||||
for (QStringRef strRef : clipboardList)
|
||||
{
|
||||
strRef = strRef.trimmed();
|
||||
if (strRef.isEmpty()) continue;
|
||||
|
||||
@@ -96,14 +97,16 @@ void DownloadFromURLDialog::downloadButtonClicked()
|
||||
const QVector<QStringRef> urls = plainText.splitRef('\n');
|
||||
|
||||
QSet<QString> uniqueURLs;
|
||||
for (QStringRef url : urls) {
|
||||
for (QStringRef url : urls)
|
||||
{
|
||||
url = url.trimmed();
|
||||
if (url.isEmpty()) continue;
|
||||
|
||||
uniqueURLs << url.toString();
|
||||
}
|
||||
|
||||
if (uniqueURLs.isEmpty()) {
|
||||
if (uniqueURLs.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("No URL entered"), tr("Please type at least one URL."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,8 @@ void ExecutionLogWidget::displayContextMenu(const QPoint &pos, const LogListView
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
// only show copy action if any of the row is selected
|
||||
if (view->currentIndex().isValid()) {
|
||||
if (view->currentIndex().isValid())
|
||||
{
|
||||
const QAction *copyAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-copy"), tr("Copy"));
|
||||
connect(copyAct, &QAction::triggered, view, &LogListView::copySelection);
|
||||
}
|
||||
|
||||
@@ -115,7 +115,8 @@ void FileSystemPathEdit::FileSystemPathEditPrivate::browseActionTriggered()
|
||||
QString directory = q->currentDirectory().isEmpty() ? QDir::homePath() : q->currentDirectory();
|
||||
|
||||
QString selectedPath;
|
||||
switch (m_mode) {
|
||||
switch (m_mode)
|
||||
{
|
||||
case FileSystemPathEdit::Mode::FileOpen:
|
||||
selectedPath = QFileDialog::getOpenFileName(q, dialogCaptionOrDefault(), directory, filter);
|
||||
break;
|
||||
@@ -139,7 +140,8 @@ QString FileSystemPathEdit::FileSystemPathEditPrivate::dialogCaptionOrDefault()
|
||||
if (!m_dialogCaption.isEmpty())
|
||||
return m_dialogCaption;
|
||||
|
||||
switch (m_mode) {
|
||||
switch (m_mode)
|
||||
{
|
||||
case FileSystemPathEdit::Mode::FileOpen:
|
||||
case FileSystemPathEdit::Mode::FileSave:
|
||||
return defaultDialogCaptionForFile.tr();
|
||||
@@ -155,7 +157,8 @@ void FileSystemPathEdit::FileSystemPathEditPrivate::modeChanged()
|
||||
{
|
||||
QStyle::StandardPixmap pixmap = QStyle::SP_DialogOpenButton;
|
||||
bool showDirsOnly = false;
|
||||
switch (m_mode) {
|
||||
switch (m_mode)
|
||||
{
|
||||
case FileSystemPathEdit::Mode::FileOpen:
|
||||
case FileSystemPathEdit::Mode::FileSave:
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -229,17 +232,21 @@ void FileSystemPathEdit::setFileNameFilter(const QString &val)
|
||||
// extract file masks
|
||||
const int openBracePos = val.indexOf(QLatin1Char('('), 0);
|
||||
const int closeBracePos = val.indexOf(QLatin1Char(')'), openBracePos + 1);
|
||||
if ((openBracePos > 0) && (closeBracePos > 0) && (closeBracePos > openBracePos + 2)) {
|
||||
if ((openBracePos > 0) && (closeBracePos > 0) && (closeBracePos > openBracePos + 2))
|
||||
{
|
||||
QString filterString = val.mid(openBracePos + 1, closeBracePos - openBracePos - 1);
|
||||
if (filterString == QLatin1String("*")) { // no filters
|
||||
if (filterString == QLatin1String("*"))
|
||||
{ // no filters
|
||||
d->m_editor->setFilenameFilters({});
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QStringList filters = filterString.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
d->m_editor->setFilenameFilters(filters);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
d->m_editor->setFilenameFilters({});
|
||||
}
|
||||
#endif
|
||||
@@ -261,7 +268,8 @@ void FileSystemPathEdit::onPathEdited()
|
||||
{
|
||||
Q_D(FileSystemPathEdit);
|
||||
QString newPath = selectedPath();
|
||||
if (newPath != d->m_lastSignaledPath) {
|
||||
if (newPath != d->m_lastSignaledPath)
|
||||
{
|
||||
emit selectedPathChanged(newPath);
|
||||
d->m_lastSignaledPath = newPath;
|
||||
d->m_editor->widget()->setToolTip(editWidgetText());
|
||||
|
||||
@@ -115,7 +115,8 @@ QValidator::State Private::FileSystemPathValidator::validate(QString &input, int
|
||||
// components.size() - 1 because when path ends with QDir::separator(), we will not see the last
|
||||
// character in the components array, yet everything past the one before the last delimiter
|
||||
// belongs to the last component
|
||||
for (; (componentWithCursorIndex < components.size() - 1) && (pathLength < pos); ++componentWithCursorIndex) {
|
||||
for (; (componentWithCursorIndex < components.size() - 1) && (pathLength < pos); ++componentWithCursorIndex)
|
||||
{
|
||||
pathLength = components[componentWithCursorIndex].position() + components[componentWithCursorIndex].size();
|
||||
}
|
||||
|
||||
@@ -140,12 +141,14 @@ QValidator::State Private::FileSystemPathValidator::validate(const QString &path
|
||||
if (pathComponents.empty())
|
||||
return strict ? QValidator::Invalid : QValidator::Intermediate;
|
||||
|
||||
for (int i = firstComponentToTest; i < lastComponentToTest; ++i) {
|
||||
for (int i = firstComponentToTest; i < lastComponentToTest; ++i)
|
||||
{
|
||||
if (pathComponents[i].isEmpty()) continue;
|
||||
|
||||
QStringRef componentPath(&path, 0, pathComponents[i].position() + pathComponents[i].size());
|
||||
m_lastTestResult = testPath(componentPath, false);
|
||||
if (m_lastTestResult != TestResult::OK) {
|
||||
if (m_lastTestResult != TestResult::OK)
|
||||
{
|
||||
m_lastTestedPath = componentPath.toString();
|
||||
return strict ? QValidator::Invalid : QValidator::Intermediate;
|
||||
}
|
||||
@@ -155,7 +158,8 @@ QValidator::State Private::FileSystemPathValidator::validate(const QString &path
|
||||
QStringRef componentPath(&path, 0, pathComponents[lastComponentToTest].position()
|
||||
+ pathComponents[lastComponentToTest].size());
|
||||
m_lastTestResult = testPath(componentPath, finalPath);
|
||||
if (m_lastTestResult != TestResult::OK) {
|
||||
if (m_lastTestResult != TestResult::OK)
|
||||
{
|
||||
m_lastTestedPath = componentPath.toString();
|
||||
return strict ? QValidator::Invalid : QValidator::Intermediate;
|
||||
}
|
||||
@@ -172,7 +176,8 @@ Private::FileSystemPathValidator::testPath(const QStringRef &path, bool pathIsCo
|
||||
if ((!pathIsComplete || m_directoriesOnly) && !fi.isDir())
|
||||
return TestResult::NotADir;
|
||||
|
||||
if (pathIsComplete) {
|
||||
if (pathIsComplete)
|
||||
{
|
||||
if (!m_directoriesOnly && fi.isDir())
|
||||
return TestResult::NotAFile;
|
||||
|
||||
@@ -249,27 +254,33 @@ QWidget *Private::FileLineEdit::widget()
|
||||
void Private::FileLineEdit::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
QLineEdit::keyPressEvent(e);
|
||||
if ((e->key() == Qt::Key_Space) && (e->modifiers() == Qt::CTRL)) {
|
||||
if ((e->key() == Qt::Key_Space) && (e->modifiers() == Qt::CTRL))
|
||||
{
|
||||
m_completerModel->setRootPath(QFileInfo(text()).absoluteDir().absolutePath());
|
||||
showCompletionPopup();
|
||||
}
|
||||
|
||||
auto *validator = qobject_cast<const FileSystemPathValidator *>(this->validator());
|
||||
if (validator) {
|
||||
if (validator)
|
||||
{
|
||||
FileSystemPathValidator::TestResult lastTestResult = validator->lastTestResult();
|
||||
QValidator::State lastState = validator->lastValidationState();
|
||||
if (lastTestResult == FileSystemPathValidator::TestResult::OK) {
|
||||
if (lastTestResult == FileSystemPathValidator::TestResult::OK)
|
||||
{
|
||||
delete m_warningAction;
|
||||
m_warningAction = nullptr;
|
||||
}
|
||||
else {
|
||||
if (!m_warningAction) {
|
||||
else
|
||||
{
|
||||
if (!m_warningAction)
|
||||
{
|
||||
m_warningAction = new QAction(this);
|
||||
addAction(m_warningAction, QLineEdit::TrailingPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_warningAction) {
|
||||
if (m_warningAction)
|
||||
{
|
||||
if (lastState == QValidator::Invalid)
|
||||
m_warningAction->setIcon(style()->standardIcon(QStyle::SP_MessageBoxCritical));
|
||||
else if (lastState == QValidator::Intermediate)
|
||||
@@ -284,7 +295,8 @@ void Private::FileLineEdit::contextMenuEvent(QContextMenuEvent *event)
|
||||
QMenu *menu = createStandardContextMenu();
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (m_browseAction) {
|
||||
if (m_browseAction)
|
||||
{
|
||||
menu->addSeparator();
|
||||
menu->addAction(m_browseAction);
|
||||
}
|
||||
@@ -301,7 +313,8 @@ void Private::FileLineEdit::showCompletionPopup()
|
||||
QString Private::FileLineEdit::warningText(FileSystemPathValidator::TestResult r)
|
||||
{
|
||||
using TestResult = FileSystemPathValidator::TestResult;
|
||||
switch (r) {
|
||||
switch (r)
|
||||
{
|
||||
case TestResult::DoesNotExist:
|
||||
return tr("'%1' does not exist");
|
||||
case TestResult::NotADir:
|
||||
|
||||
@@ -68,7 +68,8 @@ IPSubnetWhitelistOptionsDialog::~IPSubnetWhitelistOptionsDialog()
|
||||
|
||||
void IPSubnetWhitelistOptionsDialog::on_buttonBox_accepted()
|
||||
{
|
||||
if (m_modified) {
|
||||
if (m_modified)
|
||||
{
|
||||
// save to session
|
||||
QStringList subnets;
|
||||
// Operate on the m_sortFilter to grab the strings in sorted order
|
||||
@@ -77,7 +78,8 @@ void IPSubnetWhitelistOptionsDialog::on_buttonBox_accepted()
|
||||
Preferences::instance()->setWebUiAuthSubnetWhitelist(subnets);
|
||||
QDialog::accept();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QDialog::reject();
|
||||
}
|
||||
}
|
||||
@@ -86,7 +88,8 @@ void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked()
|
||||
{
|
||||
bool ok = false;
|
||||
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(m_ui->txtIPSubnet->text(), &ok);
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("The entered subnet is invalid."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ void LineEdit::resizeEvent(QResizeEvent *e)
|
||||
|
||||
void LineEdit::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if ((event->modifiers() == Qt::NoModifier) && (event->key() == Qt::Key_Escape)) {
|
||||
if ((event->modifiers() == Qt::NoModifier) && (event->key() == Qt::Key_Escape))
|
||||
{
|
||||
clear();
|
||||
}
|
||||
QLineEdit::keyPressEvent(event);
|
||||
|
||||
@@ -97,7 +97,8 @@ QVariant BaseLogModel::data(const QModelIndex &index, const int role) const
|
||||
return {};
|
||||
|
||||
const Message &message = m_messages[messageIndex];
|
||||
switch (role) {
|
||||
switch (role)
|
||||
{
|
||||
case TimeRole:
|
||||
return message.time();
|
||||
case MessageRole:
|
||||
@@ -117,7 +118,8 @@ void BaseLogModel::addNewMessage(const BaseLogModel::Message &message)
|
||||
{
|
||||
// if row is inserted on filled up buffer, the size will not change
|
||||
// but because of calling of beginInsertRows function we'll have ghost rows.
|
||||
if (m_messages.size() == MAX_VISIBLE_MESSAGES) {
|
||||
if (m_messages.size() == MAX_VISIBLE_MESSAGES)
|
||||
{
|
||||
const int lastMessage = m_messages.size() - 1;
|
||||
beginRemoveRows(QModelIndex(), lastMessage, lastMessage);
|
||||
m_messages.pop_back();
|
||||
@@ -138,7 +140,8 @@ void BaseLogModel::reset()
|
||||
|
||||
LogMessageModel::LogMessageModel(QObject *parent)
|
||||
: BaseLogModel(parent)
|
||||
, m_foregroundForMessageTypes {
|
||||
, m_foregroundForMessageTypes
|
||||
{
|
||||
{Log::NORMAL, UIThemeManager::instance()->getColor(QLatin1String("Log.Normal"), QApplication::palette().color(QPalette::WindowText))},
|
||||
{Log::INFO, UIThemeManager::instance()->getColor(QLatin1String("Log.Info"), Qt::blue)},
|
||||
{Log::WARNING, UIThemeManager::instance()->getColor(QLatin1String("Log.Warning"), QColor {255, 165, 0})}, // orange
|
||||
|
||||
@@ -281,8 +281,10 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
m_queueSeparatorMenu = m_ui->menuEdit->insertSeparator(m_ui->actionTopQueuePos);
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
for (QAction *action : asConst(m_ui->toolBar->actions())) {
|
||||
if (action->isSeparator()) {
|
||||
for (QAction *action : asConst(m_ui->toolBar->actions()))
|
||||
{
|
||||
if (action->isSeparator())
|
||||
{
|
||||
QWidget *spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
spacer->setMinimumWidth(16);
|
||||
@@ -406,30 +408,38 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
readSettings();
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
if (m_systrayIcon) {
|
||||
if (!(pref->startMinimized() || m_uiLocked)) {
|
||||
if (m_systrayIcon)
|
||||
{
|
||||
if (!(pref->startMinimized() || m_uiLocked))
|
||||
{
|
||||
show();
|
||||
activateWindow();
|
||||
raise();
|
||||
}
|
||||
else if (pref->startMinimized()) {
|
||||
else if (pref->startMinimized())
|
||||
{
|
||||
showMinimized();
|
||||
if (pref->minimizeToTray()) {
|
||||
if (pref->minimizeToTray())
|
||||
{
|
||||
hide();
|
||||
if (!pref->minimizeToTrayNotified()) {
|
||||
if (!pref->minimizeToTrayNotified())
|
||||
{
|
||||
showNotificationBaloon(tr("qBittorrent is minimized to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
|
||||
pref->setMinimizeToTrayNotified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
#endif
|
||||
// Make sure the Window is visible if we don't have a tray icon
|
||||
if (pref->startMinimized()) {
|
||||
if (pref->startMinimized())
|
||||
{
|
||||
showMinimized();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
show();
|
||||
activateWindow();
|
||||
raise();
|
||||
@@ -456,14 +466,17 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
qDebug("GUI Built");
|
||||
#ifdef Q_OS_WIN
|
||||
if (!pref->neverCheckFileAssoc() && (!Preferences::isTorrentFileAssocSet() || !Preferences::isMagnetLinkAssocSet())) {
|
||||
if (!pref->neverCheckFileAssoc() && (!Preferences::isTorrentFileAssocSet() || !Preferences::isMagnetLinkAssocSet()))
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Torrent file association"),
|
||||
tr("qBittorrent is not the default application to open torrent files or Magnet links.\nDo you want to associate qBittorrent to torrent files and Magnet links?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes)
|
||||
{
|
||||
Preferences::setTorrentFileAssoc(true);
|
||||
Preferences::setMagnetLinkAssoc(true);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
pref->setNeverCheckFileAssoc();
|
||||
}
|
||||
}
|
||||
@@ -567,7 +580,8 @@ void MainWindow::addToolbarContextMenu()
|
||||
const auto buttonStyle = static_cast<Qt::ToolButtonStyle>(pref->getToolbarTextPosition());
|
||||
if ((buttonStyle >= Qt::ToolButtonIconOnly) && (buttonStyle <= Qt::ToolButtonFollowStyle))
|
||||
m_ui->toolBar->setToolButtonStyle(buttonStyle);
|
||||
switch (buttonStyle) {
|
||||
switch (buttonStyle)
|
||||
{
|
||||
case Qt::ToolButtonIconOnly:
|
||||
iconsOnly->setChecked(true);
|
||||
break;
|
||||
@@ -635,7 +649,8 @@ bool MainWindow::defineUILockPassword()
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (newPassword.size() < 3) {
|
||||
if (newPassword.size() < 3)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid password"), tr("The password should contain at least 3 characters"));
|
||||
return false;
|
||||
}
|
||||
@@ -657,7 +672,8 @@ void MainWindow::on_actionLock_triggered()
|
||||
Preferences *const pref = Preferences::instance();
|
||||
|
||||
// Check if there is a password
|
||||
if (pref->getUILockPassword().isEmpty()) {
|
||||
if (pref->getUILockPassword().isEmpty())
|
||||
{
|
||||
if (!defineUILockPassword())
|
||||
return;
|
||||
}
|
||||
@@ -676,9 +692,11 @@ void MainWindow::handleRSSUnreadCountUpdated(int count)
|
||||
|
||||
void MainWindow::displayRSSTab(bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
if (enable)
|
||||
{
|
||||
// RSS tab
|
||||
if (!m_rssWidget) {
|
||||
if (!m_rssWidget)
|
||||
{
|
||||
m_rssWidget = new RSSWidget(m_tabs);
|
||||
connect(m_rssWidget.data(), &RSSWidget::unreadCountUpdated, this, &MainWindow::handleRSSUnreadCountUpdated);
|
||||
#ifdef Q_OS_MACOS
|
||||
@@ -689,7 +707,8 @@ void MainWindow::displayRSSTab(bool enable)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
delete m_rssWidget;
|
||||
}
|
||||
}
|
||||
@@ -714,9 +733,11 @@ void MainWindow::showFilterContextMenu(const QPoint &)
|
||||
void MainWindow::displaySearchTab(bool enable)
|
||||
{
|
||||
Preferences::instance()->setSearchEnabled(enable);
|
||||
if (enable) {
|
||||
if (enable)
|
||||
{
|
||||
// RSS tab
|
||||
if (!m_searchWidget) {
|
||||
if (!m_searchWidget)
|
||||
{
|
||||
m_searchWidget = new SearchWidget(this);
|
||||
m_tabs->insertTab(1, m_searchWidget,
|
||||
#ifndef Q_OS_MACOS
|
||||
@@ -725,7 +746,8 @@ void MainWindow::displaySearchTab(bool enable)
|
||||
tr("Search"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
delete m_searchWidget;
|
||||
}
|
||||
}
|
||||
@@ -751,7 +773,8 @@ void MainWindow::tabChanged(int newTab)
|
||||
Q_UNUSED(newTab);
|
||||
// We cannot rely on the index newTab
|
||||
// because the tab order is undetermined now
|
||||
if (m_tabs->currentWidget() == m_splitter) {
|
||||
if (m_tabs->currentWidget() == m_splitter)
|
||||
{
|
||||
qDebug("Changed tab to transfer list, refreshing the list");
|
||||
m_propertiesWidget->loadDynamicData();
|
||||
m_searchFilterAction->setVisible(true);
|
||||
@@ -759,7 +782,8 @@ void MainWindow::tabChanged(int newTab)
|
||||
}
|
||||
m_searchFilterAction->setVisible(false);
|
||||
|
||||
if (m_tabs->currentWidget() == m_searchWidget) {
|
||||
if (m_tabs->currentWidget() == m_searchWidget)
|
||||
{
|
||||
qDebug("Changed tab to search engine, giving focus to search input");
|
||||
m_searchWidget->giveFocusToSearchInput();
|
||||
}
|
||||
@@ -815,8 +839,10 @@ void MainWindow::readSettings()
|
||||
|
||||
void MainWindow::balloonClicked()
|
||||
{
|
||||
if (isHidden()) {
|
||||
if (m_uiLocked) {
|
||||
if (isHidden())
|
||||
{
|
||||
if (m_uiLocked)
|
||||
{
|
||||
// Ask for UI lock password
|
||||
if (!unlockUI())
|
||||
return;
|
||||
@@ -916,7 +942,8 @@ void MainWindow::displayTransferTab() const
|
||||
|
||||
void MainWindow::displaySearchTab()
|
||||
{
|
||||
if (!m_searchWidget) {
|
||||
if (!m_searchWidget)
|
||||
{
|
||||
m_ui->actionSearchWidget->setChecked(true);
|
||||
displaySearchTab(true);
|
||||
}
|
||||
@@ -926,7 +953,8 @@ void MainWindow::displaySearchTab()
|
||||
|
||||
void MainWindow::displayRSSTab()
|
||||
{
|
||||
if (!m_rssWidget) {
|
||||
if (!m_rssWidget)
|
||||
{
|
||||
m_ui->actionRSSReader->setChecked(true);
|
||||
displayRSSTab(true);
|
||||
}
|
||||
@@ -936,7 +964,8 @@ void MainWindow::displayRSSTab()
|
||||
|
||||
void MainWindow::displayExecutionLogTab()
|
||||
{
|
||||
if (!m_executionLog) {
|
||||
if (!m_executionLog)
|
||||
{
|
||||
m_ui->actionExecutionLogs->setChecked(true);
|
||||
on_actionExecutionLogs_triggered(true);
|
||||
}
|
||||
@@ -988,7 +1017,8 @@ void MainWindow::on_actionSetGlobalUploadLimit_triggered()
|
||||
const long newLimit = SpeedLimitDialog::askSpeedLimit(
|
||||
this, &ok, tr("Global Upload Speed Limit"), session->uploadSpeedLimit());
|
||||
|
||||
if (ok) {
|
||||
if (ok)
|
||||
{
|
||||
qDebug("Setting global upload rate limit to %.1fKb/s", newLimit / 1024.);
|
||||
session->setUploadSpeedLimit(newLimit);
|
||||
}
|
||||
@@ -1003,7 +1033,8 @@ void MainWindow::on_actionSetGlobalDownloadLimit_triggered()
|
||||
const long newLimit = SpeedLimitDialog::askSpeedLimit(
|
||||
this, &ok, tr("Global Download Speed Limit"), session->downloadSpeedLimit());
|
||||
|
||||
if (ok) {
|
||||
if (ok)
|
||||
{
|
||||
qDebug("Setting global download rate limit to %.1fKb/s", newLimit / 1024.);
|
||||
session->setDownloadSpeedLimit(newLimit);
|
||||
}
|
||||
@@ -1059,7 +1090,8 @@ bool MainWindow::unlockUI()
|
||||
Preferences *const pref = Preferences::instance();
|
||||
|
||||
const QByteArray secret = pref->getUILockPassword();
|
||||
if (!Utils::Password::PBKDF2::verify(secret, password)) {
|
||||
if (!Utils::Password::PBKDF2::verify(secret, password))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid password"), tr("The password is invalid"));
|
||||
return false;
|
||||
}
|
||||
@@ -1085,9 +1117,11 @@ void MainWindow::notifyOfUpdate(const QString &)
|
||||
// Toggle Main window visibility
|
||||
void MainWindow::toggleVisibility(const QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
switch (reason) {
|
||||
switch (reason)
|
||||
{
|
||||
case QSystemTrayIcon::Trigger:
|
||||
if (isHidden()) {
|
||||
if (isHidden())
|
||||
{
|
||||
if (m_uiLocked && !unlockUI()) // Ask for UI lock password
|
||||
return;
|
||||
|
||||
@@ -1099,7 +1133,8 @@ void MainWindow::toggleVisibility(const QSystemTrayIcon::ActivationReason reason
|
||||
raise();
|
||||
activateWindow();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
hide();
|
||||
}
|
||||
break;
|
||||
@@ -1138,7 +1173,8 @@ void MainWindow::showEvent(QShowEvent *e)
|
||||
e->accept();
|
||||
|
||||
// Make sure the window is initially centered
|
||||
if (!m_posInitialized) {
|
||||
if (!m_posInitialized)
|
||||
{
|
||||
move(Utils::Gui::screenCenter(this));
|
||||
m_posInitialized = true;
|
||||
}
|
||||
@@ -1146,14 +1182,17 @@ void MainWindow::showEvent(QShowEvent *e)
|
||||
|
||||
void MainWindow::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->matches(QKeySequence::Paste)) {
|
||||
if (event->matches(QKeySequence::Paste))
|
||||
{
|
||||
const QMimeData *mimeData {QGuiApplication::clipboard()->mimeData()};
|
||||
|
||||
if (mimeData->hasText()) {
|
||||
if (mimeData->hasText())
|
||||
{
|
||||
const bool useTorrentAdditionDialog {AddNewTorrentDialog::isEnabled()};
|
||||
const QStringList lines {mimeData->text().split('\n', QString::SkipEmptyParts)};
|
||||
|
||||
for (QString line : lines) {
|
||||
for (QString line : lines)
|
||||
{
|
||||
line = line.trimmed();
|
||||
|
||||
if (!isTorrentLink(line))
|
||||
@@ -1177,17 +1216,20 @@ void MainWindow::closeEvent(QCloseEvent *e)
|
||||
{
|
||||
Preferences *const pref = Preferences::instance();
|
||||
#ifdef Q_OS_MACOS
|
||||
if (!m_forceExit) {
|
||||
if (!m_forceExit)
|
||||
{
|
||||
hide();
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
const bool goToSystrayOnExit = pref->closeToTray();
|
||||
if (!m_forceExit && m_systrayIcon && goToSystrayOnExit && !this->isHidden()) {
|
||||
if (!m_forceExit && m_systrayIcon && goToSystrayOnExit && !this->isHidden())
|
||||
{
|
||||
e->ignore();
|
||||
QTimer::singleShot(0, this, &QWidget::hide);
|
||||
if (!pref->closeToTrayNotified()) {
|
||||
if (!pref->closeToTrayNotified())
|
||||
{
|
||||
showNotificationBaloon(tr("qBittorrent is closed to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
|
||||
pref->setCloseToTrayNotified(true);
|
||||
}
|
||||
@@ -1195,8 +1237,10 @@ void MainWindow::closeEvent(QCloseEvent *e)
|
||||
}
|
||||
#endif // Q_OS_MACOS
|
||||
|
||||
if (pref->confirmOnExit() && BitTorrent::Session::instance()->hasActiveTorrents()) {
|
||||
if (e->spontaneous() || m_forceExit) {
|
||||
if (pref->confirmOnExit() && BitTorrent::Session::instance()->hasActiveTorrents())
|
||||
{
|
||||
if (e->spontaneous() || m_forceExit)
|
||||
{
|
||||
if (!isVisible())
|
||||
show();
|
||||
QMessageBox confirmBox(QMessageBox::Question, tr("Exiting qBittorrent"),
|
||||
@@ -1208,7 +1252,8 @@ void MainWindow::closeEvent(QCloseEvent *e)
|
||||
QPushButton *alwaysBtn = confirmBox.addButton(tr("&Always Yes"), QMessageBox::YesRole);
|
||||
confirmBox.setDefaultButton(noBtn);
|
||||
confirmBox.exec();
|
||||
if (!confirmBox.clickedButton() || (confirmBox.clickedButton() == noBtn)) {
|
||||
if (!confirmBox.clickedButton() || (confirmBox.clickedButton() == noBtn))
|
||||
{
|
||||
// Cancel exit
|
||||
e->ignore();
|
||||
m_forceExit = false;
|
||||
@@ -1242,7 +1287,8 @@ void MainWindow::on_actionCreateTorrent_triggered()
|
||||
|
||||
void MainWindow::createTorrentTriggered(const QString &path)
|
||||
{
|
||||
if (m_createTorrentDlg) {
|
||||
if (m_createTorrentDlg)
|
||||
{
|
||||
m_createTorrentDlg->updateInputPath(path);
|
||||
m_createTorrentDlg->activateWindow();
|
||||
}
|
||||
@@ -1253,29 +1299,37 @@ void MainWindow::createTorrentTriggered(const QString &path)
|
||||
bool MainWindow::event(QEvent *e)
|
||||
{
|
||||
#ifndef Q_OS_MACOS
|
||||
switch (e->type()) {
|
||||
case QEvent::WindowStateChange: {
|
||||
switch (e->type())
|
||||
{
|
||||
case QEvent::WindowStateChange:
|
||||
{
|
||||
qDebug("Window change event");
|
||||
// Now check to see if the window is minimised
|
||||
if (isMinimized()) {
|
||||
if (isMinimized())
|
||||
{
|
||||
qDebug("minimisation");
|
||||
Preferences *const pref = Preferences::instance();
|
||||
if (m_systrayIcon && pref->minimizeToTray()) {
|
||||
if (m_systrayIcon && pref->minimizeToTray())
|
||||
{
|
||||
qDebug() << "Has active window:" << (qApp->activeWindow() != nullptr);
|
||||
// Check if there is a modal window
|
||||
bool hasModalWindow = false;
|
||||
for (QWidget *widget : asConst(QApplication::allWidgets())) {
|
||||
if (widget->isModal()) {
|
||||
for (QWidget *widget : asConst(QApplication::allWidgets()))
|
||||
{
|
||||
if (widget->isModal())
|
||||
{
|
||||
hasModalWindow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Iconify if there is no modal window
|
||||
if (!hasModalWindow) {
|
||||
if (!hasModalWindow)
|
||||
{
|
||||
qDebug("Minimize to Tray enabled, hiding!");
|
||||
e->ignore();
|
||||
QTimer::singleShot(0, this, &QWidget::hide);
|
||||
if (!pref->minimizeToTrayNotified()) {
|
||||
if (!pref->minimizeToTrayNotified())
|
||||
{
|
||||
showNotificationBaloon(tr("qBittorrent is minimized to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
|
||||
pref->setMinimizeToTrayNotified(true);
|
||||
}
|
||||
@@ -1285,7 +1339,8 @@ bool MainWindow::event(QEvent *e)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::ToolBarChange: {
|
||||
case QEvent::ToolBarChange:
|
||||
{
|
||||
qDebug("MAC: Received a toolbar change event!");
|
||||
bool ret = QMainWindow::event(e);
|
||||
|
||||
@@ -1309,8 +1364,10 @@ void MainWindow::dropEvent(QDropEvent *event)
|
||||
|
||||
// remove scheme
|
||||
QStringList files;
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
for (const QUrl &url : asConst(event->mimeData()->urls())) {
|
||||
if (event->mimeData()->hasUrls())
|
||||
{
|
||||
for (const QUrl &url : asConst(event->mimeData()->urls()))
|
||||
{
|
||||
if (url.isEmpty())
|
||||
continue;
|
||||
|
||||
@@ -1319,13 +1376,15 @@ void MainWindow::dropEvent(QDropEvent *event)
|
||||
: url.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
files = event->mimeData()->text().split('\n');
|
||||
}
|
||||
|
||||
// differentiate ".torrent" files/links & magnet links from others
|
||||
QStringList torrentFiles, otherFiles;
|
||||
for (const QString &file : asConst(files)) {
|
||||
for (const QString &file : asConst(files))
|
||||
{
|
||||
if (isTorrentLink(file))
|
||||
torrentFiles << file;
|
||||
else
|
||||
@@ -1334,7 +1393,8 @@ void MainWindow::dropEvent(QDropEvent *event)
|
||||
|
||||
// Download torrents
|
||||
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
|
||||
for (const QString &file : asConst(torrentFiles)) {
|
||||
for (const QString &file : asConst(torrentFiles))
|
||||
{
|
||||
if (useTorrentAdditionDialog)
|
||||
AddNewTorrentDialog::show(file, this);
|
||||
else
|
||||
@@ -1343,7 +1403,8 @@ void MainWindow::dropEvent(QDropEvent *event)
|
||||
if (!torrentFiles.isEmpty()) return;
|
||||
|
||||
// Create torrent
|
||||
for (const QString &file : asConst(otherFiles)) {
|
||||
for (const QString &file : asConst(otherFiles))
|
||||
{
|
||||
createTorrentTriggered(file);
|
||||
|
||||
// currently only handle the first entry
|
||||
@@ -1370,7 +1431,8 @@ static bool dockClickHandler(id self, SEL cmd, ...)
|
||||
Q_UNUSED(self)
|
||||
Q_UNUSED(cmd)
|
||||
|
||||
if (dockMainWindowHandle && !dockMainWindowHandle->isVisible()) {
|
||||
if (dockMainWindowHandle && !dockMainWindowHandle->isVisible())
|
||||
{
|
||||
dockMainWindowHandle->activate();
|
||||
}
|
||||
|
||||
@@ -1407,7 +1469,8 @@ void MainWindow::on_actionOpen_triggered()
|
||||
|
||||
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
|
||||
|
||||
for (const QString &file : pathsList) {
|
||||
for (const QString &file : pathsList)
|
||||
{
|
||||
if (useTorrentAdditionDialog)
|
||||
AddNewTorrentDialog::show(file, this);
|
||||
else
|
||||
@@ -1422,7 +1485,8 @@ void MainWindow::on_actionOpen_triggered()
|
||||
|
||||
void MainWindow::activate()
|
||||
{
|
||||
if (!m_uiLocked || unlockUI()) {
|
||||
if (!m_uiLocked || unlockUI())
|
||||
{
|
||||
show();
|
||||
activateWindow();
|
||||
raise();
|
||||
@@ -1436,11 +1500,13 @@ void MainWindow::optionsSaved()
|
||||
|
||||
void MainWindow::showStatusBar(bool show)
|
||||
{
|
||||
if (!show) {
|
||||
if (!show)
|
||||
{
|
||||
// Remove status bar
|
||||
setStatusBar(nullptr);
|
||||
}
|
||||
else if (!m_statusBar) {
|
||||
else if (!m_statusBar)
|
||||
{
|
||||
// Create status bar
|
||||
m_statusBar = new StatusBar;
|
||||
connect(m_statusBar.data(), &StatusBar::connectionButtonClicked, this, &MainWindow::showConnectionSettings);
|
||||
@@ -1458,26 +1524,33 @@ void MainWindow::loadPreferences(const bool configureSession)
|
||||
#else
|
||||
const bool newSystrayIntegration = pref->systrayIntegration();
|
||||
m_ui->actionLock->setVisible(newSystrayIntegration);
|
||||
if (newSystrayIntegration != (m_systrayIcon != nullptr)) {
|
||||
if (newSystrayIntegration) {
|
||||
if (newSystrayIntegration != (m_systrayIcon != nullptr))
|
||||
{
|
||||
if (newSystrayIntegration)
|
||||
{
|
||||
// create the trayicon
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
if (!configureSession) { // Program startup
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable())
|
||||
{
|
||||
if (!configureSession)
|
||||
{ // Program startup
|
||||
m_systrayCreator = new QTimer(this);
|
||||
connect(m_systrayCreator.data(), &QTimer::timeout, this, &MainWindow::createSystrayDelayed);
|
||||
m_systrayCreator->setSingleShot(true);
|
||||
m_systrayCreator->start(2000);
|
||||
qDebug("Info: System tray is unavailable, trying again later.");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
qDebug("Warning: System tray is unavailable.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
createTrayIcon();
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Destroy trayicon
|
||||
delete m_systrayIcon;
|
||||
delete m_trayIconMenu;
|
||||
@@ -1488,10 +1561,12 @@ void MainWindow::loadPreferences(const bool configureSession)
|
||||
m_systrayIcon->setIcon(getSystrayIcon());
|
||||
#endif
|
||||
// General
|
||||
if (pref->isToolbarDisplayed()) {
|
||||
if (pref->isToolbarDisplayed())
|
||||
{
|
||||
m_ui->toolBar->setVisible(true);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Clear search filter before hiding the top toolbar
|
||||
m_searchFilter->clear();
|
||||
m_ui->toolBar->setVisible(false);
|
||||
@@ -1499,13 +1574,16 @@ void MainWindow::loadPreferences(const bool configureSession)
|
||||
|
||||
showStatusBar(pref->isStatusbarDisplayed());
|
||||
|
||||
if (pref->preventFromSuspendWhenDownloading() || pref->preventFromSuspendWhenSeeding()) {
|
||||
if (!m_preventTimer->isActive()) {
|
||||
if (pref->preventFromSuspendWhenDownloading() || pref->preventFromSuspendWhenSeeding())
|
||||
{
|
||||
if (!m_preventTimer->isActive())
|
||||
{
|
||||
updatePowerManagementState();
|
||||
m_preventTimer->start(PREVENT_SUSPEND_INTERVAL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_preventTimer->stop();
|
||||
m_pwr->setActivityState(false);
|
||||
}
|
||||
@@ -1516,8 +1594,10 @@ void MainWindow::loadPreferences(const bool configureSession)
|
||||
m_propertiesWidget->getPeerList()->setAlternatingRowColors(pref->useAlternatingRowColors());
|
||||
|
||||
// Queueing System
|
||||
if (BitTorrent::Session::instance()->isQueueingSystemEnabled()) {
|
||||
if (!m_ui->actionDecreaseQueuePos->isVisible()) {
|
||||
if (BitTorrent::Session::instance()->isQueueingSystemEnabled())
|
||||
{
|
||||
if (!m_ui->actionDecreaseQueuePos->isVisible())
|
||||
{
|
||||
m_transferListWidget->hideQueuePosColumn(false);
|
||||
m_ui->actionDecreaseQueuePos->setVisible(true);
|
||||
m_ui->actionIncreaseQueuePos->setVisible(true);
|
||||
@@ -1529,8 +1609,10 @@ void MainWindow::loadPreferences(const bool configureSession)
|
||||
m_queueSeparatorMenu->setVisible(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_ui->actionDecreaseQueuePos->isVisible()) {
|
||||
else
|
||||
{
|
||||
if (m_ui->actionDecreaseQueuePos->isVisible())
|
||||
{
|
||||
m_transferListWidget->hideQueuePosColumn(true);
|
||||
m_ui->actionDecreaseQueuePos->setVisible(false);
|
||||
m_ui->actionIncreaseQueuePos->setVisible(false);
|
||||
@@ -1547,11 +1629,13 @@ void MainWindow::loadPreferences(const bool configureSession)
|
||||
m_propertiesWidget->reloadPreferences();
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
||||
if (pref->isUpdateCheckEnabled() && !m_wasUpdateCheckEnabled) {
|
||||
if (pref->isUpdateCheckEnabled() && !m_wasUpdateCheckEnabled)
|
||||
{
|
||||
m_wasUpdateCheckEnabled = true;
|
||||
checkProgramUpdate();
|
||||
}
|
||||
else if (!pref->isUpdateCheckEnabled() && m_wasUpdateCheckEnabled) {
|
||||
else if (!pref->isUpdateCheckEnabled() && m_wasUpdateCheckEnabled)
|
||||
{
|
||||
m_wasUpdateCheckEnabled = false;
|
||||
m_programUpdateTimer->stop();
|
||||
}
|
||||
@@ -1566,15 +1650,18 @@ void MainWindow::reloadSessionStats()
|
||||
|
||||
// update global information
|
||||
#ifdef Q_OS_MACOS
|
||||
if (status.payloadDownloadRate > 0) {
|
||||
if (status.payloadDownloadRate > 0)
|
||||
{
|
||||
QtMac::setBadgeLabelText(tr("%1/s", "s is a shorthand for seconds")
|
||||
.arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate)));
|
||||
}
|
||||
else if (!QtMac::badgeLabelText().isEmpty()) {
|
||||
else if (!QtMac::badgeLabelText().isEmpty())
|
||||
{
|
||||
QtMac::setBadgeLabelText("");
|
||||
}
|
||||
#else
|
||||
if (m_systrayIcon) {
|
||||
if (m_systrayIcon)
|
||||
{
|
||||
const QString toolTip = QString::fromLatin1("%1\n%2").arg(
|
||||
tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true))
|
||||
, tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadUploadRate, true)));
|
||||
@@ -1582,7 +1669,8 @@ void MainWindow::reloadSessionStats()
|
||||
}
|
||||
#endif // Q_OS_MACOS
|
||||
|
||||
if (m_displaySpeedInTitle) {
|
||||
if (m_displaySpeedInTitle)
|
||||
{
|
||||
setWindowTitle(tr("[D: %1, U: %2] qBittorrent %3", "D = Download; U = Upload; %3 is qBittorrent version")
|
||||
.arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true)
|
||||
, Utils::Misc::friendlyUnit(status.payloadUploadRate, true)
|
||||
@@ -1592,7 +1680,8 @@ void MainWindow::reloadSessionStats()
|
||||
|
||||
void MainWindow::reloadTorrentStats(const QVector<BitTorrent::TorrentHandle *> &torrents)
|
||||
{
|
||||
if (currentTabWidget() == m_transferListWidget) {
|
||||
if (currentTabWidget() == m_transferListWidget)
|
||||
{
|
||||
if (torrents.contains(m_propertiesWidget->getCurrentTorrent()))
|
||||
m_propertiesWidget->loadDynamicData();
|
||||
}
|
||||
@@ -1638,7 +1727,8 @@ void MainWindow::showNotificationBaloon(const QString &title, const QString &msg
|
||||
void MainWindow::downloadFromURLList(const QStringList &urlList)
|
||||
{
|
||||
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
|
||||
for (const QString &url : urlList) {
|
||||
for (const QString &url : urlList)
|
||||
{
|
||||
if (useTorrentAdditionDialog)
|
||||
AddNewTorrentDialog::show(url, this);
|
||||
else
|
||||
@@ -1656,19 +1746,23 @@ void MainWindow::downloadFromURLList(const QStringList &urlList)
|
||||
void MainWindow::createSystrayDelayed()
|
||||
{
|
||||
static int timeout = 20;
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable())
|
||||
{
|
||||
// Ok, systray integration is now supported
|
||||
// Create systray icon
|
||||
createTrayIcon();
|
||||
delete m_systrayCreator;
|
||||
}
|
||||
else {
|
||||
if (timeout) {
|
||||
else
|
||||
{
|
||||
if (timeout)
|
||||
{
|
||||
// Retry a bit later
|
||||
m_systrayCreator->start(2000);
|
||||
--timeout;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Timed out, apparently system really does not
|
||||
// support systray icon
|
||||
delete m_systrayCreator;
|
||||
@@ -1780,11 +1874,13 @@ void MainWindow::on_actionRSSReader_triggered()
|
||||
|
||||
void MainWindow::on_actionSearchWidget_triggered()
|
||||
{
|
||||
if (!m_hasPython && m_ui->actionSearchWidget->isChecked()) {
|
||||
if (!m_hasPython && m_ui->actionSearchWidget->isChecked())
|
||||
{
|
||||
const Utils::ForeignApps::PythonInfo pyInfo = Utils::ForeignApps::pythonInfo();
|
||||
|
||||
// Not installed
|
||||
if (!pyInfo.isValid()) {
|
||||
if (!pyInfo.isValid())
|
||||
{
|
||||
m_ui->actionSearchWidget->setChecked(false);
|
||||
Preferences::instance()->setSearchEnabled(false);
|
||||
|
||||
@@ -1802,7 +1898,8 @@ void MainWindow::on_actionSearchWidget_triggered()
|
||||
}
|
||||
|
||||
// Check version requirement
|
||||
if (!pyInfo.isSupportedVersion()) {
|
||||
if (!pyInfo.isSupportedVersion())
|
||||
{
|
||||
m_ui->actionSearchWidget->setChecked(false);
|
||||
Preferences::instance()->setSearchEnabled(false);
|
||||
|
||||
@@ -1839,7 +1936,8 @@ void MainWindow::on_actionSearchWidget_triggered()
|
||||
// an url
|
||||
void MainWindow::on_actionDownloadFromURL_triggered()
|
||||
{
|
||||
if (!m_downloadFromURLDialog) {
|
||||
if (!m_downloadFromURLDialog)
|
||||
{
|
||||
m_downloadFromURLDialog = new DownloadFromURLDialog(this);
|
||||
connect(m_downloadFromURLDialog.data(), &DownloadFromURLDialog::urlsReadyToBeDownloaded, this, &MainWindow::downloadFromURLList);
|
||||
}
|
||||
@@ -1849,19 +1947,22 @@ void MainWindow::on_actionDownloadFromURL_triggered()
|
||||
void MainWindow::handleUpdateCheckFinished(bool updateAvailable, QString newVersion, bool invokedByUser)
|
||||
{
|
||||
QMessageBox::StandardButton answer = QMessageBox::Yes;
|
||||
if (updateAvailable) {
|
||||
if (updateAvailable)
|
||||
{
|
||||
answer = QMessageBox::question(this, tr("qBittorrent Update Available")
|
||||
, tr("A new version is available.") + "<br/>"
|
||||
+ tr("Do you want to download %1?").arg(newVersion) + "<br/><br/>"
|
||||
+ QString::fromLatin1("<a href=\"https://www.qbittorrent.org/news.php\">%1</a>").arg(tr("Open changelog..."))
|
||||
, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (answer == QMessageBox::Yes) {
|
||||
if (answer == QMessageBox::Yes)
|
||||
{
|
||||
// The user want to update, let's download the update
|
||||
ProgramUpdater *updater = dynamic_cast<ProgramUpdater * >(sender());
|
||||
updater->updateProgram();
|
||||
}
|
||||
}
|
||||
else if (invokedByUser) {
|
||||
else if (invokedByUser)
|
||||
{
|
||||
QMessageBox::information(this, tr("Already Using the Latest qBittorrent Version"),
|
||||
tr("No updates available.\nYou are already using the latest version."));
|
||||
}
|
||||
@@ -1899,7 +2000,8 @@ void MainWindow::minimizeWindow()
|
||||
|
||||
void MainWindow::on_actionExecutionLogs_triggered(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
if (checked)
|
||||
{
|
||||
Q_ASSERT(!m_executionLog);
|
||||
m_executionLog = new ExecutionLogWidget(static_cast<Log::MsgType>(executionLogMsgTypes()), m_tabs);
|
||||
#ifdef Q_OS_MACOS
|
||||
@@ -1909,7 +2011,8 @@ void MainWindow::on_actionExecutionLogs_triggered(bool checked)
|
||||
m_tabs->setTabIcon(indexTab, UIThemeManager::instance()->getIcon("view-calendar-journal"));
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
delete m_executionLog;
|
||||
}
|
||||
|
||||
@@ -1997,7 +2100,8 @@ QIcon MainWindow::getSystrayIcon() const
|
||||
const TrayIcon::Style style = Preferences::instance()->trayIconStyle();
|
||||
// on Linux we use theme icons, and icons from resources everywhere else
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
switch (style) {
|
||||
switch (style)
|
||||
{
|
||||
case TrayIcon::NORMAL:
|
||||
return QIcon::fromTheme(QLatin1String("qbittorrent-tray"));
|
||||
case TrayIcon::MONO_DARK:
|
||||
@@ -2008,7 +2112,8 @@ QIcon MainWindow::getSystrayIcon() const
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch (style) {
|
||||
switch (style)
|
||||
{
|
||||
case TrayIcon::NORMAL:
|
||||
return UIThemeManager::instance()->getIcon(QLatin1String("qbittorrent-tray"));
|
||||
case TrayIcon::MONO_DARK:
|
||||
@@ -2057,7 +2162,8 @@ void MainWindow::installPython()
|
||||
|
||||
void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
||||
{
|
||||
if (result.status != Net::DownloadStatus::Success) {
|
||||
if (result.status != Net::DownloadStatus::Success)
|
||||
{
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
QMessageBox::warning(
|
||||
this, tr("Download error")
|
||||
@@ -2084,7 +2190,8 @@ void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
|
||||
Utils::Fs::forceRemove(result.filePath + ".exe");
|
||||
|
||||
// Reload search engine
|
||||
if (Utils::ForeignApps::pythonInfo().isSupportedVersion()) {
|
||||
if (Utils::ForeignApps::pythonInfo().isSupportedVersion())
|
||||
{
|
||||
m_ui->actionSearchWidget->setChecked(true);
|
||||
displaySearchTab(true);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ namespace
|
||||
|
||||
QString languageToLocalizedString(const QLocale &locale)
|
||||
{
|
||||
switch (locale.language()) {
|
||||
switch (locale.language())
|
||||
{
|
||||
case QLocale::Arabic: return QString::fromUtf8(C_LOCALE_ARABIC);
|
||||
case QLocale::Armenian: return QString::fromUtf8(C_LOCALE_ARMENIAN);
|
||||
case QLocale::Basque: return QString::fromUtf8(C_LOCALE_BASQUE);
|
||||
@@ -92,7 +93,8 @@ namespace
|
||||
case QLocale::Byelorussian: return QString::fromUtf8(C_LOCALE_BYELORUSSIAN);
|
||||
case QLocale::Catalan: return QString::fromUtf8(C_LOCALE_CATALAN);
|
||||
case QLocale::Chinese:
|
||||
switch (locale.country()) {
|
||||
switch (locale.country())
|
||||
{
|
||||
case QLocale::China: return QString::fromUtf8(C_LOCALE_CHINESE_SIMPLIFIED);
|
||||
case QLocale::HongKong: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_HK);
|
||||
default: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_TW);
|
||||
@@ -102,7 +104,8 @@ namespace
|
||||
case QLocale::Danish: return QString::fromUtf8(C_LOCALE_DANISH);
|
||||
case QLocale::Dutch: return QString::fromUtf8(C_LOCALE_DUTCH);
|
||||
case QLocale::English:
|
||||
switch (locale.country()) {
|
||||
switch (locale.country())
|
||||
{
|
||||
case QLocale::Australia: return QString::fromUtf8(C_LOCALE_ENGLISH_AUSTRALIA);
|
||||
case QLocale::UnitedKingdom: return QString::fromUtf8(C_LOCALE_ENGLISH_UNITEDKINGDOM);
|
||||
default: return QString::fromUtf8(C_LOCALE_ENGLISH);
|
||||
@@ -195,7 +198,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||
int maxHeight = -1;
|
||||
for (int i = 0; i < m_ui->tabSelection->count(); ++i)
|
||||
maxHeight = std::max(maxHeight, m_ui->tabSelection->visualItemRect(m_ui->tabSelection->item(i)).size().height());
|
||||
for (int i = 0; i < m_ui->tabSelection->count(); ++i) {
|
||||
for (int i = 0; i < m_ui->tabSelection->count(); ++i)
|
||||
{
|
||||
const QSize size(std::numeric_limits<int>::max(), static_cast<int>(maxHeight * 1.2));
|
||||
m_ui->tabSelection->item(i)->setSizeHint(size);
|
||||
}
|
||||
@@ -225,8 +229,10 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||
m_ui->hsplitter->setCollapsible(1, false);
|
||||
// Get apply button in button box
|
||||
const QList<QAbstractButton *> buttons = m_ui->buttonBox->buttons();
|
||||
for (QAbstractButton *button : buttons) {
|
||||
if (m_ui->buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) {
|
||||
for (QAbstractButton *button : buttons)
|
||||
{
|
||||
if (m_ui->buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole)
|
||||
{
|
||||
m_applyButton = button;
|
||||
break;
|
||||
}
|
||||
@@ -263,7 +269,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||
m_ui->checkShowSystray->setVisible(false);
|
||||
#else
|
||||
// Disable systray integration if it is not supported by the system
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable())
|
||||
{
|
||||
m_ui->checkShowSystray->setChecked(false);
|
||||
m_ui->checkShowSystray->setEnabled(false);
|
||||
m_ui->labelTrayIconStyle->setVisible(false);
|
||||
@@ -567,19 +574,23 @@ void OptionsDialog::initializeLanguageCombo()
|
||||
// List language files
|
||||
const QDir langDir(":/lang");
|
||||
const QStringList langFiles = langDir.entryList(QStringList("qbittorrent_*.qm"), QDir::Files);
|
||||
for (const QString &langFile : langFiles) {
|
||||
for (const QString &langFile : langFiles)
|
||||
{
|
||||
QString localeStr = langFile.mid(12); // remove "qbittorrent_"
|
||||
localeStr.chop(3); // Remove ".qm"
|
||||
QString languageName;
|
||||
if (localeStr.startsWith("eo", Qt::CaseInsensitive)) {
|
||||
if (localeStr.startsWith("eo", Qt::CaseInsensitive))
|
||||
{
|
||||
// QLocale doesn't work with that locale. Esperanto isn't a "real" language.
|
||||
languageName = QString::fromUtf8(C_LOCALE_ESPERANTO);
|
||||
}
|
||||
else if (localeStr.startsWith("ltg", Qt::CaseInsensitive)) {
|
||||
else if (localeStr.startsWith("ltg", Qt::CaseInsensitive))
|
||||
{
|
||||
// QLocale doesn't work with that locale.
|
||||
languageName = QString::fromUtf8(C_LOCALE_LATGALIAN);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QLocale locale(localeStr);
|
||||
languageName = languageToLocalizedString(locale);
|
||||
}
|
||||
@@ -634,7 +645,8 @@ void OptionsDialog::saveWindowState() const
|
||||
pref->setPrefSize(size());
|
||||
|
||||
// Splitter size
|
||||
const QStringList sizesStr = {
|
||||
const QStringList sizesStr =
|
||||
{
|
||||
QString::number(m_ui->hsplitter->sizes().first()),
|
||||
QString::number(m_ui->hsplitter->sizes().last())
|
||||
};
|
||||
@@ -647,7 +659,8 @@ void OptionsDialog::saveOptions()
|
||||
Preferences *const pref = Preferences::instance();
|
||||
// Load the translation
|
||||
QString locale = getLocale();
|
||||
if (pref->getLocale() != locale) {
|
||||
if (pref->getLocale() != locale)
|
||||
{
|
||||
auto *translator = new QTranslator;
|
||||
if (translator->load(QLatin1String(":/lang/qbittorrent_") + locale))
|
||||
qDebug("%s locale recognized, using translation.", qUtf8Printable(locale));
|
||||
@@ -689,12 +702,14 @@ void OptionsDialog::saveOptions()
|
||||
Preferences::setMagnetLinkAssoc(m_ui->checkAssociateMagnetLinks->isChecked());
|
||||
#endif
|
||||
#ifdef Q_OS_MACOS
|
||||
if (m_ui->checkAssociateTorrents->isChecked()) {
|
||||
if (m_ui->checkAssociateTorrents->isChecked())
|
||||
{
|
||||
Preferences::setTorrentFileAssoc();
|
||||
m_ui->checkAssociateTorrents->setChecked(Preferences::isTorrentFileAssocSet());
|
||||
m_ui->checkAssociateTorrents->setEnabled(!m_ui->checkAssociateTorrents->isChecked());
|
||||
}
|
||||
if (m_ui->checkAssociateMagnetLinks->isChecked()) {
|
||||
if (m_ui->checkAssociateMagnetLinks->isChecked())
|
||||
{
|
||||
Preferences::setMagnetLinkAssoc();
|
||||
m_ui->checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet());
|
||||
m_ui->checkAssociateMagnetLinks->setEnabled(!m_ui->checkAssociateMagnetLinks->isChecked());
|
||||
@@ -810,7 +825,8 @@ void OptionsDialog::saveOptions()
|
||||
session->setGlobalMaxRatio(getMaxRatio());
|
||||
session->setGlobalMaxSeedingMinutes(getMaxSeedingMinutes());
|
||||
|
||||
const QVector<MaxRatioAction> actIndex = {
|
||||
const QVector<MaxRatioAction> actIndex =
|
||||
{
|
||||
Pause,
|
||||
Remove,
|
||||
DeleteFiles,
|
||||
@@ -837,7 +853,8 @@ void OptionsDialog::saveOptions()
|
||||
// End Queueing system preferences
|
||||
// Web UI
|
||||
pref->setWebUiEnabled(isWebUiEnabled());
|
||||
if (isWebUiEnabled()) {
|
||||
if (isWebUiEnabled())
|
||||
{
|
||||
pref->setServerDomains(m_ui->textServerDomains->text());
|
||||
pref->setWebUiAddress(m_ui->textWebUiAddress->text());
|
||||
pref->setWebUiPort(m_ui->spinWebUiPort->value());
|
||||
@@ -888,7 +905,8 @@ bool OptionsDialog::isIPFilteringEnabled() const
|
||||
|
||||
Net::ProxyType OptionsDialog::getProxyType() const
|
||||
{
|
||||
switch (m_ui->comboProxyType->currentIndex()) {
|
||||
switch (m_ui->comboProxyType->currentIndex())
|
||||
{
|
||||
case 1:
|
||||
return Net::ProxyType::SOCKS4;
|
||||
case 2:
|
||||
@@ -927,7 +945,8 @@ void OptionsDialog::loadOptions()
|
||||
|
||||
#ifndef Q_OS_MACOS
|
||||
m_ui->checkShowSystray->setChecked(pref->systrayIntegration());
|
||||
if (m_ui->checkShowSystray->isChecked()) {
|
||||
if (m_ui->checkShowSystray->isChecked())
|
||||
{
|
||||
m_ui->checkMinimizeToSysTray->setChecked(pref->minimizeToTray());
|
||||
m_ui->checkCloseToSystray->setChecked(pref->closeToTray());
|
||||
m_ui->comboTrayIcon->setCurrentIndex(pref->trayIconStyle());
|
||||
@@ -1001,12 +1020,14 @@ void OptionsDialog::loadOptions()
|
||||
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
|
||||
|
||||
strValue = session->torrentExportDirectory();
|
||||
if (strValue.isEmpty()) {
|
||||
if (strValue.isEmpty())
|
||||
{
|
||||
// Disable
|
||||
m_ui->checkExportDir->setChecked(false);
|
||||
m_ui->textExportDir->setEnabled(false);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Enable
|
||||
m_ui->checkExportDir->setChecked(true);
|
||||
m_ui->textExportDir->setEnabled(true);
|
||||
@@ -1014,12 +1035,14 @@ void OptionsDialog::loadOptions()
|
||||
}
|
||||
|
||||
strValue = session->finishedTorrentExportDirectory();
|
||||
if (strValue.isEmpty()) {
|
||||
if (strValue.isEmpty())
|
||||
{
|
||||
// Disable
|
||||
m_ui->checkExportDirFin->setChecked(false);
|
||||
m_ui->textExportDirFin->setEnabled(false);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Enable
|
||||
m_ui->checkExportDirFin->setChecked(true);
|
||||
m_ui->textExportDirFin->setEnabled(true);
|
||||
@@ -1060,49 +1083,57 @@ void OptionsDialog::loadOptions()
|
||||
m_ui->spinPort->setDisabled(m_ui->checkRandomPort->isChecked());
|
||||
|
||||
intValue = session->maxConnections();
|
||||
if (intValue > 0) {
|
||||
if (intValue > 0)
|
||||
{
|
||||
// enable
|
||||
m_ui->checkMaxConnecs->setChecked(true);
|
||||
m_ui->spinMaxConnec->setEnabled(true);
|
||||
m_ui->spinMaxConnec->setValue(intValue);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// disable
|
||||
m_ui->checkMaxConnecs->setChecked(false);
|
||||
m_ui->spinMaxConnec->setEnabled(false);
|
||||
}
|
||||
intValue = session->maxConnectionsPerTorrent();
|
||||
if (intValue > 0) {
|
||||
if (intValue > 0)
|
||||
{
|
||||
// enable
|
||||
m_ui->checkMaxConnecsPerTorrent->setChecked(true);
|
||||
m_ui->spinMaxConnecPerTorrent->setEnabled(true);
|
||||
m_ui->spinMaxConnecPerTorrent->setValue(intValue);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// disable
|
||||
m_ui->checkMaxConnecsPerTorrent->setChecked(false);
|
||||
m_ui->spinMaxConnecPerTorrent->setEnabled(false);
|
||||
}
|
||||
intValue = session->maxUploads();
|
||||
if (intValue > 0) {
|
||||
if (intValue > 0)
|
||||
{
|
||||
// enable
|
||||
m_ui->checkMaxUploads->setChecked(true);
|
||||
m_ui->spinMaxUploads->setEnabled(true);
|
||||
m_ui->spinMaxUploads->setValue(intValue);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// disable
|
||||
m_ui->checkMaxUploads->setChecked(false);
|
||||
m_ui->spinMaxUploads->setEnabled(false);
|
||||
}
|
||||
intValue = session->maxUploadsPerTorrent();
|
||||
if (intValue > 0) {
|
||||
if (intValue > 0)
|
||||
{
|
||||
// enable
|
||||
m_ui->checkMaxUploadsPerTorrent->setChecked(true);
|
||||
m_ui->spinMaxUploadsPerTorrent->setEnabled(true);
|
||||
m_ui->spinMaxUploadsPerTorrent->setValue(intValue);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// disable
|
||||
m_ui->checkMaxUploadsPerTorrent->setChecked(false);
|
||||
m_ui->spinMaxUploadsPerTorrent->setEnabled(false);
|
||||
@@ -1112,7 +1143,8 @@ void OptionsDialog::loadOptions()
|
||||
Net::ProxyConfiguration proxyConf = proxyConfigManager->proxyConfiguration();
|
||||
using Net::ProxyType;
|
||||
bool useProxyAuth = false;
|
||||
switch (proxyConf.type) {
|
||||
switch (proxyConf.type)
|
||||
{
|
||||
case ProxyType::SOCKS4:
|
||||
m_ui->comboProxyType->setCurrentIndex(1);
|
||||
break;
|
||||
@@ -1185,32 +1217,37 @@ void OptionsDialog::loadOptions()
|
||||
m_ui->spinUploadRateForSlowTorrents->setValue(session->uploadRateForSlowTorrents());
|
||||
m_ui->spinSlowTorrentsInactivityTimer->setValue(session->slowTorrentsInactivityTimer());
|
||||
|
||||
if (session->globalMaxRatio() >= 0.) {
|
||||
if (session->globalMaxRatio() >= 0.)
|
||||
{
|
||||
// Enable
|
||||
m_ui->checkMaxRatio->setChecked(true);
|
||||
m_ui->spinMaxRatio->setEnabled(true);
|
||||
m_ui->comboRatioLimitAct->setEnabled(true);
|
||||
m_ui->spinMaxRatio->setValue(session->globalMaxRatio());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Disable
|
||||
m_ui->checkMaxRatio->setChecked(false);
|
||||
m_ui->spinMaxRatio->setEnabled(false);
|
||||
}
|
||||
if (session->globalMaxSeedingMinutes() >= 0) {
|
||||
if (session->globalMaxSeedingMinutes() >= 0)
|
||||
{
|
||||
// Enable
|
||||
m_ui->checkMaxSeedingMinutes->setChecked(true);
|
||||
m_ui->spinMaxSeedingMinutes->setEnabled(true);
|
||||
m_ui->spinMaxSeedingMinutes->setValue(session->globalMaxSeedingMinutes());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Disable
|
||||
m_ui->checkMaxSeedingMinutes->setChecked(false);
|
||||
m_ui->spinMaxSeedingMinutes->setEnabled(false);
|
||||
}
|
||||
m_ui->comboRatioLimitAct->setEnabled((session->globalMaxSeedingMinutes() >= 0) || (session->globalMaxRatio() >= 0.));
|
||||
|
||||
const QHash<MaxRatioAction, int> actIndex = {
|
||||
const QHash<MaxRatioAction, int> actIndex =
|
||||
{
|
||||
{Pause, 0},
|
||||
{Remove, 1},
|
||||
{DeleteFiles, 2},
|
||||
@@ -1386,16 +1423,20 @@ int OptionsDialog::getMaxUploadsPerTorrent() const
|
||||
|
||||
void OptionsDialog::on_buttonBox_accepted()
|
||||
{
|
||||
if (m_applyButton->isEnabled()) {
|
||||
if (!schedTimesOk()) {
|
||||
if (m_applyButton->isEnabled())
|
||||
{
|
||||
if (!schedTimesOk())
|
||||
{
|
||||
m_ui->tabSelection->setCurrentRow(TAB_SPEED);
|
||||
return;
|
||||
}
|
||||
if (!webUIAuthenticationOk()) {
|
||||
if (!webUIAuthenticationOk())
|
||||
{
|
||||
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
|
||||
return;
|
||||
}
|
||||
if (!isAlternativeWebUIPathValid()) {
|
||||
if (!isAlternativeWebUIPathValid())
|
||||
{
|
||||
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
|
||||
return;
|
||||
}
|
||||
@@ -1409,16 +1450,20 @@ void OptionsDialog::on_buttonBox_accepted()
|
||||
|
||||
void OptionsDialog::applySettings(QAbstractButton *button)
|
||||
{
|
||||
if (button == m_applyButton) {
|
||||
if (!schedTimesOk()) {
|
||||
if (button == m_applyButton)
|
||||
{
|
||||
if (!schedTimesOk())
|
||||
{
|
||||
m_ui->tabSelection->setCurrentRow(TAB_SPEED);
|
||||
return;
|
||||
}
|
||||
if (!webUIAuthenticationOk()) {
|
||||
if (!webUIAuthenticationOk())
|
||||
{
|
||||
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
|
||||
return;
|
||||
}
|
||||
if (!isAlternativeWebUIPathValid()) {
|
||||
if (!isAlternativeWebUIPathValid())
|
||||
{
|
||||
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
|
||||
return;
|
||||
}
|
||||
@@ -1456,24 +1501,28 @@ void OptionsDialog::toggleComboRatioLimitAct()
|
||||
|
||||
void OptionsDialog::enableProxy(const int index)
|
||||
{
|
||||
if (index >= 1) { // Any proxy type is used
|
||||
if (index >= 1)
|
||||
{ // Any proxy type is used
|
||||
//enable
|
||||
m_ui->lblProxyIP->setEnabled(true);
|
||||
m_ui->textProxyIP->setEnabled(true);
|
||||
m_ui->lblProxyPort->setEnabled(true);
|
||||
m_ui->spinProxyPort->setEnabled(true);
|
||||
m_ui->checkProxyPeerConnecs->setEnabled(true);
|
||||
if (index >= 2) { // SOCKS5 or HTTP
|
||||
if (index >= 2)
|
||||
{ // SOCKS5 or HTTP
|
||||
m_ui->checkProxyAuth->setEnabled(true);
|
||||
m_ui->isProxyOnlyForTorrents->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->checkProxyAuth->setEnabled(false);
|
||||
m_ui->isProxyOnlyForTorrents->setEnabled(false);
|
||||
m_ui->isProxyOnlyForTorrents->setChecked(true);
|
||||
}
|
||||
}
|
||||
else { // No proxy
|
||||
else
|
||||
{ // No proxy
|
||||
// disable
|
||||
m_ui->lblProxyIP->setEnabled(false);
|
||||
m_ui->textProxyIP->setEnabled(false);
|
||||
@@ -1550,13 +1599,16 @@ QString OptionsDialog::getLocale() const
|
||||
void OptionsDialog::setLocale(const QString &localeStr)
|
||||
{
|
||||
QString name;
|
||||
if (localeStr.startsWith("eo", Qt::CaseInsensitive)) {
|
||||
if (localeStr.startsWith("eo", Qt::CaseInsensitive))
|
||||
{
|
||||
name = "eo";
|
||||
}
|
||||
else if (localeStr.startsWith("ltg", Qt::CaseInsensitive)) {
|
||||
else if (localeStr.startsWith("ltg", Qt::CaseInsensitive))
|
||||
{
|
||||
name = "ltg";
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QLocale locale(localeStr);
|
||||
if (locale.language() == QLocale::Uzbek)
|
||||
name = "uz@Latn";
|
||||
@@ -1565,15 +1617,18 @@ void OptionsDialog::setLocale(const QString &localeStr)
|
||||
}
|
||||
// Attempt to find exact match
|
||||
int index = m_ui->comboI18n->findData(name, Qt::UserRole);
|
||||
if (index < 0) {
|
||||
if (index < 0)
|
||||
{
|
||||
//Attempt to find a language match without a country
|
||||
int pos = name.indexOf('_');
|
||||
if (pos > -1) {
|
||||
if (pos > -1)
|
||||
{
|
||||
QString lang = name.left(pos);
|
||||
index = m_ui->comboI18n->findData(lang, Qt::UserRole);
|
||||
}
|
||||
}
|
||||
if (index < 0) {
|
||||
if (index < 0)
|
||||
{
|
||||
// Unrecognized, use US English
|
||||
index = m_ui->comboI18n->findData("en", Qt::UserRole);
|
||||
Q_ASSERT(index >= 0);
|
||||
@@ -1616,10 +1671,12 @@ void OptionsDialog::on_addScanFolderButton_clicked()
|
||||
Preferences *const pref = Preferences::instance();
|
||||
const QString dir = QFileDialog::getExistingDirectory(this, tr("Select folder to monitor"),
|
||||
Utils::Fs::toNativePath(Utils::Fs::folderName(pref->getScanDirsLastPath())));
|
||||
if (!dir.isEmpty()) {
|
||||
if (!dir.isEmpty())
|
||||
{
|
||||
const ScanFoldersModel::PathStatus status = ScanFoldersModel::instance()->addPath(dir, ScanFoldersModel::DEFAULT_LOCATION, QString(), false);
|
||||
QString error;
|
||||
switch (status) {
|
||||
switch (status)
|
||||
{
|
||||
case ScanFoldersModel::AlreadyInList:
|
||||
error = tr("Folder is already being monitored:");
|
||||
break;
|
||||
@@ -1649,7 +1706,8 @@ void OptionsDialog::on_removeScanFolderButton_clicked()
|
||||
if (selected.isEmpty())
|
||||
return;
|
||||
Q_ASSERT(selected.count() == ScanFoldersModel::instance()->columnCount());
|
||||
for (const QModelIndex &index : selected) {
|
||||
for (const QModelIndex &index : selected)
|
||||
{
|
||||
if (index.column() == ScanFoldersModel::WATCH)
|
||||
m_removedScanDirs << index.data().toString();
|
||||
}
|
||||
@@ -1704,13 +1762,15 @@ void OptionsDialog::webUIHttpsCertChanged(const QString &path, const ShowError s
|
||||
return;
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (showError == ShowError::Show)
|
||||
QMessageBox::warning(this, tr("Invalid path"), file.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Utils::Net::isSSLCertificatesValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE))) {
|
||||
if (!Utils::Net::isSSLCertificatesValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE)))
|
||||
{
|
||||
if (showError == ShowError::Show)
|
||||
QMessageBox::warning(this, tr("Invalid certificate"), tr("This is not a valid SSL certificate."));
|
||||
return;
|
||||
@@ -1728,13 +1788,15 @@ void OptionsDialog::webUIHttpsKeyChanged(const QString &path, const ShowError sh
|
||||
return;
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (showError == ShowError::Show)
|
||||
QMessageBox::warning(this, tr("Invalid path"), file.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Utils::Net::isSSLKeyValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE))) {
|
||||
if (!Utils::Net::isSSLKeyValid(file.read(Utils::Net::MAX_SSL_FILE_SIZE)))
|
||||
{
|
||||
if (showError == ShowError::Show)
|
||||
QMessageBox::warning(this, tr("Invalid key"), tr("This is not a valid SSL key."));
|
||||
return;
|
||||
@@ -1779,7 +1841,8 @@ void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount)
|
||||
|
||||
bool OptionsDialog::schedTimesOk()
|
||||
{
|
||||
if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time()) {
|
||||
if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Time Error"), tr("The start time and the end time can't be the same."));
|
||||
return false;
|
||||
}
|
||||
@@ -1788,11 +1851,13 @@ bool OptionsDialog::schedTimesOk()
|
||||
|
||||
bool OptionsDialog::webUIAuthenticationOk()
|
||||
{
|
||||
if (webUiUsername().length() < 3) {
|
||||
if (webUiUsername().length() < 3)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Length Error"), tr("The Web UI username must be at least 3 characters long."));
|
||||
return false;
|
||||
}
|
||||
if (!webUiPassword().isEmpty() && (webUiPassword().length() < 6)) {
|
||||
if (!webUiPassword().isEmpty() && (webUiPassword().length() < 6))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Length Error"), tr("The Web UI password must be at least 6 characters long."));
|
||||
return false;
|
||||
}
|
||||
@@ -1801,7 +1866,8 @@ bool OptionsDialog::webUIAuthenticationOk()
|
||||
|
||||
bool OptionsDialog::isAlternativeWebUIPathValid()
|
||||
{
|
||||
if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().trimmed().isEmpty()) {
|
||||
if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().trimmed().isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Location Error"), tr("The alternative Web UI files location cannot be blank."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -54,12 +54,14 @@ void PreviewListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
|
||||
QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
|
||||
drawBackground(painter, opt, index);
|
||||
|
||||
switch (index.column()) {
|
||||
switch (index.column())
|
||||
{
|
||||
case PreviewSelectDialog::SIZE:
|
||||
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
|
||||
break;
|
||||
|
||||
case PreviewSelectDialog::PROGRESS: {
|
||||
case PreviewSelectDialog::PROGRESS:
|
||||
{
|
||||
const qreal progress = (index.data().toReal() * 100);
|
||||
|
||||
QStyleOptionProgressBar newopt;
|
||||
|
||||
@@ -86,12 +86,14 @@ PreviewSelectDialog::PreviewSelectDialog(QWidget *parent, const BitTorrent::Torr
|
||||
// Fill list in
|
||||
const QVector<qreal> fp = torrent->filesProgress();
|
||||
int nbFiles = torrent->filesCount();
|
||||
for (int i = 0; i < nbFiles; ++i) {
|
||||
for (int i = 0; i < nbFiles; ++i)
|
||||
{
|
||||
QString fileName = torrent->fileName(i);
|
||||
if (fileName.endsWith(QB_EXT))
|
||||
fileName.chop(4);
|
||||
QString extension = Utils::Fs::fileExtension(fileName).toUpper();
|
||||
if (Utils::Misc::isPreviewable(extension)) {
|
||||
if (Utils::Misc::isPreviewable(extension))
|
||||
{
|
||||
int row = m_previewListModel->rowCount();
|
||||
m_previewListModel->insertRow(row);
|
||||
m_previewListModel->setData(m_previewListModel->index(row, NAME), fileName);
|
||||
@@ -128,7 +130,8 @@ void PreviewSelectDialog::previewButtonClicked()
|
||||
// Only one file should be selected
|
||||
const QString path = absolutePaths.at(selectedIndexes.at(0).data().toInt());
|
||||
// File
|
||||
if (!QFile::exists(path)) {
|
||||
if (!QFile::exists(path))
|
||||
{
|
||||
const bool isSingleFile = (m_previewListModel->rowCount() == 1);
|
||||
QWidget *parent = isSingleFile ? this->parentWidget() : this;
|
||||
QMessageBox::critical(parent, tr("Preview impossible")
|
||||
@@ -156,7 +159,8 @@ void PreviewSelectDialog::loadWindowState()
|
||||
Utils::Gui::resize(this, m_storeDialogSize);
|
||||
|
||||
// Restore TreeView Header state
|
||||
if (!m_storeTreeHeaderState.value().isEmpty()) {
|
||||
if (!m_storeTreeHeaderState.value().isEmpty())
|
||||
{
|
||||
m_headerStateInitialized = m_ui->previewList->header()->restoreState(m_storeTreeHeaderState);
|
||||
}
|
||||
}
|
||||
@@ -164,14 +168,16 @@ void PreviewSelectDialog::loadWindowState()
|
||||
void PreviewSelectDialog::showEvent(QShowEvent *event)
|
||||
{
|
||||
// event originated from system
|
||||
if (event->spontaneous()) {
|
||||
if (event->spontaneous())
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// Default size, have to be called after show(), because width is needed
|
||||
// Set Name column width to 60% of TreeView
|
||||
if (!m_headerStateInitialized) {
|
||||
if (!m_headerStateInitialized)
|
||||
{
|
||||
const int nameSize = (m_ui->previewList->size().width() * 0.6);
|
||||
m_ui->previewList->header()->resizeSection(0, nameSize);
|
||||
m_headerStateInitialized = true;
|
||||
|
||||
@@ -70,7 +70,8 @@ void ProgramUpdater::checkForUpdates()
|
||||
void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
|
||||
{
|
||||
|
||||
if (result.status != Net::DownloadStatus::Success) {
|
||||
if (result.status != Net::DownloadStatus::Success)
|
||||
{
|
||||
qDebug() << "Downloading the new qBittorrent updates RSS failed:" << result.errorString;
|
||||
emit updateCheckFinished(false, QString(), m_invokedByUser);
|
||||
return;
|
||||
@@ -81,7 +82,8 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
|
||||
#ifdef Q_OS_MACOS
|
||||
const QString OS_TYPE {"Mac OS X"};
|
||||
#elif defined(Q_OS_WIN)
|
||||
const QString OS_TYPE {(::IsWindows7OrGreater()
|
||||
const QString OS_TYPE
|
||||
{(::IsWindows7OrGreater()
|
||||
&& QSysInfo::currentCpuArchitecture().endsWith("64"))
|
||||
? "Windows x64" : "Windows"};
|
||||
#endif
|
||||
@@ -92,10 +94,12 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
|
||||
QString updateLink;
|
||||
QString type;
|
||||
|
||||
while (!xml.atEnd()) {
|
||||
while (!xml.atEnd())
|
||||
{
|
||||
xml.readNext();
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.isStartElement())
|
||||
{
|
||||
if (xml.name() == "item")
|
||||
inItem = true;
|
||||
else if (inItem && xml.name() == "link")
|
||||
@@ -105,11 +109,15 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
|
||||
else if (inItem && xml.name() == "version")
|
||||
version = getStringValue(xml);
|
||||
}
|
||||
else if (xml.isEndElement()) {
|
||||
if (inItem && xml.name() == "item") {
|
||||
if (type.compare(OS_TYPE, Qt::CaseInsensitive) == 0) {
|
||||
else if (xml.isEndElement())
|
||||
{
|
||||
if (inItem && xml.name() == "item")
|
||||
{
|
||||
if (type.compare(OS_TYPE, Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
qDebug("The last update available is %s", qUtf8Printable(version));
|
||||
if (!version.isEmpty()) {
|
||||
if (!version.isEmpty())
|
||||
{
|
||||
qDebug("Detected version is %s", qUtf8Printable(version));
|
||||
if (isVersionMoreRecent(version))
|
||||
m_updateUrl = updateLink;
|
||||
@@ -138,12 +146,14 @@ void ProgramUpdater::updateProgram()
|
||||
bool ProgramUpdater::isVersionMoreRecent(const QString &remoteVersion) const
|
||||
{
|
||||
const QRegularExpressionMatch regVerMatch = QRegularExpression("([0-9.]+)").match(QBT_VERSION);
|
||||
if (regVerMatch.hasMatch()) {
|
||||
if (regVerMatch.hasMatch())
|
||||
{
|
||||
const QString localVersion = regVerMatch.captured(1);
|
||||
const QVector<QStringRef> remoteParts = remoteVersion.splitRef('.');
|
||||
const QVector<QStringRef> localParts = localVersion.splitRef('.');
|
||||
|
||||
for (int i = 0; i < qMin(remoteParts.size(), localParts.size()); ++i) {
|
||||
for (int i = 0; i < qMin(remoteParts.size(), localParts.size()); ++i)
|
||||
{
|
||||
if (remoteParts[i].toInt() > localParts[i].toInt())
|
||||
return true;
|
||||
if (remoteParts[i].toInt() < localParts[i].toInt())
|
||||
|
||||
@@ -59,7 +59,8 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||
// 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) {
|
||||
for (int x = 0; x < reqSize; ++x)
|
||||
{
|
||||
// R - real
|
||||
const float fromR = x * ratio;
|
||||
const float toR = (x + 1) * ratio;
|
||||
@@ -80,15 +81,18 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||
float value = 0;
|
||||
|
||||
// case when calculated range is (15.2 >= x < 15.7)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (x2 == toCMinusOne)
|
||||
{
|
||||
if (vecin[x2])
|
||||
value += ratio;
|
||||
++x2;
|
||||
}
|
||||
// case when (15.2 >= x < 17.8)
|
||||
else {
|
||||
else
|
||||
{
|
||||
// subcase (15.2 >= x < 16)
|
||||
if (x2 != fromR) {
|
||||
if (x2 != fromR)
|
||||
{
|
||||
if (vecin[x2])
|
||||
value += 1.0 - (fromR - fromC);
|
||||
++x2;
|
||||
@@ -100,7 +104,8 @@ QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin
|
||||
value += 1.0;
|
||||
|
||||
// subcase (17 >= x < 17.8)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (x2 == toCMinusOne)
|
||||
{
|
||||
if (vecin[x2])
|
||||
value += 1.0 - (toC - toR);
|
||||
++x2;
|
||||
@@ -123,12 +128,14 @@ bool DownloadedPiecesBar::updateImage(QImage &image)
|
||||
{
|
||||
// qDebug() << "updateImage";
|
||||
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
|
||||
if (image2.isNull()) {
|
||||
if (image2.isNull())
|
||||
{
|
||||
qDebug() << "QImage image2() allocation failed, width():" << width();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_pieces.isEmpty()) {
|
||||
if (m_pieces.isEmpty())
|
||||
{
|
||||
image2.fill(backgroundColor());
|
||||
image = image2;
|
||||
return true;
|
||||
@@ -138,10 +145,12 @@ bool DownloadedPiecesBar::updateImage(QImage &image)
|
||||
QVector<float> scaledPiecesDl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
|
||||
|
||||
// filling image
|
||||
for (int x = 0; x < scaledPieces.size(); ++x) {
|
||||
for (int x = 0; x < scaledPieces.size(); ++x)
|
||||
{
|
||||
float piecesToValue = scaledPieces.at(x);
|
||||
float piecesToValueDl = scaledPiecesDl.at(x);
|
||||
if (piecesToValueDl != 0) {
|
||||
if (piecesToValueDl != 0)
|
||||
{
|
||||
float fillRatio = piecesToValue + piecesToValueDl;
|
||||
float ratio = piecesToValueDl / fillRatio;
|
||||
|
||||
@@ -150,7 +159,8 @@ bool DownloadedPiecesBar::updateImage(QImage &image)
|
||||
|
||||
image2.setPixel(x, 0, mixedColor);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
image2.setPixel(x, 0, pieceColors()[piecesToValue * 255]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,11 @@ PeerListSortModel::PeerListSortModel(QObject *parent)
|
||||
|
||||
bool PeerListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
switch (sortColumn()) {
|
||||
switch (sortColumn())
|
||||
{
|
||||
case PeerListWidget::IP:
|
||||
case PeerListWidget::CLIENT: {
|
||||
case PeerListWidget::CLIENT:
|
||||
{
|
||||
const QString strL = left.data(UnderlyingDataRole).toString();
|
||||
const QString strR = right.data(UnderlyingDataRole).toString();
|
||||
const int result = Utils::String::naturalCompare(strL, strR, Qt::CaseInsensitive);
|
||||
|
||||
@@ -126,8 +126,10 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
|
||||
hideColumn(PeerListColumns::COUNTRY);
|
||||
// Ensure that at least one column is visible at all times
|
||||
bool atLeastOne = false;
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i) {
|
||||
if (!isColumnHidden(i)) {
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
|
||||
{
|
||||
if (!isColumnHidden(i))
|
||||
{
|
||||
atLeastOne = true;
|
||||
break;
|
||||
}
|
||||
@@ -137,7 +139,8 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
|
||||
// To also mitigate the above issue, we have to resize each column when
|
||||
// its size is 0, because explicitly 'showing' the column isn't enough
|
||||
// in the above scenario.
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i) {
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
|
||||
{
|
||||
if ((columnWidth(i) <= 0) && !isColumnHidden(i))
|
||||
resizeColumnToContents(i);
|
||||
}
|
||||
@@ -178,7 +181,8 @@ void PeerListWidget::displayToggleColumnsMenu(const QPoint &)
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->setTitle(tr("Column visibility"));
|
||||
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i) {
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
|
||||
{
|
||||
if ((i == PeerListColumns::COUNTRY) && !Preferences::instance()->resolvePeerCountries())
|
||||
continue;
|
||||
|
||||
@@ -191,7 +195,8 @@ void PeerListWidget::displayToggleColumnsMenu(const QPoint &)
|
||||
connect(menu, &QMenu::triggered, this, [this](const QAction *action)
|
||||
{
|
||||
int visibleCols = 0;
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i) {
|
||||
for (int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
|
||||
{
|
||||
if (!isColumnHidden(i))
|
||||
++visibleCols;
|
||||
|
||||
@@ -217,14 +222,17 @@ void PeerListWidget::displayToggleColumnsMenu(const QPoint &)
|
||||
|
||||
void PeerListWidget::updatePeerHostNameResolutionState()
|
||||
{
|
||||
if (Preferences::instance()->resolvePeerHostNames()) {
|
||||
if (!m_resolver) {
|
||||
if (Preferences::instance()->resolvePeerHostNames())
|
||||
{
|
||||
if (!m_resolver)
|
||||
{
|
||||
m_resolver = new Net::ReverseResolution(this);
|
||||
connect(m_resolver, &Net::ReverseResolution::ipResolved, this, &PeerListWidget::handleResolved);
|
||||
loadPeers(m_properties->getCurrentTorrent());
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
delete m_resolver;
|
||||
m_resolver = nullptr;
|
||||
}
|
||||
@@ -237,13 +245,15 @@ void PeerListWidget::updatePeerCountryResolutionState()
|
||||
return;
|
||||
|
||||
m_resolveCountries = resolveCountries;
|
||||
if (m_resolveCountries) {
|
||||
if (m_resolveCountries)
|
||||
{
|
||||
loadPeers(m_properties->getCurrentTorrent());
|
||||
showColumn(PeerListColumns::COUNTRY);
|
||||
if (columnWidth(PeerListColumns::COUNTRY) <= 0)
|
||||
resizeColumnToContents(PeerListColumns::COUNTRY);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
hideColumn(PeerListColumns::COUNTRY);
|
||||
}
|
||||
}
|
||||
@@ -258,7 +268,8 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
|
||||
|
||||
// Add Peer Action
|
||||
// Do not allow user to add peers in a private torrent
|
||||
if (!torrent->isQueued() && !torrent->isChecking() && !torrent->isPrivate()) {
|
||||
if (!torrent->isQueued() && !torrent->isChecking() && !torrent->isPrivate())
|
||||
{
|
||||
const QAction *addPeerAct = menu->addAction(UIThemeManager::instance()->getIcon("user-group-new"), tr("Add a new peer..."));
|
||||
connect(addPeerAct, &QAction::triggered, this, [this, torrent]()
|
||||
{
|
||||
@@ -274,7 +285,8 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
|
||||
});
|
||||
}
|
||||
|
||||
if (!selectionModel()->selectedRows().isEmpty()) {
|
||||
if (!selectionModel()->selectedRows().isEmpty())
|
||||
{
|
||||
const QAction *copyPeerAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-copy"), tr("Copy IP:port"));
|
||||
connect(copyPeerAct, &QAction::triggered, this, &PeerListWidget::copySelectedPeers);
|
||||
|
||||
@@ -298,7 +310,8 @@ void PeerListWidget::banSelectedPeers()
|
||||
QVector<QString> selectedIPs;
|
||||
selectedIPs.reserve(selectedIndexes.size());
|
||||
|
||||
for (const QModelIndex &index : selectedIndexes) {
|
||||
for (const QModelIndex &index : selectedIndexes)
|
||||
{
|
||||
const int row = m_proxyModel->mapToSource(index).row();
|
||||
const QString ip = m_listModel->item(row, PeerListColumns::IP_HIDDEN)->text();
|
||||
selectedIPs += ip;
|
||||
@@ -309,7 +322,8 @@ void PeerListWidget::banSelectedPeers()
|
||||
, tr("Are you sure you want to permanently ban the selected peers?"));
|
||||
if (btn != QMessageBox::Yes) return;
|
||||
|
||||
for (const QString &ip : selectedIPs) {
|
||||
for (const QString &ip : selectedIPs)
|
||||
{
|
||||
BitTorrent::Session::instance()->banIP(ip);
|
||||
LogMsg(tr("Peer \"%1\" is manually banned").arg(ip));
|
||||
}
|
||||
@@ -322,7 +336,8 @@ void PeerListWidget::copySelectedPeers()
|
||||
const QModelIndexList selectedIndexes = selectionModel()->selectedRows();
|
||||
QStringList selectedPeers;
|
||||
|
||||
for (const QModelIndex &index : selectedIndexes) {
|
||||
for (const QModelIndex &index : selectedIndexes)
|
||||
{
|
||||
const int row = m_proxyModel->mapToSource(index).row();
|
||||
const QString ip = m_listModel->item(row, PeerListColumns::IP_HIDDEN)->text();
|
||||
const QString port = m_listModel->item(row, PeerListColumns::PORT)->text();
|
||||
@@ -364,19 +379,22 @@ void PeerListWidget::loadPeers(const BitTorrent::TorrentHandle *torrent)
|
||||
for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i)
|
||||
existingPeers << i.key();
|
||||
|
||||
for (const BitTorrent::PeerInfo &peer : peers) {
|
||||
for (const BitTorrent::PeerInfo &peer : peers)
|
||||
{
|
||||
if (peer.address().ip.isNull()) continue;
|
||||
|
||||
bool isNewPeer = false;
|
||||
updatePeer(torrent, peer, isNewPeer);
|
||||
if (!isNewPeer) {
|
||||
if (!isNewPeer)
|
||||
{
|
||||
const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()};
|
||||
existingPeers.remove(peerEndpoint);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove peers that are gone
|
||||
for (const PeerEndpoint &peerEndpoint : asConst(existingPeers)) {
|
||||
for (const PeerEndpoint &peerEndpoint : asConst(existingPeers))
|
||||
{
|
||||
QStandardItem *item = m_peerItems.take(peerEndpoint);
|
||||
|
||||
QSet<QStandardItem *> &items = m_itemsByIP[peerEndpoint.address.ip];
|
||||
@@ -411,7 +429,8 @@ void PeerListWidget::updatePeer(const BitTorrent::TorrentHandle *torrent, const
|
||||
|
||||
auto itemIter = m_peerItems.find(peerEndpoint);
|
||||
isNewPeer = (itemIter == m_peerItems.end());
|
||||
if (isNewPeer) {
|
||||
if (isNewPeer)
|
||||
{
|
||||
// new item
|
||||
const int row = m_listModel->rowCount();
|
||||
m_listModel->insertRow(row);
|
||||
@@ -449,9 +468,11 @@ void PeerListWidget::updatePeer(const BitTorrent::TorrentHandle *torrent, const
|
||||
if (m_resolver)
|
||||
m_resolver->resolve(peerEndpoint.address.ip);
|
||||
|
||||
if (m_resolveCountries) {
|
||||
if (m_resolveCountries)
|
||||
{
|
||||
const QIcon icon = UIThemeManager::instance()->getFlagIcon(peer.country());
|
||||
if (!icon.isNull()) {
|
||||
if (!icon.isNull())
|
||||
{
|
||||
m_listModel->setData(m_listModel->index(row, PeerListColumns::COUNTRY), icon, Qt::DecorationRole);
|
||||
const QString countryName = Net::GeoIPManager::CountryName(peer.country());
|
||||
m_listModel->setData(m_listModel->index(row, PeerListColumns::COUNTRY), countryName, Qt::ToolTipRole);
|
||||
@@ -479,7 +500,8 @@ void PeerListWidget::handleSortColumnChanged(const int col)
|
||||
|
||||
void PeerListWidget::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (event->modifiers() & Qt::ShiftModifier) {
|
||||
if (event->modifiers() & Qt::ShiftModifier)
|
||||
{
|
||||
// Shift + scroll = horizontal scroll
|
||||
event->accept();
|
||||
|
||||
|
||||
@@ -57,18 +57,22 @@ QVector<BitTorrent::PeerAddress> PeersAdditionDialog::askForPeers(QWidget *paren
|
||||
|
||||
void PeersAdditionDialog::validateInput()
|
||||
{
|
||||
if (m_ui->textEditPeers->toPlainText().trimmed().isEmpty()) {
|
||||
if (m_ui->textEditPeers->toPlainText().trimmed().isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("No peer entered"),
|
||||
tr("Please type at least one peer."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
for (const QString &peer : asConst(m_ui->textEditPeers->toPlainText().trimmed().split('\n'))) {
|
||||
for (const QString &peer : asConst(m_ui->textEditPeers->toPlainText().trimmed().split('\n')))
|
||||
{
|
||||
const BitTorrent::PeerAddress addr = BitTorrent::PeerAddress::parse(peer);
|
||||
if (!addr.ip.isNull()) {
|
||||
if (!addr.ip.isNull())
|
||||
{
|
||||
m_peersList.append(addr);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid peer"),
|
||||
tr("The peer '%1' is invalid.").arg(peer),
|
||||
QMessageBox::Ok);
|
||||
|
||||
@@ -58,7 +58,8 @@ QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin,
|
||||
// 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) {
|
||||
for (int x = 0; x < reqSize; ++x)
|
||||
{
|
||||
// R - real
|
||||
const float fromR = x * ratio;
|
||||
const float toR = (x + 1) * ratio;
|
||||
@@ -79,15 +80,18 @@ QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin,
|
||||
float value = 0;
|
||||
|
||||
// case when calculated range is (15.2 >= x < 15.7)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (x2 == toCMinusOne)
|
||||
{
|
||||
if (vecin[x2])
|
||||
value += ratio * vecin[x2];
|
||||
++x2;
|
||||
}
|
||||
// case when (15.2 >= x < 17.8)
|
||||
else {
|
||||
else
|
||||
{
|
||||
// subcase (15.2 >= x < 16)
|
||||
if (x2 != fromR) {
|
||||
if (x2 != fromR)
|
||||
{
|
||||
if (vecin[x2])
|
||||
value += (1.0 - (fromR - fromC)) * vecin[x2];
|
||||
++x2;
|
||||
@@ -99,7 +103,8 @@ QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin,
|
||||
value += vecin[x2];
|
||||
|
||||
// subcase (17 >= x < 17.8)
|
||||
if (x2 == toCMinusOne) {
|
||||
if (x2 == toCMinusOne)
|
||||
{
|
||||
if (vecin[x2])
|
||||
value += (1.0 - (toC - toR)) * vecin[x2];
|
||||
++x2;
|
||||
@@ -121,12 +126,14 @@ QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin,
|
||||
bool PieceAvailabilityBar::updateImage(QImage &image)
|
||||
{
|
||||
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
|
||||
if (image2.isNull()) {
|
||||
if (image2.isNull())
|
||||
{
|
||||
qDebug() << "QImage image2() allocation failed, width():" << width();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_pieces.empty()) {
|
||||
if (m_pieces.empty())
|
||||
{
|
||||
image2.fill(backgroundColor());
|
||||
image = image2;
|
||||
return true;
|
||||
@@ -135,7 +142,8 @@ bool PieceAvailabilityBar::updateImage(QImage &image)
|
||||
QVector<float> scaledPieces = intToFloatVector(m_pieces, image2.width());
|
||||
|
||||
// filling image
|
||||
for (int x = 0; x < scaledPieces.size(); ++x) {
|
||||
for (int x = 0; x < scaledPieces.size(); ++x)
|
||||
{
|
||||
float piecesToValue = scaledPieces.at(x);
|
||||
image2.setPixel(x, 0, pieceColors()[piecesToValue * 255]);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ namespace
|
||||
{
|
||||
public:
|
||||
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
|
||||
: m_bytesPerPixel {((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
|
||||
: m_bytesPerPixel
|
||||
{((image.width() > 0) && (torrentInfo.totalSize() >= image.width()))
|
||||
? torrentInfo.totalSize() / image.width() : -1}
|
||||
, m_torrentInfo {torrentInfo}
|
||||
{
|
||||
@@ -133,7 +134,8 @@ void PiecesBar::clear()
|
||||
|
||||
bool PiecesBar::event(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::ToolTip) {
|
||||
if (e->type() == QEvent::ToolTip)
|
||||
{
|
||||
showToolTip(static_cast<QHelpEvent *>(e));
|
||||
return true;
|
||||
}
|
||||
@@ -167,17 +169,20 @@ void PiecesBar::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
QRect imageRect(borderWidth, borderWidth, width() - 2 * borderWidth, height() - 2 * borderWidth);
|
||||
if (m_image.isNull()) {
|
||||
if (m_image.isNull())
|
||||
{
|
||||
painter.setBrush(backgroundColor());
|
||||
painter.drawRect(imageRect);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
if (m_image.width() != imageRect.width())
|
||||
updateImage(m_image);
|
||||
painter.drawImage(imageRect, m_image);
|
||||
}
|
||||
|
||||
if (!m_highlitedRegion.isNull()) {
|
||||
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)};
|
||||
@@ -247,19 +252,23 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
|
||||
QString toolTipText;
|
||||
QTextStream stream(&toolTipText, QIODevice::WriteOnly);
|
||||
const bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
||||
if (showDetailedInformation && m_torrent->hasMetadata()) {
|
||||
if (showDetailedInformation && m_torrent->hasMetadata())
|
||||
{
|
||||
const int imagePos = e->pos().x() - borderWidth;
|
||||
if ((imagePos >=0) && (imagePos < m_image.width())) {
|
||||
if ((imagePos >=0) && (imagePos < m_image.width()))
|
||||
{
|
||||
stream << "<html><body>";
|
||||
PieceIndexToImagePos transform {m_torrent->info(), m_image};
|
||||
int pieceIndex = transform.pieceIndex(imagePos);
|
||||
const QVector<int> files {m_torrent->info().fileIndicesForPiece(pieceIndex)};
|
||||
|
||||
QString tooltipTitle;
|
||||
if (files.count() > 1) {
|
||||
if (files.count() > 1)
|
||||
{
|
||||
tooltipTitle = tr("Files in this piece:");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
if (m_torrent->info().fileSize(files.front()) == m_torrent->info().pieceLength(pieceIndex))
|
||||
tooltipTitle = tr("File in this piece");
|
||||
else
|
||||
@@ -268,14 +277,16 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
|
||||
|
||||
DetailedTooltipRenderer renderer(stream, tooltipTitle);
|
||||
|
||||
for (int f : files) {
|
||||
for (int f : files)
|
||||
{
|
||||
const QString filePath {m_torrent->info().filePath(f)};
|
||||
renderer(Utils::Misc::friendlyUnit(m_torrent->info().fileSize(f)), filePath);
|
||||
}
|
||||
stream << "</body></html>";
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
stream << simpleToolTipText();
|
||||
if (showDetailedInformation) // metadata are not available at this point
|
||||
stream << '\n' << tr("Wait until metadata become available to see detailed information");
|
||||
@@ -297,17 +308,20 @@ void PiecesBar::highlightFile(int imagePos)
|
||||
|
||||
int pieceIndex = transform.pieceIndex(imagePos);
|
||||
QVector<int> fileIndices {m_torrent->info().fileIndicesForPiece(pieceIndex)};
|
||||
if (fileIndices.count() == 1) {
|
||||
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) {
|
||||
if (newHighlitedRegion != m_highlitedRegion)
|
||||
{
|
||||
m_highlitedRegion = newHighlitedRegion;
|
||||
update();
|
||||
}
|
||||
}
|
||||
else if (!m_highlitedRegion.isEmpty()) {
|
||||
else if (!m_highlitedRegion.isEmpty())
|
||||
{
|
||||
m_highlitedRegion = QRect();
|
||||
update();
|
||||
}
|
||||
@@ -316,7 +330,8 @@ void PiecesBar::highlightFile(int imagePos)
|
||||
void PiecesBar::updatePieceColors()
|
||||
{
|
||||
m_pieceColors = QVector<QRgb>(256);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
float ratio = (i / 255.0);
|
||||
m_pieceColors[i] = mixTwoColors(backgroundColor().rgb(), pieceColor().rgb(), ratio);
|
||||
}
|
||||
|
||||
@@ -194,7 +194,8 @@ void PropertiesWidget::showPiecesDownloaded(bool show)
|
||||
|
||||
void PropertiesWidget::setVisibility(const bool visible)
|
||||
{
|
||||
if (!visible && (m_state == VISIBLE)) {
|
||||
if (!visible && (m_state == VISIBLE))
|
||||
{
|
||||
const int tabBarHeight = m_tabBar->geometry().height(); // take height before hiding
|
||||
auto *hSplitter = static_cast<QSplitter *>(parentWidget());
|
||||
m_ui->stackedProperties->setVisible(false);
|
||||
@@ -210,7 +211,8 @@ void PropertiesWidget::setVisibility(const bool visible)
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible && (m_state == REDUCED)) {
|
||||
if (visible && (m_state == REDUCED))
|
||||
{
|
||||
m_ui->stackedProperties->setVisible(true);
|
||||
auto *hSplitter = static_cast<QSplitter *>(parentWidget());
|
||||
if (m_handleWidth != -1)
|
||||
@@ -314,7 +316,8 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent
|
||||
// Hash
|
||||
m_ui->labelHashVal->setText(m_torrent->hash());
|
||||
m_propListModel->model()->clear();
|
||||
if (m_torrent->hasMetadata()) {
|
||||
if (m_torrent->hasMetadata())
|
||||
{
|
||||
// Creation date
|
||||
m_ui->labelCreatedOnVal->setText(m_torrent->creationDate().toString(Qt::DefaultLocaleShortDate));
|
||||
|
||||
@@ -333,7 +336,8 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent
|
||||
|
||||
// Expand single-item folders recursively
|
||||
QModelIndex currentIndex;
|
||||
while (m_propListModel->rowCount(currentIndex) == 1) {
|
||||
while (m_propListModel->rowCount(currentIndex) == 1)
|
||||
{
|
||||
currentIndex = m_propListModel->index(0, 0, currentIndex);
|
||||
m_ui->filesList->setExpanded(currentIndex, true);
|
||||
}
|
||||
@@ -350,7 +354,8 @@ void PropertiesWidget::readSettings()
|
||||
const Preferences *const pref = Preferences::instance();
|
||||
// Restore splitter sizes
|
||||
QStringList sizesStr = pref->getPropSplitterSizes().split(',');
|
||||
if (sizesStr.size() == 2) {
|
||||
if (sizesStr.size() == 2)
|
||||
{
|
||||
m_slideSizes << sizesStr.first().toInt();
|
||||
m_slideSizes << sizesStr.last().toInt();
|
||||
auto *hSplitter = static_cast<QSplitter *>(parentWidget());
|
||||
@@ -396,8 +401,10 @@ void PropertiesWidget::loadDynamicData()
|
||||
if (!m_torrent || (m_state != VISIBLE)) return;
|
||||
|
||||
// Transfer infos
|
||||
switch (m_ui->stackedProperties->currentIndex()) {
|
||||
case PropTabBar::MainTab: {
|
||||
switch (m_ui->stackedProperties->currentIndex())
|
||||
{
|
||||
case PropTabBar::MainTab:
|
||||
{
|
||||
m_ui->labelWastedVal->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
|
||||
|
||||
m_ui->labelUpTotalVal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalUpload())
|
||||
@@ -456,16 +463,19 @@ void PropertiesWidget::loadDynamicData()
|
||||
|
||||
m_ui->labelAddedOnVal->setText(m_torrent->addedTime().toString(Qt::DefaultLocaleShortDate));
|
||||
|
||||
if (m_torrent->hasMetadata()) {
|
||||
if (m_torrent->hasMetadata())
|
||||
{
|
||||
m_ui->labelTotalPiecesVal->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent->pieceLength())).arg(m_torrent->piecesHave()));
|
||||
|
||||
if (!m_torrent->isSeed() && !m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking()) {
|
||||
if (!m_torrent->isSeed() && !m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking())
|
||||
{
|
||||
// Pieces availability
|
||||
showPiecesAvailability(true);
|
||||
m_piecesAvailability->setAvailability(m_torrent->pieceAvailability());
|
||||
m_ui->labelAverageAvailabilityVal->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
showPiecesAvailability(false);
|
||||
}
|
||||
|
||||
@@ -474,7 +484,8 @@ void PropertiesWidget::loadDynamicData()
|
||||
m_ui->labelProgressVal->setText(Utils::String::fromDouble(progress, 1) + '%');
|
||||
m_downloadedPieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
showPiecesAvailability(false);
|
||||
}
|
||||
}
|
||||
@@ -489,7 +500,8 @@ void PropertiesWidget::loadDynamicData()
|
||||
break;
|
||||
case PropTabBar::FilesTab:
|
||||
// Files progress
|
||||
if (m_torrent->hasMetadata()) {
|
||||
if (m_torrent->hasMetadata())
|
||||
{
|
||||
qDebug("Updating priorities in files tab");
|
||||
m_ui->filesList->setUpdatesEnabled(false);
|
||||
m_propListModel->model()->updateFilesProgress(m_torrent->filesProgress());
|
||||
@@ -511,7 +523,8 @@ void PropertiesWidget::loadUrlSeeds()
|
||||
qDebug("Loading URL seeds");
|
||||
const QVector<QUrl> hcSeeds = m_torrent->urlSeeds();
|
||||
// Add url seeds
|
||||
for (const QUrl &hcSeed : hcSeeds) {
|
||||
for (const QUrl &hcSeed : hcSeeds)
|
||||
{
|
||||
qDebug("Loading URL seed: %s", qUtf8Printable(hcSeed.toString()));
|
||||
new QListWidgetItem(hcSeed.toString(), m_ui->listWebSeeds);
|
||||
}
|
||||
@@ -519,7 +532,8 @@ void PropertiesWidget::loadUrlSeeds()
|
||||
|
||||
QString PropertiesWidget::getFullPath(const QModelIndex &index) const
|
||||
{
|
||||
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType) {
|
||||
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType)
|
||||
{
|
||||
const int fileIdx = m_propListModel->getFileIndex(index);
|
||||
const QString filename {m_torrent->filePath(fileIdx)};
|
||||
const QDir saveDir {m_torrent->savePath(true)};
|
||||
@@ -568,7 +582,8 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (selectedRows.size() == 1) {
|
||||
if (selectedRows.size() == 1)
|
||||
{
|
||||
const QModelIndex index = selectedRows[0];
|
||||
|
||||
const QAction *actOpen = menu->addAction(UIThemeManager::instance()->getIcon("folder-documents"), tr("Open"));
|
||||
@@ -583,12 +598,14 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
|
||||
menu->addSeparator();
|
||||
}
|
||||
|
||||
if (!m_torrent->isSeed()) {
|
||||
if (!m_torrent->isSeed())
|
||||
{
|
||||
QMenu *subMenu = menu->addMenu(tr("Priority"));
|
||||
|
||||
const auto applyPriorities = [this, selectedRows](const BitTorrent::DownloadPriority prio)
|
||||
{
|
||||
for (const QModelIndex &index : selectedRows) {
|
||||
for (const QModelIndex &index : selectedRows)
|
||||
{
|
||||
m_propListModel->setData(
|
||||
m_propListModel->index(index.row(), PRIORITY, index.parent()), static_cast<int>(prio));
|
||||
}
|
||||
@@ -646,7 +663,8 @@ void PropertiesWidget::displayWebSeedListMenu(const QPoint &)
|
||||
const QAction *actAdd = menu->addAction(UIThemeManager::instance()->getIcon("list-add"), tr("New Web seed"));
|
||||
connect(actAdd, &QAction::triggered, this, &PropertiesWidget::askWebSeed);
|
||||
|
||||
if (!rows.isEmpty()) {
|
||||
if (!rows.isEmpty())
|
||||
{
|
||||
const QAction *actDel = menu->addAction(UIThemeManager::instance()->getIcon("list-remove"), tr("Remove Web seed"));
|
||||
connect(actDel, &QAction::triggered, this, &PropertiesWidget::deleteSelectedUrlSeeds);
|
||||
|
||||
@@ -673,16 +691,20 @@ void PropertiesWidget::openSelectedFile()
|
||||
void PropertiesWidget::configure()
|
||||
{
|
||||
// Speed widget
|
||||
if (Preferences::instance()->isSpeedWidgetEnabled()) {
|
||||
if (!m_speedWidget || !qobject_cast<SpeedWidget *>(m_speedWidget)) {
|
||||
if (Preferences::instance()->isSpeedWidgetEnabled())
|
||||
{
|
||||
if (!m_speedWidget || !qobject_cast<SpeedWidget *>(m_speedWidget))
|
||||
{
|
||||
m_ui->speedLayout->removeWidget(m_speedWidget);
|
||||
delete m_speedWidget;
|
||||
m_speedWidget = new SpeedWidget {this};
|
||||
m_ui->speedLayout->addWidget(m_speedWidget);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!m_speedWidget || !qobject_cast<QLabel *>(m_speedWidget)) {
|
||||
else
|
||||
{
|
||||
if (!m_speedWidget || !qobject_cast<QLabel *>(m_speedWidget))
|
||||
{
|
||||
m_ui->speedLayout->removeWidget(m_speedWidget);
|
||||
delete m_speedWidget;
|
||||
auto *label = new QLabel(tr("<center><b>Speed graphs are disabled</b><p>You may change this setting in Advanced Options </center>"), this);
|
||||
@@ -702,7 +724,8 @@ void PropertiesWidget::askWebSeed()
|
||||
QLatin1String("http://www."), &ok);
|
||||
if (!ok) return;
|
||||
qDebug("Adding %s web seed", qUtf8Printable(urlSeed));
|
||||
if (!m_ui->listWebSeeds->findItems(urlSeed, Qt::MatchFixedString).empty()) {
|
||||
if (!m_ui->listWebSeeds->findItems(urlSeed, Qt::MatchFixedString).empty())
|
||||
{
|
||||
QMessageBox::warning(this, "qBittorrent",
|
||||
tr("This URL seed is already in the list."),
|
||||
QMessageBox::Ok);
|
||||
@@ -755,7 +778,8 @@ void PropertiesWidget::editWebSeed()
|
||||
oldSeed, &result);
|
||||
if (!result) return;
|
||||
|
||||
if (!m_ui->listWebSeeds->findItems(newSeed, Qt::MatchFixedString).empty()) {
|
||||
if (!m_ui->listWebSeeds->findItems(newSeed, Qt::MatchFixedString).empty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("qBittorrent"),
|
||||
tr("This URL seed is already in the list."),
|
||||
QMessageBox::Ok);
|
||||
@@ -781,11 +805,13 @@ void PropertiesWidget::filteredFilesChanged()
|
||||
void PropertiesWidget::filterText(const QString &filter)
|
||||
{
|
||||
m_propListModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::WildcardUnix));
|
||||
if (filter.isEmpty()) {
|
||||
if (filter.isEmpty())
|
||||
{
|
||||
m_ui->filesList->collapseAll();
|
||||
m_ui->filesList->expand(m_propListModel->index(0, 0));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->filesList->expandAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +72,13 @@ void PropListDelegate::initProgressStyleOption(QStyleOptionProgressBar &option,
|
||||
ProgressBarDelegate::initProgressStyleOption(option, index);
|
||||
const int priority
|
||||
= index.sibling(index.row(), PRIORITY).data(TorrentContentModel::UnderlyingDataRole).toInt();
|
||||
if (static_cast<BitTorrent::DownloadPriority>(priority) == BitTorrent::DownloadPriority::Ignored) {
|
||||
if (static_cast<BitTorrent::DownloadPriority>(priority) == BitTorrent::DownloadPriority::Ignored)
|
||||
{
|
||||
option.state &= ~QStyle::State_Enabled;
|
||||
option.palette = progressBarDisabledPalette();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
option.state |= QStyle::State_Enabled;
|
||||
}
|
||||
}
|
||||
@@ -86,7 +88,8 @@ void PropListDelegate::setEditorData(QWidget *editor, const QModelIndex &index)
|
||||
auto *combobox = static_cast<QComboBox *>(editor);
|
||||
// Set combobox index
|
||||
const int priority = index.data(TorrentContentModel::UnderlyingDataRole).toInt();
|
||||
switch (static_cast<BitTorrent::DownloadPriority>(priority)) {
|
||||
switch (static_cast<BitTorrent::DownloadPriority>(priority))
|
||||
{
|
||||
case BitTorrent::DownloadPriority::Ignored:
|
||||
combobox->setCurrentIndex(0);
|
||||
break;
|
||||
@@ -106,7 +109,8 @@ QWidget *PropListDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
|
||||
{
|
||||
if (index.column() != PRIORITY) return nullptr;
|
||||
|
||||
if (m_properties) {
|
||||
if (m_properties)
|
||||
{
|
||||
const BitTorrent::TorrentHandle *torrent = m_properties->getCurrentTorrent();
|
||||
if (!torrent || !torrent->hasMetadata() || torrent->isSeed())
|
||||
return nullptr;
|
||||
@@ -131,7 +135,8 @@ void PropListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||
const int value = combobox->currentIndex();
|
||||
|
||||
BitTorrent::DownloadPriority prio = BitTorrent::DownloadPriority::Normal; // NORMAL
|
||||
switch (value) {
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
prio = BitTorrent::DownloadPriority::Ignored; // IGNORED
|
||||
break;
|
||||
|
||||
@@ -122,8 +122,10 @@ void PropTabBar::setCurrentIndex(int index)
|
||||
if (index >= m_btnGroup->buttons().size())
|
||||
index = 0;
|
||||
// If asked to hide or if the currently selected tab is clicked
|
||||
if ((index < 0) || (m_currentIndex == index)) {
|
||||
if (m_currentIndex >= 0) {
|
||||
if ((index < 0) || (m_currentIndex == index))
|
||||
{
|
||||
if (m_currentIndex >= 0)
|
||||
{
|
||||
m_btnGroup->button(m_currentIndex)->setDown(false);
|
||||
m_currentIndex = -1;
|
||||
emit visibilityToggled(false);
|
||||
@@ -131,10 +133,12 @@ void PropTabBar::setCurrentIndex(int index)
|
||||
return;
|
||||
}
|
||||
// Unselect previous tab
|
||||
if (m_currentIndex >= 0) {
|
||||
if (m_currentIndex >= 0)
|
||||
{
|
||||
m_btnGroup->button(m_currentIndex)->setDown(false);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Nothing was selected, show!
|
||||
emit visibilityToggled(true);
|
||||
}
|
||||
|
||||
@@ -79,26 +79,30 @@ namespace
|
||||
if (value <= 12.0) return {12, SizeUnit::Byte};
|
||||
|
||||
SizeUnit calculatedUnit = SizeUnit::Byte;
|
||||
while (value > 1024) {
|
||||
while (value > 1024)
|
||||
{
|
||||
value /= 1024;
|
||||
calculatedUnit = static_cast<SizeUnit>(static_cast<int>(calculatedUnit) + 1);
|
||||
}
|
||||
|
||||
if (value > 100.0) {
|
||||
if (value > 100.0)
|
||||
{
|
||||
int roundedValue = static_cast<int>(value / 40) * 40;
|
||||
while (roundedValue < value)
|
||||
roundedValue += 40;
|
||||
return {static_cast<double>(roundedValue), calculatedUnit};
|
||||
}
|
||||
|
||||
if (value > 10.0) {
|
||||
if (value > 10.0)
|
||||
{
|
||||
int roundedValue = static_cast<int>(value / 4) * 4;
|
||||
while (roundedValue < value)
|
||||
roundedValue += 4;
|
||||
return {static_cast<double>(roundedValue), calculatedUnit};
|
||||
}
|
||||
|
||||
for (const auto &roundedValue : roundingTable) {
|
||||
for (const auto &roundedValue : roundingTable)
|
||||
{
|
||||
if (value <= roundedValue)
|
||||
return {roundedValue, calculatedUnit};
|
||||
}
|
||||
@@ -215,7 +219,8 @@ void SpeedPlotView::setPeriod(const TimePeriod period)
|
||||
{
|
||||
m_period = period;
|
||||
|
||||
switch (period) {
|
||||
switch (period)
|
||||
{
|
||||
case SpeedPlotView::MIN1:
|
||||
m_viewablePointsCount = MIN1_SEC;
|
||||
m_currentData = &m_data5Min;
|
||||
@@ -266,7 +271,8 @@ quint64 SpeedPlotView::maxYValue()
|
||||
boost::circular_buffer<PointData> &queue = getCurrentData();
|
||||
|
||||
quint64 maxYValue = 0;
|
||||
for (int id = UP; id < NB_GRAPHS; ++id) {
|
||||
for (int id = UP; id < NB_GRAPHS; ++id)
|
||||
{
|
||||
|
||||
if (!m_properties[static_cast<GraphID>(id)].enable)
|
||||
continue;
|
||||
@@ -292,7 +298,8 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
|
||||
rect.adjust(0, fontMetrics.height(), 0, 0); // Add top padding for top speed text
|
||||
|
||||
// draw Y axis speed labels
|
||||
const QVector<QString> speedLabels = {
|
||||
const QVector<QString> speedLabels =
|
||||
{
|
||||
formatLabel(niceScale.arg, niceScale.unit),
|
||||
formatLabel((0.75 * niceScale.arg), niceScale.unit),
|
||||
formatLabel((0.50 * niceScale.arg), niceScale.unit),
|
||||
@@ -311,7 +318,8 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
|
||||
#endif
|
||||
|
||||
int i = 0;
|
||||
for (const QString &label : speedLabels) {
|
||||
for (const QString &label : speedLabels)
|
||||
{
|
||||
QRectF labelRect(rect.topLeft() + QPointF(-yAxisWidth, (i++) * 0.25 * rect.height() - fontMetrics.height()),
|
||||
QSizeF(2 * yAxisWidth, fontMetrics.height()));
|
||||
painter.drawText(labelRect, label, Qt::AlignRight | Qt::AlignTop);
|
||||
@@ -333,7 +341,8 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
|
||||
painter.drawLine(fullRect.left(), rect.bottom(), rect.right(), rect.bottom());
|
||||
|
||||
const int TIME_AXIS_DIVISIONS = 6;
|
||||
for (int i = 0; i < TIME_AXIS_DIVISIONS; ++i) {
|
||||
for (int i = 0; i < TIME_AXIS_DIVISIONS; ++i)
|
||||
{
|
||||
const int x = rect.left() + (i * rect.width()) / TIME_AXIS_DIVISIONS;
|
||||
painter.drawLine(x, fullRect.top(), x, fullRect.bottom());
|
||||
}
|
||||
@@ -349,12 +358,14 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
|
||||
|
||||
boost::circular_buffer<PointData> &queue = getCurrentData();
|
||||
|
||||
for (int id = UP; id < NB_GRAPHS; ++id) {
|
||||
for (int id = UP; id < NB_GRAPHS; ++id)
|
||||
{
|
||||
if (!m_properties[static_cast<GraphID>(id)].enable)
|
||||
continue;
|
||||
|
||||
QVector<QPoint> points;
|
||||
for (int i = static_cast<int>(queue.size()) - 1, j = 0; (i >= 0) && (j < m_viewablePointsCount); --i, ++j) {
|
||||
for (int i = static_cast<int>(queue.size()) - 1, j = 0; (i >= 0) && (j < m_viewablePointsCount); --i, ++j)
|
||||
{
|
||||
|
||||
int newX = rect.right() - j * xTickSize;
|
||||
int newY = rect.bottom() - queue[i].y[id] * yMultiplier;
|
||||
@@ -371,7 +382,8 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
|
||||
|
||||
double legendHeight = 0;
|
||||
int legendWidth = 0;
|
||||
for (const auto &property : asConst(m_properties)) {
|
||||
for (const auto &property : asConst(m_properties))
|
||||
{
|
||||
if (!property.enable)
|
||||
continue;
|
||||
|
||||
@@ -391,7 +403,8 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
|
||||
painter.fillRect(legendBackgroundRect, legendBackgroundColor);
|
||||
|
||||
i = 0;
|
||||
for (const auto &property : asConst(m_properties)) {
|
||||
for (const auto &property : asConst(m_properties))
|
||||
{
|
||||
if (!property.enable)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -92,7 +92,8 @@ SpeedWidget::SpeedWidget(PropertiesWidget *parent)
|
||||
|
||||
m_graphsMenuActions = m_graphsMenu->actions();
|
||||
|
||||
for (int id = SpeedPlotView::UP; id < SpeedPlotView::NB_GRAPHS; ++id) {
|
||||
for (int id = SpeedPlotView::UP; id < SpeedPlotView::NB_GRAPHS; ++id)
|
||||
{
|
||||
QAction *action = m_graphsMenuActions.at(id);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(true);
|
||||
@@ -168,7 +169,8 @@ void SpeedWidget::loadSettings()
|
||||
m_periodCombobox->setCurrentIndex(periodIndex);
|
||||
onPeriodChange(static_cast<SpeedPlotView::TimePeriod>(periodIndex));
|
||||
|
||||
for (int id = SpeedPlotView::UP; id < SpeedPlotView::NB_GRAPHS; ++id) {
|
||||
for (int id = SpeedPlotView::UP; id < SpeedPlotView::NB_GRAPHS; ++id)
|
||||
{
|
||||
QAction *action = m_graphsMenuActions.at(id);
|
||||
bool enable = preferences->getSpeedWidgetGraphEnable(id);
|
||||
|
||||
@@ -183,7 +185,8 @@ void SpeedWidget::saveSettings() const
|
||||
|
||||
preferences->setSpeedWidgetPeriod(m_periodCombobox->currentIndex());
|
||||
|
||||
for (int id = SpeedPlotView::UP; id < SpeedPlotView::NB_GRAPHS; ++id) {
|
||||
for (int id = SpeedPlotView::UP; id < SpeedPlotView::NB_GRAPHS; ++id)
|
||||
{
|
||||
QAction *action = m_graphsMenuActions.at(id);
|
||||
preferences->setSpeedWidgetGraphEnable(id, action->isChecked());
|
||||
}
|
||||
|
||||
@@ -152,7 +152,8 @@ QVector<QTreeWidgetItem *> TrackerListWidget::getSelectedTrackerItems() const
|
||||
QVector<QTreeWidgetItem *> selectedTrackers;
|
||||
selectedTrackers.reserve(selectedTrackerItems.size());
|
||||
|
||||
for (QTreeWidgetItem *item : selectedTrackerItems) {
|
||||
for (QTreeWidgetItem *item : selectedTrackerItems)
|
||||
{
|
||||
if (indexOfTopLevelItem(item) >= NB_STICKY_ITEM) // Ignore STICKY ITEMS
|
||||
selectedTrackers << item;
|
||||
}
|
||||
@@ -171,7 +172,8 @@ void TrackerListWidget::setRowColor(const int row, const QColor &color)
|
||||
void TrackerListWidget::moveSelectionUp()
|
||||
{
|
||||
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
|
||||
if (!torrent) {
|
||||
if (!torrent)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
@@ -179,9 +181,11 @@ void TrackerListWidget::moveSelectionUp()
|
||||
if (selectedTrackerItems.isEmpty()) return;
|
||||
|
||||
bool change = false;
|
||||
for (QTreeWidgetItem *item : selectedTrackerItems) {
|
||||
for (QTreeWidgetItem *item : selectedTrackerItems)
|
||||
{
|
||||
int index = indexOfTopLevelItem(item);
|
||||
if (index > NB_STICKY_ITEM) {
|
||||
if (index > NB_STICKY_ITEM)
|
||||
{
|
||||
insertTopLevelItem(index - 1, takeTopLevelItem(index));
|
||||
change = true;
|
||||
}
|
||||
@@ -197,7 +201,8 @@ void TrackerListWidget::moveSelectionUp()
|
||||
// Update torrent trackers
|
||||
QVector<BitTorrent::TrackerEntry> trackers;
|
||||
trackers.reserve(topLevelItemCount());
|
||||
for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i) {
|
||||
for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i)
|
||||
{
|
||||
const QString trackerURL = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
|
||||
BitTorrent::TrackerEntry e(trackerURL);
|
||||
e.setTier(i - NB_STICKY_ITEM);
|
||||
@@ -213,7 +218,8 @@ void TrackerListWidget::moveSelectionUp()
|
||||
void TrackerListWidget::moveSelectionDown()
|
||||
{
|
||||
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
|
||||
if (!torrent) {
|
||||
if (!torrent)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
@@ -221,9 +227,11 @@ void TrackerListWidget::moveSelectionDown()
|
||||
if (selectedTrackerItems.isEmpty()) return;
|
||||
|
||||
bool change = false;
|
||||
for (int i = selectedItems().size() - 1; i >= 0; --i) {
|
||||
for (int i = selectedItems().size() - 1; i >= 0; --i)
|
||||
{
|
||||
int index = indexOfTopLevelItem(selectedTrackerItems.at(i));
|
||||
if (index < (topLevelItemCount() - 1)) {
|
||||
if (index < (topLevelItemCount() - 1))
|
||||
{
|
||||
insertTopLevelItem(index + 1, takeTopLevelItem(index));
|
||||
change = true;
|
||||
}
|
||||
@@ -239,7 +247,8 @@ void TrackerListWidget::moveSelectionDown()
|
||||
// Update torrent trackers
|
||||
QVector<BitTorrent::TrackerEntry> trackers;
|
||||
trackers.reserve(topLevelItemCount());
|
||||
for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i) {
|
||||
for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i)
|
||||
{
|
||||
const QString trackerURL = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
|
||||
BitTorrent::TrackerEntry e(trackerURL);
|
||||
e.setTier(i - NB_STICKY_ITEM);
|
||||
@@ -294,7 +303,8 @@ void TrackerListWidget::loadStickyItems(const BitTorrent::TorrentHandle *torrent
|
||||
else
|
||||
m_LSDItem->setText(COL_STATUS, disabled);
|
||||
|
||||
if (torrent->isPrivate()) {
|
||||
if (torrent->isPrivate())
|
||||
{
|
||||
QString privateMsg = tr("This torrent is private");
|
||||
m_DHTItem->setText(COL_MSG, privateMsg);
|
||||
m_PEXItem->setText(COL_MSG, privateMsg);
|
||||
@@ -304,22 +314,26 @@ void TrackerListWidget::loadStickyItems(const BitTorrent::TorrentHandle *torrent
|
||||
// XXX: libtorrent should provide this info...
|
||||
// Count peers from DHT, PeX, LSD
|
||||
uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0;
|
||||
for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers())) {
|
||||
for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers()))
|
||||
{
|
||||
if (peer.isConnecting()) continue;
|
||||
|
||||
if (peer.fromDHT()) {
|
||||
if (peer.fromDHT())
|
||||
{
|
||||
if (peer.isSeed())
|
||||
++seedsDHT;
|
||||
else
|
||||
++peersDHT;
|
||||
}
|
||||
if (peer.fromPeX()) {
|
||||
if (peer.fromPeX())
|
||||
{
|
||||
if (peer.isSeed())
|
||||
++seedsPeX;
|
||||
else
|
||||
++peersPeX;
|
||||
}
|
||||
if (peer.fromLSD()) {
|
||||
if (peer.fromLSD())
|
||||
{
|
||||
if (peer.isSeed())
|
||||
++seedsLSD;
|
||||
else
|
||||
@@ -347,17 +361,20 @@ void TrackerListWidget::loadTrackers()
|
||||
const QHash<QString, BitTorrent::TrackerInfo> trackerData = torrent->trackerInfos();
|
||||
QStringList oldTrackerURLs = m_trackerItems.keys();
|
||||
|
||||
for (const BitTorrent::TrackerEntry &entry : asConst(torrent->trackers())) {
|
||||
for (const BitTorrent::TrackerEntry &entry : asConst(torrent->trackers()))
|
||||
{
|
||||
const QString trackerURL = entry.url();
|
||||
|
||||
QTreeWidgetItem *item = m_trackerItems.value(trackerURL, nullptr);
|
||||
if (!item) {
|
||||
if (!item)
|
||||
{
|
||||
item = new QTreeWidgetItem();
|
||||
item->setText(COL_URL, trackerURL);
|
||||
addTopLevelItem(item);
|
||||
m_trackerItems[trackerURL] = item;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
oldTrackerURLs.removeOne(trackerURL);
|
||||
}
|
||||
|
||||
@@ -365,7 +382,8 @@ void TrackerListWidget::loadTrackers()
|
||||
|
||||
const BitTorrent::TrackerInfo data = trackerData.value(trackerURL);
|
||||
|
||||
switch (entry.status()) {
|
||||
switch (entry.status())
|
||||
{
|
||||
case BitTorrent::TrackerEntry::Working:
|
||||
item->setText(COL_STATUS, tr("Working"));
|
||||
item->setText(COL_MSG, "");
|
||||
@@ -427,7 +445,8 @@ void TrackerListWidget::copyTrackerUrl()
|
||||
if (selectedTrackerItems.isEmpty()) return;
|
||||
|
||||
QStringList urlsToCopy;
|
||||
for (const QTreeWidgetItem *item : selectedTrackerItems) {
|
||||
for (const QTreeWidgetItem *item : selectedTrackerItems)
|
||||
{
|
||||
QString trackerURL = item->data(COL_URL, Qt::DisplayRole).toString();
|
||||
qDebug() << QString("Copy: ") + trackerURL;
|
||||
urlsToCopy << trackerURL;
|
||||
@@ -439,7 +458,8 @@ void TrackerListWidget::copyTrackerUrl()
|
||||
void TrackerListWidget::deleteSelectedTrackers()
|
||||
{
|
||||
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
|
||||
if (!torrent) {
|
||||
if (!torrent)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
@@ -448,7 +468,8 @@ void TrackerListWidget::deleteSelectedTrackers()
|
||||
if (selectedTrackerItems.isEmpty()) return;
|
||||
|
||||
QStringList urlsToRemove;
|
||||
for (const QTreeWidgetItem *item : selectedTrackerItems) {
|
||||
for (const QTreeWidgetItem *item : selectedTrackerItems)
|
||||
{
|
||||
QString trackerURL = item->data(COL_URL, Qt::DisplayRole).toString();
|
||||
urlsToRemove << trackerURL;
|
||||
m_trackerItems.remove(trackerURL);
|
||||
@@ -460,7 +481,8 @@ void TrackerListWidget::deleteSelectedTrackers()
|
||||
QVector<BitTorrent::TrackerEntry> remainingTrackers;
|
||||
remainingTrackers.reserve(trackers.size());
|
||||
|
||||
for (const BitTorrent::TrackerEntry &entry : trackers) {
|
||||
for (const BitTorrent::TrackerEntry &entry : trackers)
|
||||
{
|
||||
if (!urlsToRemove.contains(entry.url()))
|
||||
remainingTrackers.push_back(entry);
|
||||
}
|
||||
@@ -487,7 +509,8 @@ void TrackerListWidget::editSelectedTracker()
|
||||
QLineEdit::Normal, trackerURL.toString(), &ok).trimmed();
|
||||
if (!ok) return;
|
||||
|
||||
if (!newTrackerURL.isValid()) {
|
||||
if (!newTrackerURL.isValid())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL entered is invalid."));
|
||||
return;
|
||||
}
|
||||
@@ -495,13 +518,16 @@ void TrackerListWidget::editSelectedTracker()
|
||||
|
||||
QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
|
||||
bool match = false;
|
||||
for (BitTorrent::TrackerEntry &entry : trackers) {
|
||||
if (newTrackerURL == QUrl(entry.url())) {
|
||||
for (BitTorrent::TrackerEntry &entry : trackers)
|
||||
{
|
||||
if (newTrackerURL == QUrl(entry.url()))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL already exists."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!match && (trackerURL == QUrl(entry.url()))) {
|
||||
if (!match && (trackerURL == QUrl(entry.url())))
|
||||
{
|
||||
match = true;
|
||||
BitTorrent::TrackerEntry newEntry(newTrackerURL.toString());
|
||||
newEntry.setTier(entry.tier());
|
||||
@@ -525,16 +551,20 @@ void TrackerListWidget::reannounceSelected()
|
||||
|
||||
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
|
||||
|
||||
for (const QTreeWidgetItem *item : selItems) {
|
||||
for (const QTreeWidgetItem *item : selItems)
|
||||
{
|
||||
// DHT case
|
||||
if (item == m_DHTItem) {
|
||||
if (item == m_DHTItem)
|
||||
{
|
||||
torrent->forceDHTAnnounce();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Trackers case
|
||||
for (int i = 0; i < trackers.size(); ++i) {
|
||||
if (item->text(COL_URL) == trackers[i].url()) {
|
||||
for (int i = 0; i < trackers.size(); ++i)
|
||||
{
|
||||
if (item->text(COL_URL) == trackers[i].url())
|
||||
{
|
||||
torrent->forceReannounce(i);
|
||||
break;
|
||||
}
|
||||
@@ -556,7 +586,8 @@ void TrackerListWidget::showTrackerListMenu(const QPoint &)
|
||||
const QAction *addAct = menu->addAction(UIThemeManager::instance()->getIcon("list-add"), tr("Add a new tracker..."));
|
||||
connect(addAct, &QAction::triggered, this, &TrackerListWidget::askForTrackers);
|
||||
|
||||
if (!getSelectedTrackerItems().isEmpty()) {
|
||||
if (!getSelectedTrackerItems().isEmpty())
|
||||
{
|
||||
const QAction *editAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"),tr("Edit tracker URL..."));
|
||||
connect(editAct, &QAction::triggered, this, &TrackerListWidget::editSelectedTracker);
|
||||
|
||||
@@ -567,7 +598,8 @@ void TrackerListWidget::showTrackerListMenu(const QPoint &)
|
||||
connect(copyAct, &QAction::triggered, this, &TrackerListWidget::copyTrackerUrl);
|
||||
}
|
||||
|
||||
if (!torrent->isPaused()) {
|
||||
if (!torrent->isPaused())
|
||||
{
|
||||
const QAction *reannounceSelAct = menu->addAction(UIThemeManager::instance()->getIcon("view-refresh"), tr("Force reannounce to selected trackers"));
|
||||
connect(reannounceSelAct, &QAction::triggered, this, &TrackerListWidget::reannounceSelected);
|
||||
|
||||
@@ -597,7 +629,8 @@ void TrackerListWidget::saveSettings() const
|
||||
|
||||
QStringList TrackerListWidget::headerLabels()
|
||||
{
|
||||
return {
|
||||
return
|
||||
{
|
||||
tr("Tier")
|
||||
, tr("URL")
|
||||
, tr("Status")
|
||||
@@ -612,7 +645,8 @@ QStringList TrackerListWidget::headerLabels()
|
||||
int TrackerListWidget::visibleColumnsCount() const
|
||||
{
|
||||
int visibleCols = 0;
|
||||
for (int i = 0; i < COL_COUNT; ++i) {
|
||||
for (int i = 0; i < COL_COUNT; ++i)
|
||||
{
|
||||
if (!isColumnHidden(i))
|
||||
++visibleCols;
|
||||
}
|
||||
@@ -626,7 +660,8 @@ void TrackerListWidget::displayToggleColumnsMenu(const QPoint &)
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->setTitle(tr("Column visibility"));
|
||||
|
||||
for (int i = 0; i < COL_COUNT; ++i) {
|
||||
for (int i = 0; i < COL_COUNT; ++i)
|
||||
{
|
||||
QAction *myAct = menu->addAction(headerLabels().at(i));
|
||||
myAct->setCheckable(true);
|
||||
myAct->setChecked(!isColumnHidden(i));
|
||||
|
||||
@@ -59,7 +59,8 @@ QStringList TrackersAdditionDialog::newTrackers() const
|
||||
const QString plainText = m_ui->textEditTrackersList->toPlainText();
|
||||
|
||||
QStringList cleanTrackers;
|
||||
for (QStringRef url : asConst(plainText.splitRef('\n'))) {
|
||||
for (QStringRef url : asConst(plainText.splitRef('\n')))
|
||||
{
|
||||
url = url.trimmed();
|
||||
if (!url.isEmpty())
|
||||
cleanTrackers << url.toString();
|
||||
@@ -78,7 +79,8 @@ void TrackersAdditionDialog::on_uTorrentListButton_clicked()
|
||||
|
||||
void TrackersAdditionDialog::torrentListDownloadFinished(const Net::DownloadResult &result)
|
||||
{
|
||||
if (result.status != Net::DownloadStatus::Success) {
|
||||
if (result.status != Net::DownloadStatus::Success)
|
||||
{
|
||||
// To restore the cursor ...
|
||||
setCursor(Qt::ArrowCursor);
|
||||
m_ui->uTorrentListButton->setEnabled(true);
|
||||
@@ -92,7 +94,8 @@ void TrackersAdditionDialog::torrentListDownloadFinished(const Net::DownloadResu
|
||||
const QStringList trackersFromUser = m_ui->textEditTrackersList->toPlainText().split('\n');
|
||||
QVector<BitTorrent::TrackerEntry> existingTrackers = m_torrent->trackers();
|
||||
existingTrackers.reserve(trackersFromUser.size());
|
||||
for (const QString &userURL : trackersFromUser) {
|
||||
for (const QString &userURL : trackersFromUser)
|
||||
{
|
||||
const BitTorrent::TrackerEntry userTracker(userURL);
|
||||
if (!existingTrackers.contains(userTracker))
|
||||
existingTrackers << userTracker;
|
||||
@@ -105,12 +108,14 @@ void TrackersAdditionDialog::torrentListDownloadFinished(const Net::DownloadResu
|
||||
QBuffer buffer;
|
||||
buffer.setData(result.data);
|
||||
buffer.open(QBuffer::ReadOnly);
|
||||
while (!buffer.atEnd()) {
|
||||
while (!buffer.atEnd())
|
||||
{
|
||||
const QString line = buffer.readLine().trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
|
||||
BitTorrent::TrackerEntry newTracker(line);
|
||||
if (!existingTrackers.contains(newTracker)) {
|
||||
if (!existingTrackers.contains(newTracker))
|
||||
{
|
||||
m_ui->textEditTrackersList->insertPlainText(line + '\n');
|
||||
++nb;
|
||||
}
|
||||
|
||||
@@ -65,13 +65,16 @@ void ArticleListWidget::setRSSItem(RSS::Item *rssItem, bool unreadOnly)
|
||||
|
||||
m_unreadOnly = unreadOnly;
|
||||
m_rssItem = rssItem;
|
||||
if (m_rssItem) {
|
||||
if (m_rssItem)
|
||||
{
|
||||
connect(m_rssItem, &RSS::Item::newArticle, this, &ArticleListWidget::handleArticleAdded);
|
||||
connect(m_rssItem, &RSS::Item::articleRead, this, &ArticleListWidget::handleArticleRead);
|
||||
connect(m_rssItem, &RSS::Item::articleAboutToBeRemoved, this, &ArticleListWidget::handleArticleAboutToBeRemoved);
|
||||
|
||||
for (const auto article : asConst(rssItem->articles())) {
|
||||
if (!(m_unreadOnly && article->isRead())) {
|
||||
for (const auto article : asConst(rssItem->articles()))
|
||||
{
|
||||
if (!(m_unreadOnly && article->isRead()))
|
||||
{
|
||||
auto item = createItem(article);
|
||||
addItem(item);
|
||||
m_rssArticleToListItemMapping.insert(article, item);
|
||||
@@ -84,7 +87,8 @@ void ArticleListWidget::setRSSItem(RSS::Item *rssItem, bool unreadOnly)
|
||||
|
||||
void ArticleListWidget::handleArticleAdded(RSS::Article *rssArticle)
|
||||
{
|
||||
if (!(m_unreadOnly && rssArticle->isRead())) {
|
||||
if (!(m_unreadOnly && rssArticle->isRead()))
|
||||
{
|
||||
auto item = createItem(rssArticle);
|
||||
insertItem(0, item);
|
||||
m_rssArticleToListItemMapping.insert(rssArticle, item);
|
||||
@@ -124,13 +128,15 @@ QListWidgetItem *ArticleListWidget::createItem(RSS::Article *article) const
|
||||
|
||||
item->setData(Qt::DisplayRole, article->title());
|
||||
item->setData(Qt::UserRole, reinterpret_cast<quintptr>(article));
|
||||
if (article->isRead()) {
|
||||
if (article->isRead())
|
||||
{
|
||||
const QColor defaultColor {palette().color(QPalette::Inactive, QPalette::WindowText)};
|
||||
const QBrush foregroundBrush {UIThemeManager::instance()->getColor("RSS.ReadArticle", defaultColor)};
|
||||
item->setData(Qt::ForegroundRole, foregroundBrush);
|
||||
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(QLatin1String("sphere")));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
const QColor defaultColor {palette().color(QPalette::Active, QPalette::Link)};
|
||||
const QBrush foregroundBrush {UIThemeManager::instance()->getColor("RSS.UnreadArticle", defaultColor)};
|
||||
item->setData(Qt::ForegroundRole, foregroundBrush);
|
||||
|
||||
@@ -181,7 +181,8 @@ void AutomatedRssDownloader::loadFeedList()
|
||||
{
|
||||
const QSignalBlocker feedListSignalBlocker(m_ui->listFeeds);
|
||||
|
||||
for (const auto feed : asConst(RSS::Session::instance()->feeds())) {
|
||||
for (const auto feed : asConst(RSS::Session::instance()->feeds()))
|
||||
{
|
||||
QListWidgetItem *item = new QListWidgetItem(feed->name(), m_ui->listFeeds);
|
||||
item->setData(Qt::UserRole, feed->url());
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsTristate);
|
||||
@@ -203,7 +204,8 @@ void AutomatedRssDownloader::updateFeedList()
|
||||
|
||||
bool enable = !selection.isEmpty();
|
||||
|
||||
for (int i = 0; i < m_ui->listFeeds->count(); ++i) {
|
||||
for (int i = 0; i < m_ui->listFeeds->count(); ++i)
|
||||
{
|
||||
QListWidgetItem *item = m_ui->listFeeds->item(i);
|
||||
const QString feedURL = item->data(Qt::UserRole).toString();
|
||||
item->setHidden(!enable);
|
||||
@@ -211,7 +213,8 @@ void AutomatedRssDownloader::updateFeedList()
|
||||
bool allEnabled = true;
|
||||
bool anyEnabled = false;
|
||||
|
||||
for (const QListWidgetItem *ruleItem : asConst(selection)) {
|
||||
for (const QListWidgetItem *ruleItem : asConst(selection))
|
||||
{
|
||||
const auto rule = RSS::AutoDownloader::instance()->ruleByName(ruleItem->text());
|
||||
if (rule.feedURLs().contains(feedURL))
|
||||
anyEnabled = true;
|
||||
@@ -236,14 +239,16 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
|
||||
{
|
||||
const QList<QListWidgetItem *> selection = m_ui->listRules->selectedItems();
|
||||
QListWidgetItem *currentRuleItem = ((selection.count() == 1) ? selection.first() : nullptr);
|
||||
if (m_currentRuleItem != currentRuleItem) {
|
||||
if (m_currentRuleItem != currentRuleItem)
|
||||
{
|
||||
saveEditedRule(); // Save previous rule first
|
||||
m_currentRuleItem = currentRuleItem;
|
||||
//m_ui->listRules->setCurrentItem(m_currentRuleItem);
|
||||
}
|
||||
|
||||
// Update rule definition box
|
||||
if (m_currentRuleItem) {
|
||||
if (m_currentRuleItem)
|
||||
{
|
||||
m_currentRule = RSS::AutoDownloader::instance()->ruleByName(m_currentRuleItem->text());
|
||||
|
||||
m_ui->lineContains->setText(m_currentRule.mustContain());
|
||||
@@ -290,7 +295,8 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
|
||||
updateFieldsToolTips(m_ui->checkRegex->isChecked());
|
||||
m_ui->ruleDefBox->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_currentRule = RSS::AutoDownloadRule();
|
||||
clearRuleDefinitionBox();
|
||||
m_ui->ruleDefBox->setEnabled(false);
|
||||
@@ -376,7 +382,8 @@ void AutomatedRssDownloader::on_addRuleBtn_clicked()
|
||||
if (ruleName.isEmpty()) return;
|
||||
|
||||
// Check if this rule name already exists
|
||||
if (RSS::AutoDownloader::instance()->hasRule(ruleName)) {
|
||||
if (RSS::AutoDownloader::instance()->hasRule(ruleName))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Rule name conflict")
|
||||
, tr("A rule with this name already exists, please choose another name."));
|
||||
return;
|
||||
@@ -404,7 +411,8 @@ void AutomatedRssDownloader::on_removeRuleBtn_clicked()
|
||||
|
||||
void AutomatedRssDownloader::on_exportBtn_clicked()
|
||||
{
|
||||
if (RSS::AutoDownloader::instance()->rules().isEmpty()) {
|
||||
if (RSS::AutoDownloader::instance()->rules().isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid action")
|
||||
, tr("The list is empty, there is nothing to export."));
|
||||
return;
|
||||
@@ -416,24 +424,28 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
|
||||
, QString::fromLatin1("%1;;%2").arg(m_formatFilterJSON, m_formatFilterLegacy), &selectedFilter);
|
||||
if (path.isEmpty()) return;
|
||||
|
||||
const RSS::AutoDownloader::RulesFileFormat format {
|
||||
const RSS::AutoDownloader::RulesFileFormat format
|
||||
{
|
||||
(selectedFilter == m_formatFilterJSON)
|
||||
? RSS::AutoDownloader::RulesFileFormat::JSON
|
||||
: RSS::AutoDownloader::RulesFileFormat::Legacy
|
||||
};
|
||||
|
||||
if (format == RSS::AutoDownloader::RulesFileFormat::JSON) {
|
||||
if (format == RSS::AutoDownloader::RulesFileFormat::JSON)
|
||||
{
|
||||
if (!path.endsWith(EXT_JSON, Qt::CaseInsensitive))
|
||||
path += EXT_JSON;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
if (!path.endsWith(EXT_LEGACY, Qt::CaseInsensitive))
|
||||
path += EXT_LEGACY;
|
||||
}
|
||||
|
||||
QFile file {path};
|
||||
if (!file.open(QFile::WriteOnly)
|
||||
|| (file.write(RSS::AutoDownloader::instance()->exportRules(format)) == -1)) {
|
||||
|| (file.write(RSS::AutoDownloader::instance()->exportRules(format)) == -1))
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("I/O Error")
|
||||
, tr("Failed to create the destination file. Reason: %1").arg(file.errorString()));
|
||||
@@ -450,23 +462,27 @@ void AutomatedRssDownloader::on_importBtn_clicked()
|
||||
return;
|
||||
|
||||
QFile file {path};
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("I/O Error")
|
||||
, tr("Failed to open the file. Reason: %1").arg(file.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
const RSS::AutoDownloader::RulesFileFormat format {
|
||||
const RSS::AutoDownloader::RulesFileFormat format
|
||||
{
|
||||
(selectedFilter == m_formatFilterJSON)
|
||||
? RSS::AutoDownloader::RulesFileFormat::JSON
|
||||
: RSS::AutoDownloader::RulesFileFormat::Legacy
|
||||
};
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
RSS::AutoDownloader::instance()->importRules(file.readAll(),format);
|
||||
}
|
||||
catch (const RSS::ParsingError &error) {
|
||||
catch (const RSS::ParsingError &error)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("Import Error")
|
||||
, tr("Failed to import the selected rules file. Reason: %1").arg(error.message()));
|
||||
@@ -483,8 +499,10 @@ void AutomatedRssDownloader::displayRulesListMenu()
|
||||
|
||||
const QList<QListWidgetItem *> selection = m_ui->listRules->selectedItems();
|
||||
|
||||
if (!selection.isEmpty()) {
|
||||
if (selection.count() == 1) {
|
||||
if (!selection.isEmpty())
|
||||
{
|
||||
if (selection.count() == 1)
|
||||
{
|
||||
const QAction *delAct = menu->addAction(UIThemeManager::instance()->getIcon("list-remove"), tr("Delete rule"));
|
||||
connect(delAct, &QAction::triggered, this, &AutomatedRssDownloader::on_removeRuleBtn_clicked);
|
||||
|
||||
@@ -493,7 +511,8 @@ void AutomatedRssDownloader::displayRulesListMenu()
|
||||
const QAction *renameAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename rule..."));
|
||||
connect(renameAct, &QAction::triggered, this, &AutomatedRssDownloader::renameSelectedRule);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
const QAction *delAct = menu->addAction(UIThemeManager::instance()->getIcon("list-remove"), tr("Delete selected rules"));
|
||||
connect(delAct, &QAction::triggered, this, &AutomatedRssDownloader::on_removeRuleBtn_clicked);
|
||||
}
|
||||
@@ -513,18 +532,21 @@ void AutomatedRssDownloader::renameSelectedRule()
|
||||
if (selection.isEmpty()) return;
|
||||
|
||||
QListWidgetItem *item = selection.first();
|
||||
forever {
|
||||
forever
|
||||
{
|
||||
QString newName = AutoExpandableDialog::getText(
|
||||
this, tr("Rule renaming"), tr("Please type the new rule name")
|
||||
, QLineEdit::Normal, item->text());
|
||||
newName = newName.trimmed();
|
||||
if (newName.isEmpty()) return;
|
||||
|
||||
if (RSS::AutoDownloader::instance()->hasRule(newName)) {
|
||||
if (RSS::AutoDownloader::instance()->hasRule(newName))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Rule name conflict")
|
||||
, tr("A rule with this name already exists, please choose another name."));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Rename the rule
|
||||
RSS::AutoDownloader::instance()->renameRule(item->text(), newName);
|
||||
return;
|
||||
@@ -545,7 +567,8 @@ void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList()
|
||||
tr("Are you sure you want to clear the list of downloaded episodes for the selected rule?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
if (reply == QMessageBox::Yes)
|
||||
{
|
||||
m_currentRule.setPreviouslyMatchedEpisodes(QStringList());
|
||||
handleRuleDefinitionChanged();
|
||||
}
|
||||
@@ -554,7 +577,8 @@ void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList()
|
||||
void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feedItem)
|
||||
{
|
||||
const QString feedURL = feedItem->data(Qt::UserRole).toString();
|
||||
for (QListWidgetItem *ruleItem : asConst(m_ui->listRules->selectedItems())) {
|
||||
for (QListWidgetItem *ruleItem : asConst(m_ui->listRules->selectedItems()))
|
||||
{
|
||||
RSS::AutoDownloadRule rule = (ruleItem == m_currentRuleItem
|
||||
? m_currentRule
|
||||
: RSS::AutoDownloader::instance()->ruleByName(ruleItem->text()));
|
||||
@@ -578,11 +602,13 @@ void AutomatedRssDownloader::updateMatchingArticles()
|
||||
{
|
||||
m_ui->treeMatchingArticles->clear();
|
||||
|
||||
for (const QListWidgetItem *ruleItem : asConst(m_ui->listRules->selectedItems())) {
|
||||
for (const QListWidgetItem *ruleItem : asConst(m_ui->listRules->selectedItems()))
|
||||
{
|
||||
RSS::AutoDownloadRule rule = (ruleItem == m_currentRuleItem
|
||||
? m_currentRule
|
||||
: RSS::AutoDownloader::instance()->ruleByName(ruleItem->text()));
|
||||
for (const QString &feedURL : asConst(rule.feedURLs())) {
|
||||
for (const QString &feedURL : asConst(rule.feedURLs()))
|
||||
{
|
||||
auto feed = RSS::Session::instance()->feedByURL(feedURL);
|
||||
if (!feed) continue; // feed doesn't exist
|
||||
|
||||
@@ -605,16 +631,19 @@ void AutomatedRssDownloader::addFeedArticlesToTree(RSS::Feed *feed, const QStrin
|
||||
|
||||
// Check if this feed is already in the tree
|
||||
QTreeWidgetItem *treeFeedItem = nullptr;
|
||||
for (int i = 0; i < m_ui->treeMatchingArticles->topLevelItemCount(); ++i) {
|
||||
for (int i = 0; i < m_ui->treeMatchingArticles->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem *item = m_ui->treeMatchingArticles->topLevelItem(i);
|
||||
if (item->data(0, Qt::UserRole).toString() == feed->url()) {
|
||||
if (item->data(0, Qt::UserRole).toString() == feed->url())
|
||||
{
|
||||
treeFeedItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is none, create it
|
||||
if (!treeFeedItem) {
|
||||
if (!treeFeedItem)
|
||||
{
|
||||
treeFeedItem = new QTreeWidgetItem(QStringList() << feed->name());
|
||||
treeFeedItem->setToolTip(0, feed->name());
|
||||
QFont f = treeFeedItem->font(0);
|
||||
@@ -626,10 +655,12 @@ void AutomatedRssDownloader::addFeedArticlesToTree(RSS::Feed *feed, const QStrin
|
||||
}
|
||||
|
||||
// Insert the articles
|
||||
for (const QString &article : articles) {
|
||||
for (const QString &article : articles)
|
||||
{
|
||||
QPair<QString, QString> key(feed->name(), article);
|
||||
|
||||
if (!m_treeListEntries.contains(key)) {
|
||||
if (!m_treeListEntries.contains(key))
|
||||
{
|
||||
m_treeListEntries << key;
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << article);
|
||||
item->setToolTip(0, article);
|
||||
@@ -645,10 +676,12 @@ void AutomatedRssDownloader::addFeedArticlesToTree(RSS::Feed *feed, const QStrin
|
||||
void AutomatedRssDownloader::updateFieldsToolTips(bool regex)
|
||||
{
|
||||
QString tip;
|
||||
if (regex) {
|
||||
if (regex)
|
||||
{
|
||||
tip = "<p>" + tr("Regex mode: use Perl-compatible regular expressions") + "</p>";
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
tip = "<p>" + tr("Wildcard mode: you can use") + "<ul>"
|
||||
+ "<li>" + tr("? to match any single character") + "</li>"
|
||||
+ "<li>" + tr("* to match zero or more of any characters") + "</li>"
|
||||
@@ -676,7 +709,8 @@ void AutomatedRssDownloader::updateMustLineValidity()
|
||||
bool valid = true;
|
||||
QString error;
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
if (!text.isEmpty())
|
||||
{
|
||||
QStringList tokens;
|
||||
if (isRegex)
|
||||
tokens << text;
|
||||
@@ -684,9 +718,11 @@ void AutomatedRssDownloader::updateMustLineValidity()
|
||||
for (const QString &token : asConst(text.split('|')))
|
||||
tokens << Utils::String::wildcardToRegex(token);
|
||||
|
||||
for (const QString &token : asConst(tokens)) {
|
||||
for (const QString &token : asConst(tokens))
|
||||
{
|
||||
QRegularExpression reg(token, QRegularExpression::CaseInsensitiveOption);
|
||||
if (!reg.isValid()) {
|
||||
if (!reg.isValid())
|
||||
{
|
||||
if (isRegex)
|
||||
error = tr("Position %1: %2").arg(reg.patternErrorOffset()).arg(reg.errorString());
|
||||
valid = false;
|
||||
@@ -695,12 +731,14 @@ void AutomatedRssDownloader::updateMustLineValidity()
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
if (valid)
|
||||
{
|
||||
m_ui->lineContains->setStyleSheet("");
|
||||
m_ui->labelMustStat->setPixmap(QPixmap());
|
||||
m_ui->labelMustStat->setToolTip("");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->lineContains->setStyleSheet("QLineEdit { color: #ff0000; }");
|
||||
m_ui->labelMustStat->setPixmap(UIThemeManager::instance()->getIcon("task-attention").pixmap(16, 16));
|
||||
m_ui->labelMustStat->setToolTip(error);
|
||||
@@ -714,7 +752,8 @@ void AutomatedRssDownloader::updateMustNotLineValidity()
|
||||
bool valid = true;
|
||||
QString error;
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
if (!text.isEmpty())
|
||||
{
|
||||
QStringList tokens;
|
||||
if (isRegex)
|
||||
tokens << text;
|
||||
@@ -722,9 +761,11 @@ void AutomatedRssDownloader::updateMustNotLineValidity()
|
||||
for (const QString &token : asConst(text.split('|')))
|
||||
tokens << Utils::String::wildcardToRegex(token);
|
||||
|
||||
for (const QString &token : asConst(tokens)) {
|
||||
for (const QString &token : asConst(tokens))
|
||||
{
|
||||
QRegularExpression reg(token, QRegularExpression::CaseInsensitiveOption);
|
||||
if (!reg.isValid()) {
|
||||
if (!reg.isValid())
|
||||
{
|
||||
if (isRegex)
|
||||
error = tr("Position %1: %2").arg(reg.patternErrorOffset()).arg(reg.errorString());
|
||||
valid = false;
|
||||
@@ -733,12 +774,14 @@ void AutomatedRssDownloader::updateMustNotLineValidity()
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
if (valid)
|
||||
{
|
||||
m_ui->lineNotContains->setStyleSheet("");
|
||||
m_ui->labelMustNotStat->setPixmap(QPixmap());
|
||||
m_ui->labelMustNotStat->setToolTip("");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->lineNotContains->setStyleSheet("QLineEdit { color: #ff0000; }");
|
||||
m_ui->labelMustNotStat->setPixmap(UIThemeManager::instance()->getIcon("task-attention").pixmap(16, 16));
|
||||
m_ui->labelMustNotStat->setToolTip(error);
|
||||
@@ -750,11 +793,13 @@ void AutomatedRssDownloader::updateEpisodeFilterValidity()
|
||||
const QString text = m_ui->lineEFilter->text();
|
||||
bool valid = text.isEmpty() || m_episodeRegex->match(text).hasMatch();
|
||||
|
||||
if (valid) {
|
||||
if (valid)
|
||||
{
|
||||
m_ui->lineEFilter->setStyleSheet("");
|
||||
m_ui->labelEpFilterStat->setPixmap(QPixmap());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->lineEFilter->setStyleSheet("QLineEdit { color: #ff0000; }");
|
||||
m_ui->labelEpFilterStat->setPixmap(UIThemeManager::instance()->getIcon("task-attention").pixmap(16, 16));
|
||||
}
|
||||
|
||||
@@ -97,7 +97,8 @@ void FeedListWidget::handleFeedStateChanged(RSS::Feed *feed)
|
||||
|
||||
void FeedListWidget::handleFeedIconLoaded(RSS::Feed *feed)
|
||||
{
|
||||
if (!feed->isLoading() && !feed->hasError()) {
|
||||
if (!feed->isLoading() && !feed->hasError())
|
||||
{
|
||||
QTreeWidgetItem *item = m_rssToTreeItemMapping.value(feed);
|
||||
Q_ASSERT(item);
|
||||
|
||||
@@ -107,10 +108,12 @@ void FeedListWidget::handleFeedIconLoaded(RSS::Feed *feed)
|
||||
|
||||
void FeedListWidget::handleItemUnreadCountChanged(RSS::Item *rssItem)
|
||||
{
|
||||
if (rssItem == RSS::Session::instance()->rootFolder()) {
|
||||
if (rssItem == RSS::Session::instance()->rootFolder())
|
||||
{
|
||||
m_unreadStickyItem->setText(0, tr("Unread (%1)").arg(RSS::Session::instance()->rootFolder()->unreadCount()));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QTreeWidgetItem *item = mapRSSItem(rssItem);
|
||||
Q_ASSERT(item);
|
||||
item->setData(0, Qt::DisplayRole, QString::fromLatin1("%1 (%2)").arg(rssItem->name(), QString::number(rssItem->unreadCount())));
|
||||
@@ -151,9 +154,11 @@ QList<QTreeWidgetItem *> FeedListWidget::getAllOpenedFolders(QTreeWidgetItem *pa
|
||||
{
|
||||
QList<QTreeWidgetItem *> openedFolders;
|
||||
int nbChildren = (parent ? parent->childCount() : topLevelItemCount());
|
||||
for (int i = 0; i < nbChildren; ++i) {
|
||||
for (int i = 0; i < nbChildren; ++i)
|
||||
{
|
||||
QTreeWidgetItem *item (parent ? parent->child(i) : topLevelItem(i));
|
||||
if (isFolder(item) && item->isExpanded()) {
|
||||
if (isFolder(item) && item->isExpanded())
|
||||
{
|
||||
QList<QTreeWidgetItem *> openedSubfolders = getAllOpenedFolders(item);
|
||||
if (!openedSubfolders.empty())
|
||||
openedFolders << openedSubfolders;
|
||||
@@ -215,7 +220,8 @@ void FeedListWidget::dropEvent(QDropEvent *event)
|
||||
: RSS::Session::instance()->rootFolder());
|
||||
|
||||
// move as much items as possible
|
||||
for (QTreeWidgetItem *srcItem : asConst(selectedItems())) {
|
||||
for (QTreeWidgetItem *srcItem : asConst(selectedItems()))
|
||||
{
|
||||
auto rssItem = getRSSItem(srcItem);
|
||||
RSS::Session::instance()->moveItem(rssItem, RSS::Item::joinPath(destFolder->path(), rssItem->name()));
|
||||
}
|
||||
@@ -233,7 +239,8 @@ QTreeWidgetItem *FeedListWidget::createItem(RSS::Item *rssItem, QTreeWidgetItem
|
||||
m_rssToTreeItemMapping[rssItem] = item;
|
||||
|
||||
QIcon icon;
|
||||
if (auto feed = qobject_cast<RSS::Feed *>(rssItem)) {
|
||||
if (auto feed = qobject_cast<RSS::Feed *>(rssItem))
|
||||
{
|
||||
if (feed->isLoading())
|
||||
icon = UIThemeManager::instance()->getIcon(QLatin1String("loading"));
|
||||
else if (feed->hasError())
|
||||
@@ -243,7 +250,8 @@ QTreeWidgetItem *FeedListWidget::createItem(RSS::Item *rssItem, QTreeWidgetItem
|
||||
else
|
||||
icon = UIThemeManager::instance()->getIcon(QLatin1String("application-rss+xml"));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
icon = UIThemeManager::instance()->getIcon(QLatin1String("inode-directory"));
|
||||
}
|
||||
item->setData(0, Qt::DecorationRole, icon);
|
||||
@@ -260,7 +268,8 @@ QTreeWidgetItem *FeedListWidget::createItem(RSS::Item *rssItem, QTreeWidgetItem
|
||||
|
||||
void FeedListWidget::fill(QTreeWidgetItem *parent, RSS::Folder *rssParent)
|
||||
{
|
||||
for (const auto rssItem : asConst(rssParent->items())) {
|
||||
for (const auto rssItem : asConst(rssParent->items()))
|
||||
{
|
||||
QTreeWidgetItem *item = createItem(rssItem, parent);
|
||||
// Recursive call if this is a folder.
|
||||
if (auto folder = qobject_cast<RSS::Folder *>(rssItem))
|
||||
|
||||
@@ -59,20 +59,23 @@ HtmlBrowser::~HtmlBrowser()
|
||||
|
||||
QVariant HtmlBrowser::loadResource(int type, const QUrl &name)
|
||||
{
|
||||
if (type == QTextDocument::ImageResource) {
|
||||
if (type == QTextDocument::ImageResource)
|
||||
{
|
||||
QUrl url(name);
|
||||
if (url.scheme().isEmpty())
|
||||
url.setScheme("http");
|
||||
|
||||
QIODevice *dev = m_diskCache->data(url);
|
||||
if (dev) {
|
||||
if (dev)
|
||||
{
|
||||
qDebug() << "HtmlBrowser::loadResource() cache " << url.toString();
|
||||
QByteArray res = dev->readAll();
|
||||
delete dev;
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!m_activeRequests.contains(url)) {
|
||||
if (!m_activeRequests.contains(url))
|
||||
{
|
||||
m_activeRequests.insert(url, true);
|
||||
qDebug() << "HtmlBrowser::loadResource() get " << url.toString();
|
||||
QNetworkRequest req(url);
|
||||
@@ -90,10 +93,12 @@ void HtmlBrowser::resourceLoaded(QNetworkReply *reply)
|
||||
{
|
||||
m_activeRequests.remove(reply->request().url());
|
||||
|
||||
if ((reply->error() == QNetworkReply::NoError) && (reply->size() > 0)) {
|
||||
if ((reply->error() == QNetworkReply::NoError) && (reply->size() > 0))
|
||||
{
|
||||
qDebug() << "HtmlBrowser::resourceLoaded() save " << reply->request().url().toString();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// If resource failed to load, replace it with warning icon and store it in cache for 1 day.
|
||||
// Otherwise HTMLBrowser will keep trying to download it every time article is displayed,
|
||||
// since it's not possible to cache error responses.
|
||||
|
||||
@@ -150,13 +150,16 @@ void RSSWidget::displayRSSListMenu(const QPoint &pos)
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
const QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
|
||||
if (!selectedItems.isEmpty()) {
|
||||
if (!selectedItems.isEmpty())
|
||||
{
|
||||
menu->addAction(m_ui->actionUpdate);
|
||||
menu->addAction(m_ui->actionMarkItemsRead);
|
||||
menu->addSeparator();
|
||||
|
||||
if (selectedItems.size() == 1) {
|
||||
if (selectedItems.first() != m_feedListWidget->stickyUnreadItem()) {
|
||||
if (selectedItems.size() == 1)
|
||||
{
|
||||
if (selectedItems.first() != m_feedListWidget->stickyUnreadItem())
|
||||
{
|
||||
menu->addAction(m_ui->actionRename);
|
||||
menu->addAction(m_ui->actionDelete);
|
||||
menu->addSeparator();
|
||||
@@ -164,19 +167,22 @@ void RSSWidget::displayRSSListMenu(const QPoint &pos)
|
||||
menu->addAction(m_ui->actionNewFolder);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
menu->addAction(m_ui->actionDelete);
|
||||
menu->addSeparator();
|
||||
}
|
||||
|
||||
menu->addAction(m_ui->actionNewSubscription);
|
||||
|
||||
if (m_feedListWidget->isFeed(selectedItems.first())) {
|
||||
if (m_feedListWidget->isFeed(selectedItems.first()))
|
||||
{
|
||||
menu->addSeparator();
|
||||
menu->addAction(m_ui->actionCopyFeedURL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
menu->addAction(m_ui->actionNewSubscription);
|
||||
menu->addAction(m_ui->actionNewFolder);
|
||||
menu->addSeparator();
|
||||
@@ -190,7 +196,8 @@ void RSSWidget::displayItemsListMenu(const QPoint &)
|
||||
{
|
||||
bool hasTorrent = false;
|
||||
bool hasLink = false;
|
||||
for (const QListWidgetItem *item : asConst(m_articleListWidget->selectedItems())) {
|
||||
for (const QListWidgetItem *item : asConst(m_articleListWidget->selectedItems()))
|
||||
{
|
||||
auto article = reinterpret_cast<RSS::Article *>(item->data(Qt::UserRole).value<quintptr>());
|
||||
Q_ASSERT(article);
|
||||
|
||||
@@ -228,7 +235,8 @@ void RSSWidget::askNewFolder()
|
||||
// Determine destination folder for new item
|
||||
QTreeWidgetItem *destItem = nullptr;
|
||||
QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
|
||||
if (!selectedItems.empty()) {
|
||||
if (!selectedItems.empty())
|
||||
{
|
||||
destItem = selectedItems.first();
|
||||
if (!m_feedListWidget->isFolder(destItem))
|
||||
destItem = destItem->parent();
|
||||
@@ -268,7 +276,8 @@ void RSSWidget::on_newFeedButton_clicked()
|
||||
// Determine destination folder for new item
|
||||
QTreeWidgetItem *destItem = nullptr;
|
||||
QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
|
||||
if (!selectedItems.empty()) {
|
||||
if (!selectedItems.empty())
|
||||
{
|
||||
destItem = selectedItems.first();
|
||||
if (!m_feedListWidget->isFolder(destItem))
|
||||
destItem = destItem->parent();
|
||||
@@ -313,13 +322,17 @@ void RSSWidget::deleteSelectedItems()
|
||||
void RSSWidget::loadFoldersOpenState()
|
||||
{
|
||||
const QStringList openedFolders = Preferences::instance()->getRssOpenFolders();
|
||||
for (const QString &varPath : openedFolders) {
|
||||
for (const QString &varPath : openedFolders)
|
||||
{
|
||||
QTreeWidgetItem *parent = nullptr;
|
||||
for (const QString &name : asConst(varPath.split('\\'))) {
|
||||
for (const QString &name : asConst(varPath.split('\\')))
|
||||
{
|
||||
int nbChildren = (parent ? parent->childCount() : m_feedListWidget->topLevelItemCount());
|
||||
for (int i = 0; i < nbChildren; ++i) {
|
||||
for (int i = 0; i < nbChildren; ++i)
|
||||
{
|
||||
QTreeWidgetItem *child = (parent ? parent->child(i) : m_feedListWidget->topLevelItem(i));
|
||||
if (m_feedListWidget->getRSSItem(child)->name() == name) {
|
||||
if (m_feedListWidget->getRSSItem(child)->name() == name)
|
||||
{
|
||||
parent = child;
|
||||
parent->setExpanded(true);
|
||||
break;
|
||||
@@ -344,14 +357,16 @@ void RSSWidget::refreshAllFeeds()
|
||||
|
||||
void RSSWidget::downloadSelectedTorrents()
|
||||
{
|
||||
for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems())) {
|
||||
for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems()))
|
||||
{
|
||||
auto article = reinterpret_cast<RSS::Article *>(item->data(Qt::UserRole).value<quintptr>());
|
||||
Q_ASSERT(article);
|
||||
|
||||
// Mark as read
|
||||
article->markAsRead();
|
||||
|
||||
if (!article->torrentUrl().isEmpty()) {
|
||||
if (!article->torrentUrl().isEmpty())
|
||||
{
|
||||
if (AddNewTorrentDialog::isEnabled())
|
||||
AddNewTorrentDialog::show(article->torrentUrl(), window());
|
||||
else
|
||||
@@ -363,7 +378,8 @@ void RSSWidget::downloadSelectedTorrents()
|
||||
// open the url of the selected RSS articles in the Web browser
|
||||
void RSSWidget::openSelectedArticlesUrls()
|
||||
{
|
||||
for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems())) {
|
||||
for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems()))
|
||||
{
|
||||
auto article = reinterpret_cast<RSS::Article *>(item->data(Qt::UserRole).value<quintptr>());
|
||||
Q_ASSERT(article);
|
||||
|
||||
@@ -387,7 +403,8 @@ void RSSWidget::renameSelectedRSSItem()
|
||||
RSS::Item *rssItem = m_feedListWidget->getRSSItem(item);
|
||||
const QString parentPath = RSS::Item::parentPath(rssItem->path());
|
||||
bool ok = false;
|
||||
do {
|
||||
do
|
||||
{
|
||||
QString newName = AutoExpandableDialog::getText(
|
||||
this, tr("Please choose a new name for this RSS feed"), tr("New feed name:")
|
||||
, QLineEdit::Normal, rssItem->name(), &ok);
|
||||
@@ -395,7 +412,8 @@ void RSSWidget::renameSelectedRSSItem()
|
||||
if (!ok) return;
|
||||
|
||||
QString error;
|
||||
if (!RSS::Session::instance()->moveItem(rssItem, RSS::Item::joinPath(parentPath, newName), &error)) {
|
||||
if (!RSS::Session::instance()->moveItem(rssItem, RSS::Item::joinPath(parentPath, newName), &error))
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Rename failed"), error);
|
||||
ok = false;
|
||||
}
|
||||
@@ -404,8 +422,10 @@ void RSSWidget::renameSelectedRSSItem()
|
||||
|
||||
void RSSWidget::refreshSelectedItems()
|
||||
{
|
||||
for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems())) {
|
||||
if (item == m_feedListWidget->stickyUnreadItem()) {
|
||||
for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems()))
|
||||
{
|
||||
if (item == m_feedListWidget->stickyUnreadItem())
|
||||
{
|
||||
refreshAllFeeds();
|
||||
return;
|
||||
}
|
||||
@@ -417,7 +437,8 @@ void RSSWidget::refreshSelectedItems()
|
||||
void RSSWidget::copySelectedFeedsURL()
|
||||
{
|
||||
QStringList URLs;
|
||||
for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems())) {
|
||||
for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems()))
|
||||
{
|
||||
if (auto feed = qobject_cast<RSS::Feed *>(m_feedListWidget->getRSSItem(item)))
|
||||
URLs << feed->url();
|
||||
}
|
||||
@@ -432,7 +453,8 @@ void RSSWidget::handleCurrentFeedItemChanged(QTreeWidgetItem *currentItem)
|
||||
|
||||
void RSSWidget::on_markReadButton_clicked()
|
||||
{
|
||||
for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems())) {
|
||||
for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems()))
|
||||
{
|
||||
m_feedListWidget->getRSSItem(item)->markAsRead();
|
||||
if (item == m_feedListWidget->stickyUnreadItem())
|
||||
break; // all items was read
|
||||
@@ -444,7 +466,8 @@ void RSSWidget::handleCurrentArticleItemChanged(QListWidgetItem *currentItem, QL
|
||||
{
|
||||
m_ui->textBrowser->clear();
|
||||
|
||||
if (previousItem) {
|
||||
if (previousItem)
|
||||
{
|
||||
auto article = m_articleListWidget->getRSSArticle(previousItem);
|
||||
Q_ASSERT(article);
|
||||
article->markAsRead();
|
||||
@@ -468,10 +491,12 @@ void RSSWidget::handleCurrentArticleItemChanged(QListWidgetItem *currentItem, QL
|
||||
html += QString::fromLatin1("<div style='background-color: \"%1\";'><b>%2</b>%3</div>").arg(alternateBaseColor, tr("Author: "), article->author());
|
||||
html += "</div>"
|
||||
"<div style='margin-left: 5px; margin-right: 5px;'>";
|
||||
if (Qt::mightBeRichText(article->description())) {
|
||||
if (Qt::mightBeRichText(article->description()))
|
||||
{
|
||||
html += article->description();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QString description = article->description();
|
||||
QRegularExpression rx;
|
||||
// If description is plain text, replace BBCode tags with HTML and wrap everything in <pre></pre> so it looks nice
|
||||
|
||||
@@ -62,7 +62,8 @@ QWidget *ScanFoldersDelegate::createEditor(QWidget *parent, const QStyleOptionVi
|
||||
editor->addItem(ScanFoldersModel::pathTypeDisplayName(ScanFoldersModel::DOWNLOAD_IN_WATCH_FOLDER));
|
||||
editor->addItem(ScanFoldersModel::pathTypeDisplayName(ScanFoldersModel::DEFAULT_LOCATION));
|
||||
editor->addItem(ScanFoldersModel::pathTypeDisplayName(ScanFoldersModel::CUSTOM_LOCATION));
|
||||
if (index.data(Qt::UserRole).toInt() == ScanFoldersModel::CUSTOM_LOCATION) {
|
||||
if (index.data(Qt::UserRole).toInt() == ScanFoldersModel::CUSTOM_LOCATION)
|
||||
{
|
||||
editor->insertSeparator(3);
|
||||
editor->addItem(index.data().toString());
|
||||
}
|
||||
@@ -74,7 +75,8 @@ QWidget *ScanFoldersDelegate::createEditor(QWidget *parent, const QStyleOptionVi
|
||||
|
||||
void ScanFoldersDelegate::comboboxIndexChanged(int index)
|
||||
{
|
||||
if (index == ScanFoldersModel::CUSTOM_LOCATION) {
|
||||
if (index == ScanFoldersModel::CUSTOM_LOCATION)
|
||||
{
|
||||
auto *w = static_cast<QWidget *>(sender());
|
||||
if (w && w->parentWidget())
|
||||
w->parentWidget()->setFocus();
|
||||
@@ -86,7 +88,8 @@ void ScanFoldersDelegate::setModelData(QWidget *editor, QAbstractItemModel *mode
|
||||
auto *combobox = static_cast<QComboBox*>(editor);
|
||||
int value = combobox->currentIndex();
|
||||
|
||||
switch (value) {
|
||||
switch (value)
|
||||
{
|
||||
case ScanFoldersModel::DOWNLOAD_IN_WATCH_FOLDER:
|
||||
case ScanFoldersModel::DEFAULT_LOCATION:
|
||||
model->setData(index, value, Qt::UserRole);
|
||||
|
||||
@@ -108,9 +108,12 @@ void PluginSelectDialog::dropEvent(QDropEvent *event)
|
||||
event->acceptProposedAction();
|
||||
|
||||
QStringList files;
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
for (const QUrl &url : asConst(event->mimeData()->urls())) {
|
||||
if (!url.isEmpty()) {
|
||||
if (event->mimeData()->hasUrls())
|
||||
{
|
||||
for (const QUrl &url : asConst(event->mimeData()->urls()))
|
||||
{
|
||||
if (!url.isEmpty())
|
||||
{
|
||||
if (url.scheme().compare("file", Qt::CaseInsensitive) == 0)
|
||||
files << url.toLocalFile();
|
||||
else
|
||||
@@ -118,13 +121,15 @@ void PluginSelectDialog::dropEvent(QDropEvent *event)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
files = event->mimeData()->text().split('\n');
|
||||
}
|
||||
|
||||
if (files.isEmpty()) return;
|
||||
|
||||
for (const QString &file : asConst(files)) {
|
||||
for (const QString &file : asConst(files))
|
||||
{
|
||||
qDebug("dropped %s", qUtf8Printable(file));
|
||||
startAsyncOp();
|
||||
m_pluginManager->installPlugin(file);
|
||||
@@ -134,11 +139,13 @@ void PluginSelectDialog::dropEvent(QDropEvent *event)
|
||||
// Decode if we accept drag 'n drop or not
|
||||
void PluginSelectDialog::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
for (const QString &mime : asConst(event->mimeData()->formats())) {
|
||||
for (const QString &mime : asConst(event->mimeData()->formats()))
|
||||
{
|
||||
qDebug("mimeData: %s", qUtf8Printable(mime));
|
||||
}
|
||||
|
||||
if (event->mimeData()->hasFormat(QLatin1String("text/plain")) || event->mimeData()->hasFormat(QLatin1String("text/uri-list"))) {
|
||||
if (event->mimeData()->hasFormat(QLatin1String("text/plain")) || event->mimeData()->hasFormat(QLatin1String("text/uri-list")))
|
||||
{
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
@@ -153,11 +160,13 @@ void PluginSelectDialog::togglePluginState(QTreeWidgetItem *item, int)
|
||||
{
|
||||
PluginInfo *plugin = m_pluginManager->pluginInfo(item->text(PLUGIN_ID));
|
||||
m_pluginManager->enablePlugin(plugin->name, !plugin->enabled);
|
||||
if (plugin->enabled) {
|
||||
if (plugin->enabled)
|
||||
{
|
||||
item->setText(PLUGIN_STATE, tr("Yes"));
|
||||
setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "green");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
item->setText(PLUGIN_STATE, tr("No"));
|
||||
setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "red");
|
||||
}
|
||||
@@ -189,14 +198,17 @@ void PluginSelectDialog::on_closeButton_clicked()
|
||||
void PluginSelectDialog::on_actionUninstall_triggered()
|
||||
{
|
||||
bool error = false;
|
||||
for (QTreeWidgetItem *item : asConst(m_ui->pluginsTree->selectedItems())) {
|
||||
for (QTreeWidgetItem *item : asConst(m_ui->pluginsTree->selectedItems()))
|
||||
{
|
||||
int index = m_ui->pluginsTree->indexOfTopLevelItem(item);
|
||||
Q_ASSERT(index != -1);
|
||||
QString id = item->text(PLUGIN_ID);
|
||||
if (m_pluginManager->uninstallPlugin(id)) {
|
||||
if (m_pluginManager->uninstallPlugin(id))
|
||||
{
|
||||
delete item;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
error = true;
|
||||
// Disable it instead
|
||||
m_pluginManager->enablePlugin(id, false);
|
||||
@@ -213,16 +225,19 @@ void PluginSelectDialog::on_actionUninstall_triggered()
|
||||
|
||||
void PluginSelectDialog::enableSelection(bool enable)
|
||||
{
|
||||
for (QTreeWidgetItem *item : asConst(m_ui->pluginsTree->selectedItems())) {
|
||||
for (QTreeWidgetItem *item : asConst(m_ui->pluginsTree->selectedItems()))
|
||||
{
|
||||
int index = m_ui->pluginsTree->indexOfTopLevelItem(item);
|
||||
Q_ASSERT(index != -1);
|
||||
QString id = item->text(PLUGIN_ID);
|
||||
m_pluginManager->enablePlugin(id, enable);
|
||||
if (enable) {
|
||||
if (enable)
|
||||
{
|
||||
item->setText(PLUGIN_STATE, tr("Yes"));
|
||||
setRowColor(index, "green");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
item->setText(PLUGIN_STATE, tr("No"));
|
||||
setRowColor(index, "red");
|
||||
}
|
||||
@@ -233,7 +248,8 @@ void PluginSelectDialog::enableSelection(bool enable)
|
||||
void PluginSelectDialog::setRowColor(const int row, const QString &color)
|
||||
{
|
||||
QTreeWidgetItem *item = m_ui->pluginsTree->topLevelItem(row);
|
||||
for (int i = 0; i < m_ui->pluginsTree->columnCount(); ++i) {
|
||||
for (int i = 0; i < m_ui->pluginsTree->columnCount(); ++i)
|
||||
{
|
||||
item->setData(i, Qt::ForegroundRole, QColor(color));
|
||||
}
|
||||
}
|
||||
@@ -243,7 +259,8 @@ QVector<QTreeWidgetItem*> PluginSelectDialog::findItemsWithUrl(const QString &ur
|
||||
QVector<QTreeWidgetItem*> res;
|
||||
res.reserve(m_ui->pluginsTree->topLevelItemCount());
|
||||
|
||||
for (int i = 0; i < m_ui->pluginsTree->topLevelItemCount(); ++i) {
|
||||
for (int i = 0; i < m_ui->pluginsTree->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem *item = m_ui->pluginsTree->topLevelItem(i);
|
||||
if (url.startsWith(item->text(PLUGIN_URL), Qt::CaseInsensitive))
|
||||
res << item;
|
||||
@@ -254,7 +271,8 @@ QVector<QTreeWidgetItem*> PluginSelectDialog::findItemsWithUrl(const QString &ur
|
||||
|
||||
QTreeWidgetItem *PluginSelectDialog::findItemWithID(const QString &id)
|
||||
{
|
||||
for (int i = 0; i < m_ui->pluginsTree->topLevelItemCount(); ++i) {
|
||||
for (int i = 0; i < m_ui->pluginsTree->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem *item = m_ui->pluginsTree->topLevelItem(i);
|
||||
if (id == item->text(PLUGIN_ID))
|
||||
return item;
|
||||
@@ -278,20 +296,24 @@ void PluginSelectDialog::addNewPlugin(const QString &pluginName)
|
||||
item->setText(PLUGIN_NAME, plugin->fullName);
|
||||
item->setText(PLUGIN_URL, plugin->url);
|
||||
item->setText(PLUGIN_ID, plugin->name);
|
||||
if (plugin->enabled) {
|
||||
if (plugin->enabled)
|
||||
{
|
||||
item->setText(PLUGIN_STATE, tr("Yes"));
|
||||
setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "green");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
item->setText(PLUGIN_STATE, tr("No"));
|
||||
setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "red");
|
||||
}
|
||||
// Handle icon
|
||||
if (QFile::exists(plugin->iconPath)) {
|
||||
if (QFile::exists(plugin->iconPath))
|
||||
{
|
||||
// Good, we already have the icon
|
||||
item->setData(PLUGIN_NAME, Qt::DecorationRole, QIcon(plugin->iconPath));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// Icon is missing, we must download it
|
||||
using namespace Net;
|
||||
DownloadManager::instance()->download(
|
||||
@@ -318,7 +340,8 @@ void PluginSelectDialog::finishAsyncOp()
|
||||
void PluginSelectDialog::finishPluginUpdate()
|
||||
{
|
||||
--m_pendingUpdates;
|
||||
if ((m_pendingUpdates == 0) && !m_updatedPlugins.isEmpty()) {
|
||||
if ((m_pendingUpdates == 0) && !m_updatedPlugins.isEmpty())
|
||||
{
|
||||
m_updatedPlugins.sort(Qt::CaseInsensitive);
|
||||
QMessageBox::information(this, tr("Search plugin update"), tr("Plugins installed or updated: %1").arg(m_updatedPlugins.join(", ")));
|
||||
m_updatedPlugins.clear();
|
||||
@@ -344,7 +367,8 @@ void PluginSelectDialog::askForPluginUrl()
|
||||
tr("URL:"), QLineEdit::Normal, defaultUrl, &ok
|
||||
);
|
||||
|
||||
while (ok && !url.isEmpty() && !url.endsWith(".py")) {
|
||||
while (ok && !url.isEmpty() && !url.endsWith(".py"))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid link"), tr("The link doesn't seem to point to a search engine plugin."));
|
||||
url = AutoExpandableDialog::getText(
|
||||
this, tr("New search engine plugin URL"),
|
||||
@@ -352,7 +376,8 @@ void PluginSelectDialog::askForPluginUrl()
|
||||
);
|
||||
}
|
||||
|
||||
if (ok && !url.isEmpty()) {
|
||||
if (ok && !url.isEmpty())
|
||||
{
|
||||
startAsyncOp();
|
||||
m_pluginManager->installPlugin(url);
|
||||
}
|
||||
@@ -364,7 +389,8 @@ void PluginSelectDialog::askForLocalPlugin()
|
||||
nullptr, tr("Select search plugins"), QDir::homePath(),
|
||||
tr("qBittorrent search plugin") + QLatin1String(" (*.py)")
|
||||
);
|
||||
for (const QString &path : pathsList) {
|
||||
for (const QString &path : pathsList)
|
||||
{
|
||||
startAsyncOp();
|
||||
m_pluginManager->installPlugin(path);
|
||||
}
|
||||
@@ -372,7 +398,8 @@ void PluginSelectDialog::askForLocalPlugin()
|
||||
|
||||
void PluginSelectDialog::iconDownloadFinished(const Net::DownloadResult &result)
|
||||
{
|
||||
if (result.status != Net::DownloadStatus::Success) {
|
||||
if (result.status != Net::DownloadStatus::Success)
|
||||
{
|
||||
qDebug("Could not download favicon: %s, reason: %s", qUtf8Printable(result.url), qUtf8Printable(result.errorString));
|
||||
return;
|
||||
}
|
||||
@@ -384,8 +411,10 @@ void PluginSelectDialog::iconDownloadFinished(const Net::DownloadResult &result)
|
||||
// Detect a non-decodable icon
|
||||
QList<QSize> sizes = icon.availableSizes();
|
||||
bool invalid = (sizes.isEmpty() || icon.pixmap(sizes.first()).isNull());
|
||||
if (!invalid) {
|
||||
for (QTreeWidgetItem *item : asConst(findItemsWithUrl(result.url))) {
|
||||
if (!invalid)
|
||||
{
|
||||
for (QTreeWidgetItem *item : asConst(findItemsWithUrl(result.url)))
|
||||
{
|
||||
QString id = item->text(PLUGIN_ID);
|
||||
PluginInfo *plugin = m_pluginManager->pluginInfo(id);
|
||||
if (!plugin) continue;
|
||||
@@ -394,14 +423,16 @@ void PluginSelectDialog::iconDownloadFinished(const Net::DownloadResult &result)
|
||||
.arg(SearchPluginManager::pluginsLocation()
|
||||
, id
|
||||
, result.url.endsWith(".ico", Qt::CaseInsensitive) ? "ico" : "png");
|
||||
if (QFile::copy(filePath, iconPath)) {
|
||||
if (QFile::copy(filePath, iconPath))
|
||||
{
|
||||
// This 2nd check is necessary. Some favicons (eg from piratebay)
|
||||
// decode fine without an ext, but fail to do so when appending the ext
|
||||
// from the url. Probably a Qt bug.
|
||||
QIcon iconWithExt(iconPath);
|
||||
QList<QSize> sizesExt = iconWithExt.availableSizes();
|
||||
bool invalidExt = (sizesExt.isEmpty() || iconWithExt.pixmap(sizesExt.first()).isNull());
|
||||
if (invalidExt) {
|
||||
if (invalidExt)
|
||||
{
|
||||
Utils::Fs::forceRemove(iconPath);
|
||||
continue;
|
||||
}
|
||||
@@ -418,12 +449,14 @@ void PluginSelectDialog::iconDownloadFinished(const Net::DownloadResult &result)
|
||||
void PluginSelectDialog::checkForUpdatesFinished(const QHash<QString, PluginVersion> &updateInfo)
|
||||
{
|
||||
finishAsyncOp();
|
||||
if (updateInfo.isEmpty()) {
|
||||
if (updateInfo.isEmpty())
|
||||
{
|
||||
QMessageBox::information(this, tr("Search plugin update"), tr("All your plugins are already up to date."));
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = updateInfo.cbegin(); i != updateInfo.cend(); ++i) {
|
||||
for (auto i = updateInfo.cbegin(); i != updateInfo.cend(); ++i)
|
||||
{
|
||||
startAsyncOp();
|
||||
++m_pendingUpdates;
|
||||
m_pluginManager->updatePlugin(i.key());
|
||||
|
||||
@@ -101,8 +101,10 @@ SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, QWidget *parent)
|
||||
|
||||
// Ensure that at least one column is visible at all times
|
||||
bool atLeastOne = false;
|
||||
for (int i = 0; i < SearchSortModel::DL_LINK; ++i) {
|
||||
if (!m_ui->resultsBrowser->isColumnHidden(i)) {
|
||||
for (int i = 0; i < SearchSortModel::DL_LINK; ++i)
|
||||
{
|
||||
if (!m_ui->resultsBrowser->isColumnHidden(i))
|
||||
{
|
||||
atLeastOne = true;
|
||||
break;
|
||||
}
|
||||
@@ -218,7 +220,8 @@ void SearchJobWidget::downloadTorrents()
|
||||
void SearchJobWidget::openTorrentPages() const
|
||||
{
|
||||
const QModelIndexList rows {m_ui->resultsBrowser->selectionModel()->selectedRows()};
|
||||
for (const QModelIndex &rowIndex : rows) {
|
||||
for (const QModelIndex &rowIndex : rows)
|
||||
{
|
||||
const QString descrLink = m_proxyModel->data(
|
||||
m_proxyModel->index(rowIndex.row(), SearchSortModel::DESC_LINK)).toString();
|
||||
if (!descrLink.isEmpty())
|
||||
@@ -246,7 +249,8 @@ void SearchJobWidget::copyField(const int column) const
|
||||
const QModelIndexList rows {m_ui->resultsBrowser->selectionModel()->selectedRows()};
|
||||
QStringList list;
|
||||
|
||||
for (const QModelIndex &rowIndex : rows) {
|
||||
for (const QModelIndex &rowIndex : rows)
|
||||
{
|
||||
const QString field = m_proxyModel->data(
|
||||
m_proxyModel->index(rowIndex.row(), column)).toString();
|
||||
if (!field.isEmpty())
|
||||
@@ -273,10 +277,12 @@ void SearchJobWidget::downloadTorrent(const QModelIndex &rowIndex)
|
||||
const QString siteUrl = m_proxyModel->data(
|
||||
m_proxyModel->index(rowIndex.row(), SearchSortModel::ENGINE_URL)).toString();
|
||||
|
||||
if (torrentUrl.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||
if (torrentUrl.startsWith("magnet:", Qt::CaseInsensitive))
|
||||
{
|
||||
addTorrentToSession(torrentUrl);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
SearchDownloadHandler *downloadHandler = m_searchHandler->manager()->downloadTorrent(siteUrl, torrentUrl);
|
||||
connect(downloadHandler, &SearchDownloadHandler::downloadFinished, this, &SearchJobWidget::addTorrentToSession);
|
||||
connect(downloadHandler, &SearchDownloadHandler::downloadFinished, downloadHandler, &SearchDownloadHandler::deleteLater);
|
||||
@@ -418,7 +424,8 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
|
||||
QString SearchJobWidget::statusText(SearchJobWidget::Status st)
|
||||
{
|
||||
switch (st) {
|
||||
switch (st)
|
||||
{
|
||||
case Status::Ongoing:
|
||||
return tr("Searching...");
|
||||
case Status::Finished:
|
||||
@@ -455,7 +462,8 @@ void SearchJobWidget::displayToggleColumnsMenu(const QPoint &)
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->setTitle(tr("Column visibility"));
|
||||
|
||||
for (int i = 0; i < SearchSortModel::DL_LINK; ++i) {
|
||||
for (int i = 0; i < SearchSortModel::DL_LINK; ++i)
|
||||
{
|
||||
QAction *myAct = menu->addAction(m_searchListModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
|
||||
myAct->setCheckable(true);
|
||||
myAct->setChecked(!m_ui->resultsBrowser->isColumnHidden(i));
|
||||
@@ -465,7 +473,8 @@ void SearchJobWidget::displayToggleColumnsMenu(const QPoint &)
|
||||
connect(menu, &QMenu::triggered, this, [this](const QAction *action)
|
||||
{
|
||||
int visibleCols = 0;
|
||||
for (int i = 0; i < SearchSortModel::DL_LINK; ++i) {
|
||||
for (int i = 0; i < SearchSortModel::DL_LINK; ++i)
|
||||
{
|
||||
if (!m_ui->resultsBrowser->isColumnHidden(i))
|
||||
++visibleCols;
|
||||
|
||||
@@ -506,7 +515,8 @@ void SearchJobWidget::searchFailed()
|
||||
|
||||
void SearchJobWidget::appendSearchResults(const QVector<SearchResult> &results)
|
||||
{
|
||||
for (const SearchResult &result : results) {
|
||||
for (const SearchResult &result : results)
|
||||
{
|
||||
// Add item to search result list
|
||||
int row = m_searchListModel->rowCount();
|
||||
m_searchListModel->insertRow(row);
|
||||
@@ -543,7 +553,8 @@ CachedSettingValue<SearchJobWidget::NameFilteringMode> &SearchJobWidget::nameFil
|
||||
|
||||
void SearchJobWidget::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
downloadTorrents();
|
||||
|
||||
@@ -54,10 +54,12 @@ void SearchSortModel::setNameFilter(const QString &searchTerm)
|
||||
{
|
||||
m_searchTerm = searchTerm;
|
||||
if ((searchTerm.length() > 2)
|
||||
&& searchTerm.startsWith(QLatin1Char('"')) && searchTerm.endsWith(QLatin1Char('"'))) {
|
||||
&& searchTerm.startsWith(QLatin1Char('"')) && searchTerm.endsWith(QLatin1Char('"')))
|
||||
{
|
||||
m_searchTermWords = QStringList(m_searchTerm.mid(1, m_searchTerm.length() - 2));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_searchTermWords = searchTerm.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
}
|
||||
}
|
||||
@@ -112,9 +114,11 @@ qint64 SearchSortModel::maxSize() const
|
||||
|
||||
bool SearchSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
switch (sortColumn()) {
|
||||
switch (sortColumn())
|
||||
{
|
||||
case NAME:
|
||||
case ENGINE_URL: {
|
||||
case ENGINE_URL:
|
||||
{
|
||||
const QString strL = left.data().toString();
|
||||
const QString strR = right.data().toString();
|
||||
const int result = Utils::String::naturalCompare(strL, strR, Qt::CaseInsensitive);
|
||||
@@ -130,29 +134,34 @@ bool SearchSortModel::filterAcceptsRow(const int sourceRow, const QModelIndex &s
|
||||
{
|
||||
const QAbstractItemModel *const sourceModel = this->sourceModel();
|
||||
|
||||
if (m_isNameFilterEnabled && !m_searchTerm.isEmpty()) {
|
||||
if (m_isNameFilterEnabled && !m_searchTerm.isEmpty())
|
||||
{
|
||||
const QString name = sourceModel->data(sourceModel->index(sourceRow, NAME, sourceParent), UnderlyingDataRole).toString();
|
||||
for (const QString &word : asConst(m_searchTermWords)) {
|
||||
for (const QString &word : asConst(m_searchTermWords))
|
||||
{
|
||||
if (!name.contains(word, Qt::CaseInsensitive))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_minSize > 0) || (m_maxSize >= 0)) {
|
||||
if ((m_minSize > 0) || (m_maxSize >= 0))
|
||||
{
|
||||
const qlonglong size = sourceModel->data(sourceModel->index(sourceRow, SIZE, sourceParent), UnderlyingDataRole).toLongLong();
|
||||
if (((m_minSize > 0) && (size < m_minSize))
|
||||
|| ((m_maxSize > 0) && (size > m_maxSize)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_minSeeds > 0) || (m_maxSeeds >= 0)) {
|
||||
if ((m_minSeeds > 0) || (m_maxSeeds >= 0))
|
||||
{
|
||||
const int seeds = sourceModel->data(sourceModel->index(sourceRow, SEEDS, sourceParent), UnderlyingDataRole).toInt();
|
||||
if (((m_minSeeds > 0) && (seeds < m_minSeeds))
|
||||
|| ((m_maxSeeds > 0) && (seeds > m_maxSeeds)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_minLeeches > 0) || (m_maxLeeches >= 0)) {
|
||||
if ((m_minLeeches > 0) || (m_maxLeeches >= 0))
|
||||
{
|
||||
const int leeches = sourceModel->data(sourceModel->index(sourceRow, LEECHES, sourceParent), UnderlyingDataRole).toInt();
|
||||
if (((m_minLeeches > 0) && (leeches < m_minLeeches))
|
||||
|| ((m_maxLeeches > 0) && (leeches > m_maxLeeches)))
|
||||
|
||||
@@ -63,7 +63,8 @@ namespace
|
||||
{
|
||||
QString statusIconName(SearchJobWidget::Status st)
|
||||
{
|
||||
switch (st) {
|
||||
switch (st)
|
||||
{
|
||||
case SearchJobWidget::Status::Ongoing:
|
||||
return QLatin1String("task-ongoing");
|
||||
case SearchJobWidget::Status::Finished:
|
||||
@@ -148,14 +149,16 @@ SearchWidget::SearchWidget(MainWindow *mainWindow)
|
||||
|
||||
bool SearchWidget::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
if (object == m_ui->tabWidget->tabBar()) {
|
||||
if (object == m_ui->tabWidget->tabBar())
|
||||
{
|
||||
// Close tabs when middle-clicked
|
||||
if (event->type() != QEvent::MouseButtonRelease)
|
||||
return false;
|
||||
|
||||
const auto mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
const int tabIndex = m_ui->tabWidget->tabBar()->tabAt(mouseEvent->pos());
|
||||
if ((mouseEvent->button() == Qt::MiddleButton) && (tabIndex >= 0)) {
|
||||
if ((mouseEvent->button() == Qt::MiddleButton) && (tabIndex >= 0))
|
||||
{
|
||||
closeTab(tabIndex);
|
||||
return true;
|
||||
}
|
||||
@@ -175,7 +178,8 @@ void SearchWidget::fillCatCombobox()
|
||||
tmpList << qMakePair(SearchPluginManager::categoryFullName(cat), cat);
|
||||
std::sort(tmpList.begin(), tmpList.end(), [](const QStrPair &l, const QStrPair &r) { return (QString::localeAwareCompare(l.first, r.first) < 0); });
|
||||
|
||||
for (const QStrPair &p : asConst(tmpList)) {
|
||||
for (const QStrPair &p : asConst(tmpList))
|
||||
{
|
||||
qDebug("Supported category: %s", qUtf8Printable(p.second));
|
||||
m_ui->comboCategory->addItem(p.first, p.second);
|
||||
}
|
||||
@@ -216,14 +220,16 @@ QString SearchWidget::selectedPlugin() const
|
||||
|
||||
void SearchWidget::selectActivePage()
|
||||
{
|
||||
if (SearchPluginManager::instance()->allPlugins().isEmpty()) {
|
||||
if (SearchPluginManager::instance()->allPlugins().isEmpty())
|
||||
{
|
||||
m_ui->stackedPages->setCurrentWidget(m_ui->emptyPage);
|
||||
m_ui->lineEditSearchPattern->setEnabled(false);
|
||||
m_ui->comboCategory->setEnabled(false);
|
||||
m_ui->selectPlugin->setEnabled(false);
|
||||
m_ui->searchButton->setEnabled(false);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->stackedPages->setCurrentWidget(m_ui->searchPage);
|
||||
m_ui->lineEditSearchPattern->setEnabled(true);
|
||||
m_ui->comboCategory->setEnabled(true);
|
||||
@@ -254,11 +260,13 @@ void SearchWidget::selectMultipleBox(int index)
|
||||
|
||||
void SearchWidget::toggleFocusBetweenLineEdits()
|
||||
{
|
||||
if (m_ui->lineEditSearchPattern->hasFocus() && m_currentSearchTab) {
|
||||
if (m_ui->lineEditSearchPattern->hasFocus() && m_currentSearchTab)
|
||||
{
|
||||
m_currentSearchTab->lineEditSearchResultsFilter()->setFocus();
|
||||
m_currentSearchTab->lineEditSearchResultsFilter()->selectAll();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->lineEditSearchPattern->setFocus();
|
||||
m_ui->lineEditSearchPattern->selectAll();
|
||||
}
|
||||
@@ -284,14 +292,17 @@ void SearchWidget::giveFocusToSearchInput()
|
||||
// Function called when we click on search button
|
||||
void SearchWidget::on_searchButton_clicked()
|
||||
{
|
||||
if (!Utils::ForeignApps::pythonInfo().isValid()) {
|
||||
if (!Utils::ForeignApps::pythonInfo().isValid())
|
||||
{
|
||||
m_mainWindow->showNotificationBaloon(tr("Search Engine"), tr("Please install Python to use the Search Engine."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_activeSearchTab) {
|
||||
if (m_activeSearchTab)
|
||||
{
|
||||
m_activeSearchTab->cancelSearch();
|
||||
if (!m_isNewQueryString) {
|
||||
if (!m_isNewQueryString)
|
||||
{
|
||||
m_ui->searchButton->setText(tr("Search"));
|
||||
return;
|
||||
}
|
||||
@@ -301,7 +312,8 @@ void SearchWidget::on_searchButton_clicked()
|
||||
|
||||
const QString pattern = m_ui->lineEditSearchPattern->text().trimmed();
|
||||
// No search pattern entered
|
||||
if (pattern.isEmpty()) {
|
||||
if (pattern.isEmpty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Empty search pattern"), tr("Please type a search pattern first"));
|
||||
return;
|
||||
}
|
||||
@@ -344,10 +356,12 @@ void SearchWidget::tabStatusChanged(QWidget *tab)
|
||||
m_ui->tabWidget->setTabIcon(tabIndex, UIThemeManager::instance()->getIcon(
|
||||
statusIconName(static_cast<SearchJobWidget *>(tab)->status())));
|
||||
|
||||
if ((tab == m_activeSearchTab) && (m_activeSearchTab->status() != SearchJobWidget::Status::Ongoing)) {
|
||||
if ((tab == m_activeSearchTab) && (m_activeSearchTab->status() != SearchJobWidget::Status::Ongoing))
|
||||
{
|
||||
Q_ASSERT(m_activeSearchTab->status() != SearchJobWidget::Status::Ongoing);
|
||||
|
||||
if (m_mainWindow->isNotificationsEnabled() && (m_mainWindow->currentTabWidget() != this)) {
|
||||
if (m_mainWindow->isNotificationsEnabled() && (m_mainWindow->currentTabWidget() != this))
|
||||
{
|
||||
if (m_activeSearchTab->status() == SearchJobWidget::Status::Error)
|
||||
m_mainWindow->showNotificationBaloon(tr("Search Engine"), tr("Search has failed"));
|
||||
else
|
||||
|
||||
@@ -91,7 +91,8 @@ void ShutdownConfirmDialog::updateSeconds()
|
||||
{
|
||||
--m_timeout;
|
||||
updateText();
|
||||
if (m_timeout == 0) {
|
||||
if (m_timeout == 0)
|
||||
{
|
||||
m_timer.stop();
|
||||
accept();
|
||||
}
|
||||
@@ -107,7 +108,8 @@ void ShutdownConfirmDialog::initText()
|
||||
{
|
||||
QPushButton *okButton = m_ui->buttonBox->button(QDialogButtonBox::Ok);
|
||||
|
||||
switch (m_action) {
|
||||
switch (m_action)
|
||||
{
|
||||
case ShutdownDialogAction::Exit:
|
||||
m_msg = tr("qBittorrent will now exit.");
|
||||
okButton->setText(tr("E&xit Now"));
|
||||
|
||||
@@ -59,7 +59,8 @@ long SpeedLimitDialog::askSpeedLimit(QWidget *parent, bool *ok, const QString &t
|
||||
dlg.setWindowTitle(title);
|
||||
dlg.setupDialog((maxVal / 1024.), (defaultVal / 1024.));
|
||||
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
if (dlg.exec() == QDialog::Accepted)
|
||||
{
|
||||
if (ok) *ok = true;
|
||||
|
||||
const int val = dlg.getSpeedLimit();
|
||||
|
||||
@@ -173,17 +173,21 @@ void StatusBar::updateConnectionStatus()
|
||||
{
|
||||
const BitTorrent::SessionStatus &sessionStatus = BitTorrent::Session::instance()->status();
|
||||
|
||||
if (!BitTorrent::Session::instance()->isListening()) {
|
||||
if (!BitTorrent::Session::instance()->isListening())
|
||||
{
|
||||
m_connecStatusLblIcon->setIcon(UIThemeManager::instance()->getIcon(QLatin1String("disconnected")));
|
||||
m_connecStatusLblIcon->setToolTip(QLatin1String("<b>") + tr("Connection Status:") + QLatin1String("</b><br>") + tr("Offline. This usually means that qBittorrent failed to listen on the selected port for incoming connections."));
|
||||
}
|
||||
else {
|
||||
if (sessionStatus.hasIncomingConnections) {
|
||||
else
|
||||
{
|
||||
if (sessionStatus.hasIncomingConnections)
|
||||
{
|
||||
// Connection OK
|
||||
m_connecStatusLblIcon->setIcon(UIThemeManager::instance()->getIcon(QLatin1String("connected")));
|
||||
m_connecStatusLblIcon->setToolTip(QLatin1String("<b>") + tr("Connection Status:") + QLatin1String("</b><br>") + tr("Online"));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_connecStatusLblIcon->setIcon(UIThemeManager::instance()->getIcon(QLatin1String("firewalled")));
|
||||
m_connecStatusLblIcon->setToolTip(QLatin1String("<b>") + tr("Connection status:") + QLatin1String("</b><br>") + QLatin1String("<i>") + tr("No direct connections. This may indicate network configuration problems.") + QLatin1String("</i>"));
|
||||
}
|
||||
@@ -192,12 +196,14 @@ void StatusBar::updateConnectionStatus()
|
||||
|
||||
void StatusBar::updateDHTNodesNumber()
|
||||
{
|
||||
if (BitTorrent::Session::instance()->isDHTEnabled()) {
|
||||
if (BitTorrent::Session::instance()->isDHTEnabled())
|
||||
{
|
||||
m_DHTLbl->setVisible(true);
|
||||
m_DHTLbl->setText(tr("DHT: %1 nodes")
|
||||
.arg(BitTorrent::Session::instance()->status().dhtNodes));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_DHTLbl->setVisible(false);
|
||||
}
|
||||
}
|
||||
@@ -230,12 +236,14 @@ void StatusBar::refresh()
|
||||
|
||||
void StatusBar::updateAltSpeedsBtn(bool alternative)
|
||||
{
|
||||
if (alternative) {
|
||||
if (alternative)
|
||||
{
|
||||
m_altSpeedsBtn->setIcon(UIThemeManager::instance()->getIcon(QLatin1String("slow")));
|
||||
m_altSpeedsBtn->setToolTip(tr("Click to switch to regular speed limits"));
|
||||
m_altSpeedsBtn->setDown(true);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_altSpeedsBtn->setIcon(UIThemeManager::instance()->getIcon(QLatin1String("slow_off")));
|
||||
m_altSpeedsBtn->setToolTip(tr("Click to switch to alternative speed limits"));
|
||||
m_altSpeedsBtn->setDown(false);
|
||||
@@ -250,7 +258,8 @@ void StatusBar::capDownloadSpeed()
|
||||
bool ok = false;
|
||||
const long newLimit = SpeedLimitDialog::askSpeedLimit(
|
||||
parentWidget(), &ok, tr("Global Download Speed Limit"), session->downloadSpeedLimit());
|
||||
if (ok) {
|
||||
if (ok)
|
||||
{
|
||||
qDebug("Setting global download rate limit to %.1fKb/s", newLimit / 1024.);
|
||||
session->setDownloadSpeedLimit(newLimit);
|
||||
refresh();
|
||||
@@ -264,7 +273,8 @@ void StatusBar::capUploadSpeed()
|
||||
bool ok = false;
|
||||
const long newLimit = SpeedLimitDialog::askSpeedLimit(
|
||||
parentWidget(), &ok, tr("Global Upload Speed Limit"), session->uploadSpeedLimit());
|
||||
if (ok) {
|
||||
if (ok)
|
||||
{
|
||||
qDebug("Setting global upload rate limit to %.1fKb/s", newLimit / 1024.);
|
||||
session->setUploadSpeedLimit(newLimit);
|
||||
refresh();
|
||||
|
||||
@@ -120,7 +120,8 @@ QVariant TagFilterModel::data(const QModelIndex &index, int role) const
|
||||
Q_ASSERT(isValidRow(row));
|
||||
const TagModelItem &item = m_tagItems[row];
|
||||
|
||||
switch (role) {
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DecorationRole:
|
||||
return UIThemeManager::instance()->getIcon("inode-directory");
|
||||
case Qt::DisplayRole:
|
||||
@@ -275,7 +276,8 @@ void TagFilterModel::populate()
|
||||
[](Torrent *torrent) { return torrent->tags().isEmpty(); });
|
||||
addToModel(getSpecialUntaggedTag(), untaggedCount);
|
||||
|
||||
for (const QString &tag : asConst(session->tags())) {
|
||||
for (const QString &tag : asConst(session->tags()))
|
||||
{
|
||||
const int count = std::count_if(torrents.cbegin(), torrents.cend(),
|
||||
[tag](Torrent *torrent) { return torrent->hasTag(tag); });
|
||||
addToModel(tag, count);
|
||||
@@ -295,7 +297,8 @@ void TagFilterModel::removeFromModel(int row)
|
||||
|
||||
int TagFilterModel::findRow(const QString &tag) const
|
||||
{
|
||||
for (int i = 0; i < m_tagItems.size(); ++i) {
|
||||
for (int i = 0; i < m_tagItems.size(); ++i)
|
||||
{
|
||||
if (m_tagItems[i].tag() == tag)
|
||||
return i;
|
||||
}
|
||||
@@ -314,7 +317,8 @@ QVector<TagModelItem *> TagFilterModel::findItems(const QSet<QString> &tags)
|
||||
{
|
||||
QVector<TagModelItem *> items;
|
||||
items.reserve(tags.size());
|
||||
for (const QString &tag : tags) {
|
||||
for (const QString &tag : tags)
|
||||
{
|
||||
TagModelItem *item = findItem(tag);
|
||||
if (item)
|
||||
items.push_back(item);
|
||||
|
||||
@@ -45,7 +45,8 @@ namespace
|
||||
QString getTagFilter(const TagFilterProxyModel *const model, const QModelIndex &index)
|
||||
{
|
||||
QString tagFilter; // Defaults to All
|
||||
if (index.isValid()) {
|
||||
if (index.isValid())
|
||||
{
|
||||
if (index.row() == 1)
|
||||
tagFilter = ""; // Untagged
|
||||
else if (index.row() > 1)
|
||||
@@ -113,7 +114,8 @@ void TagFilterWidget::showMenu(QPoint)
|
||||
connect(addAct, &QAction::triggered, this, &TagFilterWidget::addTag);
|
||||
|
||||
const auto selectedRows = selectionModel()->selectedRows();
|
||||
if (!selectedRows.empty() && !TagFilterModel::isSpecialItem(selectedRows.first())) {
|
||||
if (!selectedRows.empty() && !TagFilterModel::isSpecialItem(selectedRows.first()))
|
||||
{
|
||||
const QAction *removeAct = menu->addAction(
|
||||
UIThemeManager::instance()->getIcon("list-remove")
|
||||
, tr("Remove tag"));
|
||||
@@ -155,7 +157,8 @@ void TagFilterWidget::callUpdateGeometry()
|
||||
|
||||
QSize TagFilterWidget::sizeHint() const
|
||||
{
|
||||
return {
|
||||
return
|
||||
{
|
||||
// Width should be exactly the width of the content
|
||||
sizeHintForColumn(0),
|
||||
// Height should be exactly the height of the content
|
||||
@@ -181,12 +184,15 @@ QString TagFilterWidget::askTagName()
|
||||
bool ok = false;
|
||||
QString tag = "";
|
||||
bool invalid = true;
|
||||
while (invalid) {
|
||||
while (invalid)
|
||||
{
|
||||
invalid = false;
|
||||
tag = AutoExpandableDialog::getText(
|
||||
this, tr("New Tag"), tr("Tag:"), QLineEdit::Normal, tag, &ok).trimmed();
|
||||
if (ok && !tag.isEmpty()) {
|
||||
if (!BitTorrent::Session::isValidTag(tag)) {
|
||||
if (ok && !tag.isEmpty())
|
||||
{
|
||||
if (!BitTorrent::Session::isValidTag(tag))
|
||||
{
|
||||
QMessageBox::warning(
|
||||
this, tr("Invalid tag name")
|
||||
, tr("Tag name '%1' is invalid").arg(tag));
|
||||
@@ -212,7 +218,8 @@ void TagFilterWidget::addTag()
|
||||
void TagFilterWidget::removeTag()
|
||||
{
|
||||
const auto selectedRows = selectionModel()->selectedRows();
|
||||
if (!selectedRows.empty() && !TagFilterModel::isSpecialItem(selectedRows.first())) {
|
||||
if (!selectedRows.empty() && !TagFilterModel::isSpecialItem(selectedRows.first()))
|
||||
{
|
||||
BitTorrent::Session::instance()->removeTag(
|
||||
static_cast<TagFilterProxyModel *>(model())->tag(selectedRows.first()));
|
||||
updateGeometry();
|
||||
|
||||
@@ -58,23 +58,27 @@ QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &pa
|
||||
|
||||
TorrentCategoryDialog dialog(parent);
|
||||
dialog.setCategoryName(newCategoryName);
|
||||
while (dialog.exec() == TorrentCategoryDialog::Accepted) {
|
||||
while (dialog.exec() == TorrentCategoryDialog::Accepted)
|
||||
{
|
||||
newCategoryName = dialog.categoryName();
|
||||
|
||||
if (!BitTorrent::Session::isValidCategoryName(newCategoryName)) {
|
||||
if (!BitTorrent::Session::isValidCategoryName(newCategoryName))
|
||||
{
|
||||
QMessageBox::critical(
|
||||
parent, tr("Invalid category name")
|
||||
, tr("Category name cannot contain '\\'.\n"
|
||||
"Category name cannot start/end with '/'.\n"
|
||||
"Category name cannot contain '//' sequence."));
|
||||
}
|
||||
else if (BitTorrent::Session::instance()->categories().contains(newCategoryName)) {
|
||||
else if (BitTorrent::Session::instance()->categories().contains(newCategoryName))
|
||||
{
|
||||
QMessageBox::critical(
|
||||
parent, tr("Category creation error")
|
||||
, tr("Category with the given name already exists.\n"
|
||||
"Please choose a different name and try again."));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
Session::instance()->addCategory(newCategoryName, dialog.savePath());
|
||||
return newCategoryName;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ QModelIndex TorrentContentFilterModel::parent(const QModelIndex &child) const
|
||||
|
||||
bool TorrentContentFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
if (m_model->itemType(m_model->index(sourceRow, 0, sourceParent)) == TorrentContentModelItem::FolderType) {
|
||||
if (m_model->itemType(m_model->index(sourceRow, 0, sourceParent)) == TorrentContentModelItem::FolderType)
|
||||
{
|
||||
// accept folders if they have at least one filtered item
|
||||
return hasFiltered(m_model->index(sourceRow, 0, sourceParent));
|
||||
}
|
||||
@@ -81,17 +82,21 @@ bool TorrentContentFilterModel::filterAcceptsRow(int sourceRow, const QModelInde
|
||||
|
||||
bool TorrentContentFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
switch (sortColumn()) {
|
||||
case TorrentContentModelItem::COL_NAME: {
|
||||
switch (sortColumn())
|
||||
{
|
||||
case TorrentContentModelItem::COL_NAME:
|
||||
{
|
||||
const TorrentContentModelItem::ItemType leftType = m_model->itemType(m_model->index(left.row(), 0, left.parent()));
|
||||
const TorrentContentModelItem::ItemType rightType = m_model->itemType(m_model->index(right.row(), 0, right.parent()));
|
||||
|
||||
if (leftType == rightType) {
|
||||
if (leftType == rightType)
|
||||
{
|
||||
const QString strL = left.data().toString();
|
||||
const QString strR = right.data().toString();
|
||||
return Utils::String::naturalLessThan<Qt::CaseInsensitive>(strL, strR);
|
||||
}
|
||||
if ((leftType == TorrentContentModelItem::FolderType) && (sortOrder() == Qt::AscendingOrder)) {
|
||||
if ((leftType == TorrentContentModelItem::FolderType) && (sortOrder() == Qt::AscendingOrder))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,9 +130,11 @@ bool TorrentContentFilterModel::hasFiltered(const QModelIndex &folder) const
|
||||
QString name = folder.data().toString();
|
||||
if (name.contains(filterRegExp()))
|
||||
return true;
|
||||
for (int child = 0; child < m_model->rowCount(folder); ++child) {
|
||||
for (int child = 0; child < m_model->rowCount(folder); ++child)
|
||||
{
|
||||
QModelIndex childIndex = m_model->index(child, 0, folder);
|
||||
if (m_model->hasChildren(childIndex)) {
|
||||
if (m_model->hasChildren(childIndex))
|
||||
{
|
||||
if (hasFiltered(childIndex))
|
||||
return true;
|
||||
continue;
|
||||
|
||||
@@ -85,12 +85,14 @@ namespace
|
||||
QIcon icon(const QFileInfo &info) const final
|
||||
{
|
||||
const QString ext = info.suffix();
|
||||
if (!ext.isEmpty()) {
|
||||
if (!ext.isEmpty())
|
||||
{
|
||||
QPixmap cached;
|
||||
if (QPixmapCache::find(ext, &cached)) return {cached};
|
||||
|
||||
const QPixmap pixmap = pixmapForExtension(ext);
|
||||
if (!pixmap.isNull()) {
|
||||
if (!pixmap.isNull())
|
||||
{
|
||||
QPixmapCache::insert(ext, pixmap);
|
||||
return {pixmap};
|
||||
}
|
||||
@@ -160,12 +162,14 @@ namespace
|
||||
{
|
||||
const QMimeType mimeType = m_db.mimeTypeForFile(info, QMimeDatabase::MatchExtension);
|
||||
QIcon res = QIcon::fromTheme(mimeType.iconName());
|
||||
if (!res.isNull()) {
|
||||
if (!res.isNull())
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
res = QIcon::fromTheme(mimeType.genericIconName());
|
||||
if (!res.isNull()) {
|
||||
if (!res.isNull())
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -270,10 +274,12 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu
|
||||
if (!index.isValid())
|
||||
return false;
|
||||
|
||||
if ((index.column() == TorrentContentModelItem::COL_NAME) && (role == Qt::CheckStateRole)) {
|
||||
if ((index.column() == TorrentContentModelItem::COL_NAME) && (role == Qt::CheckStateRole))
|
||||
{
|
||||
auto *item = static_cast<TorrentContentModelItem*>(index.internalPointer());
|
||||
qDebug("setData(%s, %d", qUtf8Printable(item->name()), value.toInt());
|
||||
if (static_cast<int>(item->priority()) != value.toInt()) {
|
||||
if (static_cast<int>(item->priority()) != value.toInt())
|
||||
{
|
||||
BitTorrent::DownloadPriority prio = BitTorrent::DownloadPriority::Normal;
|
||||
if (value.toInt() == Qt::PartiallyChecked)
|
||||
prio = BitTorrent::DownloadPriority::Mixed;
|
||||
@@ -290,10 +296,12 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu
|
||||
return true;
|
||||
}
|
||||
|
||||
if (role == Qt::EditRole) {
|
||||
if (role == Qt::EditRole)
|
||||
{
|
||||
Q_ASSERT(index.isValid());
|
||||
auto *item = static_cast<TorrentContentModelItem*>(index.internalPointer());
|
||||
switch (index.column()) {
|
||||
switch (index.column())
|
||||
{
|
||||
case TorrentContentModelItem::COL_NAME:
|
||||
item->setName(value.toString());
|
||||
break;
|
||||
@@ -332,8 +340,10 @@ QVariant TorrentContentModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
auto *item = static_cast<TorrentContentModelItem*>(index.internalPointer());
|
||||
|
||||
switch (role) {
|
||||
case Qt::DecorationRole: {
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
if (index.column() != TorrentContentModelItem::COL_NAME)
|
||||
return {};
|
||||
|
||||
@@ -341,7 +351,8 @@ QVariant TorrentContentModel::data(const QModelIndex &index, int role) const
|
||||
return m_fileIconProvider->icon(QFileIconProvider::Folder);
|
||||
return m_fileIconProvider->icon(QFileInfo(item->name()));
|
||||
}
|
||||
case Qt::CheckStateRole: {
|
||||
case Qt::CheckStateRole:
|
||||
{
|
||||
if (index.column() != TorrentContentModelItem::COL_NAME)
|
||||
return {};
|
||||
|
||||
@@ -384,7 +395,8 @@ QVariant TorrentContentModel::headerData(int section, Qt::Orientation orientatio
|
||||
if (orientation != Qt::Horizontal)
|
||||
return {};
|
||||
|
||||
switch (role) {
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
return m_rootItem->displayData(section);
|
||||
|
||||
@@ -476,7 +488,8 @@ void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info)
|
||||
|
||||
TorrentContentModelFolder *currentParent;
|
||||
// Iterate over files
|
||||
for (int i = 0; i < filesCount; ++i) {
|
||||
for (int i = 0; i < filesCount; ++i)
|
||||
{
|
||||
currentParent = m_rootItem;
|
||||
const QString path = Utils::Fs::toUniformPath(info.filePath(i));
|
||||
|
||||
@@ -484,10 +497,12 @@ void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info)
|
||||
QVector<QStringRef> pathFolders = path.splitRef('/', QString::SkipEmptyParts);
|
||||
pathFolders.removeLast();
|
||||
|
||||
for (const QStringRef &pathPartRef : asConst(pathFolders)) {
|
||||
for (const QStringRef &pathPartRef : asConst(pathFolders))
|
||||
{
|
||||
const QString pathPart = pathPartRef.toString();
|
||||
TorrentContentModelFolder *newParent = currentParent->childFolderWithName(pathPart);
|
||||
if (!newParent) {
|
||||
if (!newParent)
|
||||
{
|
||||
newParent = new TorrentContentModelFolder(pathPart, currentParent);
|
||||
currentParent->appendChild(newParent);
|
||||
}
|
||||
@@ -503,7 +518,8 @@ void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info)
|
||||
|
||||
void TorrentContentModel::selectAll()
|
||||
{
|
||||
for (int i = 0; i < m_rootItem->childCount(); ++i) {
|
||||
for (int i = 0; i < m_rootItem->childCount(); ++i)
|
||||
{
|
||||
TorrentContentModelItem* child = m_rootItem->child(i);
|
||||
if (child->priority() == BitTorrent::DownloadPriority::Ignored)
|
||||
child->setPriority(BitTorrent::DownloadPriority::Normal);
|
||||
|
||||
@@ -88,7 +88,8 @@ TorrentContentModelItem *TorrentContentModelFolder::child(int row) const
|
||||
|
||||
TorrentContentModelFolder *TorrentContentModelFolder::childFolderWithName(const QString &name) const
|
||||
{
|
||||
for (TorrentContentModelItem *child : asConst(m_childItems)) {
|
||||
for (TorrentContentModelItem *child : asConst(m_childItems))
|
||||
{
|
||||
if ((child->itemType() == FolderType) && (child->name() == name))
|
||||
return static_cast<TorrentContentModelFolder *>(child);
|
||||
}
|
||||
@@ -112,8 +113,10 @@ void TorrentContentModelFolder::updatePriority()
|
||||
// then the folder should have the same
|
||||
// priority
|
||||
const BitTorrent::DownloadPriority prio = m_childItems.first()->priority();
|
||||
for (int i = 1; i < m_childItems.size(); ++i) {
|
||||
if (m_childItems.at(i)->priority() != prio) {
|
||||
for (int i = 1; i < m_childItems.size(); ++i)
|
||||
{
|
||||
if (m_childItems.at(i)->priority() != prio)
|
||||
{
|
||||
setPriority(BitTorrent::DownloadPriority::Mixed);
|
||||
return;
|
||||
}
|
||||
@@ -145,7 +148,8 @@ void TorrentContentModelFolder::recalculateProgress()
|
||||
qreal tProgress = 0;
|
||||
qulonglong tSize = 0;
|
||||
qulonglong tRemaining = 0;
|
||||
for (TorrentContentModelItem *child : asConst(m_childItems)) {
|
||||
for (TorrentContentModelItem *child : asConst(m_childItems))
|
||||
{
|
||||
if (child->priority() == BitTorrent::DownloadPriority::Ignored)
|
||||
continue;
|
||||
|
||||
@@ -156,7 +160,8 @@ void TorrentContentModelFolder::recalculateProgress()
|
||||
tRemaining += child->remaining();
|
||||
}
|
||||
|
||||
if (!isRootItem() && (tSize > 0)) {
|
||||
if (!isRootItem() && (tSize > 0))
|
||||
{
|
||||
m_progress = tProgress / tSize;
|
||||
m_remaining = tRemaining;
|
||||
Q_ASSERT(m_progress <= 1.);
|
||||
@@ -168,25 +173,29 @@ void TorrentContentModelFolder::recalculateAvailability()
|
||||
qreal tAvailability = 0;
|
||||
qulonglong tSize = 0;
|
||||
bool foundAnyData = false;
|
||||
for (TorrentContentModelItem *child : asConst(m_childItems)) {
|
||||
for (TorrentContentModelItem *child : asConst(m_childItems))
|
||||
{
|
||||
if (child->priority() == BitTorrent::DownloadPriority::Ignored)
|
||||
continue;
|
||||
|
||||
if (child->itemType() == FolderType)
|
||||
static_cast<TorrentContentModelFolder*>(child)->recalculateAvailability();
|
||||
const qreal childAvailability = child->availability();
|
||||
if (childAvailability >= 0) { // -1 means "no data"
|
||||
if (childAvailability >= 0)
|
||||
{ // -1 means "no data"
|
||||
tAvailability += childAvailability * child->size();
|
||||
foundAnyData = true;
|
||||
}
|
||||
tSize += child->size();
|
||||
}
|
||||
|
||||
if (!isRootItem() && (tSize > 0) && foundAnyData) {
|
||||
if (!isRootItem() && (tSize > 0) && foundAnyData)
|
||||
{
|
||||
m_availability = tAvailability / tSize;
|
||||
Q_ASSERT(m_availability <= 1.);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_availability = -1.;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,11 +107,14 @@ QString TorrentContentModelItem::displayData(const int column) const
|
||||
if (isRootItem())
|
||||
return m_itemData.value(column);
|
||||
|
||||
switch (column) {
|
||||
switch (column)
|
||||
{
|
||||
case COL_NAME:
|
||||
return m_name;
|
||||
case COL_PRIO: {
|
||||
switch (m_priority) {
|
||||
case COL_PRIO:
|
||||
{
|
||||
switch (m_priority)
|
||||
{
|
||||
case BitTorrent::DownloadPriority::Mixed:
|
||||
return tr("Mixed", "Mixed (priorities");
|
||||
case BitTorrent::DownloadPriority::Ignored:
|
||||
@@ -124,7 +127,8 @@ QString TorrentContentModelItem::displayData(const int column) const
|
||||
return tr("Normal", "Normal (priority)");
|
||||
}
|
||||
}
|
||||
case COL_PROGRESS: {
|
||||
case COL_PROGRESS:
|
||||
{
|
||||
const qreal progress = m_progress * 100;
|
||||
return (static_cast<int>(progress) == 100)
|
||||
? QString::fromLatin1("100%")
|
||||
@@ -134,7 +138,8 @@ QString TorrentContentModelItem::displayData(const int column) const
|
||||
return Utils::Misc::friendlyUnit(m_size);
|
||||
case COL_REMAINING:
|
||||
return Utils::Misc::friendlyUnit(remaining());
|
||||
case COL_AVAILABILITY: {
|
||||
case COL_AVAILABILITY:
|
||||
{
|
||||
const int avail = availability();
|
||||
if (avail < 0)
|
||||
return tr("N/A");
|
||||
@@ -155,7 +160,8 @@ QVariant TorrentContentModelItem::underlyingData(const int column) const
|
||||
if (isRootItem())
|
||||
return m_itemData.value(column);
|
||||
|
||||
switch (column) {
|
||||
switch (column)
|
||||
{
|
||||
case COL_NAME:
|
||||
return m_name;
|
||||
case COL_PRIO:
|
||||
|
||||
@@ -62,7 +62,8 @@ TorrentContentTreeView::TorrentContentTreeView(QWidget *parent)
|
||||
|
||||
void TorrentContentTreeView::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if ((event->key() != Qt::Key_Space) && (event->key() != Qt::Key_Select)) {
|
||||
if ((event->key() != Qt::Key_Space) && (event->key() != Qt::Key_Select))
|
||||
{
|
||||
QTreeView::keyPressEvent(event);
|
||||
return;
|
||||
}
|
||||
@@ -72,7 +73,8 @@ void TorrentContentTreeView::keyPressEvent(QKeyEvent *event)
|
||||
QModelIndex current = currentNameCell();
|
||||
|
||||
QVariant value = current.data(Qt::CheckStateRole);
|
||||
if (!value.isValid()) {
|
||||
if (!value.isValid())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
@@ -82,7 +84,8 @@ void TorrentContentTreeView::keyPressEvent(QKeyEvent *event)
|
||||
|
||||
const QModelIndexList selection = selectionModel()->selectedRows(TorrentContentModelItem::COL_NAME);
|
||||
|
||||
for (const QModelIndex &index : selection) {
|
||||
for (const QModelIndex &index : selection)
|
||||
{
|
||||
Q_ASSERT(index.column() == TorrentContentModelItem::COL_NAME);
|
||||
model()->setData(index, state, Qt::CheckStateRole);
|
||||
}
|
||||
@@ -109,14 +112,16 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre
|
||||
, modelIndex.data().toString(), &ok, isFile).trimmed();
|
||||
if (!ok || !modelIndex.isValid()) return;
|
||||
|
||||
if (!Utils::Fs::isValidFileSystemName(newName)) {
|
||||
if (!Utils::Fs::isValidFileSystemName(newName))
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
||||
tr("The name is empty or contains forbidden characters, please choose a different one."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFile) {
|
||||
if (isFile)
|
||||
{
|
||||
const int fileIndex = model->getFileIndex(modelIndex);
|
||||
|
||||
if (newName.endsWith(QB_EXT))
|
||||
@@ -129,15 +134,18 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre
|
||||
const QString newFileName = newName + (useFilenameExt ? QB_EXT : QString());
|
||||
const QString newFilePath = oldFilePath.leftRef(oldFilePath.size() - oldFileName.size()) + newFileName;
|
||||
|
||||
if (oldFileName == newFileName) {
|
||||
if (oldFileName == newFileName)
|
||||
{
|
||||
qDebug("Name did not change: %s", qUtf8Printable(oldFileName));
|
||||
return;
|
||||
}
|
||||
|
||||
// check if that name is already used
|
||||
for (int i = 0; i < torrent->filesCount(); ++i) {
|
||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
||||
{
|
||||
if (i == fileIndex) continue;
|
||||
if (Utils::Fs::sameFileNames(torrent->filePath(i), newFilePath)) {
|
||||
if (Utils::Fs::sameFileNames(torrent->filePath(i), newFilePath))
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
||||
tr("This name is already in use in this folder. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
@@ -150,7 +158,8 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre
|
||||
|
||||
model->setData(modelIndex, newName);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// renaming a folder
|
||||
|
||||
const QString oldName = modelIndex.data().toString();
|
||||
@@ -171,13 +180,15 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre
|
||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < torrent->filesCount(); ++i) {
|
||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
||||
{
|
||||
const QString currentPath = torrent->filePath(i);
|
||||
|
||||
if (currentPath.startsWith(oldPath))
|
||||
continue;
|
||||
|
||||
if (currentPath.startsWith(newPath, caseSensitivity)) {
|
||||
if (currentPath.startsWith(newPath, caseSensitivity))
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("The folder could not be renamed"),
|
||||
tr("This name is already in use. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
@@ -188,10 +199,12 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre
|
||||
// Replace path in all files
|
||||
bool needForceRecheck = false;
|
||||
|
||||
for (int i = 0; i < torrent->filesCount(); ++i) {
|
||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
||||
{
|
||||
const QString currentPath = torrent->filePath(i);
|
||||
|
||||
if (currentPath.startsWith(oldPath)) {
|
||||
if (currentPath.startsWith(oldPath))
|
||||
{
|
||||
const QString path {newPath + currentPath.mid(oldPath.length())};
|
||||
|
||||
if (!needForceRecheck && QFile::exists(path))
|
||||
@@ -228,14 +241,16 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent
|
||||
, modelIndex.data().toString(), &ok, isFile).trimmed();
|
||||
if (!ok || !modelIndex.isValid()) return;
|
||||
|
||||
if (!Utils::Fs::isValidFileSystemName(newName)) {
|
||||
if (!Utils::Fs::isValidFileSystemName(newName))
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
||||
tr("The name is empty or contains forbidden characters, please choose a different one."),
|
||||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFile) {
|
||||
if (isFile)
|
||||
{
|
||||
const int fileIndex = model->getFileIndex(modelIndex);
|
||||
|
||||
if (newName.endsWith(QB_EXT))
|
||||
@@ -244,15 +259,18 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent
|
||||
const QString oldFilePath = torrent.filePath(fileIndex);
|
||||
const QString newFilePath = oldFilePath.leftRef(oldFilePath.size() - oldFileName.size()) + newName;
|
||||
|
||||
if (oldFileName == newName) {
|
||||
if (oldFileName == newName)
|
||||
{
|
||||
qDebug("Name did not change: %s", qUtf8Printable(oldFileName));
|
||||
return;
|
||||
}
|
||||
|
||||
// check if that name is already used
|
||||
for (int i = 0; i < torrent.filesCount(); ++i) {
|
||||
for (int i = 0; i < torrent.filesCount(); ++i)
|
||||
{
|
||||
if (i == fileIndex) continue;
|
||||
if (Utils::Fs::sameFileNames(torrent.filePath(i), newFilePath)) {
|
||||
if (Utils::Fs::sameFileNames(torrent.filePath(i), newFilePath))
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
||||
tr("This name is already in use in this folder. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
@@ -265,7 +283,8 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent
|
||||
|
||||
model->setData(modelIndex, newName);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// renaming a folder
|
||||
|
||||
const QString oldName = modelIndex.data().toString();
|
||||
@@ -286,13 +305,15 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent
|
||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < torrent.filesCount(); ++i) {
|
||||
for (int i = 0; i < torrent.filesCount(); ++i)
|
||||
{
|
||||
const QString currentPath = torrent.filePath(i);
|
||||
|
||||
if (currentPath.startsWith(oldPath))
|
||||
continue;
|
||||
|
||||
if (currentPath.startsWith(newPath, caseSensitivity)) {
|
||||
if (currentPath.startsWith(newPath, caseSensitivity))
|
||||
{
|
||||
RaisedMessageBox::warning(this, tr("The folder could not be renamed"),
|
||||
tr("This name is already in use. Please use a different name."),
|
||||
QMessageBox::Ok);
|
||||
@@ -301,10 +322,12 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent
|
||||
}
|
||||
|
||||
// Replace path in all files
|
||||
for (int i = 0; i < torrent.filesCount(); ++i) {
|
||||
for (int i = 0; i < torrent.filesCount(); ++i)
|
||||
{
|
||||
const QString currentPath = torrent.filePath(i);
|
||||
|
||||
if (currentPath.startsWith(oldPath)) {
|
||||
if (currentPath.startsWith(oldPath))
|
||||
{
|
||||
const QString path {newPath + currentPath.mid(oldPath.length())};
|
||||
torrent.renameFile(i, path);
|
||||
}
|
||||
@@ -317,7 +340,8 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent
|
||||
QModelIndex TorrentContentTreeView::currentNameCell()
|
||||
{
|
||||
QModelIndex current = currentIndex();
|
||||
if (!current.isValid()) {
|
||||
if (!current.isValid())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -130,7 +130,8 @@ int TorrentCreatorDialog::getPieceSize() const
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
BitTorrent::TorrentFormat TorrentCreatorDialog::getTorrentFormat() const
|
||||
{
|
||||
switch (m_ui->comboTorrentFormat->currentIndex()) {
|
||||
switch (m_ui->comboTorrentFormat->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
return BitTorrent::TorrentFormat::V2;
|
||||
case 1:
|
||||
@@ -152,7 +153,8 @@ void TorrentCreatorDialog::dropEvent(QDropEvent *event)
|
||||
{
|
||||
event->acceptProposedAction();
|
||||
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
if (event->mimeData()->hasUrls())
|
||||
{
|
||||
// only take the first one
|
||||
QUrl firstItem = event->mimeData()->urls().first();
|
||||
QString path = (firstItem.scheme().compare("file", Qt::CaseInsensitive) == 0)
|
||||
@@ -174,7 +176,8 @@ void TorrentCreatorDialog::onCreateButtonClicked()
|
||||
|
||||
// test if readable
|
||||
const QFileInfo fi(input);
|
||||
if (!fi.isReadable()) {
|
||||
if (!fi.isReadable())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Torrent creation failed"), tr("Reason: Path to file/folder is not readable."));
|
||||
return;
|
||||
}
|
||||
@@ -195,7 +198,8 @@ void TorrentCreatorDialog::onCreateButtonClicked()
|
||||
|
||||
const QStringList trackers = m_ui->trackersList->toPlainText().trimmed()
|
||||
.replace(QRegularExpression("\n\n[\n]+"), "\n\n").split('\n');
|
||||
const BitTorrent::TorrentCreatorParams params {
|
||||
const BitTorrent::TorrentCreatorParams params
|
||||
{
|
||||
m_ui->checkPrivate->isChecked()
|
||||
#if (LIBTORRENT_VERSION_NUM >= 20000)
|
||||
, getTorrentFormat()
|
||||
@@ -227,10 +231,12 @@ void TorrentCreatorDialog::handleCreationSuccess(const QString &path, const QStr
|
||||
{
|
||||
// Remove busy cursor
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
if (m_ui->checkStartSeeding->isChecked()) {
|
||||
if (m_ui->checkStartSeeding->isChecked())
|
||||
{
|
||||
// Create save path temp data
|
||||
const BitTorrent::TorrentInfo info = BitTorrent::TorrentInfo::loadFromFile(Utils::Fs::toNativePath(path));
|
||||
if (!info.isValid()) {
|
||||
if (!info.isValid())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Torrent creation failed"), tr("Reason: Created torrent is invalid. It won't be added to download list."));
|
||||
return;
|
||||
}
|
||||
@@ -238,7 +244,8 @@ void TorrentCreatorDialog::handleCreationSuccess(const QString &path, const QStr
|
||||
BitTorrent::AddTorrentParams params;
|
||||
params.savePath = branchPath;
|
||||
params.skipChecking = true;
|
||||
if (m_ui->checkIgnoreShareLimits->isChecked()) {
|
||||
if (m_ui->checkIgnoreShareLimits->isChecked())
|
||||
{
|
||||
params.ratioLimit = BitTorrent::TorrentHandle::NO_RATIO_LIMIT;
|
||||
params.seedingTimeLimit = BitTorrent::TorrentHandle::NO_SEEDING_TIME_LIMIT;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ void TrackerEntriesDialog::setTrackers(const QVector<BitTorrent::TrackerEntry> &
|
||||
int maxTier = -1;
|
||||
QHash<int, QString> tiers; // <tier, tracker URLs>
|
||||
|
||||
for (const BitTorrent::TrackerEntry &entry : trackers) {
|
||||
for (const BitTorrent::TrackerEntry &entry : trackers)
|
||||
{
|
||||
tiers[entry.tier()] += (entry.url() + '\n');
|
||||
maxTier = std::max(maxTier, entry.tier());
|
||||
}
|
||||
@@ -85,10 +86,12 @@ QVector<BitTorrent::TrackerEntry> TrackerEntriesDialog::trackers() const
|
||||
entries.reserve(lines.size());
|
||||
|
||||
int tier = 0;
|
||||
for (QStringRef line : lines) {
|
||||
for (QStringRef line : lines)
|
||||
{
|
||||
line = line.trimmed();
|
||||
|
||||
if (line.isEmpty()) {
|
||||
if (line.isEmpty())
|
||||
{
|
||||
++tier;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ QSize TransferListDelegate::sizeHint(const QStyleOptionViewItem &option, const Q
|
||||
// This happens because icon from the 'name' column is no longer drawn.
|
||||
|
||||
static int nameColHeight = -1;
|
||||
if (nameColHeight == -1) {
|
||||
if (nameColHeight == -1)
|
||||
{
|
||||
const QModelIndex nameColumn = index.sibling(index.row(), TransferListModel::TR_NAME);
|
||||
nameColHeight = QStyledItemDelegate::sizeHint(option, nameColumn).height();
|
||||
}
|
||||
|
||||
@@ -128,7 +128,8 @@ BaseFilterWidget::BaseFilterWidget(QWidget *parent, TransferListWidget *transfer
|
||||
|
||||
QSize BaseFilterWidget::sizeHint() const
|
||||
{
|
||||
return {
|
||||
return
|
||||
{
|
||||
// Width should be exactly the width of the content
|
||||
sizeHintForColumn(0),
|
||||
// Height should be exactly the height of the content
|
||||
@@ -225,7 +226,8 @@ void StatusFilterWidget::updateTorrentNumbers()
|
||||
int nbErrored = 0;
|
||||
|
||||
const QVector<BitTorrent::TorrentHandle *> torrents = BitTorrent::Session::instance()->torrents();
|
||||
for (const BitTorrent::TorrentHandle *torrent : torrents) {
|
||||
for (const BitTorrent::TorrentHandle *torrent : torrents)
|
||||
{
|
||||
if (torrent->isDownloading())
|
||||
++nbDownloading;
|
||||
if (torrent->isUploading())
|
||||
@@ -311,19 +313,23 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash)
|
||||
QString host = getHost(tracker);
|
||||
bool exists = m_trackers.contains(host);
|
||||
|
||||
if (exists) {
|
||||
if (exists)
|
||||
{
|
||||
tmp = m_trackers.value(host);
|
||||
if (tmp.contains(hash))
|
||||
return;
|
||||
|
||||
if (host != "") {
|
||||
if (host != "")
|
||||
{
|
||||
trackerItem = item(rowFromTracker(host));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
trackerItem = item(TRACKERLESS_ROW);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
trackerItem = new QListWidgetItem();
|
||||
trackerItem->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon("network-server"));
|
||||
|
||||
@@ -334,7 +340,8 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash)
|
||||
|
||||
tmp.append(hash);
|
||||
m_trackers.insert(host, tmp);
|
||||
if (host == "") {
|
||||
if (host == "")
|
||||
{
|
||||
trackerItem->setText(tr("Trackerless (%1)").arg(tmp.size()));
|
||||
if (currentRow() == TRACKERLESS_ROW)
|
||||
applyFilter(TRACKERLESS_ROW);
|
||||
@@ -342,7 +349,8 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash)
|
||||
}
|
||||
|
||||
trackerItem->setText(QString::fromLatin1("%1 (%2)").arg(host, QString::number(tmp.size())));
|
||||
if (exists) {
|
||||
if (exists)
|
||||
{
|
||||
if (currentRow() == rowFromTracker(host))
|
||||
applyFilter(currentRow());
|
||||
return;
|
||||
@@ -350,8 +358,10 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash)
|
||||
|
||||
Q_ASSERT(count() >= 4);
|
||||
int insPos = count();
|
||||
for (int i = 4; i < count(); ++i) {
|
||||
if (Utils::String::naturalLessThan<Qt::CaseSensitive>(host, item(i)->text())) {
|
||||
for (int i = 4; i < count(); ++i)
|
||||
{
|
||||
if (Utils::String::naturalLessThan<Qt::CaseSensitive>(host, item(i)->text()))
|
||||
{
|
||||
insPos = i;
|
||||
break;
|
||||
}
|
||||
@@ -371,12 +381,14 @@ void TrackerFiltersList::removeItem(const QString &tracker, const QString &hash)
|
||||
return;
|
||||
tmp.removeAll(hash);
|
||||
|
||||
if (!host.isEmpty()) {
|
||||
if (!host.isEmpty())
|
||||
{
|
||||
// Remove from 'Error' and 'Warning' view
|
||||
trackerSuccess(hash, tracker);
|
||||
row = rowFromTracker(host);
|
||||
trackerItem = item(row);
|
||||
if (tmp.empty()) {
|
||||
if (tmp.empty())
|
||||
{
|
||||
if (currentRow() == row)
|
||||
setCurrentRow(0, QItemSelectionModel::SelectCurrent);
|
||||
delete trackerItem;
|
||||
@@ -387,7 +399,8 @@ void TrackerFiltersList::removeItem(const QString &tracker, const QString &hash)
|
||||
if (trackerItem)
|
||||
trackerItem->setText(QString::fromLatin1("%1 (%2)").arg(host, QString::number(tmp.size())));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
row = 1;
|
||||
trackerItem = item(TRACKERLESS_ROW);
|
||||
trackerItem->setText(tr("Trackerless (%1)").arg(tmp.size()));
|
||||
@@ -411,10 +424,13 @@ void TrackerFiltersList::setDownloadTrackerFavicon(bool value)
|
||||
if (value == m_downloadTrackerFavicon) return;
|
||||
m_downloadTrackerFavicon = value;
|
||||
|
||||
if (m_downloadTrackerFavicon) {
|
||||
for (auto i = m_trackers.cbegin(); i != m_trackers.cend(); ++i) {
|
||||
if (m_downloadTrackerFavicon)
|
||||
{
|
||||
for (auto i = m_trackers.cbegin(); i != m_trackers.cend(); ++i)
|
||||
{
|
||||
const QString &tracker = i.key();
|
||||
if (!tracker.isEmpty()) {
|
||||
if (!tracker.isEmpty())
|
||||
{
|
||||
const QString scheme = getScheme(tracker);
|
||||
downloadFavicon(QString("%1://%2/favicon.ico")
|
||||
.arg((scheme.startsWith("http") ? scheme : "http"), getHost(tracker)));
|
||||
@@ -428,28 +444,34 @@ void TrackerFiltersList::trackerSuccess(const QString &hash, const QString &trac
|
||||
QStringList errored = m_errors.value(hash);
|
||||
QStringList warned = m_warnings.value(hash);
|
||||
|
||||
if (errored.contains(tracker)) {
|
||||
if (errored.contains(tracker))
|
||||
{
|
||||
errored.removeAll(tracker);
|
||||
if (errored.empty()) {
|
||||
if (errored.empty())
|
||||
{
|
||||
m_errors.remove(hash);
|
||||
item(ERROR_ROW)->setText(tr("Error (%1)").arg(m_errors.size()));
|
||||
if (currentRow() == ERROR_ROW)
|
||||
applyFilter(ERROR_ROW);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_errors.insert(hash, errored);
|
||||
}
|
||||
}
|
||||
|
||||
if (warned.contains(tracker)) {
|
||||
if (warned.contains(tracker))
|
||||
{
|
||||
warned.removeAll(tracker);
|
||||
if (warned.empty()) {
|
||||
if (warned.empty())
|
||||
{
|
||||
m_warnings.remove(hash);
|
||||
item(WARNING_ROW)->setText(tr("Warning (%1)").arg(m_warnings.size()));
|
||||
if (currentRow() == WARNING_ROW)
|
||||
applyFilter(WARNING_ROW);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_warnings.insert(hash, warned);
|
||||
}
|
||||
}
|
||||
@@ -495,7 +517,8 @@ void TrackerFiltersList::downloadFavicon(const QString &url)
|
||||
|
||||
void TrackerFiltersList::handleFavicoDownloadFinished(const Net::DownloadResult &result)
|
||||
{
|
||||
if (result.status != Net::DownloadStatus::Success) {
|
||||
if (result.status != Net::DownloadStatus::Success)
|
||||
{
|
||||
if (result.url.endsWith(".ico", Qt::CaseInsensitive))
|
||||
downloadFavicon(result.url.left(result.url.size() - 4) + ".png");
|
||||
return;
|
||||
@@ -503,7 +526,8 @@ void TrackerFiltersList::handleFavicoDownloadFinished(const Net::DownloadResult
|
||||
|
||||
const QString host = getHost(result.url);
|
||||
|
||||
if (!m_trackers.contains(host)) {
|
||||
if (!m_trackers.contains(host))
|
||||
{
|
||||
Utils::Fs::forceRemove(result.filePath);
|
||||
return;
|
||||
}
|
||||
@@ -515,12 +539,14 @@ void TrackerFiltersList::handleFavicoDownloadFinished(const Net::DownloadResult
|
||||
//Detect a non-decodable icon
|
||||
QList<QSize> sizes = icon.availableSizes();
|
||||
bool invalid = (sizes.isEmpty() || icon.pixmap(sizes.first()).isNull());
|
||||
if (invalid) {
|
||||
if (invalid)
|
||||
{
|
||||
if (result.url.endsWith(".ico", Qt::CaseInsensitive))
|
||||
downloadFavicon(result.url.left(result.url.size() - 4) + ".png");
|
||||
Utils::Fs::forceRemove(result.filePath);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
trackerItem->setData(Qt::DecorationRole, QIcon(result.filePath));
|
||||
m_iconPaths.append(result.filePath);
|
||||
}
|
||||
@@ -612,7 +638,8 @@ QString TrackerFiltersList::getHost(const QString &tracker) const
|
||||
|
||||
QStringList TrackerFiltersList::getHashes(const int row) const
|
||||
{
|
||||
switch (row) {
|
||||
switch (row)
|
||||
{
|
||||
case TRACKERLESS_ROW:
|
||||
return m_trackers.value("");
|
||||
case ERROR_ROW:
|
||||
|
||||
@@ -92,7 +92,8 @@ namespace
|
||||
};
|
||||
|
||||
QHash<BitTorrent::TorrentState, QColor> colors;
|
||||
for (const TorrentStateColorDescriptor &colorDescriptor : colorDescriptors) {
|
||||
for (const TorrentStateColorDescriptor &colorDescriptor : colorDescriptors)
|
||||
{
|
||||
const QColor themeColor = UIThemeManager::instance()->getColor(colorDescriptor.id, QColor());
|
||||
if (themeColor.isValid())
|
||||
colors.insert(colorDescriptor.state, themeColor);
|
||||
@@ -105,7 +106,8 @@ namespace
|
||||
|
||||
TransferListModel::TransferListModel(QObject *parent)
|
||||
: QAbstractListModel {parent}
|
||||
, m_statusStrings {
|
||||
, m_statusStrings
|
||||
{
|
||||
{BitTorrent::TorrentState::Downloading, tr("Downloading")},
|
||||
{BitTorrent::TorrentState::StalledDownloading, tr("Stalled", "Torrent is waiting for download to begin")},
|
||||
{BitTorrent::TorrentState::DownloadingMetadata, tr("Downloading metadata", "Used when loading a magnet link")},
|
||||
@@ -158,9 +160,12 @@ int TransferListModel::columnCount(const QModelIndex &) const
|
||||
|
||||
QVariant TransferListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal) {
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
if (orientation == Qt::Horizontal)
|
||||
{
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case TR_QUEUE_POSITION: return QChar('#');
|
||||
case TR_NAME: return tr("Name", "i.e: torrent name");
|
||||
case TR_SIZE: return tr("Size", "i.e: torrent size");
|
||||
@@ -195,8 +200,10 @@ QVariant TransferListModel::headerData(int section, Qt::Orientation orientation,
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
else if (role == Qt::TextAlignmentRole) {
|
||||
switch (section) {
|
||||
else if (role == Qt::TextAlignmentRole)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case TR_AMOUNT_DOWNLOADED:
|
||||
case TR_AMOUNT_UPLOADED:
|
||||
case TR_AMOUNT_DOWNLOADED_SESSION:
|
||||
@@ -324,7 +331,8 @@ QString TransferListModel::displayValue(const BitTorrent::TorrentHandle *torrent
|
||||
: m_statusStrings[state];
|
||||
};
|
||||
|
||||
switch (column) {
|
||||
switch (column)
|
||||
{
|
||||
case TR_NAME:
|
||||
return torrent->name();
|
||||
case TR_QUEUE_POSITION:
|
||||
@@ -394,7 +402,8 @@ QString TransferListModel::displayValue(const BitTorrent::TorrentHandle *torrent
|
||||
|
||||
QVariant TransferListModel::internalValue(const BitTorrent::TorrentHandle *torrent, const int column, const bool alt) const
|
||||
{
|
||||
switch (column) {
|
||||
switch (column)
|
||||
{
|
||||
case TR_NAME:
|
||||
return torrent->name();
|
||||
case TR_QUEUE_POSITION:
|
||||
@@ -469,7 +478,8 @@ QVariant TransferListModel::data(const QModelIndex &index, const int role) const
|
||||
const BitTorrent::TorrentHandle *torrent = m_torrentList.value(index.row());
|
||||
if (!torrent) return {};
|
||||
|
||||
switch (role) {
|
||||
switch (role)
|
||||
{
|
||||
case Qt::ForegroundRole:
|
||||
return m_stateThemeColors.value(torrent->state(), getDefaultColorByState(torrent->state()));
|
||||
case Qt::DisplayRole:
|
||||
@@ -483,7 +493,8 @@ QVariant TransferListModel::data(const QModelIndex &index, const int role) const
|
||||
return getIconByState(torrent->state());
|
||||
break;
|
||||
case Qt::ToolTipRole:
|
||||
switch (index.column()) {
|
||||
switch (index.column())
|
||||
{
|
||||
case TR_NAME:
|
||||
case TR_STATUS:
|
||||
case TR_CATEGORY:
|
||||
@@ -494,7 +505,8 @@ QVariant TransferListModel::data(const QModelIndex &index, const int role) const
|
||||
}
|
||||
break;
|
||||
case Qt::TextAlignmentRole:
|
||||
switch (index.column()) {
|
||||
switch (index.column())
|
||||
{
|
||||
case TR_AMOUNT_DOWNLOADED:
|
||||
case TR_AMOUNT_UPLOADED:
|
||||
case TR_AMOUNT_DOWNLOADED_SESSION:
|
||||
@@ -530,7 +542,8 @@ bool TransferListModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
if (!torrent) return false;
|
||||
|
||||
// Category and Name columns can be edited
|
||||
switch (index.column()) {
|
||||
switch (index.column())
|
||||
{
|
||||
case TR_NAME:
|
||||
torrent->setName(value.toString());
|
||||
break;
|
||||
@@ -579,7 +592,8 @@ void TransferListModel::handleTorrentAboutToBeRemoved(BitTorrent::TorrentHandle
|
||||
beginRemoveRows({}, row, row);
|
||||
m_torrentList.removeAt(row);
|
||||
m_torrentMap.remove(torrent);
|
||||
for (int &value : m_torrentMap) {
|
||||
for (int &value : m_torrentMap)
|
||||
{
|
||||
if (value > row)
|
||||
--value;
|
||||
}
|
||||
@@ -598,15 +612,18 @@ void TransferListModel::handleTorrentsUpdated(const QVector<BitTorrent::TorrentH
|
||||
{
|
||||
const int columns = (columnCount() - 1);
|
||||
|
||||
if (torrents.size() <= (m_torrentList.size() * 0.5)) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : torrents) {
|
||||
if (torrents.size() <= (m_torrentList.size() * 0.5))
|
||||
{
|
||||
for (BitTorrent::TorrentHandle *const torrent : torrents)
|
||||
{
|
||||
const int row = m_torrentMap.value(torrent, -1);
|
||||
Q_ASSERT(row >= 0);
|
||||
|
||||
emit dataChanged(index(row, 0), index(row, columns));
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// save the overhead when more than half of the torrent list needs update
|
||||
emit dataChanged(index(0, 0), index((rowCount() - 1), columns));
|
||||
}
|
||||
@@ -617,14 +634,16 @@ void TransferListModel::configure()
|
||||
const Preferences *pref = Preferences::instance();
|
||||
|
||||
HideZeroValuesMode hideZeroValuesMode = HideZeroValuesMode::Never;
|
||||
if (pref->getHideZeroValues()) {
|
||||
if (pref->getHideZeroValues())
|
||||
{
|
||||
if (pref->getHideZeroComboValues() == 1)
|
||||
hideZeroValuesMode = HideZeroValuesMode::Paused;
|
||||
else
|
||||
hideZeroValuesMode = HideZeroValuesMode::Always;
|
||||
}
|
||||
|
||||
if (m_hideZeroValuesMode != hideZeroValuesMode) {
|
||||
if (m_hideZeroValuesMode != hideZeroValuesMode)
|
||||
{
|
||||
m_hideZeroValuesMode = hideZeroValuesMode;
|
||||
emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1)));
|
||||
}
|
||||
@@ -634,7 +653,8 @@ void TransferListModel::configure()
|
||||
|
||||
QIcon getIconByState(const BitTorrent::TorrentState state)
|
||||
{
|
||||
switch (state) {
|
||||
switch (state)
|
||||
{
|
||||
case BitTorrent::TorrentState::Downloading:
|
||||
case BitTorrent::TorrentState::ForcedDownloading:
|
||||
case BitTorrent::TorrentState::DownloadingMetadata:
|
||||
@@ -673,7 +693,8 @@ QColor getDefaultColorByState(const BitTorrent::TorrentState state)
|
||||
// Color names taken from http://cloford.com/resources/colours/500col.htm
|
||||
bool dark = isDarkTheme();
|
||||
|
||||
switch (state) {
|
||||
switch (state)
|
||||
{
|
||||
case BitTorrent::TorrentState::Downloading:
|
||||
case BitTorrent::TorrentState::ForcedDownloading:
|
||||
case BitTorrent::TorrentState::DownloadingMetadata:
|
||||
|
||||
@@ -112,7 +112,8 @@ bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelI
|
||||
const QVariant leftValue = left.data(TransferListModel::UnderlyingDataRole);
|
||||
const QVariant rightValue = right.data(TransferListModel::UnderlyingDataRole);
|
||||
|
||||
switch (sortColumn) {
|
||||
switch (sortColumn)
|
||||
{
|
||||
case TransferListModel::TR_CATEGORY:
|
||||
case TransferListModel::TR_TAGS:
|
||||
case TransferListModel::TR_NAME:
|
||||
@@ -132,27 +133,33 @@ bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelI
|
||||
|
||||
case TransferListModel::TR_ADD_DATE:
|
||||
case TransferListModel::TR_SEED_DATE:
|
||||
case TransferListModel::TR_SEEN_COMPLETE_DATE: {
|
||||
case TransferListModel::TR_SEEN_COMPLETE_DATE:
|
||||
{
|
||||
const QDateTime dateL = leftValue.toDateTime();
|
||||
const QDateTime dateR = rightValue.toDateTime();
|
||||
|
||||
if (dateL.isValid() && dateR.isValid()) {
|
||||
if (dateL.isValid() && dateR.isValid())
|
||||
{
|
||||
if (dateL != dateR)
|
||||
return dateL < dateR;
|
||||
}
|
||||
else if (dateL.isValid()) {
|
||||
else if (dateL.isValid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (dateR.isValid()) {
|
||||
else if (dateR.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return hashLessThan();
|
||||
}
|
||||
|
||||
case TransferListModel::TR_QUEUE_POSITION: {
|
||||
case TransferListModel::TR_QUEUE_POSITION:
|
||||
{
|
||||
// QVariant has comparators for all basic types
|
||||
if ((leftValue > 0) || (rightValue > 0)) {
|
||||
if ((leftValue > 0) || (rightValue > 0))
|
||||
{
|
||||
if ((leftValue > 0) && (rightValue > 0))
|
||||
return leftValue < rightValue;
|
||||
|
||||
@@ -165,14 +172,17 @@ bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelI
|
||||
const QDateTime dateR = right.sibling(right.row(), TransferListModel::TR_SEED_DATE)
|
||||
.data(TransferListModel::UnderlyingDataRole).toDateTime();
|
||||
|
||||
if (dateL.isValid() && dateR.isValid()) {
|
||||
if (dateL.isValid() && dateR.isValid())
|
||||
{
|
||||
if (dateL != dateR)
|
||||
return dateL < dateR;
|
||||
}
|
||||
else if (dateL.isValid()) {
|
||||
else if (dateL.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (dateR.isValid()) {
|
||||
else if (dateR.isValid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -180,7 +190,8 @@ bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelI
|
||||
}
|
||||
|
||||
case TransferListModel::TR_SEEDS:
|
||||
case TransferListModel::TR_PEERS: {
|
||||
case TransferListModel::TR_PEERS:
|
||||
{
|
||||
// QVariant has comparators for all basic types
|
||||
// Active peers/seeds take precedence over total peers/seeds.
|
||||
if (leftValue != rightValue)
|
||||
@@ -194,7 +205,8 @@ bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelI
|
||||
return invokeLessThanForColumn(TransferListModel::TR_QUEUE_POSITION);
|
||||
}
|
||||
|
||||
case TransferListModel::TR_ETA: {
|
||||
case TransferListModel::TR_ETA:
|
||||
{
|
||||
// Sorting rules prioritized.
|
||||
// 1. Active torrents at the top
|
||||
// 2. Seeding torrents at the bottom
|
||||
@@ -215,7 +227,8 @@ bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelI
|
||||
.data(TransferListModel::UnderlyingDataRole).toInt();
|
||||
const bool isSeedingL = (queuePosL < 0);
|
||||
const bool isSeedingR = (queuePosR < 0);
|
||||
if (isSeedingL != isSeedingR) {
|
||||
if (isSeedingL != isSeedingR)
|
||||
{
|
||||
const bool isAscendingOrder = (sortOrder() == Qt::AscendingOrder);
|
||||
if (isSeedingL)
|
||||
return !isAscendingOrder;
|
||||
@@ -227,7 +240,8 @@ bool TransferListSortModel::lessThan_impl(const QModelIndex &left, const QModelI
|
||||
const qlonglong etaR = rightValue.toLongLong();
|
||||
const bool isInvalidL = ((etaL < 0) || (etaL >= MAX_ETA));
|
||||
const bool isInvalidR = ((etaR < 0) || (etaR >= MAX_ETA));
|
||||
if (isInvalidL && isInvalidR) {
|
||||
if (isInvalidL && isInvalidR)
|
||||
{
|
||||
if (isSeedingL) // Both seeding
|
||||
return invokeLessThanForColumn(TransferListModel::TR_SEED_DATE);
|
||||
|
||||
|
||||
@@ -90,7 +90,8 @@ namespace
|
||||
if (!torrent->hasMetadata())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < torrent->filesCount(); ++i) {
|
||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
||||
{
|
||||
if (Utils::Misc::isPreviewable(Utils::Fs::fileExtension(torrent->fileName(i))))
|
||||
return true;
|
||||
}
|
||||
@@ -159,7 +160,8 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *mainWindow)
|
||||
header()->setStretchLastSection(false);
|
||||
|
||||
// Default hidden columns
|
||||
if (!columnLoaded) {
|
||||
if (!columnLoaded)
|
||||
{
|
||||
setColumnHidden(TransferListModel::TR_ADD_DATE, true);
|
||||
setColumnHidden(TransferListModel::TR_SEED_DATE, true);
|
||||
setColumnHidden(TransferListModel::TR_UPLIMIT, true);
|
||||
@@ -181,8 +183,10 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *mainWindow)
|
||||
|
||||
//Ensure that at least one column is visible at all times
|
||||
bool atLeastOne = false;
|
||||
for (int i = 0; i < TransferListModel::NB_COLUMNS; ++i) {
|
||||
if (!isColumnHidden(i)) {
|
||||
for (int i = 0; i < TransferListModel::NB_COLUMNS; ++i)
|
||||
{
|
||||
if (!isColumnHidden(i))
|
||||
{
|
||||
atLeastOne = true;
|
||||
break;
|
||||
}
|
||||
@@ -275,7 +279,8 @@ void TransferListWidget::torrentDoubleClicked()
|
||||
else
|
||||
action = Preferences::instance()->getActionOnDblClOnTorrentDl();
|
||||
|
||||
switch (action) {
|
||||
switch (action)
|
||||
{
|
||||
case TOGGLE_PAUSE:
|
||||
if (torrent->isPaused())
|
||||
torrent->resume();
|
||||
@@ -283,13 +288,15 @@ void TransferListWidget::torrentDoubleClicked()
|
||||
torrent->pause();
|
||||
break;
|
||||
case PREVIEW_FILE:
|
||||
if (torrentContainsPreviewableFiles(torrent)) {
|
||||
if (torrentContainsPreviewableFiles(torrent))
|
||||
{
|
||||
auto *dialog = new PreviewSelectDialog(this, torrent);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(dialog, &PreviewSelectDialog::readyToPreviewFile, this, &TransferListWidget::previewFile);
|
||||
dialog->show();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
openDestinationFolder(torrent);
|
||||
}
|
||||
break;
|
||||
@@ -395,7 +402,8 @@ void TransferListWidget::deleteSelectedTorrents(const bool deleteLocalFiles)
|
||||
const QVector<BitTorrent::TorrentHandle *> torrents = getSelectedTorrents();
|
||||
if (torrents.empty()) return;
|
||||
|
||||
if (Preferences::instance()->confirmTorrentDeletion()) {
|
||||
if (Preferences::instance()->confirmTorrentDeletion())
|
||||
{
|
||||
auto *dialog = new DeletionConfirmationDialog(this, torrents.size(), torrents[0]->name(), deleteLocalFiles);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(dialog, &DeletionConfirmationDialog::accepted, this, [this, dialog]()
|
||||
@@ -406,7 +414,8 @@ void TransferListWidget::deleteSelectedTorrents(const bool deleteLocalFiles)
|
||||
});
|
||||
dialog->open();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
removeTorrents(torrents, deleteLocalFiles);
|
||||
}
|
||||
}
|
||||
@@ -416,7 +425,8 @@ void TransferListWidget::deleteVisibleTorrents()
|
||||
const QVector<BitTorrent::TorrentHandle *> torrents = getVisibleTorrents();
|
||||
if (torrents.empty()) return;
|
||||
|
||||
if (Preferences::instance()->confirmTorrentDeletion()) {
|
||||
if (Preferences::instance()->confirmTorrentDeletion())
|
||||
{
|
||||
auto *dialog = new DeletionConfirmationDialog(this, torrents.size(), torrents[0]->name(), false);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(dialog, &DeletionConfirmationDialog::accepted, this, [this, dialog]()
|
||||
@@ -427,7 +437,8 @@ void TransferListWidget::deleteVisibleTorrents()
|
||||
});
|
||||
dialog->open();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
removeTorrents(torrents, false);
|
||||
}
|
||||
}
|
||||
@@ -498,15 +509,18 @@ void TransferListWidget::openSelectedTorrentsFolder() const
|
||||
#ifdef Q_OS_MACOS
|
||||
// On macOS you expect both the files and folders to be opened in their parent
|
||||
// folders prehilighted for opening, so we use a custom method.
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents())) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents()))
|
||||
{
|
||||
QString path = torrent->contentPath(true);
|
||||
pathsList.insert(path);
|
||||
}
|
||||
MacUtils::openFiles(pathsList);
|
||||
#else
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents())) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents()))
|
||||
{
|
||||
QString path = torrent->contentPath(true);
|
||||
if (!pathsList.contains(path)) {
|
||||
if (!pathsList.contains(path))
|
||||
{
|
||||
if (torrent->filesCount() == 1)
|
||||
Utils::Gui::openFolderSelect(path);
|
||||
else
|
||||
@@ -519,14 +533,17 @@ void TransferListWidget::openSelectedTorrentsFolder() const
|
||||
|
||||
void TransferListWidget::previewSelectedTorrents()
|
||||
{
|
||||
for (const BitTorrent::TorrentHandle *torrent : asConst(getSelectedTorrents())) {
|
||||
if (torrentContainsPreviewableFiles(torrent)) {
|
||||
for (const BitTorrent::TorrentHandle *torrent : asConst(getSelectedTorrents()))
|
||||
{
|
||||
if (torrentContainsPreviewableFiles(torrent))
|
||||
{
|
||||
auto *dialog = new PreviewSelectDialog(this, torrent);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(dialog, &PreviewSelectDialog::readyToPreviewFile, this, &TransferListWidget::previewFile);
|
||||
dialog->show();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, tr("Unable to preview"), tr("The selected torrent \"%1\" does not contain previewable files")
|
||||
.arg(torrent->name()));
|
||||
}
|
||||
@@ -536,7 +553,8 @@ void TransferListWidget::previewSelectedTorrents()
|
||||
void TransferListWidget::setDlLimitSelectedTorrents()
|
||||
{
|
||||
QVector<BitTorrent::TorrentHandle *> torrentsList;
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents())) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents()))
|
||||
{
|
||||
if (torrent->isSeed())
|
||||
continue;
|
||||
torrentsList += torrent;
|
||||
@@ -544,8 +562,10 @@ void TransferListWidget::setDlLimitSelectedTorrents()
|
||||
if (torrentsList.empty()) return;
|
||||
|
||||
int oldLimit = torrentsList.first()->downloadLimit();
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList)) {
|
||||
if (torrent->downloadLimit() != oldLimit) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList))
|
||||
{
|
||||
if (torrent->downloadLimit() != oldLimit)
|
||||
{
|
||||
oldLimit = -1;
|
||||
break;
|
||||
}
|
||||
@@ -557,7 +577,8 @@ void TransferListWidget::setDlLimitSelectedTorrents()
|
||||
, BitTorrent::Session::instance()->globalDownloadSpeedLimit());
|
||||
if (!ok) return;
|
||||
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList)) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList))
|
||||
{
|
||||
qDebug("Applying download speed limit of %ld Kb/s to torrent %s", (newLimit / 1024l), qUtf8Printable(torrent->hash()));
|
||||
torrent->setDownloadLimit(newLimit);
|
||||
}
|
||||
@@ -569,8 +590,10 @@ void TransferListWidget::setUpLimitSelectedTorrents()
|
||||
if (torrentsList.empty()) return;
|
||||
|
||||
int oldLimit = torrentsList.first()->uploadLimit();
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList)) {
|
||||
if (torrent->uploadLimit() != oldLimit) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList))
|
||||
{
|
||||
if (torrent->uploadLimit() != oldLimit)
|
||||
{
|
||||
oldLimit = -1;
|
||||
break;
|
||||
}
|
||||
@@ -582,7 +605,8 @@ void TransferListWidget::setUpLimitSelectedTorrents()
|
||||
, BitTorrent::Session::instance()->globalUploadSpeedLimit());
|
||||
if (!ok) return;
|
||||
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList)) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(torrentsList))
|
||||
{
|
||||
qDebug("Applying upload speed limit of %ld Kb/s to torrent %s", (newLimit / 1024l), qUtf8Printable(torrent->hash()));
|
||||
torrent->setUploadLimit(newLimit);
|
||||
}
|
||||
@@ -611,7 +635,8 @@ void TransferListWidget::setMaxRatioSelectedTorrents()
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(dialog, &QDialog::accepted, this, [dialog, torrents]()
|
||||
{
|
||||
for (BitTorrent::TorrentHandle *const torrent : torrents) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : torrents)
|
||||
{
|
||||
const qreal ratio = (dialog->useDefault()
|
||||
? BitTorrent::TorrentHandle::USE_GLOBAL_RATIO : dialog->ratio());
|
||||
torrent->setRatioLimit(ratio);
|
||||
@@ -626,7 +651,8 @@ void TransferListWidget::setMaxRatioSelectedTorrents()
|
||||
|
||||
void TransferListWidget::recheckSelectedTorrents()
|
||||
{
|
||||
if (Preferences::instance()->confirmTorrentRecheck()) {
|
||||
if (Preferences::instance()->confirmTorrentRecheck())
|
||||
{
|
||||
QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Recheck confirmation"), tr("Are you sure you want to recheck the selected torrent(s)?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (ret != QMessageBox::Yes) return;
|
||||
}
|
||||
@@ -648,7 +674,8 @@ void TransferListWidget::displayDLHoSMenu(const QPoint&)
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->setTitle(tr("Column visibility"));
|
||||
|
||||
for (int i = 0; i < m_listModel->columnCount(); ++i) {
|
||||
for (int i = 0; i < m_listModel->columnCount(); ++i)
|
||||
{
|
||||
if (!BitTorrent::Session::instance()->isQueueingSystemEnabled() && (i == TransferListModel::TR_QUEUE_POSITION))
|
||||
continue;
|
||||
|
||||
@@ -661,7 +688,8 @@ void TransferListWidget::displayDLHoSMenu(const QPoint&)
|
||||
connect(menu, &QMenu::triggered, this, [this](const QAction *action)
|
||||
{
|
||||
int visibleCols = 0;
|
||||
for (int i = 0; i < TransferListModel::NB_COLUMNS; ++i) {
|
||||
for (int i = 0; i < TransferListModel::NB_COLUMNS; ++i)
|
||||
{
|
||||
if (!isColumnHidden(i))
|
||||
++visibleCols;
|
||||
|
||||
@@ -687,7 +715,8 @@ void TransferListWidget::displayDLHoSMenu(const QPoint&)
|
||||
|
||||
void TransferListWidget::setSelectedTorrentsSuperSeeding(const bool enabled) const
|
||||
{
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents())) {
|
||||
for (BitTorrent::TorrentHandle *const torrent : asConst(getSelectedTorrents()))
|
||||
{
|
||||
if (torrent->hasMetadata())
|
||||
torrent->setSuperSeeding(enabled);
|
||||
}
|
||||
@@ -730,10 +759,12 @@ void TransferListWidget::editTorrentTrackers()
|
||||
const QVector<BitTorrent::TorrentHandle *> torrents = getSelectedTorrents();
|
||||
QVector<BitTorrent::TrackerEntry> commonTrackers;
|
||||
|
||||
if (!torrents.empty()) {
|
||||
if (!torrents.empty())
|
||||
{
|
||||
commonTrackers = torrents[0]->trackers();
|
||||
|
||||
for (const BitTorrent::TorrentHandle *torrent : torrents) {
|
||||
for (const BitTorrent::TorrentHandle *torrent : torrents)
|
||||
{
|
||||
QSet<BitTorrent::TrackerEntry> trackerSet;
|
||||
|
||||
for (const BitTorrent::TrackerEntry &entry : asConst(torrent->trackers()))
|
||||
@@ -771,7 +802,8 @@ QStringList TransferListWidget::askTagsForSelection(const QString &dialogTitle)
|
||||
{
|
||||
QStringList tags;
|
||||
bool invalid = true;
|
||||
while (invalid) {
|
||||
while (invalid)
|
||||
{
|
||||
bool ok = false;
|
||||
invalid = false;
|
||||
const QString tagsInput = AutoExpandableDialog::getText(
|
||||
@@ -779,9 +811,11 @@ QStringList TransferListWidget::askTagsForSelection(const QString &dialogTitle)
|
||||
if (!ok || tagsInput.isEmpty())
|
||||
return {};
|
||||
tags = tagsInput.split(',', QString::SkipEmptyParts);
|
||||
for (QString &tag : tags) {
|
||||
for (QString &tag : tags)
|
||||
{
|
||||
tag = tag.trimmed();
|
||||
if (!BitTorrent::Session::isValidTag(tag)) {
|
||||
if (!BitTorrent::Session::isValidTag(tag))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid tag")
|
||||
, tr("Tag name: '%1' is invalid").arg(tag));
|
||||
invalid = true;
|
||||
@@ -793,7 +827,8 @@ QStringList TransferListWidget::askTagsForSelection(const QString &dialogTitle)
|
||||
|
||||
void TransferListWidget::applyToSelectedTorrents(const std::function<void (BitTorrent::TorrentHandle *const)> &fn)
|
||||
{
|
||||
for (const QModelIndex &index : asConst(selectionModel()->selectedRows())) {
|
||||
for (const QModelIndex &index : asConst(selectionModel()->selectedRows()))
|
||||
{
|
||||
BitTorrent::TorrentHandle *const torrent = m_listModel->torrentHandle(mapToSource(index));
|
||||
Q_ASSERT(torrent);
|
||||
fn(torrent);
|
||||
@@ -812,7 +847,8 @@ void TransferListWidget::renameSelectedTorrent()
|
||||
// Ask for a new Name
|
||||
bool ok = false;
|
||||
QString name = AutoExpandableDialog::getText(this, tr("Rename"), tr("New name:"), QLineEdit::Normal, torrent->name(), &ok);
|
||||
if (ok && !name.isEmpty()) {
|
||||
if (ok && !name.isEmpty())
|
||||
{
|
||||
name.replace(QRegularExpression("\r?\n|\r"), " ");
|
||||
// Rename the torrent
|
||||
m_listModel->setData(mi, name, Qt::DisplayRole);
|
||||
@@ -918,7 +954,8 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
QSet<QString> tagsInAny;
|
||||
QSet<QString> tagsInAll;
|
||||
|
||||
for (const QModelIndex &index : selectedIndexes) {
|
||||
for (const QModelIndex &index : selectedIndexes)
|
||||
{
|
||||
// Get the file name
|
||||
// Get handle and pause the torrent
|
||||
const BitTorrent::TorrentHandle *torrent = m_listModel->torrentHandle(mapToSource(index));
|
||||
@@ -931,11 +968,13 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
|
||||
tagsInAny.unite(torrent->tags());
|
||||
|
||||
if (first) {
|
||||
if (first)
|
||||
{
|
||||
firstAutoTMM = torrent->isAutoTMMEnabled();
|
||||
tagsInAll = torrent->tags();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
tagsInAll.intersect(torrent->tags());
|
||||
}
|
||||
|
||||
@@ -944,21 +983,26 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
|
||||
if (torrent->hasMetadata())
|
||||
oneHasMetadata = true;
|
||||
if (!torrent->isSeed()) {
|
||||
if (!torrent->isSeed())
|
||||
{
|
||||
oneNotSeed = true;
|
||||
if (first) {
|
||||
if (first)
|
||||
{
|
||||
sequentialDownloadMode = torrent->isSequentialDownload();
|
||||
prioritizeFirstLast = torrent->hasFirstLastPiecePriority();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
if (sequentialDownloadMode != torrent->isSequentialDownload())
|
||||
allSameSequentialDownloadMode = false;
|
||||
if (prioritizeFirstLast != torrent->hasFirstLastPiecePriority())
|
||||
allSamePrioFirstlast = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!oneNotSeed && allSameSuperSeeding && torrent->hasMetadata()) {
|
||||
else
|
||||
{
|
||||
if (!oneNotSeed && allSameSuperSeeding && torrent->hasMetadata())
|
||||
{
|
||||
if (first)
|
||||
superSeedingMode = torrent->superSeeding();
|
||||
else if (superSeedingMode != torrent->superSeeding())
|
||||
@@ -976,7 +1020,8 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
else
|
||||
needsPause = true;
|
||||
|
||||
if (torrent->isErrored() || torrent->hasMissingFiles()) {
|
||||
if (torrent->isErrored() || torrent->hasMissingFiles())
|
||||
{
|
||||
// If torrent is in "errored" or "missing files" state
|
||||
// it cannot keep further processing until you restart it.
|
||||
needsStart = true;
|
||||
@@ -990,7 +1035,8 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
|
||||
if (oneHasMetadata && oneNotSeed && !allSameSequentialDownloadMode
|
||||
&& !allSamePrioFirstlast && !allSameSuperSeeding && !allSameCategory
|
||||
&& needsStart && needsForce && needsPause && needsPreview && !allSameAutoTMM) {
|
||||
&& needsStart && needsForce && needsPause && needsPreview && !allSameAutoTMM)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1023,11 +1069,13 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
|
||||
categoryMenu->addSeparator();
|
||||
|
||||
for (const QString &category : asConst(categories)) {
|
||||
for (const QString &category : asConst(categories))
|
||||
{
|
||||
const QString escapedCategory = QString(category).replace('&', "&&"); // avoid '&' becomes accelerator key
|
||||
|
||||
QAction *cat = new QAction(UIThemeManager::instance()->getIcon("inode-directory"), escapedCategory, categoryMenu);
|
||||
if (allSameCategory && (category == firstCategory)) {
|
||||
if (allSameCategory && (category == firstCategory))
|
||||
{
|
||||
cat->setCheckable(true);
|
||||
cat->setChecked(true);
|
||||
}
|
||||
@@ -1056,7 +1104,8 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
|
||||
tagsMenu->addSeparator();
|
||||
|
||||
for (const QString &tag : asConst(tags)) {
|
||||
for (const QString &tag : asConst(tags))
|
||||
{
|
||||
auto *action = new TriStateAction(tag, tagsMenu);
|
||||
action->setCloseOnTriggered(false);
|
||||
|
||||
@@ -1086,7 +1135,8 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
listMenu->addAction(actionSetDownloadLimit);
|
||||
listMenu->addAction(actionSetUploadLimit);
|
||||
listMenu->addAction(actionSetMaxRatio);
|
||||
if (!oneNotSeed && oneHasMetadata) {
|
||||
if (!oneNotSeed && oneHasMetadata)
|
||||
{
|
||||
actionSuperSeedingMode->setCheckState(allSameSuperSeeding
|
||||
? (superSeedingMode ? Qt::Checked : Qt::Unchecked)
|
||||
: Qt::PartiallyChecked);
|
||||
@@ -1094,11 +1144,13 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
}
|
||||
listMenu->addSeparator();
|
||||
bool addedPreviewAction = false;
|
||||
if (needsPreview) {
|
||||
if (needsPreview)
|
||||
{
|
||||
listMenu->addAction(actionPreviewFile);
|
||||
addedPreviewAction = true;
|
||||
}
|
||||
if (oneNotSeed) {
|
||||
if (oneNotSeed)
|
||||
{
|
||||
actionSequentialDownload->setCheckState(allSameSequentialDownloadMode
|
||||
? (sequentialDownloadMode ? Qt::Checked : Qt::Unchecked)
|
||||
: Qt::PartiallyChecked);
|
||||
@@ -1114,13 +1166,15 @@ void TransferListWidget::displayListMenu(const QPoint &)
|
||||
|
||||
if (addedPreviewAction)
|
||||
listMenu->addSeparator();
|
||||
if (oneHasMetadata) {
|
||||
if (oneHasMetadata)
|
||||
{
|
||||
listMenu->addAction(actionForceRecheck);
|
||||
listMenu->addAction(actionForceReannounce);
|
||||
listMenu->addSeparator();
|
||||
}
|
||||
listMenu->addAction(actionOpenDestinationFolder);
|
||||
if (BitTorrent::Session::instance()->isQueueingSystemEnabled() && oneNotSeed) {
|
||||
if (BitTorrent::Session::instance()->isQueueingSystemEnabled() && oneNotSeed)
|
||||
{
|
||||
listMenu->addSeparator();
|
||||
QMenu *queueMenu = listMenu->addMenu(tr("Queue"));
|
||||
queueMenu->addAction(actionTopQueuePos);
|
||||
@@ -1142,7 +1196,8 @@ void TransferListWidget::currentChanged(const QModelIndex ¤t, const QModel
|
||||
{
|
||||
qDebug("CURRENT CHANGED");
|
||||
BitTorrent::TorrentHandle *torrent = nullptr;
|
||||
if (current.isValid()) {
|
||||
if (current.isValid())
|
||||
{
|
||||
torrent = m_listModel->torrentHandle(mapToSource(current));
|
||||
// Scroll Fix
|
||||
scrollTo(current);
|
||||
@@ -1187,7 +1242,8 @@ void TransferListWidget::applyStatusFilter(int f)
|
||||
{
|
||||
m_sortFilterModel->setStatusFilter(static_cast<TorrentFilter::Type>(f));
|
||||
// Select first item if nothing is selected
|
||||
if (selectionModel()->selectedRows(0).empty() && (m_sortFilterModel->rowCount() > 0)) {
|
||||
if (selectionModel()->selectedRows(0).empty() && (m_sortFilterModel->rowCount() > 0))
|
||||
{
|
||||
qDebug("Nothing is selected, selecting first row: %s", qUtf8Printable(m_sortFilterModel->index(0, TransferListModel::TR_NAME).data().toString()));
|
||||
selectionModel()->setCurrentIndex(m_sortFilterModel->index(0, TransferListModel::TR_NAME), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
|
||||
}
|
||||
@@ -1205,7 +1261,8 @@ bool TransferListWidget::loadSettings()
|
||||
|
||||
void TransferListWidget::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (event->modifiers() & Qt::ShiftModifier) {
|
||||
if (event->modifiers() & Qt::ShiftModifier)
|
||||
{
|
||||
// Shift + scroll = horizontal scroll
|
||||
event->accept();
|
||||
|
||||
|
||||
@@ -74,7 +74,8 @@ void TriStateWidget::paintEvent(QPaintEvent *)
|
||||
opt.menuHasCheckableItems = true;
|
||||
opt.text = m_text;
|
||||
|
||||
switch (m_checkState) {
|
||||
switch (m_checkState)
|
||||
{
|
||||
case Qt::PartiallyChecked:
|
||||
opt.state |= QStyle::State_NoChange;
|
||||
break;
|
||||
@@ -87,7 +88,8 @@ void TriStateWidget::paintEvent(QPaintEvent *)
|
||||
};
|
||||
|
||||
if ((opt.state & QStyle::State_HasFocus)
|
||||
|| rect().contains(mapFromGlobal(QCursor::pos()))) {
|
||||
|| rect().contains(mapFromGlobal(QCursor::pos())))
|
||||
{
|
||||
opt.state |= QStyle::State_Selected;
|
||||
|
||||
if (QApplication::mouseButtons() != Qt::NoButton)
|
||||
@@ -102,11 +104,13 @@ void TriStateWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
toggleCheckState();
|
||||
|
||||
if (m_closeOnTriggered) {
|
||||
if (m_closeOnTriggered)
|
||||
{
|
||||
// parent `triggered` signal will be emitted
|
||||
QWidget::mouseReleaseEvent(event);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
update();
|
||||
// need to emit parent `triggered` signal manually
|
||||
emit triggered(m_checkState == Qt::Checked);
|
||||
@@ -116,10 +120,12 @@ void TriStateWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
void TriStateWidget::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if ((event->key() == Qt::Key_Return)
|
||||
|| (event->key() == Qt::Key_Enter)) {
|
||||
|| (event->key() == Qt::Key_Enter))
|
||||
{
|
||||
toggleCheckState();
|
||||
|
||||
if (!m_closeOnTriggered) {
|
||||
if (!m_closeOnTriggered)
|
||||
{
|
||||
update();
|
||||
// need to emit parent `triggered` signal manually
|
||||
emit triggered(m_checkState == Qt::Checked);
|
||||
@@ -132,7 +138,8 @@ void TriStateWidget::keyPressEvent(QKeyEvent *event)
|
||||
|
||||
void TriStateWidget::toggleCheckState()
|
||||
{
|
||||
switch (m_checkState) {
|
||||
switch (m_checkState)
|
||||
{
|
||||
case Qt::Unchecked:
|
||||
case Qt::PartiallyChecked:
|
||||
m_checkState = Qt::Checked;
|
||||
|
||||
@@ -81,11 +81,14 @@ UIThemeManager::UIThemeManager()
|
||||
#endif
|
||||
{
|
||||
const Preferences *const pref = Preferences::instance();
|
||||
if (m_useCustomTheme) {
|
||||
if (!QResource::registerResource(pref->customUIThemePath(), "/uitheme")) {
|
||||
if (m_useCustomTheme)
|
||||
{
|
||||
if (!QResource::registerResource(pref->customUIThemePath(), "/uitheme"))
|
||||
{
|
||||
LogMsg(tr("Failed to load UI theme from file: \"%1\"").arg(pref->customUIThemePath()), Log::WARNING);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
loadColorsFromJSONConfig();
|
||||
applyPalette();
|
||||
applyStyleSheet();
|
||||
@@ -101,7 +104,8 @@ UIThemeManager *UIThemeManager::instance()
|
||||
void UIThemeManager::applyStyleSheet() const
|
||||
{
|
||||
QFile qssFile(":uitheme/stylesheet.qss");
|
||||
if (!qssFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
if (!qssFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
qApp->setStyleSheet({});
|
||||
LogMsg(tr("Couldn't apply theme stylesheet. stylesheet.qss couldn't be opened. Reason: %1").arg(qssFile.errorString())
|
||||
, Log::WARNING);
|
||||
@@ -114,7 +118,8 @@ void UIThemeManager::applyStyleSheet() const
|
||||
QIcon UIThemeManager::getIcon(const QString &iconId, const QString &fallback) const
|
||||
{
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
if (m_useSystemTheme) {
|
||||
if (m_useSystemTheme)
|
||||
{
|
||||
QIcon icon = QIcon::fromTheme(iconId);
|
||||
if (icon.name() != iconId)
|
||||
icon = QIcon::fromTheme(fallback, QIcon(getIconPathFromResources(iconId, fallback)));
|
||||
@@ -155,9 +160,11 @@ QColor UIThemeManager::getColor(const QString &id, const QColor &defaultColor) c
|
||||
QString UIThemeManager::getIconPath(const QString &iconId) const
|
||||
{
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
if (m_useSystemTheme) {
|
||||
if (m_useSystemTheme)
|
||||
{
|
||||
QString path = Utils::Fs::tempPath() + iconId + QLatin1String(".png");
|
||||
if (!QFile::exists(path)) {
|
||||
if (!QFile::exists(path))
|
||||
{
|
||||
const QIcon icon = QIcon::fromTheme(iconId);
|
||||
if (!icon.isNull())
|
||||
icon.pixmap(32).save(path);
|
||||
@@ -173,12 +180,14 @@ QString UIThemeManager::getIconPath(const QString &iconId) const
|
||||
|
||||
QString UIThemeManager::getIconPathFromResources(const QString &iconId, const QString &fallback) const
|
||||
{
|
||||
if (m_useCustomTheme) {
|
||||
if (m_useCustomTheme)
|
||||
{
|
||||
const QString customIcon = findIcon(iconId, THEME_ICONS_DIR);
|
||||
if (!customIcon.isEmpty())
|
||||
return customIcon;
|
||||
|
||||
if (!fallback.isEmpty()) {
|
||||
if (!fallback.isEmpty())
|
||||
{
|
||||
const QString fallbackIcon = findIcon(fallback, THEME_ICONS_DIR);
|
||||
if (!fallbackIcon.isEmpty())
|
||||
return fallbackIcon;
|
||||
@@ -191,26 +200,31 @@ QString UIThemeManager::getIconPathFromResources(const QString &iconId, const QS
|
||||
void UIThemeManager::loadColorsFromJSONConfig()
|
||||
{
|
||||
QFile configFile(CONFIG_FILE_NAME);
|
||||
if (!configFile.open(QIODevice::ReadOnly)) {
|
||||
if (!configFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
LogMsg(tr("Failed to open \"%1\". Reason: %2").arg(CONFIG_FILE_NAME, configFile.errorString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument configJsonDoc = QJsonDocument::fromJson(configFile.readAll(), &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
LogMsg(tr("\"%1\" has invalid format. Reason: %2").arg(CONFIG_FILE_NAME, jsonError.errorString()), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
if (!configJsonDoc.isObject()) {
|
||||
if (!configJsonDoc.isObject())
|
||||
{
|
||||
LogMsg(tr("\"%1\" has invalid format. Reason: %2").arg(CONFIG_FILE_NAME, tr("Root JSON value is not an object")), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonObject colors = configJsonDoc.object().value("colors").toObject();
|
||||
for (auto color = colors.constBegin(); color != colors.constEnd(); ++color) {
|
||||
for (auto color = colors.constBegin(); color != colors.constEnd(); ++color)
|
||||
{
|
||||
const QColor providedColor(color.value().toString());
|
||||
if (!providedColor.isValid()) {
|
||||
if (!providedColor.isValid())
|
||||
{
|
||||
LogMsg(tr("Invalid color for ID \"%1\" is provided by theme").arg(color.key()), Log::WARNING);
|
||||
continue;
|
||||
}
|
||||
@@ -257,7 +271,8 @@ void UIThemeManager::applyPalette() const
|
||||
};
|
||||
|
||||
QPalette palette = qApp->palette();
|
||||
for (const ColorDescriptor &colorDescriptor : paletteColorDescriptors) {
|
||||
for (const ColorDescriptor &colorDescriptor : paletteColorDescriptors)
|
||||
{
|
||||
const QColor defaultColor = palette.color(colorDescriptor.colorGroup, colorDescriptor.colorRole);
|
||||
const QColor newColor = getColor(colorDescriptor.id, defaultColor);
|
||||
palette.setColor(colorDescriptor.colorGroup, colorDescriptor.colorRole, newColor);
|
||||
|
||||
@@ -43,15 +43,18 @@ UpDownRatioDialog::UpDownRatioDialog(bool useDefault, qreal initialRatioValue,
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
if (useDefault) {
|
||||
if (useDefault)
|
||||
{
|
||||
m_ui->useDefaultButton->setChecked(true);
|
||||
}
|
||||
else if ((initialRatioValue == -1.) && (initialTimeValue == -1)) {
|
||||
else if ((initialRatioValue == -1.) && (initialTimeValue == -1))
|
||||
{
|
||||
m_ui->noLimitButton->setChecked(true);
|
||||
initialRatioValue = BitTorrent::Session::instance()->globalMaxRatio();
|
||||
initialTimeValue = BitTorrent::Session::instance()->globalMaxSeedingMinutes();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_ui->torrentLimitButton->setChecked(true);
|
||||
|
||||
if (initialRatioValue >= 0)
|
||||
|
||||
@@ -105,7 +105,8 @@ QPixmap Utils::Gui::scaledPixmapSvg(const QString &path, const QWidget *widget,
|
||||
|
||||
QPixmap pm;
|
||||
QPixmapCache cache;
|
||||
if (!cache.find(normalizedKey, &pm)) {
|
||||
if (!cache.find(normalizedKey, &pm))
|
||||
{
|
||||
pm = QIcon(path).pixmap(scaledHeight);
|
||||
cache.insert(normalizedKey, pm);
|
||||
}
|
||||
@@ -178,7 +179,8 @@ void Utils::Gui::openFolderSelect(const QString &absolutePath)
|
||||
QString path {Utils::Fs::toUniformPath(absolutePath)};
|
||||
const QFileInfo pathInfo {path};
|
||||
// If the item to select doesn't exist, try to open its parent
|
||||
if (!pathInfo.exists(path)) {
|
||||
if (!pathInfo.exists(path))
|
||||
{
|
||||
openPath(path.left(path.lastIndexOf('/')));
|
||||
return;
|
||||
}
|
||||
@@ -186,7 +188,8 @@ void Utils::Gui::openFolderSelect(const QString &absolutePath)
|
||||
#ifdef Q_OS_WIN
|
||||
HRESULT hresult = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
PIDLIST_ABSOLUTE pidl = ::ILCreateFromPathW(reinterpret_cast<PCTSTR>(Utils::Fs::toNativePath(path).utf16()));
|
||||
if (pidl) {
|
||||
if (pidl)
|
||||
{
|
||||
::SHOpenFolderAndSelectItems(pidl, 0, nullptr, 0);
|
||||
::ILFree(pidl);
|
||||
}
|
||||
@@ -197,11 +200,13 @@ void Utils::Gui::openFolderSelect(const QString &absolutePath)
|
||||
proc.start("xdg-mime", {"query", "default", "inode/directory"});
|
||||
proc.waitForFinished();
|
||||
const QString output = proc.readLine().simplified();
|
||||
if ((output == "dolphin.desktop") || (output == "org.kde.dolphin.desktop")) {
|
||||
if ((output == "dolphin.desktop") || (output == "org.kde.dolphin.desktop"))
|
||||
{
|
||||
proc.startDetached("dolphin", {"--select", Utils::Fs::toNativePath(path)});
|
||||
}
|
||||
else if ((output == "nautilus.desktop") || (output == "org.gnome.Nautilus.desktop")
|
||||
|| (output == "nautilus-folder-handler.desktop")) {
|
||||
|| (output == "nautilus-folder-handler.desktop"))
|
||||
{
|
||||
if (pathInfo.isDir())
|
||||
path = path.left(path.lastIndexOf('/'));
|
||||
proc.start("nautilus", {"--version"});
|
||||
@@ -213,15 +218,18 @@ void Utils::Gui::openFolderSelect(const QString &absolutePath)
|
||||
else
|
||||
proc.startDetached("nautilus", {"--no-desktop", Utils::Fs::toNativePath(path)});
|
||||
}
|
||||
else if (output == "nemo.desktop") {
|
||||
else if (output == "nemo.desktop")
|
||||
{
|
||||
if (pathInfo.isDir())
|
||||
path = path.left(path.lastIndexOf('/'));
|
||||
proc.startDetached("nemo", {"--no-desktop", Utils::Fs::toNativePath(path)});
|
||||
}
|
||||
else if ((output == "konqueror.desktop") || (output == "kfmclient_dir.desktop")) {
|
||||
else if ((output == "konqueror.desktop") || (output == "kfmclient_dir.desktop"))
|
||||
{
|
||||
proc.startDetached("konqueror", {"--select", Utils::Fs::toNativePath(path)});
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// "caja" manager can't pinpoint the file, see: https://github.com/qbittorrent/qBittorrent/issues/5003
|
||||
openPath(path.left(path.lastIndexOf('/')));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user