diff --git a/src/core/feeddownloader.cpp b/src/core/feeddownloader.cpp index 5eb49e476..82e225a21 100755 --- a/src/core/feeddownloader.cpp +++ b/src/core/feeddownloader.cpp @@ -23,19 +23,16 @@ #include #include #include -#include #include FeedDownloader::FeedDownloader(QObject *parent) - : QObject(parent), m_results(FeedDownloadResults()), m_msgUpdateMutex(new QMutex()), m_feedsUpdated(0), m_feedsToUpdate(0), + : QObject(parent), m_results(FeedDownloadResults()), m_feedsUpdated(0), m_feedsToUpdate(0), m_feedsUpdating(0), m_feedsTotalCount(0), m_stopUpdate(false) { qRegisterMetaType("FeedDownloadResults"); } FeedDownloader::~FeedDownloader() { - m_msgUpdateMutex->unlock(); - delete m_msgUpdateMutex; qDebug("Destroying FeedDownloader instance."); } @@ -109,9 +106,7 @@ void FeedDownloader::oneFeedUpdateFinished(const QList &messages) { << feed->customId() << " in thread: \'" << QThread::currentThreadId() << "\'."; - m_msgUpdateMutex->lock(); int updated_messages = messages.isEmpty() ? 0 : feed->updateMessages(messages); - m_msgUpdateMutex->unlock(); if (updated_messages > 0) { m_results.appendUpdatedFeed(QPair(feed->title(), updated_messages)); diff --git a/src/core/feeddownloader.h b/src/core/feeddownloader.h index a3386fad4..d39c8f5b6 100755 --- a/src/core/feeddownloader.h +++ b/src/core/feeddownloader.h @@ -49,8 +49,6 @@ class FeedDownloadResults { QList > m_updatedFeeds; }; -class QMutex; - // This class offers means to "update" feeds and "special" categories. // NOTE: This class is used within separate thread. class FeedDownloader : public QObject { @@ -94,7 +92,6 @@ class FeedDownloader : public QObject { void finalizeUpdate(); FeedDownloadResults m_results; - QMutex *m_msgUpdateMutex; int m_feedsUpdated; int m_feedsToUpdate; diff --git a/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp index 3284f2668..4e8a3a183 100755 --- a/src/core/feedsmodel.cpp +++ b/src/core/feedsmodel.cpp @@ -45,10 +45,7 @@ #include -FeedsModel::FeedsModel(QObject *parent) - : QAbstractItemModel(parent), m_autoUpdateTimer(new QTimer(this)), - m_feedDownloaderThread(nullptr), m_feedDownloader(nullptr), - m_dbCleanerThread(nullptr), m_dbCleaner(nullptr) { +FeedsModel::FeedsModel(QObject *parent) : QAbstractItemModel(parent) { setObjectName(QSL("FeedsModel")); // Create root item. @@ -66,9 +63,6 @@ FeedsModel::FeedsModel(QObject *parent) m_tooltipData << /*: Feed list header "titles" column tooltip.*/ tr("Titles of feeds/categories.") << /*: Feed list header "counts" column tooltip.*/ tr("Counts of unread/all mesages."); - - connect(m_autoUpdateTimer, SIGNAL(timeout()), this, SLOT(executeNextAutoUpdate())); - updateAutoUpdateStatus(); } FeedsModel::~FeedsModel() { @@ -83,78 +77,12 @@ FeedsModel::~FeedsModel() { } void FeedsModel::quit() { - if (m_autoUpdateTimer->isActive()) { - m_autoUpdateTimer->stop(); - } - - // Close worker threads. - if (m_feedDownloaderThread != nullptr && m_feedDownloaderThread->isRunning()) { - m_feedDownloader->stopRunningUpdate(); - - qDebug("Quitting feed downloader thread."); - m_feedDownloaderThread->quit(); - - if (!m_feedDownloaderThread->wait(CLOSE_LOCK_TIMEOUT)) { - qCritical("Feed downloader thread is running despite it was told to quit. Terminating it."); - m_feedDownloaderThread->terminate(); - } - } - - if (m_dbCleanerThread != nullptr && m_dbCleanerThread->isRunning()) { - qDebug("Quitting database cleaner thread."); - m_dbCleanerThread->quit(); - - if (!m_dbCleanerThread->wait(CLOSE_LOCK_TIMEOUT)) { - qCritical("Database cleaner thread is running despite it was told to quit. Terminating it."); - m_dbCleanerThread->terminate(); - } - } - - // Close workers. - if (m_feedDownloader != nullptr) { - qDebug("Feed downloader exists. Deleting it from memory."); - m_feedDownloader->deleteLater(); - } - - if (m_dbCleaner != nullptr) { - qDebug("Database cleaner exists. Deleting it from memory."); - m_dbCleaner->deleteLater(); - } - if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::ClearReadOnExit)).toBool()) { markItemCleared(m_rootItem, true); } } -void FeedsModel::updateFeeds(const QList &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::Warning, qApp->mainFormWidget(), true); - return; - } - - if (m_feedDownloader == nullptr) { - m_feedDownloader = new FeedDownloader(); - m_feedDownloaderThread = new QThread(); - - // Downloader setup. - qRegisterMetaType >("QList"); - m_feedDownloader->moveToThread(m_feedDownloaderThread); - - connect(this, SIGNAL(feedsUpdateRequested(QList)), m_feedDownloader, SLOT(updateFeeds(QList))); - connect(m_feedDownloaderThread, SIGNAL(finished()), m_feedDownloaderThread, SLOT(deleteLater())); - connect(m_feedDownloader, SIGNAL(finished(FeedDownloadResults)), this, SLOT(onFeedUpdatesFinished(FeedDownloadResults))); - connect(m_feedDownloader, SIGNAL(started()), this, SLOT(onFeedUpdatesStarted())); - connect(m_feedDownloader, SIGNAL(progress(const Feed*,int,int)), this, SLOT(onFeedUpdatesProgress(const Feed*,int,int))); - - // Connections are made, start the feed downloader thread. - m_feedDownloaderThread->start(); - } - - emit feedsUpdateRequested(feeds); -} - +/* void FeedsModel::onFeedUpdatesStarted() { //: Text display in status bar when feed update is started. qApp->mainForm()->statusBar()->showProgressFeeds(0, tr("Feed update started")); @@ -180,27 +108,7 @@ void FeedsModel::onFeedUpdatesFinished(const FeedDownloadResults &results) { emit feedsUpdateFinished(); } - -void FeedsModel::updateAllFeeds() { - updateFeeds(m_rootItem->getSubTreeFeeds()); -} - -DatabaseCleaner *FeedsModel::databaseCleaner() { - if (m_dbCleaner == nullptr) { - m_dbCleaner = new DatabaseCleaner(); - m_dbCleanerThread = new QThread(); - - // Downloader setup. - qRegisterMetaType("CleanerOrders"); - m_dbCleaner->moveToThread(m_dbCleanerThread); - connect(m_dbCleanerThread, SIGNAL(finished()), m_dbCleanerThread, SLOT(deleteLater())); - - // Connections are made, start the feed downloader thread. - m_dbCleanerThread->start(); - } - - return m_dbCleaner; -} +*/ QMimeData *FeedsModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mime_data = new QMimeData(); @@ -298,62 +206,6 @@ Qt::ItemFlags FeedsModel::flags(const QModelIndex &index) const { return base_flags | additional_flags; } -void FeedsModel::executeNextAutoUpdate() { - 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 feeds_for_update = 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. - qApp->showGuiMessage(tr("Starting auto-update of some feeds"), - tr("I will auto-update %n feed(s).", 0, feeds_for_update.size()), - QSystemTrayIcon::Information); - } -} - -void FeedsModel::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(); - - // 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."); - } -} - QVariant FeedsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal) { return QVariant(); @@ -612,10 +464,6 @@ bool FeedsModel::hasAnyFeedNewMessages() const { return false; } -bool FeedsModel::isFeedUpdateRunning() const { - return m_feedDownloader != nullptr && m_feedDownloader->isUpdateRunning(); -} - void FeedsModel::reloadChangedLayout(QModelIndexList list) { while (!list.isEmpty()) { QModelIndex indx = list.takeFirst(); @@ -727,12 +575,6 @@ void FeedsModel::loadActivatedServiceAccounts() { } } -void FeedsModel::stopRunningFeedUpdate() { - if (m_feedDownloader != nullptr) { - m_feedDownloader->stopRunningUpdate(); - } -} - QList FeedsModel::feedsForIndex(const QModelIndex &index) const { return itemForIndex(index)->getSubTreeFeeds(); } diff --git a/src/core/feedsmodel.h b/src/core/feedsmodel.h index 86f56b288..ca1a84a7e 100755 --- a/src/core/feedsmodel.h +++ b/src/core/feedsmodel.h @@ -40,9 +40,6 @@ class FeedsModel : public QAbstractItemModel { explicit FeedsModel(QObject *parent = 0); virtual ~FeedsModel(); - // Access to DB cleaner. - DatabaseCleaner *databaseCleaner(); - // Model implementation. inline QVariant data(const QModelIndex &index, int role) const { // Return data according to item. @@ -124,18 +121,9 @@ class FeedsModel : public QAbstractItemModel { return m_rootItem; } - bool isFeedUpdateRunning() const; - - // Resets global auto-update intervals according to settings - // and starts/stop the timer as needed. - void updateAutoUpdateStatus(); - // Does necessary job before quitting this component. void quit(); - // Schedules given feeds for update. - void updateFeeds(const QList &feeds); - // Adds given service root account. bool addServiceAccount(ServiceRoot *root, bool freshly_activated); @@ -143,11 +131,6 @@ class FeedsModel : public QAbstractItemModel { void loadActivatedServiceAccounts(); public slots: - void stopRunningFeedUpdate(); - - // Schedules all feeds from all accounts for update. - void updateAllFeeds(); - // Checks if new parent node is different from one used by original node. // If it is, then it reassigns original_node to new parent. void reassignNodeToNewParent(RootItem *original_node, RootItem *new_parent); @@ -182,23 +165,7 @@ class FeedsModel : public QAbstractItemModel { private slots: void onItemDataChanged(const QList &items); - // Is executed when next auto-update round could be done. - void executeNextAutoUpdate(); - - // Reacts on feed updates. - void onFeedUpdatesStarted(); - void onFeedUpdatesProgress(const Feed *feed, int current, int total); - void onFeedUpdatesFinished(const FeedDownloadResults &results); - signals: - // Update of feeds is finished. - void feedsUpdateFinished(); - - void feedsUpdateStarted(); - - // Emitted when model requests update of some feeds. - void feedsUpdateRequested(QList feeds); - // Emitted if counts of messages are changed. void messageCountsChanged(int unread_messages, int total_messages, bool any_feed_has_unread_messages); @@ -221,18 +188,6 @@ class FeedsModel : public QAbstractItemModel { QList m_headerData; QList m_tooltipData; QIcon m_countsIcon; - - // Auto-update stuff. - QTimer *m_autoUpdateTimer; - bool m_globalAutoUpdateEnabled; - int m_globalAutoUpdateInitialInterval; - int m_globalAutoUpdateRemainingInterval; - - QThread *m_feedDownloaderThread; - FeedDownloader *m_feedDownloader; - - QThread *m_dbCleanerThread; - DatabaseCleaner *m_dbCleaner; }; #endif // FEEDSMODEL_H diff --git a/src/definitions/definitions.h b/src/definitions/definitions.h index 92658b5e8..066abfff2 100755 --- a/src/definitions/definitions.h +++ b/src/definitions/definitions.h @@ -120,7 +120,7 @@ #define APP_LOG_PATH "data/log" #define APP_LOG_FILE "log.txt" -#define APP_QUIT_INSTANCE "app_quit" +#define APP_QUIT_INSTANCE "-q" #define APP_IS_RUNNING "app_is_running" #define APP_SKIN_DEFAULT "base/vergilius.xml" #define APP_THEME_DEFAULT "Faenza" diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp index c3c020fc4..b7fffb6de 100755 --- a/src/gui/dialogs/formmain.cpp +++ b/src/gui/dialogs/formmain.cpp @@ -186,7 +186,7 @@ void FormMain::updateAddItemMenu() { // NOTE: Clear here deletes items from memory but only those OWNED by the menu. m_ui->m_menuAddItem->clear(); - foreach (ServiceRoot *activated_root, tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->serviceRoots()) { + foreach (ServiceRoot *activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { QMenu *root_menu = new QMenu(activated_root->title(), m_ui->m_menuAddItem); root_menu->setIcon(activated_root->icon()); root_menu->setToolTip(activated_root->description()); @@ -233,7 +233,7 @@ void FormMain::updateAddItemMenu() { void FormMain::updateRecycleBinMenu() { m_ui->m_menuRecycleBin->clear(); - foreach (const ServiceRoot *activated_root, tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->serviceRoots()) { + foreach (const ServiceRoot *activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { QMenu *root_menu = new QMenu(activated_root->title(), m_ui->m_menuRecycleBin); root_menu->setIcon(activated_root->icon()); root_menu->setToolTip(activated_root->description()); @@ -273,7 +273,7 @@ void FormMain::updateRecycleBinMenu() { void FormMain::updateAccountsMenu() { m_ui->m_menuAccounts->clear(); - foreach (ServiceRoot *activated_root, tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->serviceRoots()) { + foreach (ServiceRoot *activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { QMenu *root_menu = new QMenu(activated_root->title(), m_ui->m_menuAccounts); root_menu->setIcon(activated_root->icon()); root_menu->setToolTip(activated_root->description()); @@ -558,7 +558,7 @@ void FormMain::showWiki() { void FormMain::showAddAccountDialog() { QScopedPointer form_update(new FormAddAccount(qApp->feedReader()->feedServices(), - tabWidget()->feedMessageViewer()->feedsView()->sourceModel(), + qApp->feedReader()->feedsModel(), this)); form_update->exec(); } diff --git a/src/gui/dialogs/formupdate.cpp b/src/gui/dialogs/formupdate.cpp index b4644561b..b6656a18f 100755 --- a/src/gui/dialogs/formupdate.cpp +++ b/src/gui/dialogs/formupdate.cpp @@ -187,11 +187,11 @@ void FormUpdate::startUpdate() { qDebug("Preparing to launch external installer '%s'.", qPrintable(QDir::toNativeSeparators(m_updateFilePath))); #if defined(Q_OS_WIN) - HINSTANCE exec_result = ShellExecute(NULL, - NULL, + HINSTANCE exec_result = ShellExecute(nullptr, + nullptr, reinterpret_cast(QDir::toNativeSeparators(m_updateFilePath).utf16()), - NULL, - NULL, + nullptr, + nullptr, SW_NORMAL); if (((int)exec_result) <= 32) { diff --git a/src/gui/discoverfeedsbutton.cpp b/src/gui/discoverfeedsbutton.cpp index 329fd7780..052d20676 100755 --- a/src/gui/discoverfeedsbutton.cpp +++ b/src/gui/discoverfeedsbutton.cpp @@ -19,6 +19,7 @@ #include "miscellaneous/application.h" #include "miscellaneous/iconfactory.h" +#include "miscellaneous/feedreader.h" #include "gui/dialogs/formmain.h" #include "gui/tabwidget.h" #include "gui/feedmessageviewer.h" @@ -77,7 +78,7 @@ void DiscoverFeedsButton::linkTriggered(QAction *action) { void DiscoverFeedsButton::fillMenu() { menu()->clear(); - foreach (const ServiceRoot *root, qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->serviceRoots()) { + foreach (const ServiceRoot *root, qApp->feedReader()->feedsModel()->serviceRoots()) { QMenu *root_menu = menu()->addMenu(root->icon(), root->title()); foreach (const QString &url, m_addresses) { diff --git a/src/gui/feedmessageviewer.cpp b/src/gui/feedmessageviewer.cpp index 0e20faa8f..d2f3492e6 100755 --- a/src/gui/feedmessageviewer.cpp +++ b/src/gui/feedmessageviewer.cpp @@ -23,6 +23,7 @@ #include "miscellaneous/iconfactory.h" #include "miscellaneous/mutex.h" #include "miscellaneous/databasecleaner.h" +#include "miscellaneous/feedreader.h" #include "core/messagesproxymodel.h" #include "core/feeddownloader.h" #include "core/feedsproxymodel.h" @@ -193,7 +194,7 @@ void FeedMessageViewer::updateMessageButtonsAvailability() { } void FeedMessageViewer::updateFeedButtonsAvailability() { - const bool is_update_running = feedsView()->sourceModel()->isFeedUpdateRunning(); + const bool is_update_running = qApp->feedReader()->isFeedUpdateRunning(); const bool critical_action_running = qApp->feedUpdateLock()->isLocked(); const RootItem *selected_item = feedsView()->selectedItem(); const bool anything_selected = selected_item != nullptr; @@ -202,6 +203,8 @@ void FeedMessageViewer::updateFeedButtonsAvailability() { const bool service_selected = anything_selected && selected_item->kind() == RootItemKind::ServiceRoot; const FormMain *form_main = qApp->mainForm(); + // TODO: přesunou do form main. + form_main->m_ui->m_actionStopRunningItemsUpdate->setEnabled(is_update_running); form_main->m_ui->m_actionBackupDatabaseSettings->setEnabled(!critical_action_running); form_main->m_ui->m_actionCleanupDatabase->setEnabled(!critical_action_running); @@ -414,7 +417,7 @@ void FeedMessageViewer::initializeViews() { void FeedMessageViewer::showDbCleanupAssistant() { if (qApp->feedUpdateLock()->tryLock()) { QScopedPointer form_pointer(new FormDatabaseCleanup(this)); - form_pointer.data()->setCleaner(m_feedsView->sourceModel()->databaseCleaner()); + form_pointer.data()->setCleaner(qApp->feedReader()->databaseCleaner()); form_pointer.data()->exec(); qApp->feedUpdateLock()->unlock(); @@ -443,7 +446,7 @@ void FeedMessageViewer::onFeedsUpdateFinished() { void FeedMessageViewer::onFeedsUpdateStarted() { // Check only "Stop running update" button. - const bool is_update_running = feedsView()->sourceModel()->isFeedUpdateRunning(); + const bool is_update_running = qApp->feedReader()->isFeedUpdateRunning(); qApp->mainForm()->m_ui->m_actionStopRunningItemsUpdate->setEnabled(is_update_running); } diff --git a/src/gui/feedsview.cpp b/src/gui/feedsview.cpp index f58eb5fe5..c789088d2 100755 --- a/src/gui/feedsview.cpp +++ b/src/gui/feedsview.cpp @@ -22,6 +22,7 @@ #include "core/feedsproxymodel.h" #include "services/abstract/rootitem.h" #include "miscellaneous/systemfactory.h" +#include "miscellaneous/feedreader.h" #include "miscellaneous/mutex.h" #include "gui/systemtrayicon.h" #include "gui/messagebox.h" @@ -50,8 +51,8 @@ FeedsView::FeedsView(QWidget *parent) setObjectName(QSL("FeedsView")); // Allocate models. - m_sourceModel = new FeedsModel(this); - m_proxyModel = new FeedsProxyModel(m_sourceModel, this); + m_sourceModel = qApp->feedReader()->feedsModel(); + m_proxyModel = qApp->feedReader()->feedsProxyModel(); // Connections. connect(m_sourceModel, SIGNAL(requireItemValidationAfterDragDrop(QModelIndex)), this, SLOT(validateItemAfterDragDrop(QModelIndex))); @@ -202,11 +203,11 @@ void FeedsView::expandCollapseCurrentItem() { } void FeedsView::updateAllItems() { - m_sourceModel->updateAllFeeds(); + qApp->feedReader()->updateAllFeeds(); } void FeedsView::updateSelectedItems() { - m_sourceModel->updateFeeds(selectedFeeds()); + qApp->feedReader()->updateFeeds(selectedFeeds()); } void FeedsView::clearSelectedFeeds() { diff --git a/src/gui/messagesview.cpp b/src/gui/messagesview.cpp index a9bae77d3..49f1001b8 100755 --- a/src/gui/messagesview.cpp +++ b/src/gui/messagesview.cpp @@ -20,6 +20,7 @@ #include "core/messagesproxymodel.h" #include "core/messagesmodel.h" #include "miscellaneous/settings.h" +#include "miscellaneous/feedreader.h" #include "network-web/networkfactory.h" #include "network-web/webfactory.h" #include "gui/dialogs/formmain.h" @@ -38,8 +39,8 @@ MessagesView::MessagesView(QWidget *parent) m_contextMenu(nullptr), m_columnsAdjusted(false), m_batchUnreadSwitch(false) { - m_sourceModel = new MessagesModel(this); - m_proxyModel = new MessagesProxyModel(m_sourceModel, this); + m_sourceModel = qApp->feedReader()->messagesModel(); + m_proxyModel = qApp->feedReader()->messagesProxyModel(); // Forward count changes to the view. createConnections(); diff --git a/src/gui/settings/settingsfeedsmessages.cpp b/src/gui/settings/settingsfeedsmessages.cpp index 95962448c..70f068225 100755 --- a/src/gui/settings/settingsfeedsmessages.cpp +++ b/src/gui/settings/settingsfeedsmessages.cpp @@ -19,6 +19,7 @@ #include "definitions/definitions.h" #include "miscellaneous/application.h" +#include "miscellaneous/feedreader.h" #include "gui/dialogs/formmain.h" #include "gui/feedmessageviewer.h" #include "gui/feedsview.h" @@ -124,10 +125,11 @@ void SettingsFeedsMessages::saveSettings() { settings()->setValue(GROUP(Messages), Messages::PreviewerFontStandard, m_ui->m_lblMessagesFont->font().toString()); qApp->mainForm()->tabWidget()->feedMessageViewer()->loadMessageViewerFonts(); - qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->updateAutoUpdateStatus(); - qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->reloadWholeLayout(); - qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->updateDateFormat(); - qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->reloadWholeLayout(); + + qApp->feedReader()->updateAutoUpdateStatus(); + qApp->feedReader()->feedsModel()->reloadWholeLayout(); + qApp->feedReader()->messagesModel()->updateDateFormat(); + qApp->feedReader()->messagesModel()->reloadWholeLayout(); onEndSaveSettings(); } diff --git a/src/main.cpp b/src/main.cpp index ebfef29d8..8cfc8ad09 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,19 +42,25 @@ int main(int argc, char *argv[]) { bool run_minimal_without_gui = false; - for (int i; i < argc; i++) { + for (int i = 0; i < argc; i++) { const QString str = QString::fromLocal8Bit(argv[i]); - if (str == "-h" || str == "--help") { + if (str == "-h") { qDebug("Usage: rssguard [OPTIONS]\n\n" - "Option\t\tGNU long option\t\tMeaning\n" - "-h\t\t--help\t\t\tDisplays this help.\n" - "-c\t\t--cron\t\t\tStarts the application without GUI and will regularly update configured feeds. **\n\n" - "** "); + "Option\t\tMeaning\n" + "-h\t\tDisplays this help.\n" + "-q\t\tQuits minimal non-GUI instance of application already running on this machine.\n" + "-c\t\tStarts the application without GUI and will regularly update configured feeds.\n\n" + "Running with '-c' option starts application without GUI and/or tray icon. No " + "message boxes will be shown, no GUI. Only minimal functionality will be enabled, including periodic checking for " + "feed/message updates. Note that you must configure the application via GUI first! So to really get periodic " + "feed updates, you must import some feeds and set their auto-update policy first in the GUI.\n\n" + "You can quick the minimal non-GUI application instance either by just killing it or by running the application " + "executable again with -q option."); return EXIT_SUCCESS; } - else if (str == "-c" || str == "--cron") { + else if (str == "-c") { run_minimal_without_gui = true; } } @@ -106,50 +112,56 @@ int main(int argc, char *argv[]) { qDebug().nospace() << "Creating main application form in thread: \'" << QThread::currentThreadId() << "\'."; - // Instantiate main application window. - FormMain main_window; - - // Set correct information for main window. - main_window.setWindowTitle(APP_LONG_NAME); - - // Now is a good time to initialize dynamic keyboard shortcuts. - DynamicShortcuts::load(qApp->userActions()); - - // Display main window. - if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::MainWindowStartsHidden)).toBool() && SystemTrayIcon::isSystemTrayActivated()) { - qDebug("Hiding the main window when the application is starting."); - main_window.switchVisibility(true); - } - else { - qDebug("Showing the main window when the application is starting."); - main_window.show(); - } - - // Display tray icon if it is enabled and available. - if (SystemTrayIcon::isSystemTrayActivated()) { - qApp->showTrayIcon(); - } - // Load activated accounts. - qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->loadActivatedServiceAccounts(); - qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates(); + qApp->feedReader()->feedsModel()->loadActivatedServiceAccounts(); // Setup single-instance behavior. QObject::connect(&application, &Application::messageReceived, &application, &Application::processExecutionMessage); - if (qApp->isFirstRun() || qApp->isFirstRun(APP_VERSION)) { - qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.\n\nPlease, check NEW stuff included in this\n" - "version by clicking this popup notification.").arg(APP_LONG_NAME), - QSystemTrayIcon::NoIcon, 0, false, &main_window, SLOT(showAbout())); + if (!run_minimal_without_gui) { + // Instantiate main application window. + FormMain main_window; + + // Set correct information for main window. + main_window.setWindowTitle(APP_LONG_NAME); + + // Now is a good time to initialize dynamic keyboard shortcuts. + DynamicShortcuts::load(qApp->userActions()); + + // Display main window. + if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::MainWindowStartsHidden)).toBool() && SystemTrayIcon::isSystemTrayActivated()) { + qDebug("Hiding the main window when the application is starting."); + main_window.switchVisibility(true); + } + else { + qDebug("Showing the main window when the application is starting."); + main_window.show(); + } + + // Display tray icon if it is enabled and available. + if (SystemTrayIcon::isSystemTrayActivated()) { + qApp->showTrayIcon(); + } + + if (qApp->isFirstRun() || qApp->isFirstRun(APP_VERSION)) { + qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.\n\nPlease, check NEW stuff included in this\n" + "version by clicking this popup notification.").arg(APP_LONG_NAME), + QSystemTrayIcon::NoIcon, 0, false, &main_window, SLOT(showAbout())); + } + else { + qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.").arg(APP_NAME), QSystemTrayIcon::NoIcon); + } + + if (qApp->settings()->value(GROUP(General), SETTING(General::UpdateOnStartup)).toBool()) { + QTimer::singleShot(STARTUP_UPDATE_DELAY, application.system(), SLOT(checkForUpdatesOnStartup())); + } + + qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates(); + + // Enter global event loop. + return Application::exec(); } else { - qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.").arg(APP_NAME), QSystemTrayIcon::NoIcon); + return Application::exec(); } - - if (qApp->settings()->value(GROUP(General), SETTING(General::UpdateOnStartup)).toBool()) { - QTimer::singleShot(STARTUP_UPDATE_DELAY, application.system(), SLOT(checkForUpdatesOnStartup())); - } - - // Enter global event loop. - return Application::exec(); } diff --git a/src/miscellaneous/application.cpp b/src/miscellaneous/application.cpp index d6c1d19b7..6378dbb66 100755 --- a/src/miscellaneous/application.cpp +++ b/src/miscellaneous/application.cpp @@ -84,6 +84,38 @@ bool Application::isFirstRun(const QString &version) { } } +SystemFactory *Application::system() { + if (m_system == nullptr) { + m_system = new SystemFactory(this); + } + + return m_system; +} + +SkinFactory *Application::skins() { + if (m_skins == nullptr) { + m_skins = new SkinFactory(this); + } + + return m_skins; +} + +Localization *Application::localization() { + if (m_localization == nullptr) { + m_localization = new Localization(this); + } + + return m_localization; +} + +DatabaseFactory *Application::database() { + if (m_database == nullptr) { + m_database = new DatabaseFactory(this); + } + + return m_database; +} + void Application::eliminateFirstRun() { settings()->setValue(GROUP(General), General::FirstRun, false); } @@ -115,6 +147,14 @@ DownloadManager *Application::downloadManager() { return m_downloadManager; } +Settings *Application::settings() { + if (m_settings == nullptr) { + m_settings = Settings::setupSettings(this); + } + + return m_settings; +} + Mutex *Application::feedUpdateLock() { if (m_updateFeedsLock.isNull()) { // NOTE: Cannot use parent hierarchy because this method can be usually called @@ -125,10 +165,18 @@ Mutex *Application::feedUpdateLock() { return m_updateFeedsLock.data(); } +FormMain *Application::mainForm() { + return m_mainForm; +} + QWidget *Application::mainFormWidget() { return m_mainForm; } +void Application::setMainForm(FormMain *main_form) { + m_mainForm = main_form; +} + void Application::backupDatabaseSettings(bool backup_database, bool backup_settings, const QString &target_path, const QString &backup_name) { if (!QFileInfo(target_path).isWritable()) { @@ -201,8 +249,7 @@ void Application::processExecutionMessage(const QString &message) { SystemTrayIcon *Application::trayIcon() { if (m_trayIcon == nullptr) { m_trayIcon = new SystemTrayIcon(APP_ICON_PATH, APP_ICON_PLAIN_PATH, m_mainForm); - connect(m_trayIcon, SIGNAL(shown()), - m_mainForm->tabWidget()->feedMessageViewer()->feedsView()->sourceModel(), SLOT(notifyWithCounts())); + connect(m_trayIcon, &SystemTrayIcon::shown, m_feedReader->feedsModel(), &FeedsModel::notifyWithCounts); } return m_trayIcon; diff --git a/src/miscellaneous/application.h b/src/miscellaneous/application.h index 92c7415ef..02bfb5516 100755 --- a/src/miscellaneous/application.h +++ b/src/miscellaneous/application.h @@ -68,61 +68,25 @@ class Application : public QtSingleApplication { // Check whether GIVEN VERSION of the application starts for the first time. bool isFirstRun(const QString &version); - inline SystemFactory *system() { - if (m_system == NULL) { - m_system = new SystemFactory(this); - } - - return m_system; - } - - inline SkinFactory *skins() { - if (m_skins == NULL) { - m_skins = new SkinFactory(this); - } - - return m_skins; - } - - inline Localization *localization() { - if (m_localization == NULL) { - m_localization = new Localization(this); - } - - return m_localization; - } - - inline DatabaseFactory *database() { - if (m_database == NULL) { - m_database = new DatabaseFactory(this); - } - - return m_database; - } - + SystemFactory *system(); + SkinFactory *skins(); + Localization *localization(); + DatabaseFactory *database(); IconFactory *icons(); DownloadManager *downloadManager(); - - inline Settings *settings() { - if (m_settings == NULL) { - m_settings = Settings::setupSettings(this); - } - - return m_settings; - } + Settings *settings(); // Access to application-wide close lock. Mutex *feedUpdateLock(); - inline FormMain *mainForm() { - return m_mainForm; - } - + FormMain *mainForm(); QWidget *mainFormWidget(); - void setMainForm(FormMain *main_form) { - m_mainForm = main_form; - } + // Access to application tray icon. Always use this in cooperation with + // SystemTrayIcon::isSystemTrayActivated(). + SystemTrayIcon *trayIcon(); + + void setMainForm(FormMain *main_form); inline QString tempFolderPath() { return IOFactory::getSystemFolder(QStandardPaths::TempLocation); @@ -142,10 +106,6 @@ class Application : public QtSingleApplication { const QString &source_database_file_path = QString(), const QString &source_settings_file_path = QString()); - // Access to application tray icon. Always use this in cooperation with - // SystemTrayIcon::isSystemTrayActivated(). - SystemTrayIcon *trayIcon(); - void showTrayIcon(); void deleteTrayIcon(); diff --git a/src/miscellaneous/feedreader.cpp b/src/miscellaneous/feedreader.cpp old mode 100644 new mode 100755 index 59c08bc60..ee8ad06a3 --- a/src/miscellaneous/feedreader.cpp +++ b/src/miscellaneous/feedreader.cpp @@ -27,6 +27,8 @@ #include "core/messagesproxymodel.h" #include "core/feeddownloader.h" #include "miscellaneous/databasecleaner.h" +#include "miscellaneous/application.h" +#include "miscellaneous/mutex.h" #include #include @@ -38,9 +40,12 @@ FeedReader::FeedReader(QObject *parent) m_dbCleanerThread(nullptr), m_dbCleaner(nullptr) { m_feedDownloader = new FeedDownloader(this); m_feedsModel = new FeedsModel(this); - m_feedProxyModel = new FeedsProxyModel(m_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(); } FeedReader::~FeedReader() { @@ -59,6 +64,67 @@ QList FeedReader::feedServices() { return m_feedServices; } +void FeedReader::updateFeeds(const QList &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::Warning, qApp->mainFormWidget(), true); + return; + } + + if (m_feedDownloader == nullptr) { + m_feedDownloader = new FeedDownloader(); + m_feedDownloaderThread = new QThread(); + + // Downloader setup. + qRegisterMetaType >("QList"); + m_feedDownloader->moveToThread(m_feedDownloaderThread); + + connect(this, &FeedReader::feedsUpdateRequested, m_feedDownloader, &FeedDownloader::updateFeeds); + connect(m_feedDownloaderThread, &QThread::finished, m_feedDownloaderThread, &QThread::deleteLater); + + // Connections are made, start the feed downloader thread. + m_feedDownloaderThread->start(); + } + + emit feedsUpdateRequested(feeds); +} + +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(); + + // 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."); + } +} + +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(); +} + DatabaseCleaner *FeedReader::databaseCleaner() { if (m_dbCleaner == nullptr) { m_dbCleaner = new DatabaseCleaner(); @@ -91,13 +157,87 @@ MessagesModel *FeedReader::messagesModel() const { void FeedReader::start() { } +void FeedReader::executeNextAutoUpdate() { + 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 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. + // TODO: Co dělat v non-GUI módu. + qApp->showGuiMessage(tr("Starting auto-update of some feeds"), + tr("I will auto-update %n feed(s).", 0, feeds_for_update.size()), + QSystemTrayIcon::Information); + } +} + void FeedReader::stop() { + if (m_autoUpdateTimer->isActive()) { + m_autoUpdateTimer->stop(); + } + + // Close worker threads. + if (m_feedDownloaderThread != nullptr && m_feedDownloaderThread->isRunning()) { + m_feedDownloader->stopRunningUpdate(); + + qDebug("Quitting feed downloader thread."); + m_feedDownloaderThread->quit(); + + if (!m_feedDownloaderThread->wait(CLOSE_LOCK_TIMEOUT)) { + qCritical("Feed downloader thread is running despite it was told to quit. Terminating it."); + m_feedDownloaderThread->terminate(); + } + } + + if (m_dbCleanerThread != nullptr && m_dbCleanerThread->isRunning()) { + qDebug("Quitting database cleaner thread."); + m_dbCleanerThread->quit(); + + if (!m_dbCleanerThread->wait(CLOSE_LOCK_TIMEOUT)) { + qCritical("Database cleaner thread is running despite it was told to quit. Terminating it."); + m_dbCleanerThread->terminate(); + } + } + + // Close workers. + if (m_feedDownloader != nullptr) { + qDebug("Feed downloader exists. Deleting it from memory."); + m_feedDownloader->deleteLater(); + } + + if (m_dbCleaner != nullptr) { + qDebug("Database cleaner exists. Deleting it from memory."); + m_dbCleaner->deleteLater(); + } } MessagesProxyModel *FeedReader::messagesProxyModel() const { return m_messagesProxyModel; } -FeedsProxyModel *FeedReader::feedProxyModel() const { - return m_feedProxyModel; +FeedsProxyModel *FeedReader::feedsProxyModel() const { + return m_feedsProxyModel; } diff --git a/src/miscellaneous/feedreader.h b/src/miscellaneous/feedreader.h old mode 100644 new mode 100755 index 503fc8e23..43cdfe852 --- a/src/miscellaneous/feedreader.h +++ b/src/miscellaneous/feedreader.h @@ -20,6 +20,8 @@ #include +#include "services/abstract/feed.h" + class FeedDownloader; class FeedsModel; @@ -29,7 +31,6 @@ class FeedsProxyModel; class ServiceEntryPoint; class DatabaseCleaner; class QTimer; -class Feed; class FeedReader : public QObject { Q_OBJECT @@ -48,18 +49,40 @@ class FeedReader : public QObject { FeedDownloader *feedDownloader() const; FeedsModel *feedsModel() const; MessagesModel *messagesModel() const; - FeedsProxyModel *feedProxyModel() const; + FeedsProxyModel *feedsProxyModel() const; MessagesProxyModel *messagesProxyModel() const; + // Schedules given feeds for update. + void updateFeeds(const QList &feeds); + + bool isFeedUpdateRunning() const; + + // Resets global auto-update intervals according to settings + // and starts/stop the timer as needed. + void updateAutoUpdateStatus(); + public slots: + // Schedules all feeds from all accounts for update. + void updateAllFeeds(); + + void stopRunningFeedUpdate(); + void start(); void stop(); + private slots: + // Is executed when next auto-update round could be done. + void executeNextAutoUpdate(); + + signals: + // Emitted when model requests update of some feeds. + void feedsUpdateRequested(QList feeds); + private: QList m_feedServices; FeedsModel *m_feedsModel; - FeedsProxyModel *m_feedProxyModel; + FeedsProxyModel *m_feedsProxyModel; MessagesModel *m_messagesModel; MessagesProxyModel *m_messagesProxyModel;