From a96cabfe730304658e6a9d0d297a6a7cf630140a Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Sat, 6 Feb 2016 11:38:00 +0300 Subject: [PATCH 1/3] Make AddNewTorrentDialog behavior uniform --- src/gui/addnewtorrentdialog.cpp | 130 ++++++++++++-------------------- src/gui/addnewtorrentdialog.h | 1 - src/gui/addnewtorrentdialog.ui | 2 +- 3 files changed, 48 insertions(+), 85 deletions(-) diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index cb95fab2c718..3d9122e04a6a 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -69,9 +69,9 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) Preferences* const pref = Preferences::instance(); ui->start_torrent_cb->setChecked(!pref->addTorrentsInPause()); - ui->save_path_combo->addItem(Utils::Fs::toNativePath(pref->getSavePath()), pref->getSavePath()); + ui->savePathComboBox->addItem(Utils::Fs::toNativePath(pref->getSavePath()), pref->getSavePath()); loadSavePathHistory(); - connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int))); + connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int))); connect(ui->browse_button, SIGNAL(clicked()), SLOT(browseButton_clicked())); ui->default_save_path_cb->setVisible(false); // Default path is selected by default @@ -272,12 +272,7 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show) ui->adv_button->setText(QString::fromUtf8(C_UP)); ui->settings_group->setVisible(true); ui->infoGroup->setVisible(true); - if (m_hasMetadata && (m_torrentInfo.filesCount() > 1)) { - ui->content_tree->setVisible(true); - } - else { - ui->content_tree->setVisible(false); - } + ui->content_tree->setVisible(m_hasMetadata); static_cast(layout())->insertWidget(layout()->indexOf(ui->never_show_cb) + 1, ui->adv_button); } else { @@ -292,7 +287,7 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show) void AddNewTorrentDialog::saveSavePathHistory() const { - QDir selected_save_path(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString()); + QDir selected_save_path(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString()); Preferences* const pref = Preferences::instance(); // Get current history QStringList history = pref->getAddNewTorrentDialogPathHistory(); @@ -314,20 +309,12 @@ void AddNewTorrentDialog::saveSavePathHistory() const int AddNewTorrentDialog::indexOfSavePath(const QString &save_path) { QDir saveDir(save_path); - for(int i = 0; i < ui->save_path_combo->count(); ++i) - if (QDir(ui->save_path_combo->itemData(i).toString()) == saveDir) + for (int i = 0; i < ui->savePathComboBox->count(); ++i) + if (QDir(ui->savePathComboBox->itemData(i).toString()) == saveDir) return i; return -1; } -void AddNewTorrentDialog::updateFileNameInSavePaths(const QString &new_filename) -{ - for(int i = 0; i < ui->save_path_combo->count(); ++i) { - const QDir folder(ui->save_path_combo->itemData(i).toString()); - ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(folder.absoluteFilePath(new_filename))); - } -} - void AddNewTorrentDialog::updateDiskSpaceLabel() { // Determine torrent size @@ -349,8 +336,8 @@ void AddNewTorrentDialog::updateDiskSpaceLabel() QString size_string = torrent_size ? Utils::Misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable.")); size_string += " ("; size_string += tr("Free space on disk: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath( - ui->save_path_combo->itemData( - ui->save_path_combo->currentIndex()).toString()))); + ui->savePathComboBox->itemData( + ui->savePathComboBox->currentIndex()).toString()))); size_string += ")"; ui->size_lbl->setText(size_string); } @@ -359,7 +346,7 @@ void AddNewTorrentDialog::onSavePathChanged(int index) { // Toggle default save path setting checkbox visibility ui->default_save_path_cb->setChecked(false); - ui->default_save_path_cb->setVisible(QDir(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString()) != QDir(Preferences::instance()->getSavePath())); + ui->default_save_path_cb->setVisible(QDir(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString()) != QDir(Preferences::instance()->getSavePath())); // Remember index m_oldIndex = index; @@ -369,51 +356,36 @@ void AddNewTorrentDialog::onSavePathChanged(int index) void AddNewTorrentDialog::browseButton_clicked() { - disconnect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavePathChanged(int))); + disconnect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavePathChanged(int))); + // User is asking for a new save path - QString cur_save_path = ui->save_path_combo->itemText(m_oldIndex); - QString new_path, old_filename, new_filename; - - if (m_torrentInfo.isValid() && (m_torrentInfo.filesCount() == 1)) { - old_filename = Utils::Fs::fileName(cur_save_path); - new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), cur_save_path, QString(), 0, QFileDialog::DontConfirmOverwrite); - if (!new_path.isEmpty()) - new_path = Utils::Fs::branchPath(new_path, &new_filename); - qDebug() << "new_path: " << new_path; - qDebug() << "new_filename: " << new_filename; - } - else { - if (!cur_save_path.isEmpty() && QDir(cur_save_path).exists()) - new_path = QFileDialog::getExistingDirectory(this, tr("Choose save path"), cur_save_path); - else - new_path = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath()); - } - if (!new_path.isEmpty()) { - const int existing_index = indexOfSavePath(new_path); - if (existing_index >= 0) { - ui->save_path_combo->setCurrentIndex(existing_index); + QString curSavePath = ui->savePathComboBox->itemText(m_oldIndex); + QString newPath; + + if (!curSavePath.isEmpty() && QDir(curSavePath).exists()) + newPath = QFileDialog::getExistingDirectory(this, tr("Choose save path"), curSavePath); + else + newPath = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath()); + + if (!newPath.isEmpty()) { + const int existingIndex = indexOfSavePath(newPath); + if (existingIndex >= 0) { + ui->savePathComboBox->setCurrentIndex(existingIndex); } else { // New path, prepend to combo box - if (!new_filename.isEmpty()) - ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(QDir(new_path).absoluteFilePath(new_filename)), new_path); - else - ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(new_path), new_path); - ui->save_path_combo->setCurrentIndex(0); - } - // Update file name in all save_paths - if (!new_filename.isEmpty() && !Utils::Fs::sameFileNames(old_filename, new_filename)) { - m_torrentInfo.renameFile(0, new_filename); - updateFileNameInSavePaths(new_filename); + ui->savePathComboBox->insertItem(0, Utils::Fs::toNativePath(newPath), newPath); + ui->savePathComboBox->setCurrentIndex(0); } onSavePathChanged(0); } else { // Restore index - ui->save_path_combo->setCurrentIndex(m_oldIndex); + ui->savePathComboBox->setCurrentIndex(m_oldIndex); } - connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int))); + + connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int))); } void AddNewTorrentDialog::renameSelectedFile() @@ -537,7 +509,7 @@ void AddNewTorrentDialog::loadSavePathHistory() QStringList raw_path_history = Preferences::instance()->getAddNewTorrentDialogPathHistory(); foreach (const QString &sp, raw_path_history) if (QDir(sp) != default_save_path) - ui->save_path_combo->addItem(Utils::Fs::toNativePath(sp), sp); + ui->savePathComboBox->addItem(Utils::Fs::toNativePath(sp), sp); } void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&) @@ -545,7 +517,7 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&) QMenu myFilesLlistMenu; const QModelIndexList selectedRows = ui->content_tree->selectionModel()->selectedRows(0); QAction *actRename = 0; - if ((selectedRows.size() == 1) && (m_torrentInfo.filesCount() > 1)) { + if (selectedRows.size() == 1) { actRename = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename...")); myFilesLlistMenu.addSeparator(); } @@ -607,7 +579,7 @@ void AddNewTorrentDialog::accept() saveSavePathHistory(); pref->useAdditionDialog(!ui->never_show_cb->isChecked()); - QString savePath = ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString(); + QString savePath = ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString(); if (ui->default_save_path_cb->isChecked()) { pref->setSavePath(savePath); pref->apply(); @@ -682,30 +654,22 @@ void AddNewTorrentDialog::setupTreeview() ui->date_lbl->setText(!m_torrentInfo.creationDate().isNull() ? m_torrentInfo.creationDate().toString(Qt::DefaultLocaleShortDate) : tr("Not available")); // Prepare content tree - if (m_torrentInfo.filesCount() > 1) { - m_contentModel = new TorrentContentFilterModel(this); - connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel())); - ui->content_tree->setModel(m_contentModel); - ui->content_tree->hideColumn(PROGRESS); - m_contentDelegate = new PropListDelegate(); - ui->content_tree->setItemDelegate(m_contentDelegate); - connect(ui->content_tree, SIGNAL(clicked(const QModelIndex &)), ui->content_tree, SLOT(edit(const QModelIndex &))); - connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &))); - - // List files in torrent - m_contentModel->model()->setupModelData(m_torrentInfo); - if (!m_headerState.isEmpty()) - ui->content_tree->header()->restoreState(m_headerState); - - // Expand root folder - ui->content_tree->setExpanded(m_contentModel->index(0, 0), true); - } - else { - // Update save paths (append file name to them) - QString single_file_relpath = m_torrentInfo.filePath(0); - for (int i = 0; i < ui->save_path_combo->count(); ++i) - ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(QDir(ui->save_path_combo->itemText(i)).absoluteFilePath(single_file_relpath))); - } + m_contentModel = new TorrentContentFilterModel(this); + connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel())); + ui->content_tree->setModel(m_contentModel); + ui->content_tree->hideColumn(PROGRESS); + m_contentDelegate = new PropListDelegate(); + ui->content_tree->setItemDelegate(m_contentDelegate); + connect(ui->content_tree, SIGNAL(clicked(const QModelIndex &)), ui->content_tree, SLOT(edit(const QModelIndex &))); + connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &))); + + // List files in torrent + m_contentModel->model()->setupModelData(m_torrentInfo); + if (!m_headerState.isEmpty()) + ui->content_tree->header()->restoreState(m_headerState); + + // Expand root folder + ui->content_tree->setExpanded(m_contentModel->index(0, 0), true); } updateDiskSpaceLabel(); diff --git a/src/gui/addnewtorrentdialog.h b/src/gui/addnewtorrentdialog.h index 5de761b87087..adf4fbde9aec 100644 --- a/src/gui/addnewtorrentdialog.h +++ b/src/gui/addnewtorrentdialog.h @@ -87,7 +87,6 @@ protected slots: void loadSavePathHistory(); void saveSavePathHistory() const; int indexOfSavePath(const QString& save_path); - void updateFileNameInSavePaths(const QString& new_filename); void loadState(); void saveState(); void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString()); diff --git a/src/gui/addnewtorrentdialog.ui b/src/gui/addnewtorrentdialog.ui index f4126124db5d..d968e99cce00 100644 --- a/src/gui/addnewtorrentdialog.ui +++ b/src/gui/addnewtorrentdialog.ui @@ -20,7 +20,7 @@ - + 0 From d05d5a85a506b9f7a76d5d74df953e931aa827af Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Tue, 9 Feb 2016 11:55:02 +0300 Subject: [PATCH 2/3] Move Preferences backend code into SettingsStorage class Closes #4764 --- src/app/application.cpp | 5 +- src/app/upgrade.h | 4 +- src/base/base.pri | 2 + src/base/preferences.cpp | 199 ++++++--------------------------- src/base/preferences.h | 25 ++--- src/base/settingsstorage.cpp | 209 +++++++++++++++++++++++++++++++++++ src/base/settingsstorage.h | 65 +++++++++++ 7 files changed, 320 insertions(+), 189 deletions(-) create mode 100644 src/base/settingsstorage.cpp create mode 100644 src/base/settingsstorage.h diff --git a/src/app/application.cpp b/src/app/application.cpp index 1a20c1f454de..22b6b8e77952 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -59,6 +59,7 @@ #include "application.h" #include "base/logger.h" +#include "base/settingsstorage.h" #include "base/preferences.h" #include "base/utils/fs.h" #include "base/utils/misc.h" @@ -80,6 +81,7 @@ Application::Application(const QString &id, int &argc, char **argv) #endif { Logger::initInstance(); + SettingsStorage::initInstance(); Preferences::initInstance(); #if defined(Q_OS_MACX) && !defined(DISABLE_GUI) @@ -146,7 +148,7 @@ void Application::torrentFinished(BitTorrent::TorrentHandle *const torrent) QString program = pref->getAutoRunProgram(); program.replace("%N", torrent->name()); - program.replace("%L", torrent->label()); + program.replace("%L", torrent->category()); program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath())); program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath())); program.replace("%D", Utils::Fs::toNativePath(torrent->savePath())); @@ -466,6 +468,7 @@ void Application::cleanup() #endif Net::DownloadManager::freeInstance(); Preferences::freeInstance(); + SettingsStorage::freeInstance(); Logger::freeInstance(); IconProvider::freeInstance(); #ifndef DISABLE_GUI diff --git a/src/app/upgrade.h b/src/app/upgrade.h index 7dc907b70d09..42928c2c5b90 100644 --- a/src/app/upgrade.h +++ b/src/app/upgrade.h @@ -129,8 +129,8 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent = bool upgrade(bool ask = true) { - // Move RSS cookies to common storage - Preferences::instance()->moveRSSCookies(); + // Upgrade preferences + Preferences::instance()->upgrade(); QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup"); QDir backupFolderDir(backupFolderPath); diff --git a/src/base/base.pri b/src/base/base.pri index e5edf06e4b7e..d52c234454e1 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -4,6 +4,7 @@ HEADERS += \ $$PWD/filesystemwatcher.h \ $$PWD/qinisettings.h \ $$PWD/logger.h \ + $$PWD/settingsstorage.h \ $$PWD/preferences.h \ $$PWD/iconprovider.h \ $$PWD/http/irequesthandler.h \ @@ -58,6 +59,7 @@ SOURCES += \ $$PWD/tristatebool.cpp \ $$PWD/filesystemwatcher.cpp \ $$PWD/logger.cpp \ + $$PWD/settingsstorage.cpp \ $$PWD/preferences.cpp \ $$PWD/iconprovider.cpp \ $$PWD/http/connection.cpp \ diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index e38b78375b8e..6ce3281f86dc 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -30,15 +30,9 @@ * Contact : hammered999@gmail.com */ -#include "preferences.h" -#include "qinisettings.h" -#include "logger.h" - #include #include #include -#include -#include #ifndef DISABLE_GUI #include @@ -56,74 +50,19 @@ #endif #include -#include "base/utils/fs.h" -#include "base/utils/misc.h" +#include "utils/fs.h" +#include "utils/misc.h" +#include "settingsstorage.h" +#include "logger.h" +#include "preferences.h" Preferences* Preferences::m_instance = 0; Preferences::Preferences() : m_randomPort(rand() % 64512 + 1024) - , dirty(false) - , lock(QReadWriteLock::Recursive) { qRegisterMetaTypeStreamOperators("MaxRatioAction"); - - QIniSettings *settings = new QIniSettings; -#ifndef Q_OS_MAC - QIniSettings *settings_new = new QIniSettings("qBittorrent", "qBittorrent_new"); - QStringList keys = settings_new->allKeys(); - bool use_new = false; - - // This means that the PC closed either due to power outage - // or because the disk was full. In any case the settings weren't transfered - // in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf - // contains the most recent settings. - if (!keys.isEmpty()) { - Logger::instance()->addMessage(tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING); - use_new = true; - dirty = true; - } - else { - keys = settings->allKeys(); - } -#else - QStringList keys = settings->allKeys(); -#endif - - // Copy everything into memory. This means even keys inserted in the file manually - // or that we don't touch directly in this code(eg disabled by ifdef). This ensures - // that they will be copied over when save our settings to disk. - for (QStringList::const_iterator i = keys.begin(), e = keys.end(); i != e; ++i) { -#ifndef Q_OS_MAC - if (!use_new) - m_data[*i] = settings->value(*i); - else - m_data[*i] = settings_new->value(*i); -#else - m_data[*i] = settings->value(*i); -#endif - } - - //Ensures sync to disk before we attempt to manipulate the files from save(). - delete settings; -#ifndef Q_OS_MAC - QString new_path = settings_new->fileName(); - delete settings_new; - Utils::Fs::forceRemove(new_path); - - if (use_new) - save(); -#endif - - timer.setSingleShot(true); - timer.setInterval(5 * 1000); - connect(&timer, SIGNAL(timeout()), SLOT(save())); -} - -Preferences::~Preferences() -{ - save(); } Preferences *Preferences::instance() @@ -145,71 +84,14 @@ void Preferences::freeInstance() } } -bool Preferences::save() -{ - QWriteLocker locker(&lock); - - if (!dirty) return false; - -#ifndef Q_OS_MAC - // QSettings delete the file before writing it out. This can result in problems - // if the disk is full or a power outage occurs. Those events might occur - // between deleting the file and recreating it. This is a safety measure. - // Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds - // replace qBittorrent_new.ini/qBittorrent.conf with it. - QIniSettings *settings = new QIniSettings("qBittorrent", "qBittorrent_new"); -#else - QIniSettings *settings = new QIniSettings; -#endif - - for (QHash::const_iterator i = m_data.begin(), e = m_data.end(); i != e; ++i) - settings->setValue(i.key(), i.value()); - - dirty = false; - locker.unlock(); - -#ifndef Q_OS_MAC - settings->sync(); // Important to get error status - QString new_path = settings->fileName(); - QSettings::Status status = settings->status(); - - if (status != QSettings::NoError) { - if (status == QSettings::AccessError) - Logger::instance()->addMessage(tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL); - else - Logger::instance()->addMessage(tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL); - - delete settings; - Utils::Fs::forceRemove(new_path); - return false; - } - delete settings; - QString final_path = new_path; - int index = final_path.lastIndexOf("_new", -1, Qt::CaseInsensitive); - final_path.remove(index, 4); - Utils::Fs::forceRemove(final_path); - QFile::rename(new_path, final_path); -#else - delete settings; -#endif - - return true; -} - const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const { - QReadLocker locker(&lock); - return m_data.value(key, defaultValue); + return SettingsStorage::instance()->loadValue(key, defaultValue); } void Preferences::setValue(const QString &key, const QVariant &value) { - QWriteLocker locker(&lock); - if (m_data.value(key) == value) - return; - dirty = true; - timer.start(); - m_data.insert(key, value); + SettingsStorage::instance()->storeValue(key, value); } // General options @@ -643,7 +525,6 @@ void Preferences::setActionOnDblClOnTorrentFn(int act) // Connection options int Preferences::getSessionPort() const { - QReadLocker locker(&lock); if (useRandomPort()) return m_randomPort; return value("Preferences/Connection/PortRangeMin", 8999).toInt(); @@ -2487,22 +2368,6 @@ void Preferences::setTransHeaderState(const QByteArray &state) #endif } -// Temp code. -// See TorrentStatistics::loadStats() for details. -QVariantHash Preferences::getStats() const -{ - return value("Stats/AllStats").toHash(); -} - -void Preferences::removeStats() -{ - QWriteLocker locker(&lock); - dirty = true; - if (!timer.isActive()) - timer.start(); - m_data.remove("Stats/AllStats"); -} - //From old RssSettings class bool Preferences::isRSSEnabled() const { @@ -2574,31 +2439,6 @@ void Preferences::setToolbarTextPosition(const int position) setValue("Toolbar/textPosition", position); } -void Preferences::moveRSSCookies() -{ - QList cookies = getNetworkCookies(); - QVariantMap hostsTable = value("Rss/hosts_cookies").toMap(); - foreach (const QString &key, hostsTable.keys()) { - QVariant value = hostsTable[key]; - QList rawCookies = value.toByteArray().split(':'); - foreach (const QByteArray &rawCookie, rawCookies) { - foreach (QNetworkCookie cookie, QNetworkCookie::parseCookies(rawCookie)) { - cookie.setDomain(key); - cookie.setPath("/"); - cookie.setExpirationDate(QDateTime::currentDateTime().addYears(10)); - cookies << cookie; - } - } - } - - setNetworkCookies(cookies); - - QWriteLocker locker(&lock); - dirty = true; - timer.start(); - m_data.remove("Rss/hosts_cookies"); -} - QList Preferences::getNetworkCookies() const { QList cookies; @@ -2639,8 +2479,31 @@ void Preferences::setSpeedWidgetGraphEnable(int id, const bool enable) setValue("SpeedWidget/graph_enable_" + QString::number(id), enable); } +void Preferences::upgrade() +{ + // Move RSS cookies to global storage + QList cookies = getNetworkCookies(); + QVariantMap hostsTable = value("Rss/hosts_cookies").toMap(); + foreach (const QString &key, hostsTable.keys()) { + QVariant value = hostsTable[key]; + QList rawCookies = value.toByteArray().split(':'); + foreach (const QByteArray &rawCookie, rawCookies) { + foreach (QNetworkCookie cookie, QNetworkCookie::parseCookies(rawCookie)) { + cookie.setDomain(key); + cookie.setPath("/"); + cookie.setExpirationDate(QDateTime::currentDateTime().addYears(10)); + cookies << cookie; + } + } + } + + setNetworkCookies(cookies); + + SettingsStorage::instance()->removeValue("Rss/hosts_cookies"); +} + void Preferences::apply() { - if (save()) + if (SettingsStorage::instance()->save()) emit changed(); } diff --git a/src/base/preferences.h b/src/base/preferences.h index ca29ab46bac0..bcf25cb9b732 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -41,7 +41,7 @@ #include #include -#include "base/types.h" +#include "types.h" enum scheduler_days { @@ -89,26 +89,20 @@ namespace DNS }; } +class SettingsStorage; + class Preferences: public QObject { Q_OBJECT Q_DISABLE_COPY(Preferences) -private: Preferences(); - ~Preferences(); - static Preferences* m_instance; - QHash m_data; - int m_randomPort; - bool dirty; - QTimer timer; - mutable QReadWriteLock lock; const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; void setValue(const QString &key, const QVariant &value); -private slots: - bool save(); + static Preferences* m_instance; + int m_randomPort; signals: void changed(); @@ -526,11 +520,6 @@ private slots: int getToolbarTextPosition() const; void setToolbarTextPosition(const int position); - // Temp code. - // See TorrentStatistics::loadStats() for details. - QVariantHash getStats() const; - void removeStats(); - //From old RssSettings class bool isRSSEnabled() const; void setRSSEnabled(const bool enabled); @@ -548,8 +537,6 @@ private slots: // Network QList getNetworkCookies() const; void setNetworkCookies(const QList &cookies); - // Temporary method for upgrade purposes - void moveRSSCookies(); // SpeedWidget int getSpeedWidgetPeriod() const; @@ -557,6 +544,8 @@ private slots: bool getSpeedWidgetGraphEnable(int id) const; void setSpeedWidgetGraphEnable(int id, const bool enable); + void upgrade(); + public slots: void setStatusFilterState(bool checked); void setLabelFilterState(bool checked); diff --git a/src/base/settingsstorage.cpp b/src/base/settingsstorage.cpp new file mode 100644 index 000000000000..cbc93e1f043b --- /dev/null +++ b/src/base/settingsstorage.cpp @@ -0,0 +1,209 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 Vladimir Golovnev + * Copyright (C) 2014 sledgehammer999 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include +#include +#include + +#include "logger.h" +#include "utils/fs.h" +#include "settingsstorage.h" + +namespace +{ + inline QSettings *createSettings(const QString &name) + { + #ifdef Q_OS_WIN + return new QSettings(QSettings::IniFormat, QSettings::UserScope, "qBittorrent", name); + #else + return new QSettings("qBittorrent", name); + #endif + } + + QString mapKey(const QString &key) + { + static const QHash keyMapping = {}; + return keyMapping.value(key, key); + } +} + +SettingsStorage *SettingsStorage::m_instance = nullptr; + +SettingsStorage::SettingsStorage() + : m_dirty(false) + , m_lock(QReadWriteLock::Recursive) +{ + QSettings *settings; + QStringList keys; + +#ifdef Q_OS_MAC + settings = createSettings("qBittorrent"); +#else + settings = createSettings("qBittorrent_new"); + QString newPath = settings->fileName(); + + // This means that the PC closed either due to power outage + // or because the disk was full. In any case the settings weren't transfered + // in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf + // contains the most recent settings. + if (!settings->allKeys().isEmpty()) { + Logger::instance()->addMessage(tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING); + m_dirty = true; + } + else { + delete settings; + settings = createSettings("qBittorrent"); + } +#endif + + keys = settings->allKeys(); + + // Copy everything into memory. This means even keys inserted in the file manually + // or that we don't touch directly in this code(eg disabled by ifdef). This ensures + // that they will be copied over when save our settings to disk. + foreach (const QString &key, keys) + m_data[key] = settings->value(key); + + //Ensures sync to disk before we attempt to manipulate the files from save(). + delete settings; + +#ifndef Q_OS_MAC + Utils::Fs::forceRemove(newPath); + + if (m_dirty) + save(); +#endif + + m_timer.setSingleShot(true); + m_timer.setInterval(5 * 1000); + connect(&m_timer, SIGNAL(timeout()), SLOT(save())); +} + +SettingsStorage::~SettingsStorage() +{ + save(); +} + +void SettingsStorage::initInstance() +{ + if (!m_instance) + m_instance = new SettingsStorage; +} + +void SettingsStorage::freeInstance() +{ + delete m_instance; + m_instance = nullptr; +} + +SettingsStorage *SettingsStorage::instance() +{ + return m_instance; +} + +bool SettingsStorage::save() +{ + QWriteLocker locker(&m_lock); + + if (!m_dirty) return false; + +#ifndef Q_OS_MAC + // QSettings delete the file before writing it out. This can result in problems + // if the disk is full or a power outage occurs. Those events might occur + // between deleting the file and recreating it. This is a safety measure. + // Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds + // replace qBittorrent_new.ini/qBittorrent.conf with it. + QSettings *settings = createSettings("qBittorrent_new"); +#else + QSettings *settings = createSettings("qBittorrent"); +#endif + + foreach (const QString &key, m_data.keys()) + settings->setValue(key, m_data[key]); + + m_dirty = false; + locker.unlock(); + +#ifndef Q_OS_MAC + settings->sync(); // Important to get error status + QString newPath = settings->fileName(); + QSettings::Status status = settings->status(); + + if (status != QSettings::NoError) { + if (status == QSettings::AccessError) + Logger::instance()->addMessage(tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL); + else + Logger::instance()->addMessage(tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL); + + delete settings; + Utils::Fs::forceRemove(newPath); + return false; + } + + delete settings; + QString finalPath = newPath; + int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive); + finalPath.remove(index, 4); + Utils::Fs::forceRemove(finalPath); + QFile::rename(newPath, finalPath); +#else + delete settings; +#endif + + return true; +} + +QVariant SettingsStorage::loadValue(const QString &key, const QVariant &defaultValue) const +{ + QReadLocker locker(&m_lock); + return m_data.value(mapKey(key), defaultValue); +} + +void SettingsStorage::storeValue(const QString &key, const QVariant &value) +{ + QString realKey = mapKey(key); + QWriteLocker locker(&m_lock); + if (m_data.value(realKey) != value) { + m_dirty = true; + m_timer.start(); + m_data.insert(realKey, value); + } +} + +void SettingsStorage::removeValue(const QString &key) +{ + QString realKey = mapKey(key); + QWriteLocker locker(&m_lock); + if (m_data.contains(realKey)) { + m_dirty = true; + m_timer.start(); + m_data.remove(realKey); + } +} diff --git a/src/base/settingsstorage.h b/src/base/settingsstorage.h new file mode 100644 index 000000000000..1f035fad4ace --- /dev/null +++ b/src/base/settingsstorage.h @@ -0,0 +1,65 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 Vladimir Golovnev + * Copyright (C) 2014 sledgehammer999 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef SETTINGSSTORAGE_H +#define SETTINGSSTORAGE_H + +#include +#include +#include +#include + +class SettingsStorage: public QObject +{ + Q_OBJECT + SettingsStorage(); + ~SettingsStorage(); + +public: + static void initInstance(); + static void freeInstance(); + static SettingsStorage* instance(); + + QVariant loadValue(const QString &key, const QVariant &defaultValue = QVariant()) const; + void storeValue(const QString &key, const QVariant &value); + void removeValue(const QString &key); + +public slots: + bool save(); + +private: + static SettingsStorage *m_instance; + + QVariantHash m_data; + bool m_dirty; + QTimer m_timer; + mutable QReadWriteLock m_lock; +}; + +#endif // SETTINGSSTORAGE_H From dd3466322455ad5779a01a80bbfea2e229cd5891 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Tue, 9 Feb 2016 11:56:48 +0300 Subject: [PATCH 3/3] Implement Advanced Saving Management subsystem Closes #4696 --- src/app/application.cpp | 2 +- src/base/bittorrent/private/statistics.cpp | 34 +- src/base/bittorrent/session.cpp | 560 +++++++++++++----- src/base/bittorrent/session.h | 79 ++- src/base/bittorrent/torrenthandle.cpp | 91 ++- src/base/bittorrent/torrenthandle.h | 16 +- src/base/bittorrent/torrentinfo.cpp | 4 +- src/base/bittorrent/torrentinfo.h | 2 +- src/base/http/types.h | 2 +- src/base/preferences.cpp | 208 +------ src/base/preferences.h | 39 +- src/base/rss/rssdownloadrule.cpp | 17 +- src/base/rss/rssdownloadrule.h | 6 +- src/base/rss/rssfeed.cpp | 2 +- src/base/settingsstorage.cpp | 37 +- src/base/torrentfilter.cpp | 29 +- src/base/torrentfilter.h | 14 +- src/base/types.h | 32 +- src/base/utils/fs.cpp | 17 +- src/base/utils/fs.h | 4 +- src/base/utils/misc.h | 1 + src/gui/addnewtorrentdialog.cpp | 232 +++++--- src/gui/addnewtorrentdialog.h | 21 +- src/gui/addnewtorrentdialog.ui | 136 ++++- src/gui/mainwindow.cpp | 6 +- src/gui/options.ui | 379 ++++++++---- src/gui/options_imp.cpp | 78 ++- src/gui/options_imp.h | 3 - src/gui/rss/automatedrssdownloader.cpp | 40 +- src/gui/rss/automatedrssdownloader.h | 2 +- src/gui/rss/automatedrssdownloader.ui | 6 +- src/gui/rss/rss_imp.cpp | 2 +- src/gui/scanfoldersdelegate.cpp | 4 +- src/gui/search/searchwidget.cpp | 2 +- src/gui/torrentmodel.cpp | 12 +- src/gui/torrentmodel.h | 2 +- src/gui/transferlistfilterswidget.cpp | 293 ++++----- src/gui/transferlistfilterswidget.h | 27 +- src/gui/transferlistsortmodel.cpp | 8 +- src/gui/transferlistsortmodel.h | 4 +- src/gui/transferlistwidget.cpp | 128 ++-- src/gui/transferlistwidget.h | 8 +- src/webui/abstractwebapplication.cpp | 2 +- src/webui/btjson.cpp | 23 +- src/webui/btjson.h | 2 +- src/webui/extra_translations.h | 2 +- src/webui/prefjson.cpp | 30 +- src/webui/webapplication.cpp | 42 +- src/webui/webapplication.h | 2 +- src/webui/webui.qrc | 82 +-- src/webui/www/private/index.html | 4 +- src/webui/www/public/download.html | 34 +- src/webui/www/public/filters.html | 4 +- .../{newlabel.html => newcategory.html} | 30 +- src/webui/www/public/preferences_content.html | 14 +- src/webui/www/public/scripts/client.js | 166 +++--- src/webui/www/public/scripts/dynamicTable.js | 14 +- src/webui/www/public/scripts/mocha-init.js | 20 +- src/webui/www/public/upload.html | 2 +- 59 files changed, 1789 insertions(+), 1273 deletions(-) rename src/webui/www/public/{newlabel.html => newcategory.html} (62%) diff --git a/src/app/application.cpp b/src/app/application.cpp index 22b6b8e77952..ae2be596b605 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -229,7 +229,7 @@ void Application::processParams(const QStringList ¶ms) foreach (QString param, params) { param = param.trimmed(); #ifndef DISABLE_GUI - if (Preferences::instance()->useAdditionDialog()) + if (AddNewTorrentDialog::isEnabled()) AddNewTorrentDialog::show(param, m_window); else #endif diff --git a/src/base/bittorrent/private/statistics.cpp b/src/base/bittorrent/private/statistics.cpp index a3b41ca13f75..aa554cff09e0 100644 --- a/src/base/bittorrent/private/statistics.cpp +++ b/src/base/bittorrent/private/statistics.cpp @@ -3,7 +3,6 @@ #include #include "base/qinisettings.h" -#include "base/preferences.h" #include "base/bittorrent/sessionstatus.h" #include "base/bittorrent/session.h" #include "statistics.h" @@ -76,40 +75,9 @@ void Statistics::save() const void Statistics::load() { - // Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file. - // This code reads the data from there, writes it to the new file, and removes the keys - // from the old file. This code should be removed after some time has passed. - // e.g. When we reach v3.3.0 - // Don't forget to remove: - // 1. Preferences::getStats() - // 2. Preferences::removeStats() - // 3. #include "base/preferences.h" - Preferences* const pref = Preferences::instance(); QIniSettings s("qBittorrent", "qBittorrent-data"); - QVariantHash v = pref->getStats(); - - // Let's test if the qbittorrent.ini holds the key - if (!v.isEmpty()) { - m_dirty = true; - - // If the user has used qbt > 3.1.5 and then reinstalled/used - // qbt < 3.1.6, there will be stats in qbittorrent-data.ini too - // so we need to merge those 2. - if (s.contains("Stats/AllStats")) { - QVariantHash tmp = s.value("Stats/AllStats").toHash(); - v["AlltimeDL"] = v["AlltimeDL"].toULongLong() + tmp["AlltimeDL"].toULongLong(); - v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong(); - } - } - else { - v = s.value("Stats/AllStats").toHash(); - } + QVariantHash v = s.value("Stats/AllStats").toHash(); m_alltimeDL = v["AlltimeDL"].toULongLong(); m_alltimeUL = v["AlltimeUL"].toULongLong(); - - if (m_dirty) { - save(); - pref->removeStats(); - } } diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index b182745df883..358f5cd00f07 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -28,10 +28,6 @@ * exception statement from your version. */ -#include "session.h" - -using namespace BitTorrent; - #include #include #include @@ -44,6 +40,7 @@ using namespace BitTorrent; #include #include #include +#include #include #include @@ -70,6 +67,7 @@ using namespace BitTorrent; #include "base/utils/string.h" #include "base/unicodestrings.h" #include "base/logger.h" +#include "base/settingsstorage.h" #include "base/preferences.h" #include "base/torrentfilter.h" #include "base/net/downloadmanager.h" @@ -94,20 +92,73 @@ static const char RESUME_FOLDER[] = "BT_backup"; namespace libt = libtorrent; using namespace BitTorrent; -static bool readFile(const QString &path, QByteArray &buf); -static bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, int &prio, MagnetUri &magnetUri); +#define SETTINGS_KEY(name) "BitTorrent/Session/" name +const QString KEY_CATEGORIES = SETTINGS_KEY("Categories"); +const QString KEY_MAXRATIOACTION = SETTINGS_KEY("MaxRatioAction"); +const QString KEY_DEFAULTSAVEPATH = SETTINGS_KEY("DefaultSavePath"); +const QString KEY_TEMPPATH = SETTINGS_KEY("TempPath"); +const QString KEY_SUBCATEGORIESENABLED = SETTINGS_KEY("SubcategoriesEnabled"); +const QString KEY_TEMPPATHENABLED = SETTINGS_KEY("TempPathEnabled"); +const QString KEY_DISABLEASMBYDEFAULT = SETTINGS_KEY("DisableASMByDefault"); +const QString KEY_DISABLEASMONCATEGORYCHANGED = SETTINGS_KEY("DisableASMTriggers/CategoryChanged"); +const QString KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED = SETTINGS_KEY("DisableASMTriggers/DefaultSavePathChanged"); +const QString KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED = SETTINGS_KEY("DisableASMTriggers/CategorySavePathChanged"); +const QString KEY_ADDTORRENTPAUSED = SETTINGS_KEY("AddTorrentPaused"); + +namespace +{ + bool readFile(const QString &path, QByteArray &buf); + bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri); + + void torrentQueuePositionUp(const libt::torrent_handle &handle); + void torrentQueuePositionDown(const libt::torrent_handle &handle); + void torrentQueuePositionTop(const libt::torrent_handle &handle); + void torrentQueuePositionBottom(const libt::torrent_handle &handle); + + QStringMap map_cast(const QVariantMap &map) + { + QStringMap result; + foreach (const QString &key, map.keys()) + result[key] = map.value(key).toString(); + return result; + } + + QVariantMap map_cast(const QStringMap &map) + { + QVariantMap result; + foreach (const QString &key, map.keys()) + result[key] = map.value(key); + return result; + } + + QString normalizePath(QString path, const QString &defaultPath = Utils::Fs::QDesktopServicesDownloadLocation()) + { + path = Utils::Fs::fromNativePath(path.trimmed()); + return !path.isEmpty() ? path : defaultPath; + } + + QStringMap expandCategories(const QStringMap &categories) + { + QStringMap expanded = categories; -static void torrentQueuePositionUp(const libt::torrent_handle &handle); -static void torrentQueuePositionDown(const libt::torrent_handle &handle); -static void torrentQueuePositionTop(const libt::torrent_handle &handle); -static void torrentQueuePositionBottom(const libt::torrent_handle &handle); + foreach (const QString &category, categories.keys()) { + foreach (const QString &subcat, Session::expandCategory(category)) { + if (!expanded.contains(subcat)) + expanded[subcat] = ""; + } + } + + return expanded; + } +} // Session -Session *Session::m_instance = 0; +Session *Session::m_instance = nullptr; Session::Session(QObject *parent) : QObject(parent) + , m_settings(SettingsStorage::instance()) , m_LSDEnabled(false) , m_DHTEnabled(false) , m_PeXEnabled(false) @@ -118,10 +169,8 @@ Session::Session(QObject *parent) , m_globalMaxRatio(-1) , m_numResumeData(0) , m_extraLimit(0) - , m_appendLabelToSavePath(false) , m_appendExtension(false) , m_refreshInterval(0) - , m_highRatioAction(MaxRatioAction::Pause) { Preferences* const pref = Preferences::instance(); Logger* const logger = Logger::instance(); @@ -174,6 +223,13 @@ Session::Session(QObject *parent) m_nativeSession->add_extension(&libt::create_ut_pex_plugin); m_nativeSession->add_extension(&libt::create_smart_ban_plugin); + m_categories = map_cast(m_settings->loadValue(KEY_CATEGORIES).toMap()); + if (isSubcategoriesEnabled()) { + // if subcategories support changed manually + m_categories = expandCategories(m_categories); + m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories)); + } + m_refreshTimer = new QTimer(this); m_refreshTimer->setInterval(2000); connect(m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); @@ -185,6 +241,10 @@ Session::Session(QObject *parent) m_statistics = new Statistics(this); + m_maxRatioAction = static_cast(m_settings->loadValue(KEY_MAXRATIOACTION, Pause).toInt()); + m_defaultSavePath = normalizePath(m_settings->loadValue(KEY_DEFAULTSAVEPATH).toString()); + m_tempPath = normalizePath(m_settings->loadValue(KEY_TEMPPATH).toString(), m_defaultSavePath + "/temp"); + // Apply user settings to BitTorrent session configure(); connect(pref, SIGNAL(changed()), SLOT(configure())); @@ -231,17 +291,19 @@ bool Session::isQueueingEnabled() const bool Session::isTempPathEnabled() const { - return !m_tempPath.isEmpty(); + return m_settings->loadValue(KEY_TEMPPATHENABLED, false).toBool(); } -bool Session::isAppendExtensionEnabled() const +void Session::setTempPathEnabled(bool enabled) { - return m_appendExtension; + m_settings->storeValue(KEY_TEMPPATHENABLED, enabled); + foreach (TorrentHandle *const torrent, m_torrents) + torrent->handleTempPathChanged(); } -bool Session::useAppendLabelToSavePath() const +bool Session::isAppendExtensionEnabled() const { - return m_appendLabelToSavePath; + return m_appendExtension; } QString Session::defaultSavePath() const @@ -254,6 +316,200 @@ QString Session::tempPath() const return m_tempPath; } +bool Session::isValidCategoryName(const QString &name) +{ + QRegExp re(R"#(^([^\\\/]|[^\\\/]([^\\\/]|\/(?=[^\/]))*[^\\\/])$)#"); + if (!name.isEmpty() && (re.indexIn(name) != 0)) { + qDebug() << "Incorrect category name:" << name; + return false; + } + + return true; +} + +QStringList Session::expandCategory(const QString &category) +{ + QStringList result; + if (!isValidCategoryName(category)) + return result; + + int index = 0; + while ((index = category.indexOf('/', index)) >= 0) { + result << category.left(index); + ++index; + } + result << category; + + return result; +} + +QStringList Session::categories() const +{ + return m_categories.keys(); +} + +QString Session::categorySavePath(const QString &categoryName) const +{ + QString basePath = m_defaultSavePath; + QString path = m_categories.value(categoryName); + if (categoryName.isEmpty()) return basePath; + + if (path.isEmpty()) // use implicit save path + path = Utils::Fs::toValidFileSystemName(categoryName, true); + + if (!QDir::isAbsolutePath(path)) + path = QString("%1/%2").arg(basePath).arg(path); + + return path; +} + +bool Session::addCategory(const QString &name, const QString &savePath) +{ + if (name.isEmpty()) return false; + if (!isValidCategoryName(name) || m_categories.contains(name)) + return false; + + if (isSubcategoriesEnabled()) { + foreach (const QString &parent, expandCategory(name)) { + if ((parent != name) && !m_categories.contains(parent)) { + m_categories[parent] = ""; + emit categoryAdded(parent); + } + } + } + + m_categories[name] = savePath; + m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories)); + emit categoryAdded(name); + + return true; +} + +bool Session::editCategory(const QString &name, const QString &savePath) +{ + if (!m_categories.contains(name)) return false; + if (categorySavePath(name) == savePath) return false; + + m_categories[name] = savePath; + if (isDisableASMWhenCategorySavePathChanged()) { + foreach (TorrentHandle *const torrent, torrents()) + if (torrent->category() == name) + torrent->setASMEnabled(false); + } + else { + foreach (TorrentHandle *const torrent, torrents()) + if (torrent->category() == name) + torrent->handleCategorySavePathChanged(); + } + + return true; +} + +bool Session::removeCategory(const QString &name) +{ + foreach (TorrentHandle *const torrent, torrents()) + if (torrent->belongsToCategory(name)) + torrent->setCategory(""); + + // remove stored category and its subcategories if exist + bool result = false; + if (isSubcategoriesEnabled()) { + // remove subcategories + QString test = name + "/"; + foreach (const QString &category, m_categories.keys()) { + if (category.startsWith(test)) { + m_categories.remove(category); + result = true; + emit categoryRemoved(category); + } + } + } + + result = (m_categories.remove(name) > 0) || result; + + if (result) { + // update stored categories + m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories)); + emit categoryRemoved(name); + } + + return result; +} + +bool Session::isSubcategoriesEnabled() const +{ + return m_settings->loadValue(KEY_SUBCATEGORIESENABLED, false).toBool(); +} + +void Session::setSubcategoriesEnabled(bool value) +{ + if (isSubcategoriesEnabled() == value) return; + + if (value) { + // expand categories to include all parent categories + m_categories = expandCategories(m_categories); + // update stored categories + m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories)); + } + else { + // reload categories + m_categories = map_cast(m_settings->loadValue(KEY_CATEGORIES).toMap()); + } + + m_settings->storeValue(KEY_SUBCATEGORIESENABLED, value); + emit subcategoriesSupportChanged(); +} + +bool Session::isASMDisabledByDefault() const +{ + return m_settings->loadValue(KEY_DISABLEASMBYDEFAULT, true).toBool(); +} + +void Session::setASMDisabledByDefault(bool value) +{ + m_settings->storeValue(KEY_DISABLEASMBYDEFAULT, value); +} + +bool Session::isDisableASMWhenCategoryChanged() const +{ + return m_settings->loadValue(KEY_DISABLEASMONCATEGORYCHANGED, false).toBool(); +} + +void Session::setDisableASMWhenCategoryChanged(bool value) +{ + m_settings->storeValue(KEY_DISABLEASMONCATEGORYCHANGED, value); +} + +bool Session::isDisableASMWhenDefaultSavePathChanged() const +{ + return m_settings->loadValue(KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED, true).toBool(); +} + +void Session::setDisableASMWhenDefaultSavePathChanged(bool value) +{ + m_settings->storeValue(KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED, value); +} + +bool Session::isDisableASMWhenCategorySavePathChanged() const +{ + return m_settings->loadValue(KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED, true).toBool(); +} + +void Session::setDisableASMWhenCategorySavePathChanged(bool value) +{ + m_settings->storeValue(KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED, value); +} + +bool Session::isAddTorrentPaused() const +{ + return m_settings->loadValue(KEY_ADDTORRENTPAUSED, false).toBool(); +} + +void Session::setAddTorrentPaused(bool value) +{ + m_settings->storeValue(KEY_ADDTORRENTPAUSED, value); +} + qreal Session::globalMaxRatio() const { return m_globalMaxRatio; @@ -442,22 +698,12 @@ void Session::configure() setListeningPort(); } - // * Save path - setDefaultSavePath(pref->getSavePath()); - - // * Temp path - if (pref->isTempPathEnabled()) - setDefaultTempPath(pref->getTempPath()); - else - setDefaultTempPath(); - uint newRefreshInterval = pref->getRefreshInterval(); if (newRefreshInterval != m_refreshInterval) { m_refreshInterval = newRefreshInterval; m_refreshTimer->setInterval(m_refreshInterval); } - setAppendLabelToSavePath(pref->appendTorrentLabel()); setAppendExtension(pref->useIncompleteFilesExtension()); preAllocateAllFiles(pref->preAllocateAllFiles()); @@ -587,7 +833,6 @@ void Session::configure() } // * Maximum ratio - m_highRatioAction = pref->getMaxRatioAction(); setGlobalMaxRatio(pref->getGlobalMaxRatio()); // Ip Filter @@ -690,7 +935,7 @@ void Session::processBigRatios() if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) { Logger* const logger = Logger::instance(); - if (m_highRatioAction == MaxRatioAction::Remove) { + if (m_maxRatioAction == Remove) { logger->addMessage(tr("'%1' reached the maximum ratio you set. Removing...").arg(torrent->name())); deleteTorrent(torrent->hash()); } @@ -974,18 +1219,19 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri const TorrentInfo &torrentInfo, const QByteArray &fastresumeData) { if (!addData.resumed) { - // manage save path - QString defaultSavePath = this->defaultSavePath(); - if (addData.savePath.isEmpty()) - addData.savePath = defaultSavePath; - if (!addData.savePath.endsWith("/")) - addData.savePath += "/"; - if (useAppendLabelToSavePath()) { - if ((addData.savePath == defaultSavePath) && !addData.label.isEmpty()) - addData.savePath += QString("%1/").arg(addData.label); + if (addData.savePath.isEmpty() && isASMDisabledByDefault()) + addData.savePath = m_defaultSavePath; + } + + if (!addData.category.isEmpty()) { + if (!m_categories.contains(addData.category) && !addCategory(addData.category)) { + qWarning() << "Couldn't create category" << addData.category; + addData.category = ""; } } + addData.savePath = Utils::Fs::fromNativePath(addData.savePath); + libt::add_torrent_params p; InfoHash hash; std::vector buf(fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size()); @@ -1082,7 +1328,9 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri // Set actual save path (e.g. temporary folder) if (isTempPathEnabled() && !addData.disableTempPath && !addData.hasSeedStatus) savePath = m_tempPath; - else + else if (addData.savePath.isEmpty()) // using Advanced mode + savePath = categorySavePath(addData.category); + else // using Simple mode savePath = addData.savePath; p.save_path = Utils::String::toStdString(Utils::Fs::toNativePath(savePath)); @@ -1341,51 +1589,32 @@ void Session::saveResumeData() } } -void Session::setDefaultSavePath(const QString &path) +void Session::setDefaultSavePath(QString path) { - if (path.isEmpty()) return; + path = normalizePath(path); + if (m_defaultSavePath == path) return; - m_defaultSavePath = Utils::Fs::fromNativePath(path); - if (!m_defaultSavePath.endsWith("/")) - m_defaultSavePath += "/"; -} + m_defaultSavePath = path; + m_settings->storeValue(KEY_DEFAULTSAVEPATH, m_defaultSavePath); -void Session::setDefaultTempPath(const QString &path) -{ - QString tempPath; - - if (!path.isEmpty()) { - tempPath = Utils::Fs::fromNativePath(path); - if (!tempPath.endsWith("/")) - tempPath += "/"; - } - - if (m_tempPath != tempPath) { - m_tempPath = tempPath; - foreach (TorrentHandle *const torrent, m_torrents) - torrent->handleTempPathChanged(); - } + if (isDisableASMWhenDefaultSavePathChanged()) + foreach (TorrentHandle *const torrent, torrents()) + torrent->setASMEnabled(false); + else + foreach (TorrentHandle *const torrent, torrents()) + torrent->handleCategorySavePathChanged(); } -void Session::setAppendLabelToSavePath(bool append) +void Session::setTempPath(QString path) { - if (m_appendLabelToSavePath != append) { - m_appendLabelToSavePath = append; - foreach (TorrentHandle *const torrent, m_torrents) { - QString label = torrent->label(); - if (label.isEmpty()) continue; + path = normalizePath(path, m_defaultSavePath + "/temp"); + if (m_tempPath == path) return; - QString testedOldSavePath = m_defaultSavePath; - QString newSavePath = m_defaultSavePath; - if (!m_appendLabelToSavePath) - testedOldSavePath += QString("%1/").arg(label); - else - newSavePath += QString("%1/").arg(label); + m_tempPath = path; + m_settings->storeValue(KEY_TEMPPATH, m_tempPath); - if (torrent->savePath() == testedOldSavePath) - torrent->move(newSavePath); - } - } + foreach (TorrentHandle *const torrent, m_torrents) + torrent->handleTempPathChanged(); } void Session::setAppendExtension(bool append) @@ -1536,6 +1765,19 @@ bool Session::isListening() const return m_nativeSession->is_listening(); } +MaxRatioAction Session::maxRatioAction() const +{ + return m_maxRatioAction; +} + +void Session::setMaxRatioAction(MaxRatioAction act) +{ + if (m_maxRatioAction != act) { + m_maxRatioAction = act; + m_settings->storeValue(KEY_MAXRATIOACTION, act); + } +} + // Torrents will a ratio superior to the given value will // be automatically deleted void Session::setGlobalMaxRatio(qreal ratio) @@ -1586,22 +1828,14 @@ void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent) emit torrentSavePathChanged(torrent); } -void Session::handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel) +void Session::handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory) { - if (m_appendLabelToSavePath) { - QString testedOldSavePath = m_defaultSavePath; - if (!oldLabel.isEmpty()) - testedOldSavePath += QString("%1/").arg(oldLabel); - QString newLabel = torrent->label(); - if (torrent->savePath() == testedOldSavePath) { - QString newSavePath = m_defaultSavePath; - if (!newLabel.isEmpty()) - newSavePath += QString("%1/").arg(newLabel); - torrent->move(newSavePath); - } - } + emit torrentCategoryChanged(torrent, oldCategory); +} - emit torrentLabelChanged(torrent, oldLabel); +void Session::handleTorrentSavingModeChanged(TorrentHandle * const torrent) +{ + emit torrentSavingModeChanged(torrent); } void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList &newTrackers) @@ -2138,7 +2372,7 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle) bool addPaused = data.addPaused; if (data.addPaused == TriStateBool::Undefined) - addPaused = pref->addTorrentsInPause(); + addPaused = isAddTorrentPaused(); // Start torrent because it was added in paused state if (!addPaused) @@ -2351,86 +2585,90 @@ void Session::handleStateUpdateAlert(libt::state_update_alert *p) emit torrentsUpdated(); } -bool readFile(const QString &path, QByteArray &buf) -{ - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - qDebug("Cannot read file %s: %s", qPrintable(path), qPrintable(file.errorString())); - return false; - } - - buf = file.readAll(); - return true; -} - -bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, int &prio, MagnetUri &magnetUri) +namespace { - out = AddTorrentData(); - out.resumed = true; - out.skipChecking = false; - - libt::lazy_entry fast; - libt::error_code ec; - libt::lazy_bdecode(data.constData(), data.constData() + data.size(), fast, ec); - if (ec || (fast.type() != libt::lazy_entry::dict_t)) return false; + bool readFile(const QString &path, QByteArray &buf) + { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qDebug("Cannot read file %s: %s", qPrintable(path), qPrintable(file.errorString())); + return false; + } - out.savePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(fast.dict_find_string_value("qBt-savePath"))); - if (out.savePath.isEmpty()) { - Logger::instance()->addMessage("Empty torrent save path was loaded from .fastresume file! Using default one...", Log::WARNING); - out.savePath = Preferences::instance()->getSavePath(); + buf = file.readAll(); + return true; } - out.ratioLimit = Utils::String::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble(); - out.label = Utils::String::fromStdString(fast.dict_find_string_value("qBt-label")); - out.name = Utils::String::fromStdString(fast.dict_find_string_value("qBt-name")); - out.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus"); - out.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled"); - - magnetUri = MagnetUri(Utils::String::fromStdString(fast.dict_find_string_value("qBt-magnetUri"))); - out.addPaused = fast.dict_find_int_value("qBt-paused"); - out.addForced = fast.dict_find_int_value("qBt-forced"); - - prio = fast.dict_find_int_value("qBt-queuePosition"); - - return true; -} + bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri) + { + torrentData = AddTorrentData(); + torrentData.resumed = true; + torrentData.skipChecking = false; + + libt::lazy_entry fast; + libt::error_code ec; + libt::lazy_bdecode(data.constData(), data.constData() + data.size(), fast, ec); + if (ec || (fast.type() != libt::lazy_entry::dict_t)) return false; + + torrentData.savePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(fast.dict_find_string_value("qBt-savePath"))); + torrentData.ratioLimit = Utils::String::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble(); + // ************************************************************************************** + // Workaround to convert legacy label to category + // TODO: Should be removed in future + torrentData.category = Utils::String::fromStdString(fast.dict_find_string_value("qBt-label")); + if (torrentData.category.isEmpty()) + // ************************************************************************************** + torrentData.category = Utils::String::fromStdString(fast.dict_find_string_value("qBt-category")); + torrentData.name = Utils::String::fromStdString(fast.dict_find_string_value("qBt-name")); + torrentData.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus"); + torrentData.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled"); + + magnetUri = MagnetUri(Utils::String::fromStdString(fast.dict_find_string_value("qBt-magnetUri"))); + torrentData.addPaused = fast.dict_find_int_value("qBt-paused"); + torrentData.addForced = fast.dict_find_int_value("qBt-forced"); + + prio = fast.dict_find_int_value("qBt-queuePosition"); -void torrentQueuePositionUp(const libt::torrent_handle &handle) -{ - try { - handle.queue_position_up(); - } - catch (std::exception &exc) { - qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + return true; } -} -void torrentQueuePositionDown(const libt::torrent_handle &handle) -{ - try { - handle.queue_position_down(); - } - catch (std::exception &exc) { - qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + void torrentQueuePositionUp(const libt::torrent_handle &handle) + { + try { + handle.queue_position_up(); + } + catch (std::exception &exc) { + qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + } } -} -void torrentQueuePositionTop(const libt::torrent_handle &handle) -{ - try { - handle.queue_position_top(); - } - catch (std::exception &exc) { - qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + void torrentQueuePositionDown(const libt::torrent_handle &handle) + { + try { + handle.queue_position_down(); + } + catch (std::exception &exc) { + qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + } } -} -void torrentQueuePositionBottom(const libt::torrent_handle &handle) -{ - try { - handle.queue_position_bottom(); + void torrentQueuePositionTop(const libt::torrent_handle &handle) + { + try { + handle.queue_position_top(); + } + catch (std::exception &exc) { + qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + } } - catch (std::exception &exc) { - qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + + void torrentQueuePositionBottom(const libt::torrent_handle &handle) + { + try { + handle.queue_position_bottom(); + } + catch (std::exception &exc) { + qDebug() << Q_FUNC_INFO << " fails: " << exc.what(); + } } } diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index cf76a142281d..072e478bd756 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -110,8 +111,19 @@ class FilterParserThread; class BandwidthScheduler; class Statistics; class ResumeDataSavingManager; +class SettingsStorage; -typedef QPair QStringPair; +enum MaxRatioAction +{ + Pause, + Remove +}; + +enum TorrentExportFolder +{ + Regular, + Finished +}; namespace BitTorrent { @@ -127,7 +139,7 @@ namespace BitTorrent struct AddTorrentParams { QString name; - QString label; + QString category; QString savePath; bool disableTempPath = false; // e.g. for imported torrents bool sequential = false; @@ -165,11 +177,49 @@ namespace BitTorrent bool isPexEnabled() const; bool isQueueingEnabled() const; qreal globalMaxRatio() const; - bool isTempPathEnabled() const; bool isAppendExtensionEnabled() const; - bool useAppendLabelToSavePath() const; + QString defaultSavePath() const; + void setDefaultSavePath(QString path); QString tempPath() const; + void setTempPath(QString path); + bool isTempPathEnabled() const; + void setTempPathEnabled(bool enabled); + + static bool isValidCategoryName(const QString &name); + // returns category itself and all top level categories + static QStringList expandCategory(const QString &category); + + QStringList categories() const; + QString categorySavePath(const QString &categoryName) const; + bool addCategory(const QString &name, const QString &savePath = ""); + bool editCategory(const QString &name, const QString &savePath); + bool removeCategory(const QString &name); + bool isSubcategoriesEnabled() const; + void setSubcategoriesEnabled(bool value); + + // Advanced Saving Management subsystem (ASM) + // + // Each torrent can be either in Simple mode or in Advanced mode + // In Simple mode torrent has explicit save path + // In Advanced Mode torrent has implicit save path (based on Default + // save path and Category save path) + // In Advanced Mode torrent save path can be changed in following cases: + // 1. Default save path changed + // 2. Torrent category save path changed + // 3. Torrent category changed + // (unless otherwise is specified) + bool isASMDisabledByDefault() const; + void setASMDisabledByDefault(bool value); + bool isDisableASMWhenCategoryChanged() const; + void setDisableASMWhenCategoryChanged(bool value); + bool isDisableASMWhenDefaultSavePathChanged() const; + void setDisableASMWhenDefaultSavePathChanged(bool value); + bool isDisableASMWhenCategorySavePathChanged() const; + void setDisableASMWhenCategorySavePathChanged(bool value); + + bool isAddTorrentPaused() const; + void setAddTorrentPaused(bool value); TorrentHandle *findTorrent(const InfoHash &hash) const; QHash torrents() const; @@ -184,6 +234,9 @@ namespace BitTorrent int uploadRateLimit() const; bool isListening() const; + MaxRatioAction maxRatioAction() const; + void setMaxRatioAction(MaxRatioAction act); + void changeSpeedLimitMode(bool alternative); void setDownloadRateLimit(int rate); void setUploadRateLimit(int rate); @@ -208,7 +261,8 @@ namespace BitTorrent // TorrentHandle interface void handleTorrentRatioLimitChanged(TorrentHandle *const torrent); void handleTorrentSavePathChanged(TorrentHandle *const torrent); - void handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel); + void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory); + void handleTorrentSavingModeChanged(TorrentHandle *const torrent); void handleTorrentMetadataReceived(TorrentHandle *const torrent); void handleTorrentPaused(TorrentHandle *const torrent); void handleTorrentResumed(TorrentHandle *const torrent); @@ -236,7 +290,8 @@ namespace BitTorrent void torrentFinished(BitTorrent::TorrentHandle *const torrent); void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent); void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent); - void torrentLabelChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel); + void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory); + void torrentSavingModeChanged(BitTorrent::TorrentHandle *const torrent); void allTorrentsFinished(); void metadataLoaded(const BitTorrent::TorrentInfo &info); void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent); @@ -254,6 +309,9 @@ namespace BitTorrent void trackerlessStateChanged(BitTorrent::TorrentHandle *const torrent, bool trackerless); void downloadFromUrlFailed(const QString &url, const QString &reason); void downloadFromUrlFinished(const QString &url); + void categoryAdded(const QString &categoryName); + void categoryRemoved(const QString &categoryName); + void subcategoriesSupportChanged(); private slots: void configure(); @@ -287,8 +345,6 @@ namespace BitTorrent void adjustLimits(libtorrent::session_settings &sessionSettings); const QStringList getListeningIPs(); void setListeningPort(); - void setDefaultSavePath(const QString &path); - void setDefaultTempPath(const QString &path = QString()); void preAllocateAllFiles(bool b); void setMaxConnectionsPerTorrent(int max); void setMaxUploadsPerTorrent(int max); @@ -296,7 +352,6 @@ namespace BitTorrent void enableDHT(bool enable); void changeSpeedLimitMode_impl(bool alternative); - void setAppendLabelToSavePath(bool append); void setAppendExtension(bool append); void startUpTorrents(); @@ -333,6 +388,8 @@ namespace BitTorrent void dispatchAlerts(std::auto_ptr alertPtr); void getPendingAlerts(QVector &out, ulong time = 0); + SettingsStorage *m_settings; + // BitTorrent libtorrent::session *m_nativeSession; @@ -346,10 +403,9 @@ namespace BitTorrent qreal m_globalMaxRatio; int m_numResumeData; int m_extraLimit; - bool m_appendLabelToSavePath; bool m_appendExtension; uint m_refreshInterval; - MaxRatioAction m_highRatioAction; + MaxRatioAction m_maxRatioAction; QList m_additionalTrackers; QString m_defaultSavePath; QString m_tempPath; @@ -376,6 +432,7 @@ namespace BitTorrent QHash m_addingTorrents; QHash m_downloadedTorrents; TorrentStatusReport m_torrentStatusReport; + QStringMap m_categories; QMutex m_alertsMutex; QWaitCondition m_alertsWaitCondition; diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index 899e9aebf989..2ed59064dfaf 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -80,7 +80,7 @@ AddTorrentData::AddTorrentData() AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms) : resumed(false) , name(params.name) - , label(params.label) + , category(params.category) , savePath(params.savePath) , disableTempPath(params.disableTempPath) , sequential(params.sequential) @@ -201,7 +201,8 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle , m_renameCount(0) , m_name(data.name) , m_savePath(Utils::Fs::toNativePath(data.savePath)) - , m_label(data.label) + , m_category(data.category) + , m_useASM(data.savePath.isEmpty()) , m_hasSeedStatus(data.hasSeedStatus) , m_ratioLimit(data.ratioLimit) , m_tempPathDisabled(data.disableTempPath) @@ -209,7 +210,8 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle , m_pauseAfterRecheck(false) , m_needSaveResumeData(false) { - Q_ASSERT(!m_savePath.isEmpty()); + if (m_useASM) + m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category)); updateStatus(); m_hash = InfoHash(m_nativeStatus.info_hash); @@ -330,6 +332,22 @@ QString TorrentHandle::contentPath(bool actual) const return rootPath(actual); } +bool TorrentHandle::isASMEnabled() const +{ + return m_useASM; +} + +void TorrentHandle::setASMEnabled(bool enabled) +{ + if (m_useASM == enabled) return; + + m_useASM = enabled; + m_session->handleTorrentSavingModeChanged(this); + + if (m_useASM) + move_impl(m_session->categorySavePath(m_category)); +} + QString TorrentHandle::nativeActualSavePath() const { return Utils::String::fromStdString(m_nativeStatus.save_path); @@ -507,9 +525,22 @@ qreal TorrentHandle::progress() const return progress; } -QString TorrentHandle::label() const +QString TorrentHandle::category() const { - return m_label; + return m_category; +} + +bool TorrentHandle::belongsToCategory(const QString &category) const +{ + if (m_category.isEmpty()) return category.isEmpty(); + if (!Session::isValidCategoryName(category)) return false; + + if (m_category == category) return true; + + if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + "/")) + return true; + + return false; } QDateTime TorrentHandle::addedTime() const @@ -1110,17 +1141,41 @@ void TorrentHandle::setName(const QString &name) } } -void TorrentHandle::setLabel(const QString &label) +bool TorrentHandle::setCategory(const QString &category) { - if (m_label != label) { - QString oldLabel = m_label; - m_label = label; + if (m_category != category) { + if (!category.isEmpty()) { + if (!Session::isValidCategoryName(category)) return false; + if (!m_session->categories().contains(category)) + if (!m_session->addCategory(category)) + return false; + } + + QString oldCategory = m_category; + m_category = category; m_needSaveResumeData = true; - m_session->handleTorrentLabelChanged(this, oldLabel); + m_session->handleTorrentCategoryChanged(this, oldCategory); + + if (m_useASM) { + if (!m_session->isDisableASMWhenCategoryChanged()) + move_impl(m_session->categorySavePath(m_category)); + else + setASMEnabled(false); + } } + + return true; } void TorrentHandle::move(QString path) +{ + m_useASM = false; + m_session->handleTorrentSavingModeChanged(this); + + move_impl(path); +} + +void TorrentHandle::move_impl(QString path) { path = Utils::Fs::toNativePath(path); if (path == savePath()) return; @@ -1469,9 +1524,9 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert resumeData["qBt-paused"] = isPaused(); resumeData["qBt-forced"] = isForced(); } - resumeData["qBt-savePath"] = Utils::String::toStdString(m_savePath); + resumeData["qBt-savePath"] = m_useASM ? "" : Utils::String::toStdString(m_savePath); resumeData["qBt-ratioLimit"] = Utils::String::toStdString(QString::number(m_ratioLimit)); - resumeData["qBt-label"] = Utils::String::toStdString(m_label); + resumeData["qBt-category"] = Utils::String::toStdString(m_category); resumeData["qBt-name"] = Utils::String::toStdString(m_name); resumeData["qBt-seedStatus"] = m_hasSeedStatus; resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled; @@ -1532,12 +1587,6 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p) updateStatus(); - if (filesCount() == 1) { - // Single-file torrent - // Renaming a file corresponds to changing the save path - m_session->handleTorrentSavePathChanged(this); - } - --m_renameCount; while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty()) m_moveFinishedTriggers.takeFirst()(); @@ -1598,6 +1647,12 @@ void TorrentHandle::handleTempPathChanged() adjustActualSavePath(); } +void TorrentHandle::handleCategorySavePathChanged() +{ + if (m_useASM) + move_impl(m_session->categorySavePath(m_category)); +} + void TorrentHandle::handleAppendExtensionToggled() { if (!hasMetadata()) return; diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index 0f1ee3fce399..fef582b002ba 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -90,7 +90,7 @@ namespace BitTorrent bool resumed; // for both new and resumed torrents QString name; - QString label; + QString category; QString savePath; bool disableTempPath; bool sequential; @@ -227,11 +227,16 @@ namespace BitTorrent QString rootPath(bool actual = false) const; QString contentPath(bool actual = false) const; + bool isASMEnabled() const; + void setASMEnabled(bool enabled); + QString category() const; + bool belongsToCategory(const QString &category) const; + bool setCategory(const QString &category); + int filesCount() const; int piecesCount() const; int piecesHave() const; qreal progress() const; - QString label() const; QDateTime addedTime() const; qreal ratioLimit() const; @@ -307,7 +312,6 @@ namespace BitTorrent qlonglong nextAnnounce() const; void setName(const QString &name); - void setLabel(const QString &label); void setSequentialDownload(bool b); void toggleSequentialDownload(); void setFirstLastPiecePriority(bool b); @@ -344,6 +348,7 @@ namespace BitTorrent void handleAlert(libtorrent::alert *a); void handleStateUpdate(const libtorrent::torrent_status &nativeStatus); void handleTempPathChanged(); + void handleCategorySavePathChanged(); void handleAppendExtensionToggled(); void saveResumeData(); @@ -379,6 +384,7 @@ namespace BitTorrent void adjustActualSavePath(); void adjustActualSavePath_impl(); + void move_impl(QString path); void moveStorage(const QString &newPath); void appendExtensionsToIncompleteFiles(); void removeExtensionsFromIncompleteFiles(); @@ -405,10 +411,12 @@ namespace BitTorrent QQueue m_moveFinishedTriggers; int m_renameCount; + bool m_useASM; + // Persistent data QString m_name; QString m_savePath; - QString m_label; + QString m_category; bool m_hasSeedStatus; qreal m_ratioLimit; bool m_tempPathDisabled; diff --git a/src/base/bittorrent/torrentinfo.cpp b/src/base/bittorrent/torrentinfo.cpp index ffe2ad90cf2c..e67a9d959e3c 100644 --- a/src/base/bittorrent/torrentinfo.cpp +++ b/src/base/bittorrent/torrentinfo.cpp @@ -44,8 +44,8 @@ namespace libt = libtorrent; using namespace BitTorrent; TorrentInfo::TorrentInfo(NativeConstPtr nativeInfo) - : m_nativeInfo(nativeInfo) { + m_nativeInfo = boost::const_pointer_cast(nativeInfo); } TorrentInfo::TorrentInfo(const TorrentInfo &other) @@ -219,5 +219,5 @@ void TorrentInfo::renameFile(uint index, const QString &newPath) TorrentInfo::NativePtr TorrentInfo::nativeInfo() const { - return *reinterpret_cast(&m_nativeInfo); + return m_nativeInfo; } diff --git a/src/base/bittorrent/torrentinfo.h b/src/base/bittorrent/torrentinfo.h index f3900a825783..218be643f07c 100644 --- a/src/base/bittorrent/torrentinfo.h +++ b/src/base/bittorrent/torrentinfo.h @@ -91,7 +91,7 @@ namespace BitTorrent NativePtr nativeInfo() const; private: - NativeConstPtr m_nativeInfo; + NativePtr m_nativeInfo; }; } diff --git a/src/base/http/types.h b/src/base/http/types.h index 28743e83f804..d748d93427bb 100644 --- a/src/base/http/types.h +++ b/src/base/http/types.h @@ -34,7 +34,7 @@ #include #include -typedef QMap QStringMap; +#include "base/types.h" namespace Http { diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 6ce3281f86dc..82d1b2e38221 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #ifndef DISABLE_GUI #include @@ -62,7 +63,6 @@ Preferences* Preferences::m_instance = 0; Preferences::Preferences() : m_randomPort(rand() % 64512 + 1024) { - qRegisterMetaTypeStreamOperators("MaxRatioAction"); } Preferences *Preferences::instance() @@ -277,40 +277,6 @@ void Preferences::setWinStartup(bool b) #endif // Downloads -QString Preferences::getSavePath() const -{ - QString save_path = value("Preferences/Downloads/SavePath").toString(); - if (!save_path.isEmpty()) - return Utils::Fs::fromNativePath(save_path); - return Utils::Fs::QDesktopServicesDownloadLocation(); -} - -void Preferences::setSavePath(const QString &save_path) -{ - setValue("Preferences/Downloads/SavePath", Utils::Fs::fromNativePath(save_path)); -} - -bool Preferences::isTempPathEnabled() const -{ - return value("Preferences/Downloads/TempPathEnabled", false).toBool(); -} - -void Preferences::setTempPathEnabled(bool enabled) -{ - setValue("Preferences/Downloads/TempPathEnabled", enabled); -} - -QString Preferences::getTempPath() const -{ - const QString temp = QDir(getSavePath()).absoluteFilePath("temp"); - return Utils::Fs::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString()); -} - -void Preferences::setTempPath(const QString &path) -{ - setValue("Preferences/Downloads/TempPath", Utils::Fs::fromNativePath(path)); -} - bool Preferences::useIncompleteFilesExtension() const { return value("Preferences/Downloads/UseIncompleteExtension", false).toBool(); @@ -321,16 +287,6 @@ void Preferences::useIncompleteFilesExtension(bool enabled) setValue("Preferences/Downloads/UseIncompleteExtension", enabled); } -bool Preferences::appendTorrentLabel() const -{ - return value("Preferences/Downloads/AppendLabel", false).toBool(); -} - -void Preferences::setAppendTorrentLabel(bool b) -{ - setValue("Preferences/Downloads/AppendLabel", b); -} - QString Preferences::lastLocationPath() const { return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString()); @@ -351,36 +307,6 @@ void Preferences::preAllocateAllFiles(bool enabled) return setValue("Preferences/Downloads/PreAllocation", enabled); } -bool Preferences::useAdditionDialog() const -{ - return value("Preferences/Downloads/NewAdditionDialog", true).toBool(); -} - -void Preferences::useAdditionDialog(bool b) -{ - setValue("Preferences/Downloads/NewAdditionDialog", b); -} - -bool Preferences::additionDialogFront() const -{ - return value("Preferences/Downloads/NewAdditionDialogFront", true).toBool(); -} - -void Preferences::additionDialogFront(bool b) -{ - setValue("Preferences/Downloads/NewAdditionDialogFront", b); -} - -bool Preferences::addTorrentsInPause() const -{ - return value("Preferences/Downloads/StartInPause", false).toBool(); -} - -void Preferences::addTorrentsInPause(bool b) -{ - setValue("Preferences/Downloads/StartInPause", b); -} - QVariantHash Preferences::getScanDirs() const { return value("Preferences/Downloads/ScanDirsV2").toHash(); @@ -878,16 +804,6 @@ void Preferences::setGlobalMaxRatio(qreal ratio) setValue("Preferences/Bittorrent/MaxRatio", ratio); } -MaxRatioAction Preferences::getMaxRatioAction() const -{ - return value("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(MaxRatioAction::Pause)).value(); -} - -void Preferences::setMaxRatioAction(MaxRatioAction act) -{ - setValue("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(act)); -} - // IP Filter bool Preferences::isFilteringEnabled() const { @@ -1501,51 +1417,6 @@ void Preferences::useSystemIconTheme(bool enabled) } #endif -QStringList Preferences::getTorrentLabels() const -{ - return value("TransferListFilters/customLabels").toStringList(); -} - -void Preferences::setTorrentLabels(const QStringList& labels) -{ - setValue("TransferListFilters/customLabels", labels); -} - -void Preferences::addTorrentLabelExternal(const QString &label) -{ - addTorrentLabel(label); - QString toEmit = label; - emit externalLabelAdded(toEmit); -} - -void Preferences::addTorrentLabel(const QString& label) -{ - QStringList labels = value("TransferListFilters/customLabels").toStringList(); - if (labels.contains(label)) - return; - labels << label; - setValue("TransferListFilters/customLabels", labels); -} - -void Preferences::removeTorrentLabel(const QString& label) -{ - QStringList labels = value("TransferListFilters/customLabels").toStringList(); - if (!labels.contains(label)) - return; - labels.removeOne(label); - setValue("TransferListFilters/customLabels", labels); -} - -QString Preferences::getDefaultLabel() const -{ - return value("Preferences/Downloads/DefaultLabel").toString(); -} - -void Preferences::setDefaultLabel(const QString &defaultLabel) -{ - setValue("Preferences/Downloads/DefaultLabel", defaultLabel); -} - bool Preferences::recursiveDownloadDisabled() const { return value("Preferences/Advanced/DisableRecursiveDownload", false).toBool(); @@ -1904,63 +1775,6 @@ void Preferences::setTrayIconStyle(TrayIcon::Style style) // Stuff that don't appear in the Options GUI but are saved // in the same file. -QByteArray Preferences::getAddNewTorrentDialogState() const -{ -#ifdef QBT_USES_QT5 - return value("AddNewTorrentDialog/qt5/treeHeaderState").toByteArray(); -#else - return value("AddNewTorrentDialog/treeHeaderState").toByteArray(); -#endif -} - -void Preferences::setAddNewTorrentDialogState(const QByteArray &state) -{ -#ifdef QBT_USES_QT5 - setValue("AddNewTorrentDialog/qt5/treeHeaderState", state); -#else - setValue("AddNewTorrentDialog/treeHeaderState", state); -#endif -} - -int Preferences::getAddNewTorrentDialogPos() const -{ - return value("AddNewTorrentDialog/y", -1).toInt(); -} - -void Preferences::setAddNewTorrentDialogPos(const int &pos) -{ - setValue("AddNewTorrentDialog/y", pos); -} - -int Preferences::getAddNewTorrentDialogWidth() const -{ - return value("AddNewTorrentDialog/width", -1).toInt(); -} - -void Preferences::setAddNewTorrentDialogWidth(const int &width) -{ - setValue("AddNewTorrentDialog/width", width); -} - -bool Preferences::getAddNewTorrentDialogExpanded() const -{ - return value("AddNewTorrentDialog/expanded", false).toBool(); -} - -void Preferences::setAddNewTorrentDialogExpanded(const bool expanded) -{ - setValue("AddNewTorrentDialog/expanded", expanded); -} - -QStringList Preferences::getAddNewTorrentDialogPathHistory() const -{ - return value("TorrentAdditionDlg/save_path_history").toStringList(); -} - -void Preferences::setAddNewTorrentDialogPathHistory(const QStringList &history) -{ - setValue("TorrentAdditionDlg/save_path_history", history); -} QDateTime Preferences::getDNSLastUpd() const { @@ -2320,14 +2134,14 @@ void Preferences::setStatusFilterState(const bool checked) setValue("TransferListFilters/statusFilterState", checked); } -bool Preferences::getLabelFilterState() const +bool Preferences::getCategoryFilterState() const { - return value("TransferListFilters/labelFilterState", true).toBool(); + return value("TransferListFilters/CategoryFilterState", true).toBool(); } -void Preferences::setLabelFilterState(const bool checked) +void Preferences::setCategoryFilterState(const bool checked) { - setValue("TransferListFilters/labelFilterState", checked); + setValue("TransferListFilters/CategoryFilterState", checked); } bool Preferences::getTrackerFilterState() const @@ -2499,7 +2313,19 @@ void Preferences::upgrade() setNetworkCookies(cookies); + QStringList labels = value("TransferListFilters/customLabels").toStringList(); + if (!labels.isEmpty()) { + QVariantMap categories = value("BitTorrent/Session/Categories").toMap(); + foreach (const QString &label, labels) { + if (!categories.contains(label)) + categories[label] = ""; + } + setValue("BitTorrent/Session/Categories", categories); + SettingsStorage::instance()->removeValue("TransferListFilters/customLabels"); + } + SettingsStorage::instance()->removeValue("Rss/hosts_cookies"); + SettingsStorage::instance()->removeValue("Preferences/Downloads/AppendLabel"); } void Preferences::apply() diff --git a/src/base/preferences.h b/src/base/preferences.h index bcf25cb9b732..102b09e2417f 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -106,7 +106,6 @@ class Preferences: public QObject signals: void changed(); - void externalLabelAdded(QString&); public: static void initInstance(); @@ -152,28 +151,12 @@ class Preferences: public QObject #endif // Downloads - QString getSavePath() const; - void setSavePath(const QString &save_path); - bool isTempPathEnabled() const; - void setTempPathEnabled(bool enabled); - QString getTempPath() const; - void setTempPath(const QString &path); - QString getDefaultLabel() const; - void setDefaultLabel(const QString &defaultLabel); bool useIncompleteFilesExtension() const; void useIncompleteFilesExtension(bool enabled); - bool appendTorrentLabel() const; - void setAppendTorrentLabel(bool b); QString lastLocationPath() const; void setLastLocationPath(const QString &path); bool preAllocateAllFiles() const; void preAllocateAllFiles(bool enabled); - bool useAdditionDialog() const; - void useAdditionDialog(bool b); - bool additionDialogFront() const; - void additionDialogFront(bool b); - bool addTorrentsInPause() const; - void addTorrentsInPause(bool b); QVariantHash getScanDirs() const; void setScanDirs(const QVariantHash &dirs); QString getScanDirsLastPath() const; @@ -275,8 +258,6 @@ class Preferences: public QObject void setTrackersList(const QString &val); qreal getGlobalMaxRatio() const; void setGlobalMaxRatio(qreal ratio); - MaxRatioAction getMaxRatioAction() const; - void setMaxRatioAction(MaxRatioAction act); // IP Filter bool isFilteringEnabled() const; @@ -400,11 +381,6 @@ class Preferences: public QObject bool useSystemIconTheme() const; void useSystemIconTheme(bool enabled); #endif - QStringList getTorrentLabels() const; - void setTorrentLabels(const QStringList& labels); - void addTorrentLabelExternal(const QString &label); - void addTorrentLabel(const QString& label); - void removeTorrentLabel(const QString& label); bool recursiveDownloadDisabled() const; void disableRecursiveDownload(bool disable = true); #ifdef Q_OS_WIN @@ -437,19 +413,8 @@ class Preferences: public QObject TrayIcon::Style trayIconStyle() const; void setTrayIconStyle(TrayIcon::Style style); - // Stuff that don't appear in the Options GUI but are saved // in the same file. - QByteArray getAddNewTorrentDialogState() const; - void setAddNewTorrentDialogState(const QByteArray &state); - int getAddNewTorrentDialogPos() const; - void setAddNewTorrentDialogPos(const int &pos); - int getAddNewTorrentDialogWidth() const; - void setAddNewTorrentDialogWidth(const int &width); - bool getAddNewTorrentDialogExpanded() const; - void setAddNewTorrentDialogExpanded(const bool expanded); - QStringList getAddNewTorrentDialogPathHistory() const; - void setAddNewTorrentDialogPathHistory(const QStringList &history); QDateTime getDNSLastUpd() const; void setDNSLastUpd(const QDateTime &date); QString getDNSLastIP() const; @@ -511,7 +476,7 @@ class Preferences: public QObject QByteArray getTorImportGeometry() const; void setTorImportGeometry(const QByteArray &geometry); bool getStatusFilterState() const; - bool getLabelFilterState() const; + bool getCategoryFilterState() const; bool getTrackerFilterState() const; int getTransSelFilter() const; void setTransSelFilter(const int &index); @@ -548,7 +513,7 @@ class Preferences: public QObject public slots: void setStatusFilterState(bool checked); - void setLabelFilterState(bool checked); + void setCategoryFilterState(bool checked); void setTrackerFilterState(bool checked); void apply(); diff --git a/src/base/rss/rssdownloadrule.cpp b/src/base/rss/rssdownloadrule.cpp index b3a505b46923..6d5bfc8ce098 100644 --- a/src/base/rss/rssdownloadrule.cpp +++ b/src/base/rss/rssdownloadrule.cpp @@ -178,7 +178,7 @@ DownloadRulePtr DownloadRule::fromVariantHash(const QVariantHash &ruleHash) rule->setRssFeeds(ruleHash.value("affected_feeds").toStringList()); rule->setEnabled(ruleHash.value("enabled", false).toBool()); rule->setSavePath(ruleHash.value("save_path").toString()); - rule->setLabel(ruleHash.value("label_assigned").toString()); + rule->setCategory(ruleHash.value("category_assigned").toString()); rule->setAddPaused(AddPausedState(ruleHash.value("add_paused").toUInt())); rule->setLastMatch(ruleHash.value("last_match").toDateTime()); rule->setIgnoreDays(ruleHash.value("ignore_days").toInt()); @@ -194,7 +194,7 @@ QVariantHash DownloadRule::toVariantHash() const hash["save_path"] = m_savePath; hash["affected_feeds"] = m_rssFeeds; hash["enabled"] = m_enabled; - hash["label_assigned"] = m_label; + hash["category_assigned"] = m_category; hash["use_regex"] = m_useRegex; hash["add_paused"] = m_apstate; hash["episode_filter"] = m_episodeFilter; @@ -210,10 +210,7 @@ bool DownloadRule::operator==(const DownloadRule &other) const void DownloadRule::setSavePath(const QString &savePath) { - if (!savePath.isEmpty() && (QDir(savePath) != QDir(Preferences::instance()->getSavePath()))) - m_savePath = Utils::Fs::fromNativePath(savePath); - else - m_savePath = QString(); + m_savePath = Utils::Fs::fromNativePath(savePath); } DownloadRule::AddPausedState DownloadRule::addPaused() const @@ -226,14 +223,14 @@ void DownloadRule::setAddPaused(const DownloadRule::AddPausedState &aps) m_apstate = aps; } -QString DownloadRule::label() const +QString DownloadRule::category() const { - return m_label; + return m_category; } -void DownloadRule::setLabel(const QString &label) +void DownloadRule::setCategory(const QString &category) { - m_label = label; + m_category = category; } bool DownloadRule::isEnabled() const diff --git a/src/base/rss/rssdownloadrule.h b/src/base/rss/rssdownloadrule.h index b22c14594a2b..c3dcfd29a6ec 100644 --- a/src/base/rss/rssdownloadrule.h +++ b/src/base/rss/rssdownloadrule.h @@ -69,8 +69,8 @@ namespace Rss void setSavePath(const QString &savePath); AddPausedState addPaused() const; void setAddPaused(const AddPausedState &aps); - QString label() const; - void setLabel(const QString &label); + QString category() const; + void setCategory(const QString &category); bool isEnabled() const; void setEnabled(bool enable); void setLastMatch(const QDateTime &d); @@ -93,7 +93,7 @@ namespace Rss QStringList m_mustNotContain; QString m_episodeFilter; QString m_savePath; - QString m_label; + QString m_category; bool m_enabled; QStringList m_rssFeeds; bool m_useRegex; diff --git a/src/base/rss/rssfeed.cpp b/src/base/rss/rssfeed.cpp index 06696404510c..d0c38e6f9233 100644 --- a/src/base/rss/rssfeed.cpp +++ b/src/base/rss/rssfeed.cpp @@ -391,7 +391,7 @@ void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article) BitTorrent::AddTorrentParams params; params.savePath = matchingRule->savePath(); - params.label = matchingRule->label(); + params.category = matchingRule->category(); if (matchingRule->addPaused() == DownloadRule::ALWAYS_PAUSED) params.addPaused = TriStateBool::True; else if (matchingRule->addPaused() == DownloadRule::NEVER_PAUSED) diff --git a/src/base/settingsstorage.cpp b/src/base/settingsstorage.cpp index cbc93e1f043b..dfe852c2a337 100644 --- a/src/base/settingsstorage.cpp +++ b/src/base/settingsstorage.cpp @@ -47,9 +47,44 @@ namespace #endif } +#ifdef QBT_USES_QT5 + typedef QHash MappingTable; +#else + class MappingTable: public QHash + { + public: + MappingTable(std::initializer_list> list) + { + reserve(static_cast(list.size())); + for (std::initializer_list>::const_iterator it = list.begin(); it != list.end(); ++it) + insert(it->first, it->second); + } + }; +#endif + QString mapKey(const QString &key) { - static const QHash keyMapping = {}; + static const MappingTable keyMapping = { + + { "BitTorrent/Session/MaxRatioAction", "Preferences/Bittorrent/MaxRatioAction" }, + { "BitTorrent/Session/DefaultSavePath", "Preferences/Downloads/SavePath" }, + { "BitTorrent/Session/TempPath", "Preferences/Downloads/TempPath" }, + { "BitTorrent/Session/TempPathEnabled", "Preferences/Downloads/TempPathEnabled" }, + { "BitTorrent/Session/AddTorrentPaused", "Preferences/Downloads/StartInPause" }, +#ifdef QBT_USES_QT5 + { "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/qt5/treeHeaderState" }, +#else + { "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/treeHeaderState" }, +#endif + { "AddNewTorrentDialog/Width", "AddNewTorrentDialog/width" }, + { "AddNewTorrentDialog/Position", "AddNewTorrentDialog/y" }, + { "AddNewTorrentDialog/Expanded", "AddNewTorrentDialog/expanded" }, + { "AddNewTorrentDialog/SavePathHistory", "TorrentAdditionDlg/save_path_history" }, + { "AddNewTorrentDialog/Enabled", "Preferences/Downloads/NewAdditionDialog" }, + { "AddNewTorrentDialog/TopLevel", "Preferences/Downloads/NewAdditionDialogFront" } + + }; + return keyMapping.value(key, key); } } diff --git a/src/base/torrentfilter.cpp b/src/base/torrentfilter.cpp index 02f9eea5d9b1..09daf45dc29c 100644 --- a/src/base/torrentfilter.cpp +++ b/src/base/torrentfilter.cpp @@ -29,7 +29,7 @@ #include "bittorrent/torrenthandle.h" #include "torrentfilter.h" -const QString TorrentFilter::AnyLabel; +const QString TorrentFilter::AnyCategory; const QStringSet TorrentFilter::AnyHash = (QStringSet() << QString()); const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading); @@ -49,16 +49,16 @@ TorrentFilter::TorrentFilter() { } -TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString label) +TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString category) : m_type(type) - , m_label(label) + , m_category(category) , m_hashSet(hashSet) { } -TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString label) +TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString category) : m_type(All) - , m_label(label) + , m_category(category) , m_hashSet(hashSet) { setTypeByName(filter); @@ -108,13 +108,13 @@ bool TorrentFilter::setHashSet(const QStringSet &hashSet) return false; } -bool TorrentFilter::setLabel(const QString &label) +bool TorrentFilter::setCategory(const QString &category) { // QString::operator==() doesn't distinguish between empty and null strings. - if ((m_label != label) - || (m_label.isNull() && !label.isNull()) - || (!m_label.isNull() && label.isNull())) { - m_label = label; + if ((m_category != category) + || (m_category.isNull() && !category.isNull()) + || (!m_category.isNull() && category.isNull())) { + m_category = category; return true; } @@ -125,7 +125,7 @@ bool TorrentFilter::match(TorrentHandle *const torrent) const { if (!torrent) return false; - return (matchState(torrent) && matchHash(torrent) && matchLabel(torrent)); + return (matchState(torrent) && matchHash(torrent) && matchCategory(torrent)); } bool TorrentFilter::matchState(BitTorrent::TorrentHandle *const torrent) const @@ -160,9 +160,8 @@ bool TorrentFilter::matchHash(BitTorrent::TorrentHandle *const torrent) const else return m_hashSet.contains(torrent->hash()); } -bool TorrentFilter::matchLabel(BitTorrent::TorrentHandle *const torrent) const +bool TorrentFilter::matchCategory(BitTorrent::TorrentHandle *const torrent) const { - if (m_label.isNull()) return true; - else if (m_label.isEmpty()) return torrent->label().isEmpty(); - else return (torrent->label() == m_label); + if (m_category.isNull()) return true; + else return (torrent->belongsToCategory(m_category)); } diff --git a/src/base/torrentfilter.h b/src/base/torrentfilter.h index c02ba176b4f0..1efde98be01d 100644 --- a/src/base/torrentfilter.h +++ b/src/base/torrentfilter.h @@ -58,7 +58,7 @@ class TorrentFilter Errored }; - static const QString AnyLabel; + static const QString AnyCategory; static const QStringSet AnyHash; static const TorrentFilter DownloadingTorrent; @@ -71,24 +71,24 @@ class TorrentFilter static const TorrentFilter ErroredTorrent; TorrentFilter(); - // label: pass empty string for "no label" or null string (QString()) for "any label" - TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString label = AnyLabel); - TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString label = AnyLabel); + // category: pass empty string for "no category" or null string (QString()) for "any category" + TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString category = AnyCategory); + TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString category = AnyCategory); bool setType(Type type); bool setTypeByName(const QString &filter); bool setHashSet(const QStringSet &hashSet); - bool setLabel(const QString &label); + bool setCategory(const QString &category); bool match(BitTorrent::TorrentHandle *const torrent) const; private: bool matchState(BitTorrent::TorrentHandle *const torrent) const; bool matchHash(BitTorrent::TorrentHandle *const torrent) const; - bool matchLabel(BitTorrent::TorrentHandle *const torrent) const; + bool matchCategory(BitTorrent::TorrentHandle *const torrent) const; Type m_type; - QString m_label; + QString m_category; QStringSet m_hashSet; }; diff --git a/src/base/types.h b/src/base/types.h index 746fd2f4bdb6..795b0ace6192 100644 --- a/src/base/types.h +++ b/src/base/types.h @@ -29,25 +29,10 @@ #ifndef TYPES_H #define TYPES_H -#include -#include +#include const qlonglong MAX_ETA = 8640000; -enum class MaxRatioAction -{ - Pause, - Remove -}; - -Q_DECLARE_METATYPE(MaxRatioAction) - -enum class TorrentExportFolder -{ - Regular, - Finished -}; - enum class ShutdownAction { None, @@ -56,19 +41,6 @@ enum class ShutdownAction Hibernate }; -template -inline QDataStream &operator<<(QDataStream &out, const T &val) -{ - return (out << static_cast(val)); -} - -template -inline QDataStream &operator>>(QDataStream &in, T &val) -{ - int tmp; - in >> tmp; - val = static_cast(tmp); - return in; -} +typedef QMap QStringMap; #endif // TYPES_H diff --git a/src/base/utils/fs.cpp b/src/base/utils/fs.cpp index f411e866872f..1a758f755c4d 100644 --- a/src/base/utils/fs.cpp +++ b/src/base/utils/fs.cpp @@ -250,22 +250,23 @@ bool Utils::Fs::sameFiles(const QString& path1, const QString& path2) return same; } -QString Utils::Fs::toValidFileSystemName(const QString &filename) +QString Utils::Fs::toValidFileSystemName(const QString &name, bool allowSeparators) { - static const QRegExp regex("[\\\\/:?\"*<>|]"); + QRegExp regex(allowSeparators ? "[:?\"*<>|]+" : "[\\\\/:?\"*<>|]+"); - QString validName = filename.trimmed(); + QString validName = name.trimmed(); validName.replace(regex, " "); - qDebug() << "toValidFileSystemName:" << filename << "=>" << validName; + qDebug() << "toValidFileSystemName:" << name << "=>" << validName; return validName; } -bool Utils::Fs::isValidFileSystemName(const QString& filename) +bool Utils::Fs::isValidFileSystemName(const QString &name, bool allowSeparators) { - if (filename.isEmpty()) return false; - const QRegExp regex("[\\\\/:?\"*<>|]"); - return !filename.contains(regex); + if (name.isEmpty()) return false; + + QRegExp regex(allowSeparators ? "[:?\"*<>|]" : "[\\\\/:?\"*<>|]"); + return !name.contains(regex); } qlonglong Utils::Fs::freeDiskSpaceOnPath(QString path) diff --git a/src/base/utils/fs.h b/src/base/utils/fs.h index 77d0833d5ef3..673845505a7d 100644 --- a/src/base/utils/fs.h +++ b/src/base/utils/fs.h @@ -48,8 +48,8 @@ namespace Utils QString folderName(const QString& file_path); qint64 computePathSize(const QString& path); bool sameFiles(const QString& path1, const QString& path2); - QString toValidFileSystemName(const QString &filename); - bool isValidFileSystemName(const QString& filename); + QString toValidFileSystemName(const QString &name, bool allowSeparators = false); + bool isValidFileSystemName(const QString& name, bool allowSeparators = false); qlonglong freeDiskSpaceOnPath(QString path); QString branchPath(const QString& file_path, QString* removed = 0); bool sameFileNames(const QString& first, const QString& second); diff --git a/src/base/utils/misc.h b/src/base/utils/misc.h index bfc4d00e43c1..39c421f46253 100644 --- a/src/base/utils/misc.h +++ b/src/base/utils/misc.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "base/types.h" /* Miscellaneous functions that can be useful */ diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 3d9122e04a6a..caa75714472c 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -35,7 +35,7 @@ #include #include -#include "base/preferences.h" +#include "base/settingsstorage.h" #include "base/net/downloadmanager.h" #include "base/net/downloadhandler.h" #include "base/bittorrent/session.h" @@ -44,16 +44,34 @@ #include "base/bittorrent/torrenthandle.h" #include "base/utils/fs.h" #include "base/utils/misc.h" +#include "base/utils/string.h" #include "base/unicodestrings.h" #include "guiiconprovider.h" #include "autoexpandabledialog.h" #include "messageboxraised.h" -#include "ui_addnewtorrentdialog.h" #include "proplistdelegate.h" #include "torrentcontentmodel.h" #include "torrentcontentfiltermodel.h" +#include "ui_addnewtorrentdialog.h" #include "addnewtorrentdialog.h" +#define SETTINGS_KEY(name) "AddNewTorrentDialog/" name +const QString KEY_ENABLED = SETTINGS_KEY("Enabled"); +const QString KEY_DEFAULTSAVEPATH = SETTINGS_KEY("DefaultSavePath"); +const QString KEY_DEFAULTCATEGORY = SETTINGS_KEY("DefaultCategory"); +const QString KEY_TREEHEADERSTATE = SETTINGS_KEY("TreeHeaderState"); +const QString KEY_WIDTH = SETTINGS_KEY("Width"); +const QString KEY_EXPANDED = SETTINGS_KEY("Expanded"); +const QString KEY_POSITION = SETTINGS_KEY("Position"); +const QString KEY_TOPLEVEL = SETTINGS_KEY("TopLevel"); +const QString KEY_SAVEPATHHISTORY = SETTINGS_KEY("SavePathHistory"); + +namespace +{ + //just a shortcut + inline SettingsStorage *settings() { return SettingsStorage::instance(); } +} + AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::AddNewTorrentDialog) @@ -67,34 +85,36 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) ui->lblMetaLoading->setVisible(false); ui->progMetaLoading->setVisible(false); - Preferences* const pref = Preferences::instance(); - ui->start_torrent_cb->setChecked(!pref->addTorrentsInPause()); - ui->savePathComboBox->addItem(Utils::Fs::toNativePath(pref->getSavePath()), pref->getSavePath()); - loadSavePathHistory(); + auto session = BitTorrent::Session::instance(); + + ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused()); + (session->isASMDisabledByDefault() ? ui->simpleModeRadioButton : ui->advancedModeRadioButton)->setChecked(true); + populateSavePathComboBox(); connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int))); - connect(ui->browse_button, SIGNAL(clicked()), SLOT(browseButton_clicked())); - ui->default_save_path_cb->setVisible(false); // Default path is selected by default + connect(ui->browseButton, SIGNAL(clicked()), SLOT(browseButton_clicked())); + ui->defaultSavePathCheckBox->setVisible(false); // Default path is selected by default - // Load labels - const QStringList customLabels = pref->getTorrentLabels(); - const QString defaultLabel = pref->getDefaultLabel(); + // Load categories + QStringList categories = session->categories(); + std::sort(categories.begin(), categories.end(), Utils::String::NaturalCompare()); + QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString(); - if (!defaultLabel.isEmpty()) - ui->label_combo->addItem(defaultLabel); - ui->label_combo->addItem(""); + if (!defaultCategory.isEmpty()) + ui->categoryComboBox->addItem(defaultCategory); + ui->categoryComboBox->addItem(""); - foreach (const QString& label, customLabels) - if (label != defaultLabel) - ui->label_combo->addItem(label); + foreach (const QString &category, categories) + if (category != defaultCategory) + ui->categoryComboBox->addItem(category); - ui->label_combo->model()->sort(0); - ui->content_tree->header()->setSortIndicator(0, Qt::AscendingOrder); + ui->categoryComboBox->model()->sort(0); + ui->contentTreeView->header()->setSortIndicator(0, Qt::AscendingOrder); loadState(); // Signal / slots connect(ui->adv_button, SIGNAL(clicked(bool)), SLOT(showAdvancedSettings(bool))); - editHotkey = new QShortcut(QKeySequence("F2"), ui->content_tree, 0, 0, Qt::WidgetShortcut); + editHotkey = new QShortcut(QKeySequence("F2"), ui->contentTreeView, 0, 0, Qt::WidgetShortcut); connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedFile())); - connect(ui->content_tree, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedFile())); + connect(ui->contentTreeView, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedFile())); ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); } @@ -108,27 +128,45 @@ AddNewTorrentDialog::~AddNewTorrentDialog() delete editHotkey; } +bool AddNewTorrentDialog::isEnabled() +{ + return SettingsStorage::instance()->loadValue(KEY_ENABLED, true).toBool(); +} + +void AddNewTorrentDialog::setEnabled(bool value) +{ + SettingsStorage::instance()->storeValue(KEY_ENABLED, value); +} + +bool AddNewTorrentDialog::isTopLevel() +{ + return SettingsStorage::instance()->loadValue(KEY_TOPLEVEL, true).toBool(); +} + +void AddNewTorrentDialog::setTopLevel(bool value) +{ + SettingsStorage::instance()->storeValue(KEY_TOPLEVEL, value); +} + void AddNewTorrentDialog::loadState() { - const Preferences* const pref = Preferences::instance(); - m_headerState = pref->getAddNewTorrentDialogState(); - int width = pref->getAddNewTorrentDialogWidth(); + m_headerState = settings()->loadValue(KEY_TREEHEADERSTATE).toByteArray(); + int width = settings()->loadValue(KEY_WIDTH, -1).toInt(); if (width >= 0) { QRect geo = geometry(); geo.setWidth(width); setGeometry(geo); } - ui->adv_button->setChecked(pref->getAddNewTorrentDialogExpanded()); + ui->adv_button->setChecked(settings()->loadValue(KEY_EXPANDED).toBool()); } void AddNewTorrentDialog::saveState() { - Preferences* const pref = Preferences::instance(); if (m_contentModel) - pref->setAddNewTorrentDialogState(ui->content_tree->header()->saveState()); - pref->setAddNewTorrentDialogPos(pos().y()); - pref->setAddNewTorrentDialogWidth(width()); - pref->setAddNewTorrentDialogExpanded(ui->adv_button->isChecked()); + settings()->storeValue(KEY_TREEHEADERSTATE, ui->contentTreeView->header()->saveState()); + settings()->storeValue(KEY_POSITION, pos().y()); + settings()->storeValue(KEY_WIDTH, width()); + settings()->storeValue(KEY_EXPANDED, ui->adv_button->isChecked()); } void AddNewTorrentDialog::show(QString source, QWidget *parent) @@ -256,9 +294,8 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri) void AddNewTorrentDialog::showEvent(QShowEvent *event) { QDialog::showEvent(event); - Preferences* const pref = Preferences::instance(); - if (!pref->additionDialogFront()) - return; + if (!isTopLevel()) return; + activateWindow(); raise(); } @@ -272,7 +309,7 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show) ui->adv_button->setText(QString::fromUtf8(C_UP)); ui->settings_group->setVisible(true); ui->infoGroup->setVisible(true); - ui->content_tree->setVisible(m_hasMetadata); + ui->contentTreeView->setVisible(m_hasMetadata); static_cast(layout())->insertWidget(layout()->indexOf(ui->never_show_cb) + 1, ui->adv_button); } else { @@ -287,21 +324,20 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show) void AddNewTorrentDialog::saveSavePathHistory() const { - QDir selected_save_path(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString()); - Preferences* const pref = Preferences::instance(); + QDir selectedSavePath(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString()); // Get current history - QStringList history = pref->getAddNewTorrentDialogPathHistory(); - QList history_dirs; + QStringList history = settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList(); + QList historyDirs; foreach(const QString dir, history) - history_dirs << QDir(dir); - if (!history_dirs.contains(selected_save_path)) { + historyDirs << QDir(dir); + if (!historyDirs.contains(selectedSavePath)) { // Add save path to history - history.push_front(selected_save_path.absolutePath()); + history.push_front(selectedSavePath.absolutePath()); // Limit list size if (history.size() > 8) history.pop_back(); // Save history - pref->setAddNewTorrentDialogPathHistory(history); + settings()->storeValue(KEY_SAVEPATHHISTORY, history); } } @@ -345,8 +381,10 @@ void AddNewTorrentDialog::updateDiskSpaceLabel() void AddNewTorrentDialog::onSavePathChanged(int index) { // Toggle default save path setting checkbox visibility - ui->default_save_path_cb->setChecked(false); - ui->default_save_path_cb->setVisible(QDir(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString()) != QDir(Preferences::instance()->getSavePath())); + ui->defaultSavePathCheckBox->setChecked(false); + ui->defaultSavePathCheckBox->setVisible( + QDir(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString()) + != QDir(defaultSavePath())); // Remember index m_oldIndex = index; @@ -354,6 +392,17 @@ void AddNewTorrentDialog::onSavePathChanged(int index) updateDiskSpaceLabel(); } +void AddNewTorrentDialog::categoryChanged(int index) +{ + Q_UNUSED(index); + + if (ui->advancedModeRadioButton->isChecked()) { + QString savePath = BitTorrent::Session::instance()->categorySavePath(ui->categoryComboBox->currentText()); + ui->savePathComboBox->setItemText(0, Utils::Fs::toNativePath(savePath)); + ui->savePathComboBox->setItemData(0, savePath); + } +} + void AddNewTorrentDialog::browseButton_clicked() { disconnect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavePathChanged(int))); @@ -390,7 +439,7 @@ void AddNewTorrentDialog::browseButton_clicked() void AddNewTorrentDialog::renameSelectedFile() { - const QModelIndexList selectedIndexes = ui->content_tree->selectionModel()->selectedRows(0); + const QModelIndexList selectedIndexes = ui->contentTreeView->selectionModel()->selectedRows(0); if (selectedIndexes.size() != 1) return; const QModelIndex &index = selectedIndexes.first(); @@ -490,7 +539,7 @@ void AddNewTorrentDialog::setdialogPosition() qApp->processEvents(); QPoint center(Utils::Misc::screenCenter(this)); // Adjust y - int y = Preferences::instance()->getAddNewTorrentDialogPos(); + int y = settings()->loadValue(KEY_POSITION, -1).toInt(); if (y >= 0) { center.setY(y); } @@ -502,20 +551,23 @@ void AddNewTorrentDialog::setdialogPosition() move(center); } -void AddNewTorrentDialog::loadSavePathHistory() +void AddNewTorrentDialog::populateSavePathComboBox() { - QDir default_save_path(Preferences::instance()->getSavePath()); + QString defSavePath = defaultSavePath(); + + ui->savePathComboBox->clear(); + ui->savePathComboBox->addItem(Utils::Fs::toNativePath(defSavePath), defSavePath); + QDir defaultSaveDir(defSavePath); // Load save path history - QStringList raw_path_history = Preferences::instance()->getAddNewTorrentDialogPathHistory(); - foreach (const QString &sp, raw_path_history) - if (QDir(sp) != default_save_path) - ui->savePathComboBox->addItem(Utils::Fs::toNativePath(sp), sp); + foreach (const QString &savePath, settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList()) + if (QDir(savePath) != defaultSaveDir) + ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath); } void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&) { QMenu myFilesLlistMenu; - const QModelIndexList selectedRows = ui->content_tree->selectionModel()->selectedRows(0); + const QModelIndexList selectedRows = ui->contentTreeView->selectionModel()->selectedRows(0); QAction *actRename = 0; if (selectedRows.size() == 1) { actRename = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename...")); @@ -557,39 +609,34 @@ void AddNewTorrentDialog::accept() if (!m_hasMetadata) disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo &))); - Preferences *const pref = Preferences::instance(); BitTorrent::AddTorrentParams params; if (ui->skip_check_cb->isChecked()) // TODO: Check if destination actually exists params.skipChecking = true; - // Label - params.label = ui->label_combo->currentText(); + // Category + params.category = ui->categoryComboBox->currentText(); - if (ui->defaultLabel->isChecked()) - pref->setDefaultLabel(params.label); + if (ui->defaultCategoryCheckbox->isChecked()) + settings()->storeValue(KEY_DEFAULTCATEGORY, params.category); // Save file priorities if (m_contentModel) params.filePriorities = m_contentModel->model()->getFilePriorities(); - params.addPaused = !ui->start_torrent_cb->isChecked(); - - saveSavePathHistory(); - pref->useAdditionDialog(!ui->never_show_cb->isChecked()); + params.addPaused = !ui->startTorrentCheckBox->isChecked(); QString savePath = ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString(); - if (ui->default_save_path_cb->isChecked()) { - pref->setSavePath(savePath); - pref->apply(); - } - else { - // if we don't use default save path... - if (QDir(savePath) != QDir(pref->getSavePath())) - params.savePath = savePath; + if (ui->simpleModeRadioButton->isChecked()) { + params.savePath = savePath; + saveSavePathHistory(); + if (ui->defaultSavePathCheckBox->isChecked()) + settings()->storeValue(KEY_DEFAULTSAVEPATH, savePath); } + setEnabled(!ui->never_show_cb->isChecked()); + // Add torrent if (!m_hasMetadata) BitTorrent::Session::instance()->addTorrent(m_hash, params); @@ -656,28 +703,35 @@ void AddNewTorrentDialog::setupTreeview() // Prepare content tree m_contentModel = new TorrentContentFilterModel(this); connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel())); - ui->content_tree->setModel(m_contentModel); - ui->content_tree->hideColumn(PROGRESS); + ui->contentTreeView->setModel(m_contentModel); + ui->contentTreeView->hideColumn(PROGRESS); m_contentDelegate = new PropListDelegate(); - ui->content_tree->setItemDelegate(m_contentDelegate); - connect(ui->content_tree, SIGNAL(clicked(const QModelIndex &)), ui->content_tree, SLOT(edit(const QModelIndex &))); - connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &))); + ui->contentTreeView->setItemDelegate(m_contentDelegate); + connect(ui->contentTreeView, SIGNAL(clicked(const QModelIndex &)), ui->contentTreeView, SLOT(edit(const QModelIndex &))); + connect(ui->contentTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &))); // List files in torrent m_contentModel->model()->setupModelData(m_torrentInfo); if (!m_headerState.isEmpty()) - ui->content_tree->header()->restoreState(m_headerState); + ui->contentTreeView->header()->restoreState(m_headerState); // Expand root folder - ui->content_tree->setExpanded(m_contentModel->index(0, 0), true); + ui->contentTreeView->setExpanded(m_contentModel->index(0, 0), true); } updateDiskSpaceLabel(); - showAdvancedSettings(Preferences::instance()->getAddNewTorrentDialogExpanded()); + showAdvancedSettings(settings()->loadValue(KEY_EXPANDED, false).toBool()); // Set dialog position setdialogPosition(); } +QString AddNewTorrentDialog::defaultSavePath() const +{ + return Utils::Fs::fromNativePath( + settings()->loadValue(KEY_DEFAULTSAVEPATH, + BitTorrent::Session::instance()->defaultSavePath()).toString()); +} + void AddNewTorrentDialog::handleDownloadFailed(const QString &url, const QString &reason) { MessageBoxRaised::critical(0, tr("Download Error"), QString("Cannot download '%1': %2").arg(url).arg(reason)); @@ -701,3 +755,25 @@ void AddNewTorrentDialog::handleDownloadFinished(const QString &url, const QStri else this->deleteLater(); } + +void AddNewTorrentDialog::savingModeChanged(bool enabled) +{ + if (!enabled) return; + + if (ui->simpleModeRadioButton->isChecked()) { + populateSavePathComboBox(); + ui->savePathComboBox->setEnabled(true); + ui->browseButton->setEnabled(true); + ui->savePathComboBox->blockSignals(false); + ui->savePathComboBox->setCurrentIndex(m_oldIndex < ui->savePathComboBox->count() ? m_oldIndex : ui->savePathComboBox->count() - 1); + } + else { + ui->savePathComboBox->blockSignals(true); + ui->savePathComboBox->clear(); + QString savePath = BitTorrent::Session::instance()->categorySavePath(ui->categoryComboBox->currentText()); + ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath); + ui->savePathComboBox->setEnabled(false); + ui->browseButton->setEnabled(false); + ui->defaultSavePathCheckBox->setVisible(false); + } +} diff --git a/src/gui/addnewtorrentdialog.h b/src/gui/addnewtorrentdialog.h index adf4fbde9aec..a67f239f5663 100644 --- a/src/gui/addnewtorrentdialog.h +++ b/src/gui/addnewtorrentdialog.h @@ -58,10 +58,12 @@ class AddNewTorrentDialog: public QDialog public: ~AddNewTorrentDialog(); - static void show(QString source, QWidget *parent = 0); + static bool isEnabled(); + static void setEnabled(bool value); + static bool isTopLevel(); + static void setTopLevel(bool value); -protected: - void showEvent(QShowEvent *event); + static void show(QString source, QWidget *parent = 0); private slots: void showAdvancedSettings(bool show); @@ -75,24 +77,27 @@ private slots: void handleDownloadFailed(const QString &url, const QString &reason); void handleRedirectedToMagnet(const QString &url, const QString &magnetUri); void handleDownloadFinished(const QString &url, const QString &filePath); + void savingModeChanged(bool enabled); + void categoryChanged(int index); -protected slots: - virtual void accept(); - virtual void reject(); + void accept() override; + void reject() override; private: explicit AddNewTorrentDialog(QWidget *parent = 0); bool loadTorrent(const QString &torrentPath); bool loadMagnet(const BitTorrent::MagnetUri &magnetUri); - void loadSavePathHistory(); + void populateSavePathComboBox(); void saveSavePathHistory() const; int indexOfSavePath(const QString& save_path); void loadState(); void saveState(); void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString()); void setupTreeview(); + QString defaultSavePath() const; + + void showEvent(QShowEvent *event) override; -private: Ui::AddNewTorrentDialog *ui; TorrentContentFilterModel *m_contentModel; PropListDelegate *m_contentDelegate; diff --git a/src/gui/addnewtorrentdialog.ui b/src/gui/addnewtorrentdialog.ui index d968e99cce00..064abc680db7 100644 --- a/src/gui/addnewtorrentdialog.ui +++ b/src/gui/addnewtorrentdialog.ui @@ -17,6 +17,50 @@ Save at + + + + 20 + + + + + Saving Management: + + + + + + + Simple + + + true + + + + + + + Advanced + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -33,7 +77,7 @@ - + Browse... @@ -42,7 +86,7 @@ - + Set as default save path @@ -75,14 +119,14 @@ - + - Set as default label + Set as default category - + @@ -92,12 +136,12 @@ - Set label: + Category: - + 140 @@ -115,7 +159,7 @@ - + Start torrent @@ -231,7 +275,7 @@ - + Qt::CustomContextMenu @@ -311,6 +355,20 @@
torrentcontenttreeview.h
+ + savePathComboBox + browseButton + simpleModeRadioButton + advancedModeRadioButton + defaultSavePathCheckBox + never_show_cb + adv_button + startTorrentCheckBox + categoryComboBox + defaultCategoryCheckbox + skip_check_cb + contentTreeView + @@ -320,8 +378,8 @@ accept() - 248 - 254 + 403 + 579 157 @@ -336,8 +394,8 @@ reject() - 316 - 260 + 403 + 579 286 @@ -345,5 +403,57 @@ + + simpleModeRadioButton + toggled(bool) + AddNewTorrentDialog + savingModeChanged(bool) + + + 154 + 39 + + + 122 + 6 + + + + + advancedModeRadioButton + toggled(bool) + AddNewTorrentDialog + savingModeChanged(bool) + + + 218 + 44 + + + 209 + 7 + + + + + categoryComboBox + currentIndexChanged(int) + AddNewTorrentDialog + categoryChanged(int) + + + 337 + 205 + + + 403 + 160 + + + + + savingModeChanged(bool) + categoryChanged(int) + diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 8dee0e34486e..dd8390403b17 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1004,7 +1004,7 @@ void MainWindow::dropEvent(QDropEvent *event) } // Add file to download list - const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); + const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled(); foreach (QString file, files) { qDebug("Dropped file %s on download list", qPrintable(file)); if (useTorrentAdditionDialog) @@ -1039,7 +1039,7 @@ void MainWindow::on_actionOpen_triggered() const QStringList pathsList = QFileDialog::getOpenFileNames(0, tr("Open Torrent Files"), pref->getMainLastDir(), tr("Torrent Files") + QString::fromUtf8(" (*.torrent)")); - const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); + const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled(); if (!pathsList.isEmpty()) { foreach (QString file, pathsList) { qDebug("Dropped file %s on download list", qPrintable(file)); @@ -1249,7 +1249,7 @@ void MainWindow::showNotificationBaloon(QString title, QString msg) const void MainWindow::downloadFromURLList(const QStringList& url_list) { - const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); + const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled(); foreach (QString url, url_list) { if ((url.size() == 40 && !url.contains(QRegExp("[^0-9A-Fa-f]"))) || (url.size() == 32 && !url.contains(QRegExp("[^2-7A-Za-z]")))) diff --git a/src/gui/options.ui b/src/gui/options.ui index b6ced09e6de2..717ab4642cbd 100644 --- a/src/gui/options.ui +++ b/src/gui/options.ui @@ -546,8 +546,8 @@ 0 0 - 487 - 1040 + 454 + 1212 @@ -599,7 +599,7 @@
- + 0 @@ -607,13 +607,57 @@ - Hard Disk + Saving Management - + + + 10 + + + + + Default Saving Mode: + + + + + + + Simple + + + true + + + + + + + Advanced + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + - Save files to location: + Default Save Path @@ -658,10 +702,134 @@ - - + + + + + + + 10 + + + + + Enable Subcategories: + + + + + + + Yes + + + buttonGroup + + + + + + + No + + + true + + + buttonGroup + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + When Torrent Category changed + + + + + + Relocate torrent + + + true + + + + + + + Switch torrent to Simple Mode + + + false + + + + + + + + + + When Default Save Path changed + + + + + + Relocate affected torrents + + + + + + + Switch affected torrents to Simple Mode + + + true + + + + + + + + + + When Category changed + + + + - Append the label of the torrent to the save path + Relocate affected torrents + + + + + + + Switch affected torrents to Simple Mode + + + true @@ -728,93 +896,6 @@ - - - - Automatically add torrents from: - - - - 9 - - - - - - - - 0 - 1 - - - - - 250 - 150 - - - - QAbstractItemView::AllEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - Qt::ElideNone - - - false - - - 80 - - - false - - - - - - - - - Add folder... - - - - - - - false - - - Remove folder - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - @@ -926,6 +1007,93 @@ + + + + Automatically add torrents from: + + + + 9 + + + + + + + + 0 + 1 + + + + + 250 + 150 + + + + QAbstractItemView::AllEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideNone + + + false + + + 80 + + + false + + + + + + + + + Add folder... + + + + + + + false + + + Remove folder + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + @@ -1062,8 +1230,8 @@ 0 0 - 450 - 658 + 361 + 586 @@ -1592,8 +1760,8 @@ 0 0 - 376 - 444 + 275 + 401 @@ -1979,8 +2147,8 @@ 0 0 - 555 - 527 + 440 + 481 @@ -2376,8 +2544,8 @@ 0 0 - 419 - 537 + 332 + 480 @@ -2950,4 +3118,7 @@ + + + diff --git a/src/gui/options_imp.cpp b/src/gui/options_imp.cpp index 17a2c1e99ed0..3d77e1bd1ece 100644 --- a/src/gui/options_imp.cpp +++ b/src/gui/options_imp.cpp @@ -51,6 +51,7 @@ #include "advancedsettings.h" #include "guiiconprovider.h" #include "scanfoldersdelegate.h" +#include "addnewtorrentdialog.h" #include "options_imp.h" #ifndef QT_NO_OPENSSL @@ -168,8 +169,12 @@ options_imp::options_imp(QWidget *parent) #endif // Downloads tab connect(textSavePath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); + connect(radioBtnEnableSubcategories, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(radioBtnAdvancedMode, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(radioBtnRelocateOnCategoryChanged, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(radioBtnRelocateOnCategorySavePathChanged, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(radioBtnRelocateOnDefaultSavePathChanged, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(textTempPath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); - connect(checkAppendLabel, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkAppendqB, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkPreallocateAll, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkAdditionDialog, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); @@ -197,7 +202,7 @@ options_imp::options_imp(QWidget *parent) autoRun_param->setText(QString::fromUtf8("%1\n %2\n %3\n %4\n %5\n %6\n %7\n %8\n %9\n %10") .arg(tr("Supported parameters (case sensitive):")) .arg(tr("%N: Torrent name")) - .arg(tr("%L: Label")) + .arg(tr("%L: Category")) .arg(tr("%F: Content path (same as root path for multifile torrent)")) .arg(tr("%R: Root path (first torrent subdirectory path)")) .arg(tr("%D: Save path")) @@ -429,16 +434,22 @@ void options_imp::saveOptions() #endif // End General preferences + auto session = BitTorrent::Session::instance(); + // Downloads preferences - pref->setSavePath(getSavePath()); - pref->setTempPathEnabled(isTempPathEnabled()); - pref->setTempPath(getTempPath()); - pref->setAppendTorrentLabel(checkAppendLabel->isChecked()); + session->setDefaultSavePath(Utils::Fs::expandPathAbs(textSavePath->text())); + session->setSubcategoriesEnabled(radioBtnEnableSubcategories->isChecked()); + session->setASMDisabledByDefault(radioBtnSimpleMode->isChecked()); + session->setDisableASMWhenCategoryChanged(radioBtnDisableASMOnCategoryChanged->isChecked()); + session->setDisableASMWhenCategorySavePathChanged(radioBtnDisableASMOnCategorySavePathChanged->isChecked()); + session->setDisableASMWhenDefaultSavePathChanged(radioBtnDisableASMOnDefaultSavePathChanged->isChecked()); + session->setTempPathEnabled(checkTempFolder->isChecked()); + session->setTempPath(Utils::Fs::expandPathAbs(textTempPath->text())); pref->useIncompleteFilesExtension(checkAppendqB->isChecked()); pref->preAllocateAllFiles(preAllocateAllFiles()); - pref->useAdditionDialog(useAdditionDialog()); - pref->additionDialogFront(checkAdditionDialogFront->isChecked()); - pref->addTorrentsInPause(addTorrentsInPause()); + AddNewTorrentDialog::setEnabled(useAdditionDialog()); + AddNewTorrentDialog::setTopLevel(checkAdditionDialogFront->isChecked()); + session->setAddTorrentPaused(addTorrentsInPause()); ScanFoldersModel::instance()->removeFromFSWatcher(removedScanDirs); ScanFoldersModel::instance()->addToFSWatcher(addedScanDirs); ScanFoldersModel::instance()->makePersistent(); @@ -500,7 +511,7 @@ void options_imp::saveOptions() pref->setAddTrackersEnabled(checkEnableAddTrackers->isChecked()); pref->setTrackersList(textTrackers->toPlainText()); pref->setGlobalMaxRatio(getMaxRatio()); - pref->setMaxRatioAction(static_cast(comboRatioLimitAct->currentIndex())); + session->setMaxRatioAction(static_cast(comboRatioLimitAct->currentIndex())); // End Bittorrent preferences // Misc preferences // * IPFilter @@ -610,18 +621,21 @@ void options_imp::loadOptions() #endif // End General preferences - // Downloads preferences - checkAdditionDialog->setChecked(pref->useAdditionDialog()); - checkAdditionDialogFront->setChecked(pref->additionDialogFront()); - checkStartPaused->setChecked(pref->addTorrentsInPause()); + auto session = BitTorrent::Session::instance(); - textSavePath->setText(Utils::Fs::toNativePath(pref->getSavePath())); - if (pref->isTempPathEnabled()) - checkTempFolder->setChecked(true); - else - checkTempFolder->setChecked(false); - textTempPath->setText(Utils::Fs::toNativePath(pref->getTempPath())); - checkAppendLabel->setChecked(pref->appendTorrentLabel()); + // Downloads preferences + checkAdditionDialog->setChecked(AddNewTorrentDialog::isEnabled()); + checkAdditionDialogFront->setChecked(AddNewTorrentDialog::isTopLevel()); + checkStartPaused->setChecked(session->isAddTorrentPaused()); + + textSavePath->setText(Utils::Fs::toNativePath(session->defaultSavePath())); + (session->isSubcategoriesEnabled() ? radioBtnEnableSubcategories : radioBtnDisableSubcategories)->setChecked(true); + (session->isASMDisabledByDefault() ? radioBtnSimpleMode : radioBtnAdvancedMode)->setChecked(true); + (session->isDisableASMWhenCategoryChanged() ? radioBtnDisableASMOnCategoryChanged : radioBtnRelocateOnCategoryChanged)->setChecked(true); + (session->isDisableASMWhenCategorySavePathChanged() ? radioBtnDisableASMOnCategorySavePathChanged : radioBtnRelocateOnCategorySavePathChanged)->setChecked(true); + (session->isDisableASMWhenDefaultSavePathChanged() ? radioBtnDisableASMOnDefaultSavePathChanged : radioBtnRelocateOnDefaultSavePathChanged)->setChecked(true); + checkTempFolder->setChecked(session->isTempPathEnabled()); + textTempPath->setText(Utils::Fs::toNativePath(session->tempPath())); checkAppendqB->setChecked(pref->useIncompleteFilesExtension()); checkPreallocateAll->setChecked(pref->preAllocateAllFiles()); @@ -844,7 +858,7 @@ void options_imp::loadOptions() spinMaxRatio->setEnabled(false); comboRatioLimitAct->setEnabled(false); } - comboRatioLimitAct->setCurrentIndex(static_cast(pref->getMaxRatioAction())); + comboRatioLimitAct->setCurrentIndex(session->maxRatioAction()); // End Bittorrent preferences // Web UI preferences @@ -974,26 +988,6 @@ qreal options_imp::getMaxRatio() const return -1; } -// Return Save Path -QString options_imp::getSavePath() const -{ - if (textSavePath->text().trimmed().isEmpty()) { - QString save_path = Preferences::instance()->getSavePath(); - textSavePath->setText(Utils::Fs::toNativePath(save_path)); - } - return Utils::Fs::expandPathAbs(textSavePath->text()); -} - -QString options_imp::getTempPath() const -{ - return Utils::Fs::expandPathAbs(textTempPath->text()); -} - -bool options_imp::isTempPathEnabled() const -{ - return checkTempFolder->isChecked(); -} - // Return max connections number int options_imp::getMaxConnecs() const { diff --git a/src/gui/options_imp.h b/src/gui/options_imp.h index 6f063a5e0b22..6270cd6a60f8 100644 --- a/src/gui/options_imp.h +++ b/src/gui/options_imp.h @@ -114,9 +114,6 @@ private slots: bool WinStartup() const; #endif // Downloads - QString getSavePath() const; - bool isTempPathEnabled() const; - QString getTempPath() const; bool preAllocateAllFiles() const; bool useAdditionDialog() const; bool addTorrentsInPause() const; diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 5b5493763f4e..7721462a70a6 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -34,17 +34,18 @@ #include #include -#include "automatedrssdownloader.h" -#include "ui_automatedrssdownloader.h" -#include "base/rss/rssdownloadrulelist.h" #include "base/preferences.h" +#include "base/bittorrent/session.h" +#include "base/rss/rssdownloadrulelist.h" #include "base/rss/rssmanager.h" #include "base/rss/rssfolder.h" #include "base/rss/rssfeed.h" -#include "guiiconprovider.h" -#include "autoexpandabledialog.h" #include "base/utils/fs.h" #include "base/utils/string.h" +#include "guiiconprovider.h" +#include "autoexpandabledialog.h" +#include "ui_automatedrssdownloader.h" +#include "automatedrssdownloader.h" AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent) : QDialog(parent), @@ -85,7 +86,7 @@ AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& "
  • " + tr("Normal range: 1x25-40; matches episodes 25 through 40 of season one") + "
  • " + "
  • " + tr("Infinite range: 1x25-; matches episodes 25 and upward of season one") + "
  • " + ""; ui->lineEFilter->setToolTip(tip); - initLabelCombobox(); + initCategoryCombobox(); loadFeedList(); loadSettings(); ok = connect(ui->listRules, SIGNAL(itemSelectionChanged()), SLOT(updateRuleDefinitionBox())); @@ -253,11 +254,11 @@ void AutomatedRssDownloader::updateRuleDefinitionBox() ui->checkRegex->blockSignals(true); ui->checkRegex->setChecked(rule->useRegex()); ui->checkRegex->blockSignals(false); - if (rule->label().isEmpty()) { - ui->comboLabel->setCurrentIndex(-1); - ui->comboLabel->clearEditText(); + if (rule->category().isEmpty()) { + ui->comboCategory->setCurrentIndex(-1); + ui->comboCategory->clearEditText(); } else { - ui->comboLabel->setCurrentIndex(ui->comboLabel->findText(rule->label())); + ui->comboCategory->setCurrentIndex(ui->comboCategory->findText(rule->category())); } ui->comboAddPaused->setCurrentIndex(rule->addPaused()); ui->spinIgnorePeriod->setValue(rule->ignoreDays()); @@ -293,7 +294,7 @@ void AutomatedRssDownloader::clearRuleDefinitionBox() ui->lineNotContains->clear(); ui->saveDiffDir_check->setChecked(false); ui->lineSavePath->clear(); - ui->comboLabel->clearEditText(); + ui->comboCategory->clearEditText(); ui->checkRegex->setChecked(false); ui->spinIgnorePeriod->setValue(0); updateFieldsToolTips(ui->checkRegex->isChecked()); @@ -309,13 +310,12 @@ Rss::DownloadRulePtr AutomatedRssDownloader::getCurrentRule() const return Rss::DownloadRulePtr(); } -void AutomatedRssDownloader::initLabelCombobox() +void AutomatedRssDownloader::initCategoryCombobox() { - // Load custom labels - QStringList customLabels = Preferences::instance()->getTorrentLabels(); - std::sort(customLabels.begin(), customLabels.end(), Utils::String::NaturalCompare()); - foreach (const QString& l, customLabels) - ui->comboLabel->addItem(l); + // Load torrent categories + QStringList categories = BitTorrent::Session::instance()->categories(); + std::sort(categories.begin(), categories.end(), Utils::String::NaturalCompare()); + ui->comboCategory->addItems(categories); } void AutomatedRssDownloader::saveEditedRule() @@ -344,11 +344,9 @@ void AutomatedRssDownloader::saveEditedRule() rule->setSavePath(ui->lineSavePath->text()); else rule->setSavePath(""); - rule->setLabel(ui->comboLabel->currentText()); + rule->setCategory(ui->comboCategory->currentText()); + rule->setAddPaused(Rss::DownloadRule::AddPausedState(ui->comboAddPaused->currentIndex())); - // Save new label - if (!rule->label().isEmpty()) - Preferences::instance()->addTorrentLabelExternal(rule->label()); rule->setIgnoreDays(ui->spinIgnorePeriod->value()); //rule->setRssFeeds(getSelectedFeeds()); // Save it diff --git a/src/gui/rss/automatedrssdownloader.h b/src/gui/rss/automatedrssdownloader.h index 8bbda030c094..b89e667c4eac 100644 --- a/src/gui/rss/automatedrssdownloader.h +++ b/src/gui/rss/automatedrssdownloader.h @@ -90,7 +90,7 @@ private slots: private: Rss::DownloadRulePtr getCurrentRule() const; - void initLabelCombobox(); + void initCategoryCombobox(); void addFeedArticlesToTree(const Rss::FeedPtr& feed, const QStringList& articles); private: diff --git a/src/gui/rss/automatedrssdownloader.ui b/src/gui/rss/automatedrssdownloader.ui index 54a0fd32b0bd..e756b94d4563 100644 --- a/src/gui/rss/automatedrssdownloader.ui +++ b/src/gui/rss/automatedrssdownloader.ui @@ -177,14 +177,14 @@ - Assign Label: + Assign Category:
    - + - true + false diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index 002ca17e5523..f3d7a0df4d6c 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -352,7 +352,7 @@ void RSSImp::downloadSelectedTorrents() if (article->torrentUrl().isEmpty()) continue; - if (Preferences::instance()->useAdditionDialog()) + if (AddNewTorrentDialog::isEnabled()) AddNewTorrentDialog::show(article->torrentUrl()); else BitTorrent::Session::instance()->addTorrent(article->torrentUrl()); diff --git a/src/gui/scanfoldersdelegate.cpp b/src/gui/scanfoldersdelegate.cpp index 6de8dcce7c5a..0d331ce7e8fc 100644 --- a/src/gui/scanfoldersdelegate.cpp +++ b/src/gui/scanfoldersdelegate.cpp @@ -36,7 +36,7 @@ #include #include "base/scanfoldersmodel.h" -#include "base/preferences.h" +#include "base/bittorrent/session.h" #include "scanfoldersdelegate.h" @@ -102,7 +102,7 @@ void ScanFoldersDelegate::setModelData(QWidget *editor, QAbstractItemModel *mode 0, tr("Choose save path"), index.data(Qt::UserRole).toInt() == ScanFoldersModel::CUSTOM_LOCATION ? index.data().toString() : - Preferences::instance()->getSavePath()), + BitTorrent::Session::instance()->defaultSavePath()), Qt::DisplayRole); break; diff --git a/src/gui/search/searchwidget.cpp b/src/gui/search/searchwidget.cpp index 465ac2d53234..876bfcddb93a 100644 --- a/src/gui/search/searchwidget.cpp +++ b/src/gui/search/searchwidget.cpp @@ -259,7 +259,7 @@ void SearchWidget::saveResultsColumnsWidth() void SearchWidget::downloadTorrent(QString url) { - if (Preferences::instance()->useAdditionDialog()) + if (AddNewTorrentDialog::isEnabled()) AddNewTorrentDialog::show(url, this); else BitTorrent::Session::instance()->addTorrent(url); diff --git a/src/gui/torrentmodel.cpp b/src/gui/torrentmodel.cpp index 451a5a02d020..63439a189ca7 100644 --- a/src/gui/torrentmodel.cpp +++ b/src/gui/torrentmodel.cpp @@ -104,7 +104,7 @@ QVariant TorrentModel::headerData(int section, Qt::Orientation orientation, int case TR_UPSPEED: return tr("Up Speed", "i.e: Upload speed"); case TR_RATIO: return tr("Ratio", "Share ratio"); case TR_ETA: return tr("ETA", "i.e: Estimated Time of Arrival / Time left"); - case TR_LABEL: return tr("Label"); + case TR_CATEGORY: return tr("Category"); case TR_ADD_DATE: return tr("Added On", "Torrent was added to transfer list on 01/01/2010 08:00"); case TR_SEED_DATE: return tr("Completed On", "Torrent was completed on 01/01/2010 08:00"); case TR_TRACKER: return tr("Tracker"); @@ -196,8 +196,8 @@ QVariant TorrentModel::data(const QModelIndex &index, int role) const return torrent->eta(); case TR_RATIO: return torrent->realRatio(); - case TR_LABEL: - return torrent->label(); + case TR_CATEGORY: + return torrent->category(); case TR_ADD_DATE: return torrent->addedTime(); case TR_SEED_DATE: @@ -250,13 +250,13 @@ bool TorrentModel::setData(const QModelIndex &index, const QVariant &value, int BitTorrent::TorrentHandle *const torrent = m_torrents.value(index.row()); if (!torrent) return false; - // Label, seed date and Name columns can be edited + // Category, seed date and Name columns can be edited switch(index.column()) { case TR_NAME: torrent->setName(value.toString()); break; - case TR_LABEL: - torrent->setLabel(value.toString()); + case TR_CATEGORY: + torrent->setCategory(value.toString()); break; default: return false; diff --git a/src/gui/torrentmodel.h b/src/gui/torrentmodel.h index 82eae18e7b33..8bd3b0ecf186 100644 --- a/src/gui/torrentmodel.h +++ b/src/gui/torrentmodel.h @@ -61,7 +61,7 @@ class TorrentModel : public QAbstractListModel TR_UPSPEED, TR_ETA, TR_RATIO, - TR_LABEL, + TR_CATEGORY, TR_ADD_DATE, TR_SEED_DATE, TR_TRACKER, diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 1453d6311062..b3aca3bd192d 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -177,162 +177,156 @@ void StatusFiltersWidget::handleNewTorrent(BitTorrent::TorrentHandle *const) {} void StatusFiltersWidget::torrentAboutToBeDeleted(BitTorrent::TorrentHandle *const) {} -LabelFiltersList::LabelFiltersList(QWidget *parent, TransferListWidget *transferList) +CategoryFiltersList::CategoryFiltersList(QWidget *parent, TransferListWidget *transferList) : FiltersBase(parent, transferList) - , m_totalTorrents(0) - , m_totalLabeled(0) { - connect(BitTorrent::Session::instance(), SIGNAL(torrentLabelChanged(BitTorrent::TorrentHandle *const, QString)), SLOT(torrentChangedLabel(BitTorrent::TorrentHandle *const, QString))); - - // Add Label filters - QListWidgetItem *allLabels = new QListWidgetItem(this); - allLabels->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the label filter"))); - allLabels->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); - QListWidgetItem *noLabel = new QListWidgetItem(this); - noLabel->setData(Qt::DisplayRole, QVariant(tr("Unlabeled (0)"))); - noLabel->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); - - const Preferences* const pref = Preferences::instance(); - QStringList labelList = pref->getTorrentLabels(); - for (int i=0; i < labelList.size(); ++i) - addItem(labelList[i], false); + connect(BitTorrent::Session::instance(), SIGNAL(torrentCategoryChanged(BitTorrent::TorrentHandle *const, QString)), SLOT(torrentCategoryChanged(BitTorrent::TorrentHandle *const, QString))); + connect(BitTorrent::Session::instance(), SIGNAL(categoryAdded(QString)), SLOT(addItem(QString))); + connect(BitTorrent::Session::instance(), SIGNAL(categoryRemoved(QString)), SLOT(categoryRemoved(QString))); + connect(BitTorrent::Session::instance(), SIGNAL(subcategoriesSupportChanged()), SLOT(subcategoriesSupportChanged())); - setCurrentRow(0, QItemSelectionModel::SelectCurrent); - toggleFilter(pref->getLabelFilterState()); + refresh(); + toggleFilter(Preferences::instance()->getCategoryFilterState()); } -LabelFiltersList::~LabelFiltersList() +void CategoryFiltersList::refresh() { - Preferences::instance()->setTorrentLabels(m_labels.keys()); + clear(); + m_categories.clear(); + m_totalTorrents = 0; + m_totalCategorized = 0; + + QListWidgetItem *allCategories = new QListWidgetItem(this); + allCategories->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the category filter"))); + allCategories->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); + QListWidgetItem *noCategory = new QListWidgetItem(this); + noCategory->setData(Qt::DisplayRole, QVariant(tr("Uncategorized (0)"))); + noCategory->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); + + foreach (const QString &category, BitTorrent::Session::instance()->categories()) + addItem(category, false); + + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) + handleNewTorrent(torrent); + + setCurrentRow(0, QItemSelectionModel::SelectCurrent); } -void LabelFiltersList::addItem(QString &label, bool hasTorrent) +void CategoryFiltersList::addItem(const QString &category, bool hasTorrent) { - int labelCount = 0; - QListWidgetItem *labelItem = 0; - label = Utils::Fs::toValidFileSystemName(label.trimmed()); - item(0)->setText(tr("All (%1)", "this is for the label filter").arg(m_totalTorrents)); + if (category.isEmpty()) return; - if (label.isEmpty()) { - item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); - return; - } + int torrentsInCategory = 0; + QListWidgetItem *categoryItem = 0; - bool exists = m_labels.contains(label); + bool exists = m_categories.contains(category); if (exists) { - labelCount = m_labels.value(label); - labelItem = item(rowFromLabel(label)); + torrentsInCategory = m_categories.value(category); + categoryItem = item(rowFromCategory(category)); } else { - labelItem = new QListWidgetItem(); - labelItem->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); + categoryItem = new QListWidgetItem(); + categoryItem->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); } - if (hasTorrent) { - ++m_totalLabeled; - ++labelCount; - } - item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); + if (hasTorrent) + ++torrentsInCategory; - Preferences::instance()->addTorrentLabel(label); - m_labels.insert(label, labelCount); - labelItem->setText(tr("%1 (%2)", "label_name (10)").arg(label).arg(labelCount)); - if (exists) - return; + m_categories.insert(category, torrentsInCategory); + categoryItem->setText(tr("%1 (%2)", "category_name (10)").arg(category).arg(torrentsInCategory)); + if (exists) return; Q_ASSERT(count() >= 2); - for (int i = 2; itext(), less))) - less = (label.localeAwareCompare(item(i)->text()) < 0); + if (!(Utils::String::naturalSort(category, item(i)->text(), less))) + less = (category.localeAwareCompare(item(i)->text()) < 0); if (less) { - insertItem(i, labelItem); + insertItem(i, categoryItem); updateGeometry(); return; } } - QListWidget::addItem(labelItem); + QListWidget::addItem(categoryItem); updateGeometry(); } -void LabelFiltersList::removeItem(const QString &label) +void CategoryFiltersList::removeItem(const QString &category) { - item(0)->setText(tr("All (%1)", "this is for the label filter").arg(m_totalTorrents)); - if (label.isEmpty()) { - // In case we here from torrentAboutToBeDeleted() - item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); - return; - } + if (category.isEmpty()) return; - --m_totalLabeled; - item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); + int torrentsInCategory = m_categories.value(category) - 1; + int row = rowFromCategory(category); + if (row < 2) return; - int labelCount = m_labels.value(label) - 1; - int row = rowFromLabel(label); - if (row < 2) - return; - - QListWidgetItem *labelItem = item(row); - labelItem->setText(tr("%1 (%2)", "label_name (10)").arg(label).arg(labelCount)); - m_labels.insert(label, labelCount); + QListWidgetItem *categoryItem = item(row); + categoryItem->setText(tr("%1 (%2)", "category_name (10)").arg(category).arg(torrentsInCategory)); + m_categories.insert(category, torrentsInCategory); } -void LabelFiltersList::removeSelectedLabel() +void CategoryFiltersList::removeSelectedCategory() { QList items = selectedItems(); if (items.size() == 0) return; - const int labelRow = row(items.first()); - if (labelRow < 2) return; + const int categoryRow = row(items.first()); + if (categoryRow < 2) return; - const QString &label = labelFromRow(labelRow); - Q_ASSERT(m_labels.contains(label)); - m_labels.remove(label); - // Select first label - setCurrentRow(0, QItemSelectionModel::SelectCurrent); - // Un display filter - delete takeItem(labelRow); - transferList->removeLabelFromRows(label); - // Save custom labels to remember it was deleted - Preferences::instance()->removeTorrentLabel(label); + BitTorrent::Session::instance()->removeCategory(categoryFromRow(categoryRow)); updateGeometry(); } -void LabelFiltersList::removeUnusedLabels() +void CategoryFiltersList::removeUnusedCategories() { - QStringList unusedLabels; - QHash::const_iterator i; - for (i = m_labels.begin(); i != m_labels.end(); ++i) { - if (i.value() == 0) - unusedLabels << i.key(); + foreach (const QString &category, m_categories.keys()) + if (m_categories[category] == 0) + BitTorrent::Session::instance()->removeCategory(category); + updateGeometry(); +} + +void CategoryFiltersList::torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory) +{ + qDebug() << "Torrent category changed from" << oldCategory << "to" << torrent->category(); + + if (torrent->category().isEmpty() && !oldCategory.isEmpty()) + --m_totalCategorized; + else if (!torrent->category().isEmpty() && oldCategory.isEmpty()) + ++m_totalCategorized; + + item(1)->setText(tr("Uncategorized (%1)").arg(m_totalTorrents - m_totalCategorized)); + + if (BitTorrent::Session::instance()->isSubcategoriesEnabled()) { + foreach (const QString &subcategory, BitTorrent::Session::expandCategory(oldCategory)) + removeItem(subcategory); + foreach (const QString &subcategory, BitTorrent::Session::expandCategory(torrent->category())) + addItem(subcategory, true); } - foreach (const QString &label, unusedLabels) { - m_labels.remove(label); - delete takeItem(rowFromLabel(label)); - Preferences::instance()->removeTorrentLabel(label); + else { + removeItem(oldCategory); + addItem(torrent->category(), true); } +} - if (!unusedLabels.isEmpty()) - updateGeometry(); +void CategoryFiltersList::categoryRemoved(const QString &category) +{ + m_categories.remove(category); + delete takeItem(rowFromCategory(category)); } -void LabelFiltersList::torrentChangedLabel(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel) +void CategoryFiltersList::subcategoriesSupportChanged() { - qDebug("Torrent label changed from %s to %s", qPrintable(oldLabel), qPrintable(torrent->label())); - removeItem(oldLabel); - QString newLabel = torrent->label(); - addItem(newLabel, true); + refresh(); } -void LabelFiltersList::showMenu(QPoint) +void CategoryFiltersList::showMenu(QPoint) { QMenu menu(this); - QAction *addAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("Add label...")); + QAction *addAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("Add category...")); QAction *removeAct = 0; QAction *removeUnusedAct = 0; if (!selectedItems().empty() && row(selectedItems().first()) > 1) - removeAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove label")); - removeUnusedAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove unused labels")); + removeAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove category")); + removeUnusedAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove unused categories")); menu.addSeparator(); QAction *startAct = menu.addAction(GuiIconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); QAction *pauseAct = menu.addAction(GuiIconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); @@ -343,10 +337,10 @@ void LabelFiltersList::showMenu(QPoint) return; if (act == removeAct) { - removeSelectedLabel(); + removeSelectedCategory(); } else if (act == removeUnusedAct) { - removeUnusedLabels(); + removeUnusedCategories(); } else if (act == deleteTorrentsAct) { transferList->deleteVisibleTorrents(); @@ -359,64 +353,90 @@ void LabelFiltersList::showMenu(QPoint) } else if (act == addAct) { bool ok; - QString label = ""; + QString category = ""; bool invalid; do { invalid = false; - label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, label, &ok); - if (ok && !label.isEmpty()) { - if (Utils::Fs::isValidFileSystemName(label)) { - addItem(label, false); + category = AutoExpandableDialog::getText(this, tr("New Category"), tr("Category:"), QLineEdit::Normal, category, &ok); + if (ok && !category.isEmpty()) { + if (!BitTorrent::Session::isValidCategoryName(category)) { + QMessageBox::warning(this, tr("Invalid category name"), + tr("Category name must not contain '\\'.\n" + "Category name must not start/end with '/'.\n" + "Category name must not contain '//' sequence.")); + invalid = true; } else { - QMessageBox::warning(this, tr("Invalid label name"), tr("Please don't use any special characters in the label name.")); - invalid = true; + BitTorrent::Session::instance()->addCategory(category); } } } while (invalid); } } -void LabelFiltersList::applyFilter(int row) +void CategoryFiltersList::applyFilter(int row) { - transferList->applyLabelFilter(labelFromRow(row)); + if (row >= 0) + transferList->applyCategoryFilter(categoryFromRow(row)); } -void LabelFiltersList::handleNewTorrent(BitTorrent::TorrentHandle *const torrent) +void CategoryFiltersList::handleNewTorrent(BitTorrent::TorrentHandle *const torrent) { Q_ASSERT(torrent); + ++m_totalTorrents; - QString label = torrent->label(); - addItem(label, true); - // FIXME: Drop this confusion. - // labelFilters->addItem() may have changed the label, update the model accordingly. - torrent->setLabel(label); + if (!torrent->category().isEmpty()) + ++m_totalCategorized; + + item(0)->setText(tr("All (%1)", "this is for the category filter").arg(m_totalTorrents)); + item(1)->setText(tr("Uncategorized (%1)").arg(m_totalTorrents - m_totalCategorized)); + + if (BitTorrent::Session::instance()->isSubcategoriesEnabled()) { + foreach (const QString &subcategory, BitTorrent::Session::expandCategory(torrent->category())) + addItem(subcategory, true); + } + else { + addItem(torrent->category(), true); + } } -void LabelFiltersList::torrentAboutToBeDeleted(BitTorrent::TorrentHandle *const torrent) +void CategoryFiltersList::torrentAboutToBeDeleted(BitTorrent::TorrentHandle *const torrent) { Q_ASSERT(torrent); + --m_totalTorrents; - removeItem(torrent->label()); + if (!torrent->category().isEmpty()) + --m_totalCategorized; + + item(0)->setText(tr("All (%1)", "this is for the category filter").arg(m_totalTorrents)); + item(1)->setText(tr("Uncategorized (%1)").arg(m_totalTorrents - m_totalCategorized)); + + if (BitTorrent::Session::instance()->isSubcategoriesEnabled()) { + foreach (const QString &subcategory, BitTorrent::Session::expandCategory(torrent->category())) + removeItem(subcategory); + } + else { + removeItem(torrent->category()); + } } -QString LabelFiltersList::labelFromRow(int row) const +QString CategoryFiltersList::categoryFromRow(int row) const { if (row == 0) return QString(); // All - if (row == 1) return QLatin1String(""); // Unlabeled + if (row == 1) return QLatin1String(""); // Uncategorized - const QString &label = item(row)->text(); - QStringList parts = label.split(" "); + const QString &category = item(row)->text(); + QStringList parts = category.split(" "); Q_ASSERT(parts.size() >= 2); parts.removeLast(); // Remove trailing number return parts.join(" "); } -int LabelFiltersList::rowFromLabel(const QString &label) const +int CategoryFiltersList::rowFromCategory(const QString &category) const { - Q_ASSERT(!label.isEmpty()); + Q_ASSERT(!category.isEmpty()); for (int i = 2; isetData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the label filter"))); + allTrackers->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the tracker filter"))); allTrackers->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("network-server")); QListWidgetItem *noTracker = new QListWidgetItem(this); noTracker->setData(Qt::DisplayRole, QVariant(tr("Trackerless (0)"))); @@ -799,13 +819,13 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi StatusFiltersWidget *statusFilters = new StatusFiltersWidget(this, transferList); frameLayout->addWidget(statusFilters); - QCheckBox *labelLabel = new QCheckBox(tr("Labels"), this); - labelLabel->setChecked(pref->getLabelFilterState()); - labelLabel->setFont(font); - frameLayout->addWidget(labelLabel); + QCheckBox *categoryLabel = new QCheckBox(tr("Categories"), this); + categoryLabel->setChecked(pref->getCategoryFilterState()); + categoryLabel->setFont(font); + frameLayout->addWidget(categoryLabel); - LabelFiltersList *labelFilters = new LabelFiltersList(this, transferList); - frameLayout->addWidget(labelFilters); + CategoryFiltersList *categoryFilters = new CategoryFiltersList(this, transferList); + frameLayout->addWidget(categoryFilters); QCheckBox *trackerLabel = new QCheckBox(tr("Trackers"), this); trackerLabel->setChecked(pref->getTrackerFilterState()); @@ -817,9 +837,8 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi connect(statusLabel, SIGNAL(toggled(bool)), statusFilters, SLOT(toggleFilter(bool))); connect(statusLabel, SIGNAL(toggled(bool)), pref, SLOT(setStatusFilterState(const bool))); - connect(labelLabel, SIGNAL(toggled(bool)), labelFilters, SLOT(toggleFilter(bool))); - connect(labelLabel, SIGNAL(toggled(bool)), pref, SLOT(setLabelFilterState(const bool))); - connect(pref, SIGNAL(externalLabelAdded(QString&)), labelFilters, SLOT(addItem(QString&))); + connect(categoryLabel, SIGNAL(toggled(bool)), categoryFilters, SLOT(toggleFilter(bool))); + connect(categoryLabel, SIGNAL(toggled(bool)), pref, SLOT(setCategoryFilterState(const bool))); connect(trackerLabel, SIGNAL(toggled(bool)), trackerFilters, SLOT(toggleFilter(bool))); connect(trackerLabel, SIGNAL(toggled(bool)), pref, SLOT(setTrackerFilterState(const bool))); connect(this, SIGNAL(trackerSuccess(const QString &, const QString &)), trackerFilters, SLOT(trackerSuccess(const QString &, const QString &))); diff --git a/src/gui/transferlistfilterswidget.h b/src/gui/transferlistfilterswidget.h index 28336f77a2b7..3a78c9ff749d 100644 --- a/src/gui/transferlistfilterswidget.h +++ b/src/gui/transferlistfilterswidget.h @@ -90,22 +90,22 @@ private slots: virtual void torrentAboutToBeDeleted(BitTorrent::TorrentHandle *const); }; -class LabelFiltersList: public FiltersBase +class CategoryFiltersList: public FiltersBase { Q_OBJECT public: - LabelFiltersList(QWidget *parent, TransferListWidget *transferList); - ~LabelFiltersList(); + CategoryFiltersList(QWidget *parent, TransferListWidget *transferList); private slots: // Redefine addItem() to make sure the list stays sorted - void addItem(QString &label, bool hasTorrent = false); - void removeItem(const QString &label); - void removeSelectedLabel(); - void removeUnusedLabels(); - void torrentChangedLabel(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel); - + void addItem(const QString &category, bool hasTorrent = false); + void removeItem(const QString &category); + void removeSelectedCategory(); + void removeUnusedCategories(); + void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory); + void categoryRemoved(const QString &category); + void subcategoriesSupportChanged(); private: // These 4 methods are virtual slots in the base class. @@ -114,13 +114,14 @@ private slots: virtual void applyFilter(int row); virtual void handleNewTorrent(BitTorrent::TorrentHandle *const torrent); virtual void torrentAboutToBeDeleted(BitTorrent::TorrentHandle *const torrent); - QString labelFromRow(int row) const; - int rowFromLabel(const QString &label) const; + QString categoryFromRow(int row) const; + int rowFromCategory(const QString &category) const; + void refresh(); private: - QHash m_labels; + QHash m_categories; int m_totalTorrents; - int m_totalLabeled; + int m_totalCategorized; }; class TrackerFiltersList: public FiltersBase diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index 4e7f97c922ae..c69253666887 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -47,15 +47,15 @@ void TransferListSortModel::setStatusFilter(TorrentFilter::Type filter) invalidateFilter(); } -void TransferListSortModel::setLabelFilter(const QString &label) +void TransferListSortModel::setCategoryFilter(const QString &category) { - if (m_filter.setLabel(label)) + if (m_filter.setCategory(category)) invalidateFilter(); } -void TransferListSortModel::disableLabelFilter() +void TransferListSortModel::disableCategoryFilter() { - if (m_filter.setLabel(TorrentFilter::AnyLabel)) + if (m_filter.setCategory(TorrentFilter::AnyCategory)) invalidateFilter(); } diff --git a/src/gui/transferlistsortmodel.h b/src/gui/transferlistsortmodel.h index ded73a7ab750..f5f13d597a5d 100644 --- a/src/gui/transferlistsortmodel.h +++ b/src/gui/transferlistsortmodel.h @@ -44,8 +44,8 @@ class TransferListSortModel: public QSortFilterProxyModel TransferListSortModel(QObject *parent = 0); void setStatusFilter(TorrentFilter::Type filter); - void setLabelFilter(const QString &label); - void disableLabelFilter(); + void setCategoryFilter(const QString &category); + void disableCategoryFilter(); void setTrackerFilter(const QStringList &hashes); void disableTrackerFilter(); diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 5344b78bb24f..db6c1a9f9b6b 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -60,6 +60,7 @@ #include "propertieswidget.h" #include "guiiconprovider.h" #include "base/utils/fs.h" +#include "base/utils/string.h" #include "autoexpandabledialog.h" #include "transferlistsortmodel.h" @@ -565,21 +566,30 @@ void TransferListWidget::toggleSelectedFirstLastPiecePrio() const torrent->toggleFirstLastPiecePriority(); } -void TransferListWidget::askNewLabelForSelection() +void TransferListWidget::setSelectedASMEnabled(bool enabled) const { - // Ask for label + foreach (BitTorrent::TorrentHandle *const torrent, getSelectedTorrents()) + torrent->setASMEnabled(enabled); +} + +void TransferListWidget::askNewCategoryForSelection() +{ + // Ask for category bool ok; bool invalid; do { invalid = false; - const QString label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, "", &ok).trimmed(); - if (ok && !label.isEmpty()) { - if (Utils::Fs::isValidFileSystemName(label)) { - setSelectionLabel(label); + const QString category = AutoExpandableDialog::getText(this, tr("New Category"), tr("Category:"), QLineEdit::Normal, "", &ok).trimmed(); + if (ok && !category.isEmpty()) { + if (!BitTorrent::Session::isValidCategoryName(category)) { + QMessageBox::warning(this, tr("Invalid category name"), + tr("Category name must not contain '\\'.\n" + "Category name must not start/end with '/'.\n" + "Category name must not contain '//' sequence.")); + invalid = true; } else { - QMessageBox::warning(this, tr("Invalid label name"), tr("Please don't use any special characters in the label name.")); - invalid = true; + setSelectionCategory(category); } } } while(invalid); @@ -605,19 +615,10 @@ void TransferListWidget::renameSelectedTorrent() } } -void TransferListWidget::setSelectionLabel(QString label) +void TransferListWidget::setSelectionCategory(QString category) { foreach (const QModelIndex &index, selectionModel()->selectedRows()) - listModel->setData(listModel->index(mapToSource(index).row(), TorrentModel::TR_LABEL), label, Qt::DisplayRole); -} - -void TransferListWidget::removeLabelFromRows(QString label) -{ - for (int i = 0; i < listModel->rowCount(); ++i) { - if (listModel->data(listModel->index(i, TorrentModel::TR_LABEL)) == label) { - listModel->setData(listModel->index(i, TorrentModel::TR_LABEL), "", Qt::DisplayRole); - } - } + listModel->setData(listModel->index(mapToSource(index).row(), TorrentModel::TR_CATEGORY), category, Qt::DisplayRole); } void TransferListWidget::displayListMenu(const QPoint&) @@ -671,6 +672,9 @@ void TransferListWidget::displayListMenu(const QPoint&) QAction actionFirstLastPiece_prio(tr("Download first and last pieces first"), 0); actionFirstLastPiece_prio.setCheckable(true); connect(&actionFirstLastPiece_prio, SIGNAL(triggered()), this, SLOT(toggleSelectedFirstLastPiecePrio())); + QAction actionEnableASM(tr("Enable Advanced Saving Management"), 0); + actionEnableASM.setCheckable(true); + connect(&actionEnableASM, SIGNAL(triggered(bool)), this, SLOT(setSelectedASMEnabled(bool))); // End of actions // Enable/disable pause/start action given the DL state @@ -680,8 +684,10 @@ void TransferListWidget::displayListMenu(const QPoint&) bool all_same_sequential_download_mode = true, all_same_prio_firstlast = true; bool sequential_download_mode = false, prioritize_first_last = false; bool one_has_metadata = false, one_not_seed = false; - bool all_same_label = true; - QString first_label; + bool allSameCategory = true; + bool allSameASM = true; + bool firstASM = false; + QString firstCategory; bool first = true; BitTorrent::TorrentHandle *torrent; @@ -692,10 +698,15 @@ void TransferListWidget::displayListMenu(const QPoint&) torrent = listModel->torrentHandle(mapToSource(index)); if (!torrent) continue; - if (first_label.isEmpty() && first) - first_label = torrent->label(); + if (firstCategory.isEmpty() && first) + firstCategory = torrent->category(); + if (firstCategory != torrent->category()) + allSameCategory = false; - all_same_label = (first_label == torrent->label()); + if (first) + firstASM = torrent->isASMEnabled(); + if (firstASM != torrent->isASMEnabled()) + allSameASM = false; if (torrent->hasMetadata()) one_has_metadata = true; @@ -738,8 +749,8 @@ void TransferListWidget::displayListMenu(const QPoint&) first = false; if (one_has_metadata && one_not_seed && !all_same_sequential_download_mode - && !all_same_prio_firstlast && !all_same_super_seeding && !all_same_label - && needs_start && needs_force && needs_pause && needs_preview) { + && !all_same_prio_firstlast && !all_same_super_seeding && !allSameCategory + && needs_start && needs_force && needs_pause && needs_preview && !allSameASM) { break; } } @@ -756,24 +767,30 @@ void TransferListWidget::displayListMenu(const QPoint&) listMenu.addAction(&actionSetTorrentPath); if (selectedIndexes.size() == 1) listMenu.addAction(&actionRename); - // Label Menu - QStringList customLabels = Preferences::instance()->getTorrentLabels(); - customLabels.sort(); - QList labelActions; - QMenu *labelMenu = listMenu.addMenu(GuiIconProvider::instance()->getIcon("view-categories"), tr("Label")); - labelActions << labelMenu->addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("New...", "New label...")); - labelActions << labelMenu->addAction(GuiIconProvider::instance()->getIcon("edit-clear"), tr("Reset", "Reset label")); - labelMenu->addSeparator(); - foreach (QString label, customLabels) { - label.replace('&', "&&"); // avoid '&' becomes accelerator key - QAction *lb = new QAction(GuiIconProvider::instance()->getIcon("inode-directory"), label, labelMenu); - if (all_same_label && (label == first_label)) { - lb->setCheckable(true); - lb->setChecked(true); + // Category Menu + QStringList categories = BitTorrent::Session::instance()->categories(); + std::sort(categories.begin(), categories.end(), Utils::String::NaturalCompare()); + QList categoryActions; + QMenu *categoryMenu = listMenu.addMenu(GuiIconProvider::instance()->getIcon("view-categories"), tr("Category")); + categoryActions << categoryMenu->addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("New...", "New category...")); + categoryActions << categoryMenu->addAction(GuiIconProvider::instance()->getIcon("edit-clear"), tr("Reset", "Reset category")); + categoryMenu->addSeparator(); + foreach (QString category, categories) { + category.replace('&', "&&"); // avoid '&' becomes accelerator key + QAction *cat = new QAction(GuiIconProvider::instance()->getIcon("inode-directory"), category, categoryMenu); + if (allSameCategory && (category == firstCategory)) { + cat->setCheckable(true); + cat->setChecked(true); } - labelMenu->addAction(lb); - labelActions << lb; + categoryMenu->addAction(cat); + categoryActions << cat; } + + if (allSameASM) { + actionEnableASM.setChecked(firstASM); + listMenu.addAction(&actionEnableASM); + } + listMenu.addSeparator(); if (one_not_seed) listMenu.addAction(&actionSet_download_limit); @@ -801,6 +818,7 @@ void TransferListWidget::displayListMenu(const QPoint&) added_preview_action = true; } } + if (added_preview_action) listMenu.addSeparator(); if (one_has_metadata) { @@ -823,20 +841,20 @@ void TransferListWidget::displayListMenu(const QPoint&) QAction *act = 0; act = listMenu.exec(QCursor::pos()); if (act) { - // Parse label actions only (others have slots assigned) - int i = labelActions.indexOf(act); + // Parse category actions only (others have slots assigned) + int i = categoryActions.indexOf(act); if (i >= 0) { - // Label action + // Category action if (i == 0) { - // New Label - askNewLabelForSelection(); + // New Category + askNewCategoryForSelection(); } else { - QString label = ""; + QString category = ""; if (i > 1) - label = customLabels.at(i - 2); - // Update Label - setSelectionLabel(label); + category = categories.at(i - 2); + // Update Category + setSelectionCategory(category); } } } @@ -854,12 +872,12 @@ void TransferListWidget::currentChanged(const QModelIndex& current, const QModel emit currentTorrentChanged(torrent); } -void TransferListWidget::applyLabelFilter(QString label) +void TransferListWidget::applyCategoryFilter(QString category) { - if (label.isNull()) - nameFilterModel->disableLabelFilter(); + if (category.isNull()) + nameFilterModel->disableCategoryFilter(); else - nameFilterModel->setLabelFilter(label); + nameFilterModel->setCategoryFilter(category); } void TransferListWidget::applyTrackerFilterAll() diff --git a/src/gui/transferlistwidget.h b/src/gui/transferlistwidget.h index c6f7136eafca..ac0e167c1631 100644 --- a/src/gui/transferlistwidget.h +++ b/src/gui/transferlistwidget.h @@ -59,7 +59,7 @@ class TransferListWidget: public QTreeView TorrentModel* getSourceModel() const; public slots: - void setSelectionLabel(QString label); + void setSelectionCategory(QString category); void setSelectedTorrentsLocation(); void pauseAllTorrents(); void resumeAllTorrents(); @@ -86,11 +86,10 @@ public slots: void displayDLHoSMenu(const QPoint&); void applyNameFilter(const QString& name); void applyStatusFilter(int f); - void applyLabelFilter(QString label); + void applyCategoryFilter(QString category); void applyTrackerFilterAll(); void applyTrackerFilter(const QStringList &hashes); void previewFile(QString filePath); - void removeLabelFromRows(QString label); void renameSelectedTorrent(); protected: @@ -106,7 +105,8 @@ protected slots: void toggleSelectedTorrentsSuperSeeding() const; void toggleSelectedTorrentsSequentialDownload() const; void toggleSelectedFirstLastPiecePrio() const; - void askNewLabelForSelection(); + void setSelectedASMEnabled(bool enabled) const; + void askNewCategoryForSelection(); void saveSettings(); signals: diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index e9d88d7549c8..7e52c134b024 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -243,7 +243,7 @@ void AbstractWebApplication::translateDocument(QString& data) "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel", "PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc", "StatusBar", "AboutDlg", "about", "PeerListWidget", "StatusFiltersWidget", - "LabelFiltersList" + "CategoryFiltersList" }; const size_t context_count = sizeof(contexts) / sizeof(contexts[0]); int i = 0; diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index fd0380e08a28..e6ad4eb74daa 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -102,7 +102,7 @@ static const char KEY_TORRENT_ETA[] = "eta"; static const char KEY_TORRENT_STATE[] = "state"; static const char KEY_TORRENT_SEQUENTIAL_DOWNLOAD[] = "seq_dl"; static const char KEY_TORRENT_FIRST_LAST_PIECE_PRIO[] = "f_l_piece_prio"; -static const char KEY_TORRENT_LABEL[] = "label"; +static const char KEY_TORRENT_CATEGORY[] = "category"; static const char KEY_TORRENT_SUPER_SEEDING[] = "super_seeding"; static const char KEY_TORRENT_FORCE_START[] = "force_start"; static const char KEY_TORRENT_SAVE_PATH[] = "save_path"; @@ -279,13 +279,13 @@ class QTorrentCompare * - "seq_dl": Torrent sequential download state * - "f_l_piece_prio": Torrent first last piece priority state * - "force_start": Torrent force start state - * - "label": Torrent label + * - "category": Torrent category */ -QByteArray btjson::getTorrents(QString filter, QString label, +QByteArray btjson::getTorrents(QString filter, QString category, QString sortedColumn, bool reverse, int limit, int offset) { QVariantList torrentList; - TorrentFilter torrentFilter(filter, TorrentFilter::AnyHash, label); + TorrentFilter torrentFilter(filter, TorrentFilter::AnyHash, category); foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) { if (torrentFilter.match(torrent)) torrentList.append(toMap(torrent)); @@ -317,8 +317,8 @@ QByteArray btjson::getTorrents(QString filter, QString label, * - "full_update": full data update flag * - "torrents": dictionary contains information about torrents. * - "torrents_removed": a list of hashes of removed torrents - * - "labels": list of labels - * - "labels_removed": list of removed labels + * - "categories": list of categories + * - "categories_removed": list of removed categories * - "server_state": map contains information about the state of the server * The keys of the 'torrents' dictionary are hashes of torrents. * Each value of the 'torrents' dictionary contains map. The map can contain following keys: @@ -362,11 +362,11 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData data["torrents"] = torrents; - QVariantList labels; - foreach (QString s, Preferences::instance()->getTorrentLabels()) - labels << s; + QVariantList categories; + foreach (const QString &category, BitTorrent::Session::instance()->categories()) + categories << category; - data["labels"] = labels; + data["categories"] = categories; QVariantMap serverState = getTranserInfoMap(); serverState[KEY_SYNC_MAINDATA_QUEUEING] = BitTorrent::Session::instance()->isQueueingEnabled(); @@ -705,7 +705,7 @@ QVariantMap toMap(BitTorrent::TorrentHandle *const torrent) ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = torrent->isSequentialDownload(); if (torrent->hasMetadata()) ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = torrent->hasFirstLastPiecePriority(); - ret[KEY_TORRENT_LABEL] = torrent->label(); + ret[KEY_TORRENT_CATEGORY] = torrent->category(); ret[KEY_TORRENT_SUPER_SEEDING] = torrent->superSeeding(); ret[KEY_TORRENT_FORCE_START] = torrent->isForced(); ret[KEY_TORRENT_SAVE_PATH] = Utils::Fs::toNativePath(torrent->savePath()); @@ -760,6 +760,7 @@ void processMap(QVariantMap prevData, QVariantMap data, QVariantMap &syncData) case QMetaType::Double: case QMetaType::ULongLong: case QMetaType::UInt: + case QMetaType::QDateTime: if (prevData[key] != data[key]) syncData[key] = data[key]; break; diff --git a/src/webui/btjson.h b/src/webui/btjson.h index bada11240aa6..f58e8d032692 100644 --- a/src/webui/btjson.h +++ b/src/webui/btjson.h @@ -43,7 +43,7 @@ class btjson btjson() {} public: - static QByteArray getTorrents(QString filter = "all", QString label = QString(), + static QByteArray getTorrents(QString filter = "all", QString category = QString(), QString sortedColumn = "name", bool reverse = false, int limit = 0, int offset = 0); static QByteArray getSyncMainData(int acceptedResponseId, QVariantMap &lastData, QVariantMap &lastAcceptedData); static QByteArray getSyncTorrentPeersData(int acceptedResponseId, QString hash, QVariantMap &lastData, QVariantMap &lastAcceptedData); diff --git a/src/webui/extra_translations.h b/src/webui/extra_translations.h index 46099c5b9c32..e49389319b47 100644 --- a/src/webui/extra_translations.h +++ b/src/webui/extra_translations.h @@ -84,7 +84,7 @@ static const char *__TRANSLATIONS__[] = { QT_TRANSLATE_NOOP("HttpServer", "Active"), QT_TRANSLATE_NOOP("HttpServer", "Inactive"), QT_TRANSLATE_NOOP("HttpServer", "Save files to location:"), - QT_TRANSLATE_NOOP("HttpServer", "Label:"), + QT_TRANSLATE_NOOP("HttpServer", "Category:"), QT_TRANSLATE_NOOP("HttpServer", "Cookie:"), QT_TRANSLATE_NOOP("HttpServer", "Type folder here"), QT_TRANSLATE_NOOP("HttpServer", "Run an external program on torrent completion"), diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index 133116452540..26d430651cfc 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -28,11 +28,6 @@ * Contact : chris@qbittorrent.org */ -#include "prefjson.h" -#include "base/preferences.h" -#include "base/scanfoldersmodel.h" -#include "base/utils/fs.h" - #ifndef QT_NO_OPENSSL #include #include @@ -40,7 +35,13 @@ #include #include #include + +#include "base/preferences.h" +#include "base/scanfoldersmodel.h" +#include "base/utils/fs.h" +#include "base/bittorrent/session.h" #include "jsonutils.h" +#include "prefjson.h" prefjson::prefjson() { @@ -49,13 +50,14 @@ prefjson::prefjson() QByteArray prefjson::getPreferences() { const Preferences* const pref = Preferences::instance(); + auto session = BitTorrent::Session::instance(); QVariantMap data; // Downloads // Hard Disk - data["save_path"] = Utils::Fs::toNativePath(pref->getSavePath()); - data["temp_path_enabled"] = pref->isTempPathEnabled(); - data["temp_path"] = Utils::Fs::toNativePath(pref->getTempPath()); + data["save_path"] = Utils::Fs::toNativePath(session->defaultSavePath()); + data["temp_path_enabled"] = session->isTempPathEnabled(); + data["temp_path"] = Utils::Fs::toNativePath(session->tempPath()); data["preallocate_all"] = pref->preAllocateAllFiles(); data["incomplete_files_ext"] = pref->useIncompleteFilesExtension(); QVariantHash dirs = pref->getScanDirs(); @@ -140,7 +142,7 @@ QByteArray prefjson::getPreferences() // Share Ratio Limiting data["max_ratio_enabled"] = (pref->getGlobalMaxRatio() >= 0.); data["max_ratio"] = pref->getGlobalMaxRatio(); - data["max_ratio_act"] = static_cast(pref->getMaxRatioAction()); + data["max_ratio_act"] = BitTorrent::Session::instance()->maxRatioAction(); // Web UI // Language @@ -168,16 +170,17 @@ QByteArray prefjson::getPreferences() void prefjson::setPreferences(const QString& json) { Preferences* const pref = Preferences::instance(); + auto session = BitTorrent::Session::instance(); const QVariantMap m = json::fromJson(json).toMap(); // Downloads // Hard Disk if (m.contains("save_path")) - pref->setSavePath(m["save_path"].toString()); + session->setDefaultSavePath(m["save_path"].toString()); if (m.contains("temp_path_enabled")) - pref->setTempPathEnabled(m["temp_path_enabled"].toBool()); + session->setTempPathEnabled(m["temp_path_enabled"].toBool()); if (m.contains("temp_path")) - pref->setTempPath(m["temp_path"].toString()); + session->setTempPath(m["temp_path"].toString()); if (m.contains("preallocate_all")) pref->preAllocateAllFiles(m["preallocate_all"].toBool()); if (m.contains("incomplete_files_ext")) @@ -351,7 +354,8 @@ void prefjson::setPreferences(const QString& json) else pref->setGlobalMaxRatio(-1); if (m.contains("max_ratio_act")) - pref->setMaxRatioAction(static_cast(m["max_ratio_act"].toInt())); + BitTorrent::Session::instance()->setMaxRatioAction( + static_cast(m["max_ratio_act"].toInt())); // Web UI // Language diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 2cfab6f2adc3..f95e387e2684 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -49,8 +49,8 @@ #include "websessiondata.h" #include "webapplication.h" -static const int API_VERSION = 8; -static const int API_VERSION_MIN = 8; +static const int API_VERSION = 9; +static const int API_VERSION_MIN = 9; const QString WWW_FOLDER = ":/www/public/"; const QString PRIVATE_FOLDER = ":/www/private/"; @@ -114,7 +114,7 @@ QMap > WebApplication::initialize ADD_ACTION(command, topPrio); ADD_ACTION(command, bottomPrio); ADD_ACTION(command, recheck); - ADD_ACTION(command, setLabel); + ADD_ACTION(command, setCategory); ADD_ACTION(command, getSavePath); ADD_ACTION(version, api); ADD_ACTION(version, api_min); @@ -216,7 +216,7 @@ void WebApplication::action_public_images() // GET params: // - filter (string): all, downloading, seeding, completed, paused, resumed, active, inactive -// - label (string): torrent label for filtering by it (empty string means "unlabeled"; no "label" param presented means "any label") +// - category (string): torrent category for filtering by it (empty string means "uncategorized"; no "category" param presented means "any category") // - sort (string): name of column for sorting by its value // - reverse (bool): enable reverse sorting // - limit (int): set limit number of torrents returned (if greater than 0, otherwise - unlimited) @@ -227,7 +227,7 @@ void WebApplication::action_query_torrents() const QStringMap& gets = request().gets; print(btjson::getTorrents( - gets["filter"], gets["label"], gets["sort"], gets["reverse"] == "true", + gets["filter"], gets["category"], gets["sort"], gets["reverse"] == "true", gets["limit"].toInt(), gets["offset"].toInt() ), Http::CONTENT_TYPE_JSON); } @@ -326,7 +326,7 @@ void WebApplication::action_command_download() QString urls = request().posts["urls"]; QStringList list = urls.split('\n'); QString savepath = request().posts["savepath"]; - QString label = request().posts["label"]; + QString category = request().posts["category"]; QString cookie = request().posts["cookie"]; QList cookies; if (!cookie.isEmpty()) { @@ -345,11 +345,11 @@ void WebApplication::action_command_download() } savepath = savepath.trimmed(); - label = label.trimmed(); + category = category.trimmed(); BitTorrent::AddTorrentParams params; params.savePath = savepath; - params.label = label; + params.category = category; foreach (QString url, list) { url = url.trimmed(); @@ -365,10 +365,10 @@ void WebApplication::action_command_upload() qDebug() << Q_FUNC_INFO; CHECK_URI(0); QString savepath = request().posts["savepath"]; - QString label = request().posts["label"]; + QString category = request().posts["category"]; savepath = savepath.trimmed(); - label = label.trimmed(); + category = category.trimmed(); foreach(const Http::UploadedFile& torrent, request().files) { QString filePath = saveTmpFile(torrent.data); @@ -382,7 +382,7 @@ void WebApplication::action_command_upload() else { BitTorrent::AddTorrentParams params; params.savePath = savepath; - params.label = label; + params.category = category; if (!BitTorrent::Session::instance()->addTorrent(torrentInfo, params)) { status(500, "Internal Server Error"); print(QObject::tr("Error: Could not add torrent to session."), Http::CONTENT_TYPE_TXT); @@ -709,29 +709,29 @@ void WebApplication::action_command_recheck() torrent->forceRecheck(); } -void WebApplication::action_command_setLabel() +void WebApplication::action_command_setCategory() { CHECK_URI(0); - CHECK_PARAMETERS("hashes" << "label"); + CHECK_PARAMETERS("hashes" << "category"); QStringList hashes = request().posts["hashes"].split("|"); - QString label = request().posts["label"].trimmed(); - if (!Utils::Fs::isValidFileSystemName(label) && !label.isEmpty()) { - status(400, "Labels must not contain special characters"); - return; - } + QString category = request().posts["category"].trimmed(); foreach (const QString &hash, hashes) { BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); - if (torrent) - torrent->setLabel(label); + if (torrent) { + if (!torrent->setCategory(category)) { + status(400, "Incorrect category name"); + return; + } + } } } void WebApplication::action_command_getSavePath() { CHECK_URI(0); - print(Preferences::instance()->getSavePath()); + print(BitTorrent::Session::instance()->defaultSavePath()); } bool WebApplication::isPublicScope() diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 8cf8fac8e8e8..a87acc8f884d 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -87,7 +87,7 @@ class WebApplication: public AbstractWebApplication void action_command_topPrio(); void action_command_bottomPrio(); void action_command_recheck(); - void action_command_setLabel(); + void action_command_setCategory(); void action_command_getSavePath(); void action_version_api(); void action_version_api_min(); diff --git a/src/webui/webui.qrc b/src/webui/webui.qrc index 407b6b05f953..011bb5d72f7b 100644 --- a/src/webui/webui.qrc +++ b/src/webui/webui.qrc @@ -1,43 +1,43 @@ - - www/private/index.html - www/private/login.html - www/public/css/Core.css - www/public/css/dynamicTable.css - www/public/css/Layout.css - www/public/css/style.css - www/public/css/Tabs.css - www/public/css/Window.css - www/public/scripts/client.js - www/public/scripts/contextmenu.js - www/public/scripts/download.js - www/public/scripts/dynamicTable.js - www/public/scripts/excanvas-compressed.js - www/public/scripts/misc.js - www/public/scripts/mocha.js - www/public/scripts/mocha-init.js - www/public/scripts/mocha-yc.js - www/public/scripts/mootools-1.2-core-yc.js - www/public/scripts/mootools-1.2-more.js - www/public/scripts/parametrics.js - www/public/scripts/progressbar.js - www/public/about.html - www/public/addtrackers.html - www/public/confirmdeletion.html - www/public/download.html - www/public/downloadlimit.html - www/public/filters.html - www/public/newlabel.html - www/public/preferences.html - www/public/preferences_content.html - www/public/properties.html - www/public/properties_content.html - www/public/scripts/prop-general.js - www/public/scripts/prop-trackers.js - www/public/scripts/prop-webseeds.js - www/public/scripts/prop-files.js - www/public/transferlist.html - www/public/upload.html - www/public/uploadlimit.html - + + www/private/index.html + www/private/login.html + www/public/css/Core.css + www/public/css/dynamicTable.css + www/public/css/Layout.css + www/public/css/style.css + www/public/css/Tabs.css + www/public/css/Window.css + www/public/scripts/client.js + www/public/scripts/contextmenu.js + www/public/scripts/download.js + www/public/scripts/dynamicTable.js + www/public/scripts/excanvas-compressed.js + www/public/scripts/misc.js + www/public/scripts/mocha.js + www/public/scripts/mocha-init.js + www/public/scripts/mocha-yc.js + www/public/scripts/mootools-1.2-core-yc.js + www/public/scripts/mootools-1.2-more.js + www/public/scripts/parametrics.js + www/public/scripts/progressbar.js + www/public/about.html + www/public/addtrackers.html + www/public/confirmdeletion.html + www/public/download.html + www/public/downloadlimit.html + www/public/filters.html + www/public/preferences.html + www/public/preferences_content.html + www/public/properties.html + www/public/properties_content.html + www/public/scripts/prop-general.js + www/public/scripts/prop-trackers.js + www/public/scripts/prop-webseeds.js + www/public/scripts/prop-files.js + www/public/transferlist.html + www/public/upload.html + www/public/uploadlimit.html + www/public/newcategory.html + diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 37794c4e73d5..95262b82527e 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -106,8 +106,8 @@

    qBittorrent Web User Interface QBT_TR(Force Resume)QBT_TR QBT_TR(Force Resume)QBT_TR
  • QBT_TR(Delete)QBT_TR QBT_TR(Delete)QBT_TR
  • - QBT_TR(Label)QBT_TR QBT_TR(Label)QBT_TR -
      + QBT_TR(Category)QBT_TR QBT_TR(Category)QBT_TR +
      • QBT_TR(Priority)QBT_TR diff --git a/src/webui/www/public/download.html b/src/webui/www/public/download.html index 0a2cce4ae3ab..a5c612e02ad0 100644 --- a/src/webui/www/public/download.html +++ b/src/webui/www/public/download.html @@ -1,33 +1,33 @@ - - QBT_TR(Add Torrent Link)QBT_TR - + + QBT_TR(Add Torrent Link)QBT_TR + - - + +
        -
        -

        QBT_TR(Download Torrents from their URLs or Magnet links)QBT_TR

        - +
        +

        QBT_TR(Download Torrents from their URLs or Magnet links)QBT_TR

        +

        QBT_TR(Only one link per line)QBT_TR

        - - + +
        - - + +
        - - + +
        @@ -39,13 +39,13 @@

        QBT_TR(Download Torrents from their URLs or Magnet links)QBT var submitted = false; $('downloadForm').addEventListener("submit", function() { - $('download_spinner').style.display = "block"; + $('download_spinner').style.display = "block"; submitted = true; }); $('download_frame').addEventListener("load", function() { - if (submitted) - window.parent.closeWindows(); + if (submitted) + window.parent.closeWindows(); });
        diff --git a/src/webui/www/public/filters.html b/src/webui/www/public/filters.html index 60403f855bcd..367ddd2a77d7 100644 --- a/src/webui/www/public/filters.html +++ b/src/webui/www/public/filters.html @@ -11,6 +11,6 @@
      • QBT_TR(Errored (0))QBT_TR

      • -QBT_TR(Labels)QBT_TR -
          +QBT_TR(Categories)QBT_TR +
          diff --git a/src/webui/www/public/newlabel.html b/src/webui/www/public/newcategory.html similarity index 62% rename from src/webui/www/public/newlabel.html rename to src/webui/www/public/newcategory.html index bff86e307a90..cca0929fad56 100644 --- a/src/webui/www/public/newlabel.html +++ b/src/webui/www/public/newcategory.html @@ -2,41 +2,41 @@ - QBT_TR(New Label)QBT_TR + QBT_TR(New Category)QBT_TR