Martin Rotter 45cae956bc dialog
2020-06-26 19:47:17 +02:00

282 lines
9.6 KiB
C++

// For license of this file, see <project-root-folder>/LICENSE.md.
#include "miscellaneous/feedreader.h"
#include "core/feeddownloader.h"
#include "core/feedsmodel.h"
#include "core/feedsproxymodel.h"
#include "core/messagesmodel.h"
#include "core/messagesproxymodel.h"
#include "miscellaneous/application.h"
#include "miscellaneous/databasequeries.h"
#include "miscellaneous/mutex.h"
#include "gui/dialogs/formmessagefiltersmanager.h"
#include "services/abstract/cacheforserviceroot.h"
#include "services/abstract/serviceroot.h"
#include "services/gmail/gmailentrypoint.h"
#include "services/inoreader/inoreaderentrypoint.h"
#include "services/owncloud/owncloudserviceentrypoint.h"
#include "services/standard/standardserviceentrypoint.h"
#include "services/tt-rss/ttrssserviceentrypoint.h"
#include <QtConcurrent/QtConcurrentRun>
#include <QThread>
#include <QTimer>
FeedReader::FeedReader(QObject* parent)
: QObject(parent),
m_autoUpdateTimer(new QTimer(this)), m_feedDownloader(nullptr) {
m_feedsModel = new FeedsModel(this);
m_feedsProxyModel = new FeedsProxyModel(m_feedsModel, this);
m_messagesModel = new MessagesModel(this);
m_messagesProxyModel = new MessagesProxyModel(m_messagesModel, this);
connect(m_autoUpdateTimer, &QTimer::timeout, this, &FeedReader::executeNextAutoUpdate);
updateAutoUpdateStatus();
asyncCacheSaveFinished();
if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) {
qDebug("Requesting update for all feeds on application startup.");
QTimer::singleShot(qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateStartupDelay)).toDouble() * 1000,
this,
&FeedReader::updateAllFeeds);
}
}
FeedReader::~FeedReader() {
qDebug("Destroying FeedReader instance.");
qDeleteAll(m_feedServices);
qDeleteAll(m_messageFilters);
}
QList<ServiceEntryPoint*> FeedReader::feedServices() {
if (m_feedServices.isEmpty()) {
// NOTE: All installed services create their entry points here.
m_feedServices.append(new GmailEntryPoint());
m_feedServices.append(new InoreaderEntryPoint());
m_feedServices.append(new OwnCloudServiceEntryPoint());
m_feedServices.append(new StandardServiceEntryPoint());
m_feedServices.append(new TtRssServiceEntryPoint());
}
return m_feedServices;
}
void FeedReader::updateFeeds(const QList<Feed*>& feeds) {
if (!qApp->feedUpdateLock()->tryLock()) {
qApp->showGuiMessage(tr("Cannot update all items"),
tr("You cannot update all items because another critical operation is ongoing."),
QSystemTrayIcon::MessageIcon::Warning, qApp->mainFormWidget(), true);
return;
}
if (m_feedDownloader == nullptr) {
qDebug("Creating FeedDownloader singleton.");
m_feedDownloaderThread = new QThread();
m_feedDownloader = new FeedDownloader();
// Downloader setup.
qRegisterMetaType<QList<Feed*>>("QList<Feed*>");
m_feedDownloader->moveToThread(m_feedDownloaderThread);
connect(m_feedDownloaderThread, &QThread::finished, m_feedDownloaderThread, &QThread::deleteLater);
connect(m_feedDownloaderThread, &QThread::finished, m_feedDownloader, &FeedDownloader::deleteLater);
connect(m_feedDownloader, &FeedDownloader::updateFinished, this, &FeedReader::feedUpdatesFinished);
connect(m_feedDownloader, &FeedDownloader::updateProgress, this, &FeedReader::feedUpdatesProgress);
connect(m_feedDownloader, &FeedDownloader::updateStarted, this, &FeedReader::feedUpdatesStarted);
connect(m_feedDownloader, &FeedDownloader::updateFinished, qApp->feedUpdateLock(), &Mutex::unlock);
m_feedDownloaderThread->start();
}
QMetaObject::invokeMethod(m_feedDownloader, "updateFeeds",
Qt::ConnectionType::QueuedConnection,
Q_ARG(QList<Feed*>, feeds));
}
void FeedReader::showMessageFiltersManager() {
FormMessageFiltersManager manager(qApp->mainFormWidget());
manager.exec();
}
void FeedReader::updateAutoUpdateStatus() {
// Restore global intervals.
// NOTE: Specific per-feed interval are left intact.
m_globalAutoUpdateInitialInterval = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt();
m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval;
m_globalAutoUpdateEnabled = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateEnabled)).toBool();
m_globalAutoUpdateOnlyUnfocused = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateOnlyUnfocused)).toBool();
// Start global auto-update timer if it is not running yet.
// NOTE: The timer must run even if global auto-update
// is not enabled because user can still enable auto-update
// for individual feeds.
if (!m_autoUpdateTimer->isActive()) {
m_autoUpdateTimer->setInterval(AUTO_UPDATE_INTERVAL);
m_autoUpdateTimer->start();
qDebug("Auto-update timer started with interval %d.", m_autoUpdateTimer->interval());
}
else {
qDebug("Auto-update timer is already running.");
}
}
bool FeedReader::autoUpdateEnabled() const {
return m_globalAutoUpdateEnabled;
}
int FeedReader::autoUpdateRemainingInterval() const {
return m_globalAutoUpdateRemainingInterval;
}
int FeedReader::autoUpdateInitialInterval() const {
return m_globalAutoUpdateInitialInterval;
}
void FeedReader::loadSaveMessageFilters() {
// Load all message filters from database.
// All plugin services will hook active filters to
// all feeds.
QSqlDatabase database = qApp->database()->connection(QSL("FeedReader"));
m_messageFilters = DatabaseQueries::getMessageFilters(database);
for (auto* filter : m_messageFilters) {
filter->setParent(this);
}
}
void FeedReader::updateAllFeeds() {
updateFeeds(m_feedsModel->rootItem()->getSubTreeFeeds());
}
void FeedReader::stopRunningFeedUpdate() {
if (m_feedDownloader != nullptr) {
m_feedDownloader->stopRunningUpdate();
}
}
bool FeedReader::isFeedUpdateRunning() const {
return m_feedDownloader != nullptr && m_feedDownloader->isUpdateRunning();
}
FeedDownloader* FeedReader::feedDownloader() const {
return m_feedDownloader;
}
FeedsModel* FeedReader::feedsModel() const {
return m_feedsModel;
}
MessagesModel* FeedReader::messagesModel() const {
return m_messagesModel;
}
void FeedReader::executeNextAutoUpdate() {
if (qApp->mainFormWidget()->isActiveWindow() && m_globalAutoUpdateOnlyUnfocused) {
qDebug("Delaying scheduled feed auto-update for one minute since window is focused and updates"
"while focused are disabled by the user.");
// Cannot update, quit.
return;
}
if (!qApp->feedUpdateLock()->tryLock()) {
qDebug("Delaying scheduled feed auto-updates for one minute due to another running update.");
// Cannot update, quit.
return;
}
// If global auto-update is enabled and its interval counter reached zero,
// then we need to restore it.
if (m_globalAutoUpdateEnabled && --m_globalAutoUpdateRemainingInterval < 0) {
// We should start next auto-update interval.
m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval;
}
qDebug("Starting auto-update event, pass %d/%d.", m_globalAutoUpdateRemainingInterval, m_globalAutoUpdateInitialInterval);
// Pass needed interval data and lets the model decide which feeds
// should be updated in this pass.
QList<Feed*> feeds_for_update = m_feedsModel->feedsForScheduledUpdate(m_globalAutoUpdateEnabled &&
m_globalAutoUpdateRemainingInterval == 0);
qApp->feedUpdateLock()->unlock();
if (!feeds_for_update.isEmpty()) {
// Request update for given feeds.
updateFeeds(feeds_for_update);
// NOTE: OSD/bubble informing about performing
// of scheduled update can be shown now.
if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::EnableAutoUpdateNotification)).toBool()) {
qApp->showGuiMessage(tr("Starting auto-update of some feeds"),
tr("I will auto-update %n feed(s).", nullptr, feeds_for_update.size()),
QSystemTrayIcon::Information);
}
}
}
void FeedReader::checkServicesForAsyncOperations() {
for (ServiceRoot* service : m_feedsModel->serviceRoots()) {
auto cache = dynamic_cast<CacheForServiceRoot*>(service);
if (cache != nullptr) {
cache->saveAllCachedData();
}
}
asyncCacheSaveFinished();
}
void FeedReader::asyncCacheSaveFinished() {
qDebug("I will start next check for cached service data in 60 seconds.");
QTimer::singleShot(60000, this, [&] {
qDebug("Starting next check for cached service data in NOW.");
checkServicesForAsyncOperations();
});
}
QList<MessageFilter*> FeedReader::messageFilters() const {
return m_messageFilters;
}
void FeedReader::quit() {
if (m_autoUpdateTimer->isActive()) {
m_autoUpdateTimer->stop();
}
// Stop running updates.
if (m_feedDownloader != nullptr) {
m_feedDownloader->stopRunningUpdate();
if (m_feedDownloader->isUpdateRunning()) {
QEventLoop loop(this);
connect(m_feedDownloader, &FeedDownloader::updateFinished, &loop, &QEventLoop::quit);
loop.exec();
}
// Both thread and downloader are auto-deleted when worker thread exits.
m_feedDownloaderThread->quit();
}
if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::ClearReadOnExit)).toBool()) {
m_feedsModel->markItemCleared(m_feedsModel->rootItem(), true);
}
m_feedsModel->stopServiceAccounts();
}
MessagesProxyModel* FeedReader::messagesProxyModel() const {
return m_messagesProxyModel;
}
FeedsProxyModel* FeedReader::feedsProxyModel() const {
return m_feedsProxyModel;
}