Improve "Watched folders" feature

Make "file system watcher" an application core component
and separate it from its presentation model.
This commit is contained in:
Vladimir Golovnev (Glassez) 2021-04-23 12:02:25 +03:00
parent 9565b695ef
commit 2993fdb169
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
29 changed files with 1636 additions and 1016 deletions

View file

@ -43,6 +43,7 @@
#include <QTranslator>
#include "base/bittorrent/session.h"
#include "base/exceptions.h"
#include "base/global.h"
#include "base/net/dnsupdater.h"
#include "base/net/portforwarder.h"
@ -50,8 +51,8 @@
#include "base/preferences.h"
#include "base/rss/rss_autodownloader.h"
#include "base/rss/rss_session.h"
#include "base/scanfoldersmodel.h"
#include "base/torrentfileguard.h"
#include "base/torrentfileswatcher.h"
#include "base/unicodestrings.h"
#include "base/utils/fs.h"
#include "base/utils/net.h"
@ -63,10 +64,11 @@
#include "banlistoptionsdialog.h"
#include "ipsubnetwhitelistoptionsdialog.h"
#include "rss/automatedrssdownloader.h"
#include "scanfoldersdelegate.h"
#include "ui_optionsdialog.h"
#include "uithememanager.h"
#include "utils.h"
#include "watchedfolderoptionsdialog.h"
#include "watchedfoldersmodel.h"
#define SETTINGS_KEY(name) "OptionsDialog/" name
@ -236,11 +238,12 @@ OptionsDialog::OptionsDialog(QWidget *parent)
m_applyButton = m_ui->buttonBox->button(QDialogButtonBox::Apply);
connect(m_applyButton, &QPushButton::clicked, this, &OptionsDialog::applySettings);
auto watchedFoldersModel = new WatchedFoldersModel(TorrentFilesWatcher::instance(), this);
connect(watchedFoldersModel, &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton);
m_ui->scanFoldersView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
m_ui->scanFoldersView->setModel(ScanFoldersModel::instance());
m_ui->scanFoldersView->setItemDelegate(new ScanFoldersDelegate(this, m_ui->scanFoldersView));
connect(ScanFoldersModel::instance(), &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton);
connect(m_ui->scanFoldersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ThisType::handleScanFolderViewSelectionChanged);
m_ui->scanFoldersView->setModel(watchedFoldersModel);
connect(m_ui->scanFoldersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ThisType::handleWatchedFolderViewSelectionChanged);
connect(m_ui->scanFoldersView, &QTreeView::doubleClicked, this, &ThisType::editWatchedFolderOptions);
// Languages supported
initializeLanguageCombo();
@ -368,8 +371,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->actionTorrentFnOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->checkTempFolder, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkTempFolder, &QAbstractButton::toggled, m_ui->textTempPath, &QWidget::setEnabled);
connect(m_ui->addScanFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->removeScanFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->senderEmailTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->lineEditDestEmail, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
@ -609,9 +612,6 @@ OptionsDialog::~OptionsDialog()
hSplitterSizes.append(QString::number(size));
m_storeHSplitterSize = hSplitterSizes;
for (const QString &path : asConst(m_addedScanDirs))
ScanFoldersModel::instance()->removePath(path);
ScanFoldersModel::instance()->configure(); // reloads "removed" paths
delete m_ui;
}
@ -736,11 +736,8 @@ void OptionsDialog::saveOptions()
AddNewTorrentDialog::setTopLevel(m_ui->checkAdditionDialogFront->isChecked());
session->setAddTorrentPaused(addTorrentsInPause());
session->setTorrentContentLayout(static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
ScanFoldersModel::instance()->removeFromFSWatcher(m_removedScanDirs);
ScanFoldersModel::instance()->addToFSWatcher(m_addedScanDirs);
ScanFoldersModel::instance()->makePersistent();
m_removedScanDirs.clear();
m_addedScanDirs.clear();
auto watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
watchedFoldersModel->apply();
session->setTorrentExportDirectory(getTorrentExportDir());
session->setFinishedTorrentExportDirectory(getFinishedTorrentExportDir());
pref->setMailNotificationEnabled(m_ui->groupMailNotification->isChecked());
@ -1648,57 +1645,85 @@ int OptionsDialog::getActionOnDblClOnTorrentFn() const
return m_ui->actionTorrentFnOnDblClBox->currentIndex();
}
void OptionsDialog::on_addScanFolderButton_clicked()
void OptionsDialog::on_addWatchedFolderButton_clicked()
{
Preferences *const pref = Preferences::instance();
const QString dir = QFileDialog::getExistingDirectory(this, tr("Select folder to monitor"),
Utils::Fs::toNativePath(Utils::Fs::folderName(pref->getScanDirsLastPath())));
if (!dir.isEmpty())
Utils::Fs::toNativePath(Utils::Fs::folderName(pref->getScanDirsLastPath())));
if (dir.isEmpty())
return;
auto dialog = new WatchedFolderOptionsDialog({}, this);
dialog->setModal(true);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::accepted, this, [this, dialog, dir, pref]()
{
const ScanFoldersModel::PathStatus status = ScanFoldersModel::instance()->addPath(dir, ScanFoldersModel::DEFAULT_LOCATION, QString(), false);
QString error;
switch (status)
try
{
case ScanFoldersModel::AlreadyInList:
error = tr("Folder is already being monitored:");
break;
case ScanFoldersModel::DoesNotExist:
error = tr("Folder does not exist:");
break;
case ScanFoldersModel::CannotRead:
error = tr("Folder is not readable:");
break;
default:
auto watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
watchedFoldersModel->addFolder(dir, dialog->watchedFolderOptions());
pref->setScanDirsLastPath(dir);
m_addedScanDirs << dir;
for (int i = 0; i < ScanFoldersModel::instance()->columnCount(); ++i)
for (int i = 0; i < watchedFoldersModel->columnCount(); ++i)
m_ui->scanFoldersView->resizeColumnToContents(i);
enableApplyButton();
}
catch (const RuntimeError &err)
{
QMessageBox::critical(this, tr("Adding entry failed"), err.message());
}
});
if (!error.isEmpty())
QMessageBox::critical(this, tr("Adding entry failed"), QString::fromLatin1("%1\n%2").arg(error, dir));
}
dialog->open();
}
void OptionsDialog::on_removeScanFolderButton_clicked()
void OptionsDialog::on_editWatchedFolderButton_clicked()
{
const QModelIndex selected
= m_ui->scanFoldersView->selectionModel()->selectedIndexes().at(0);
editWatchedFolderOptions(selected);
}
void OptionsDialog::on_removeWatchedFolderButton_clicked()
{
const QModelIndexList selected
= m_ui->scanFoldersView->selectionModel()->selectedIndexes();
if (selected.isEmpty())
return;
Q_ASSERT(selected.count() == ScanFoldersModel::instance()->columnCount());
for (const QModelIndex &index : selected)
{
if (index.column() == ScanFoldersModel::WATCH)
m_removedScanDirs << index.data().toString();
}
ScanFoldersModel::instance()->removePath(selected.first().row(), false);
m_ui->scanFoldersView->model()->removeRow(index.row());
}
void OptionsDialog::handleScanFolderViewSelectionChanged()
void OptionsDialog::handleWatchedFolderViewSelectionChanged()
{
m_ui->removeScanFolderButton->setEnabled(!m_ui->scanFoldersView->selectionModel()->selectedIndexes().isEmpty());
const QModelIndexList selectedIndexes = m_ui->scanFoldersView->selectionModel()->selectedIndexes();
m_ui->removeWatchedFolderButton->setEnabled(!selectedIndexes.isEmpty());
m_ui->editWatchedFolderButton->setEnabled(selectedIndexes.count() == 1);
}
void OptionsDialog::editWatchedFolderOptions(const QModelIndex &index)
{
if (!index.isValid())
return;
auto watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
auto dialog = new WatchedFolderOptionsDialog(watchedFoldersModel->folderOptions(index.row()), this);
dialog->setModal(true);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::accepted, this, [this, dialog, index, watchedFoldersModel]()
{
if (index.isValid())
{
// The index could be invalidated while the dialog was displayed,
// for example, if you deleted the folder using the Web API.
watchedFoldersModel->setFolderOptions(index.row(), dialog->watchedFolderOptions());
enableApplyButton();
}
});
dialog->open();
}
QString OptionsDialog::askForExportDir(const QString &currentExportPath)