diff --git a/src/core/feeddownloader.cpp b/src/core/feeddownloader.cpp index 65f9df45d..763e52d07 100755 --- a/src/core/feeddownloader.cpp +++ b/src/core/feeddownloader.cpp @@ -27,12 +27,14 @@ FeedDownloader::FeedDownloader(QObject *parent) - : QObject(parent), m_results(FeedDownloadResults()), m_feedsUpdated(0), m_feedsToUpdate(0), + : QObject(parent), m_results(FeedDownloadResults()), m_msgUpdateMutex(new QMutex()), 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."); } @@ -79,7 +81,7 @@ void FeedDownloader::updateFeeds(const QList &feeds) { break; } - connect(feeds.at(i), SIGNAL(updated(int)), this, SLOT(oneFeedUpdateFinished(int)), + connect(feeds.at(i), &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished, (Qt::ConnectionType) (Qt::UniqueConnection | Qt::AutoConnection)); QThreadPool::globalInstance()->start(feeds.at(i)); @@ -92,14 +94,24 @@ void FeedDownloader::stopRunningUpdate() { m_stopUpdate = true; } -void FeedDownloader::oneFeedUpdateFinished(int updated_messages) { - const Feed *feed = qobject_cast(sender()); +void FeedDownloader::oneFeedUpdateFinished(const QList &messages) { + Feed *feed = qobject_cast(sender()); - disconnect(feed, SIGNAL(updated(int)), this, SLOT(oneFeedUpdateFinished(int))); + disconnect(feed, &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished); m_feedsUpdated++; m_feedsUpdating--; + // Now make sure, that messages are actually stored to SQL in a locked state. + + qDebug().nospace() << "Saving messages of feed " + << 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 88ce11dd5..16bef8d34 100755 --- a/src/core/feeddownloader.h +++ b/src/core/feeddownloader.h @@ -22,6 +22,8 @@ #include +#include "core/message.h" + class Feed; @@ -70,7 +72,7 @@ class FeedDownloader : public QObject { void stopRunningUpdate(); private slots: - void oneFeedUpdateFinished(int updated_messages); + void oneFeedUpdateFinished(const QList &messages); signals: // Emitted if feed updates started. @@ -90,6 +92,7 @@ class FeedDownloader : public QObject { void finalizeUpdate(); FeedDownloadResults m_results; + QMutex *m_msgUpdateMutex; int m_feedsUpdated; int m_feedsToUpdate; diff --git a/src/main.cpp b/src/main.cpp index acfd09e40..21ce8f705 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,9 +47,7 @@ int main(int argc, char *argv[]) { QObject::tr("LANG_AUTHOR"); // Ensure that ini format is used as application settings storage on Mac OS. -#ifdef Q_OS_MAC QSettings::setDefaultFormat(QSettings::IniFormat); -#endif // Setup debug output system. qInstallMessageHandler(Debugging::debugHandler); @@ -65,6 +63,7 @@ int main(int argc, char *argv[]) { } // Register needed metatypes. + qRegisterMetaType >("QList"); qRegisterMetaType >("QList"); // Add an extra path for non-system icon themes and set current icon theme @@ -114,7 +113,7 @@ int main(int argc, char *argv[]) { qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates(); // Setup single-instance behavior. - QObject::connect(&application, SIGNAL(messageReceived(QString)), &application, SLOT(processExecutionMessage(QString))); + 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" diff --git a/src/miscellaneous/application.cpp b/src/miscellaneous/application.cpp index e47de26b7..4339b73c1 100755 --- a/src/miscellaneous/application.cpp +++ b/src/miscellaneous/application.cpp @@ -42,7 +42,7 @@ Application::Application(const QString &id, int &argc, char **argv) : QtSingleApplication(id, argc, argv), - m_updateFeedsLock(nullptr), m_updateMessagesLock(nullptr), m_feedServices(QList()), m_userActions(QList()), m_mainForm(nullptr), + m_updateFeedsLock(nullptr), m_feedServices(QList()), m_userActions(QList()), m_mainForm(nullptr), m_trayIcon(nullptr), m_settings(nullptr), m_system(nullptr), m_skins(nullptr), m_localization(nullptr), m_icons(nullptr), m_database(nullptr), m_downloadManager(nullptr) { connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit())); @@ -127,16 +127,6 @@ Mutex *Application::feedUpdateLock() { return m_updateFeedsLock.data(); } -Mutex *Application::messageUpdateLock() { - if (m_updateMessagesLock.isNull()) { - // NOTE: Cannot use parent hierarchy because this method can be usually called - // from any thread. - m_updateMessagesLock.reset(new Mutex()); - } - - return m_updateMessagesLock.data(); -} - void Application::backupDatabaseSettings(bool backup_database, bool backup_settings, const QString &target_path, const QString &backup_name) { if (!QFileInfo(target_path).isWritable()) { diff --git a/src/miscellaneous/application.h b/src/miscellaneous/application.h index 734a073af..c7dbf1ecf 100755 --- a/src/miscellaneous/application.h +++ b/src/miscellaneous/application.h @@ -113,7 +113,6 @@ class Application : public QtSingleApplication { // Access to application-wide close lock. Mutex *feedUpdateLock(); - Mutex *messageUpdateLock(); inline FormMain *mainForm() { return m_mainForm; @@ -187,7 +186,6 @@ class Application : public QtSingleApplication { // tries to lock the lock for writing), then no other // action will be allowed to lock for reading. QScopedPointer m_updateFeedsLock; - QScopedPointer m_updateMessagesLock; QList m_feedServices; QList m_userActions; diff --git a/src/miscellaneous/iconfactory.cpp b/src/miscellaneous/iconfactory.cpp index 9d9d91a9c..b200e230b 100755 --- a/src/miscellaneous/iconfactory.cpp +++ b/src/miscellaneous/iconfactory.cpp @@ -101,7 +101,9 @@ void IconFactory::loadCurrentIconTheme() { // Display list of installed themes. qDebug("Installed icon themes are: %s.", - qPrintable(QStringList(installed_themes).replaceInStrings(QRegExp(QSL("^|$")), QSL("\'")).join(QSL(", ")))); + qPrintable(QStringList(installed_themes) + .replaceInStrings(QRegExp(QSL("^|$")), QSL("\'")) + .replaceInStrings(QRegExp(QSL("^\\'$")), QSL("\'\'")).join(QSL(", ")))); if (installed_themes.contains(theme_name_from_settings)) { // Desired icon theme is installed and can be loaded. @@ -111,8 +113,8 @@ void IconFactory::loadCurrentIconTheme() { else { // Desired icon theme is not currently available. // Install "default" icon theme instead. - qDebug("Icon theme '%s' cannot be loaded because it is not installed. No icon theme (or default icon theme) is loaded now.", - qPrintable(theme_name_from_settings)); + qWarning("Icon theme '%s' cannot be loaded because it is not installed. No icon theme (or default icon theme) is loaded now.", + qPrintable(theme_name_from_settings)); QIcon::setThemeName(APP_NO_THEME); } } diff --git a/src/miscellaneous/localization.cpp b/src/miscellaneous/localization.cpp index 1e970bb61..df2d709cd 100755 --- a/src/miscellaneous/localization.cpp +++ b/src/miscellaneous/localization.cpp @@ -44,11 +44,13 @@ void Localization::loadActiveLanguage() { qDebug("Starting to load active localization. Desired localization is '%s'.", qPrintable(desired_localization)); if (app_translator->load(QLocale(desired_localization), "rssguard", QSL("-"), APP_LANG_PATH)) { + const QString real_loaded_locale = app_translator->translate("QObject", "LANG_ABBREV"); + Application::installTranslator(app_translator); qDebug("Application localization '%s' loaded successfully, specifically sublocalization '%s' was loaded.", qPrintable(desired_localization), - qPrintable(app_translator->translate("QObject", "LANG_ABBREV"))); - desired_localization = app_translator->translate("QObject", "LANG_ABBREV"); + qPrintable(real_loaded_locale)); + desired_localization = real_loaded_locale; } else { qWarning("Application localization '%s' was not loaded. Loading '%s' instead.", diff --git a/src/miscellaneous/skinfactory.cpp b/src/miscellaneous/skinfactory.cpp index e038a711e..28a4e752d 100755 --- a/src/miscellaneous/skinfactory.cpp +++ b/src/miscellaneous/skinfactory.cpp @@ -59,7 +59,7 @@ void SkinFactory::loadCurrentSkin() { } } - qFatal("Failed to load selected or default skin(s). Quitting!"); + qFatal("Failed to load selected or default skin. Quitting!"); } void SkinFactory::loadSkinFromData(const Skin &skin) { diff --git a/src/services/abstract/feed.cpp b/src/services/abstract/feed.cpp index 3f7a36c45..061079886 100755 --- a/src/services/abstract/feed.cpp +++ b/src/services/abstract/feed.cpp @@ -88,17 +88,6 @@ void Feed::setCountOfUnreadMessages(int count_unread_messages) { m_unreadCount = count_unread_messages; } -int Feed::update() { - QList msgs = obtainNewMessages(); - - if (msgs.size() > 0) { - return updateMessages(msgs); - } - else { - return 0; - } -} - void Feed::setAutoUpdateInitialInterval(int auto_update_interval) { // If new initial auto-update interval is set, then // we should reset time that remains to the next auto-update. @@ -134,9 +123,13 @@ void Feed::updateCounts(bool including_total_count) { } void Feed::run() { - qDebug().nospace() << "Updating feed " << customId() << " in thread: \'" << QThread::currentThreadId() << "\'."; + qDebug().nospace() << "Downloading new messages for feed " + << customId() << " in thread: \'" + << QThread::currentThreadId() << "\'."; - emit updated(update()); + QList msgs = obtainNewMessages(); + + emit messagesObtained(msgs); } int Feed::updateMessages(const QList &messages) { @@ -144,13 +137,6 @@ int Feed::updateMessages(const QList &messages) { int account_id = getParentServiceRoot()->accountId(); bool anything_updated = false; bool ok; - - // MySQL seems to be more error prone with transactions when called - // from more threads in the same time. SQLite does not have that limitation. - if (qApp->database()->activeDatabaseDriver() == DatabaseFactory::MYSQL) { - qApp->messageUpdateLock()->lock(); - } - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); int updated_messages = DatabaseQueries::updateMessages(database, messages, custom_id, account_id, url(), &anything_updated, &ok); @@ -176,9 +162,5 @@ int Feed::updateMessages(const QList &messages) { getParentServiceRoot()->itemChanged(items_to_update); } - if (qApp->database()->activeDatabaseDriver() == DatabaseFactory::MYSQL) { - qApp->messageUpdateLock()->unlock(); - } - return updated_messages; } diff --git a/src/services/abstract/feed.h b/src/services/abstract/feed.h index b86c90580..d22ba6bdb 100755 --- a/src/services/abstract/feed.h +++ b/src/services/abstract/feed.h @@ -61,13 +61,6 @@ class Feed : public RootItem, public QRunnable { void setCountOfAllMessages(int count_all_messages); void setCountOfUnreadMessages(int count_unread_messages); - // Performs synchronous update and returns number of newly updated messages. - // NOTE: This is called from worker thread, not from main UI thread. - // NOTE: This should COMPLETELY download ALL messages from online source - // into locale "Messages" table, INCLUDING contents (or excerpts) of those - // messages. - int update(); - QVariant data(int column, int role) const; int autoUpdateInitialInterval() const; @@ -95,19 +88,18 @@ class Feed : public RootItem, public QRunnable { m_url = url; } + int updateMessages(const QList &messages); void updateCounts(bool including_total_count); // Runs update in thread (thread pooled). void run(); - protected: + private: + // Performs synchronous obtaining of new messages for this feed. virtual QList obtainNewMessages() = 0; - private: - int updateMessages(const QList &messages); - signals: - void updated(int updated_messages); + void messagesObtained(QList messages); private: QString m_url;