diff --git a/src/librssguard/core/feeddownloader.cpp b/src/librssguard/core/feeddownloader.cpp index 0b0830cce..7d7032bd6 100644 --- a/src/librssguard/core/feeddownloader.cpp +++ b/src/librssguard/core/feeddownloader.cpp @@ -226,10 +226,10 @@ void FeedDownloader::updateOneFeed(Feed* feed) { if (!important_msgs.isEmpty()) { // Now we push new read states to the service. - QList chngs = QList::fromStdList( - boolinq::from(important_msgs).select([](const Message& msg) { + auto list = boolinq::from(important_msgs).select([](const Message& msg) { return ImportanceChange(msg, RootItem::Importance::Important); - }).toStdList()); + }).toStdList(); + QList chngs = FROM_STD_LIST(QList, list); if (feed->getParentServiceRoot()->onBeforeSwitchMessageImportance(feed, chngs)) { qDebugNN << LOGSEC_FEEDDOWNLOADER diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h index 29f086c2f..6d33304b0 100755 --- a/src/librssguard/definitions/definitions.h +++ b/src/librssguard/definitions/definitions.h @@ -6,6 +6,9 @@ #include #include +// +// Constants. +// #define SERVICE_CODE_STD_RSS "std-rss" #define SERVICE_CODE_TT_RSS "tt-rss" #define SERVICE_CODE_OWNCLOUD "owncloud" @@ -157,49 +160,6 @@ #define APP_LOCAL_THEME_FOLDER "icons" #define APP_NO_THEME "" -#ifndef qDebugNN -#define qDebugNN qDebug().noquote().nospace() -#endif - -#ifndef qWarningNN -#define qWarningNN qWarning().noquote().nospace() -#endif - -#ifndef qCriticalNN -#define qCriticalNN qCritical().noquote().nospace() -#endif - -#ifndef qInfoNN -#define qInfoNN qInfo().noquote().nospace() -#endif - -#define QUOTE_W_SPACE_DOT(x) " '" << (x) << "'." -#define QUOTE_W_SPACE(x) " '" << (x) << "' " -#define QUOTE_NO_SPACE(x) "'" << (x) << "'" - -#ifndef QSL - -// Thin macro wrapper for literal strings. -// They are much more memory efficient and faster. -// Use it for all literals except for two cases: -// a) Methods which take QLatin1String (use QLatin1String for literal argument too), -// b) Construction of empty literals "", use QString() instead of QStringLiteral(""). -#define QSL(x) QStringLiteral(x) -#endif - -#ifndef QL1S - -// Macro for latin strings. Latin strings are -// faster than QStrings created from literals. -#define QL1S(x) QLatin1String(x) -#endif - -#ifndef QL1C - -// Macro for latin chars. -#define QL1C(x) QLatin1Char(x) -#endif - // Indexes of columns as they are DEFINED IN THE TABLE for MESSAGES. #define MSG_DB_ID_INDEX 0 #define MSG_DB_READ_INDEX 1 @@ -285,4 +245,56 @@ #define APP_DESKTOP_ENTRY_PATH QSL(":/desktop") #endif +// +// Source code specific enhancements. +// +#if QT_VERSION >= 0x050E00 // Qt >= 5.14.0 +#define FROM_STD_LIST(x, y) (x(y.begin(), y.end())) +#else +#define FROM_STD_LIST(x, y) (x::fromStdList(y)) +#endif + +#ifndef qDebugNN +#define qDebugNN qDebug().noquote().nospace() +#endif + +#ifndef qWarningNN +#define qWarningNN qWarning().noquote().nospace() +#endif + +#ifndef qCriticalNN +#define qCriticalNN qCritical().noquote().nospace() +#endif + +#ifndef qInfoNN +#define qInfoNN qInfo().noquote().nospace() +#endif + +#define QUOTE_W_SPACE_DOT(x) " '" << (x) << "'." +#define QUOTE_W_SPACE(x) " '" << (x) << "' " +#define QUOTE_NO_SPACE(x) "'" << (x) << "'" + +#ifndef QSL + +// Thin macro wrapper for literal strings. +// They are much more memory efficient and faster. +// Use it for all literals except for two cases: +// a) Methods which take QLatin1String (use QLatin1String for literal argument too), +// b) Construction of empty literals "", use QString() instead of QStringLiteral(""). +#define QSL(x) QStringLiteral(x) +#endif + +#ifndef QL1S + +// Macro for latin strings. Latin strings are +// faster than QStrings created from literals. +#define QL1S(x) QLatin1String(x) +#endif + +#ifndef QL1C + +// Macro for latin chars. +#define QL1C(x) QLatin1Char(x) +#endif + #endif // DEFINITIONS_H diff --git a/src/librssguard/gui/messagesview.cpp b/src/librssguard/gui/messagesview.cpp index 8288ebeda..18accb58c 100644 --- a/src/librssguard/gui/messagesview.cpp +++ b/src/librssguard/gui/messagesview.cpp @@ -237,7 +237,7 @@ void MessagesView::initializeContextMenu() { auto rows = boolinq::from(mapped_indexes).select([](const QModelIndex& idx) { return idx.row(); }).toStdList(); - auto messages = m_sourceModel->messagesAt(QList::fromStdList(rows)); + auto messages = m_sourceModel->messagesAt(FROM_STD_LIST(QList, rows)); auto extra_context_menu = m_sourceModel->loadedItem()->getParentServiceRoot()->contextMenuMessagesList(messages); if (!extra_context_menu.isEmpty()) { diff --git a/src/librssguard/services/abstract/cacheforserviceroot.cpp b/src/librssguard/services/abstract/cacheforserviceroot.cpp index 53806f1cc..4790edaa2 100644 --- a/src/librssguard/services/abstract/cacheforserviceroot.cpp +++ b/src/librssguard/services/abstract/cacheforserviceroot.cpp @@ -2,21 +2,35 @@ #include "services/abstract/cacheforserviceroot.h" +#include "3rd-party/boolinq/boolinq.h" #include "miscellaneous/application.h" #include "miscellaneous/mutex.h" +#include "services/abstract/label.h" #include #include -CacheForServiceRoot::CacheForServiceRoot() : m_cacheSaveMutex(new Mutex(QMutex::NonRecursive, nullptr)) {} +CacheForServiceRoot::CacheForServiceRoot() : m_cacheSaveMutex(new QMutex(QMutex::NonRecursive)) {} -CacheForServiceRoot::~CacheForServiceRoot() { - m_cacheSaveMutex->deleteLater(); +void CacheForServiceRoot::addMessageStatesToCache(const QList& ids_of_messages, Label* lbl, bool assign) { + auto custom_ids = lbl->getParentServiceRoot()->customIDsOfMessages(ids_of_messages); + + if (assign) { + m_cachedLabelAssignments[lbl->customId()].append(custom_ids); + m_cachedLabelAssignments[lbl->customId()].removeDuplicates(); + + // Remove the same messages from "deassign" list. + auto deassign = m_cachedLabelDeassignments[lbl->customId()]; + auto list = boolinq::from(deassign.begin(), deassign.end()).where([custom_ids](const QString& id) { + return !custom_ids.contains(id); + }).toStdList(); + + m_cachedLabelDeassignments[lbl->customId()] = FROM_STD_LIST(QStringList, list); + } } void CacheForServiceRoot::addMessageStatesToCache(const QList& ids_of_messages, RootItem::Importance importance) { - m_cacheSaveMutex->lock(); - + QMutexLocker lck(m_cacheSaveMutex.data()); QList& list_act = m_cachedStatesImportant[importance]; QList& list_other = m_cachedStatesImportant[importance == RootItem::Importance::Important ? RootItem::Importance::NotImportant @@ -39,13 +53,10 @@ void CacheForServiceRoot::addMessageStatesToCache(const QList& ids_of_m list_act.append(set_act.values()); list_other.clear(); list_other.append(set_other.values()); - - m_cacheSaveMutex->unlock(); } void CacheForServiceRoot::addMessageStatesToCache(const QStringList& ids_of_messages, RootItem::ReadStatus read) { - m_cacheSaveMutex->lock(); - + QMutexLocker lck(m_cacheSaveMutex.data()); QStringList& list_act = m_cachedStatesRead[read]; QStringList& list_other = m_cachedStatesRead[read == RootItem::ReadStatus::Read ? RootItem::ReadStatus::Unread @@ -68,12 +79,10 @@ void CacheForServiceRoot::addMessageStatesToCache(const QStringList& ids_of_mess list_act.append(set_act.values()); list_other.clear(); list_other.append(set_other.values()); - - m_cacheSaveMutex->unlock(); } void CacheForServiceRoot::saveCacheToFile(int acc_id) { - m_cacheSaveMutex->lock(); + QMutexLocker lck(m_cacheSaveMutex.data()); // Save to file. const QString file_cache = qApp->userDataFolder() + QDir::separator() + QString::number(acc_id) + "-cached-msgs.dat"; @@ -87,15 +96,13 @@ void CacheForServiceRoot::saveCacheToFile(int acc_id) { if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QDataStream stream(&file); - stream << m_cachedStatesImportant << m_cachedStatesRead; + stream << m_cachedStatesImportant << m_cachedStatesRead << m_cachedLabelAssignments << m_cachedLabelDeassignments; file.flush(); file.close(); } clearCache(); } - - m_cacheSaveMutex->unlock(); } void CacheForServiceRoot::clearCache() { @@ -104,7 +111,8 @@ void CacheForServiceRoot::clearCache() { } void CacheForServiceRoot::loadCacheFromFile(int acc_id) { - m_cacheSaveMutex->lock(); + QMutexLocker lck(m_cacheSaveMutex.data()); + clearCache(); // Load from file. @@ -115,24 +123,19 @@ void CacheForServiceRoot::loadCacheFromFile(int acc_id) { if (file.open(QIODevice::ReadOnly)) { QDataStream stream(&file); - stream >> m_cachedStatesImportant >> m_cachedStatesRead; + stream >> m_cachedStatesImportant >> m_cachedStatesRead >> m_cachedLabelAssignments >> m_cachedLabelDeassignments; file.flush(); file.close(); } file.remove(); } - - m_cacheSaveMutex->unlock(); } QPair, QMap>> CacheForServiceRoot::takeMessageCache() { - m_cacheSaveMutex->lock(); + QMutexLocker lck(m_cacheSaveMutex.data()); if (isEmpty()) { - // No cached changes. - m_cacheSaveMutex->unlock(); - return QPair, QMap>>(); } @@ -146,11 +149,11 @@ QPair, QMapunlock(); return QPair, QMap>>(cached_data_read, cached_data_imp); } bool CacheForServiceRoot::isEmpty() const { - return m_cachedStatesRead.isEmpty() && m_cachedStatesImportant.isEmpty(); + return m_cachedStatesRead.isEmpty() && m_cachedStatesImportant.isEmpty() && + m_cachedLabelAssignments.isEmpty() && m_cachedLabelDeassignments.isEmpty(); } diff --git a/src/librssguard/services/abstract/cacheforserviceroot.h b/src/librssguard/services/abstract/cacheforserviceroot.h index 74f228665..d260da7ef 100644 --- a/src/librssguard/services/abstract/cacheforserviceroot.h +++ b/src/librssguard/services/abstract/cacheforserviceroot.h @@ -9,13 +9,13 @@ #include #include -class Mutex; +class QMutex; class CacheForServiceRoot { public: explicit CacheForServiceRoot(); - virtual ~CacheForServiceRoot(); + void addMessageStatesToCache(const QList& ids_of_messages, Label* lbl, bool assign); void addMessageStatesToCache(const QList& ids_of_messages, RootItem::Importance importance); void addMessageStatesToCache(const QStringList& ids_of_messages, RootItem::ReadStatus read); @@ -29,9 +29,20 @@ class CacheForServiceRoot { protected: QPair, QMap>> takeMessageCache(); - Mutex* m_cacheSaveMutex; + QScopedPointer m_cacheSaveMutex; + // Map where key is label's custom ID and value is list of message custom IDs + // which we want to assign to the label. + QMap m_cachedLabelAssignments; + + // Map where key is label's custom ID and value is list of message custom IDs + // which we want to remove from the label assignment. + QMap m_cachedLabelDeassignments; + + // Map of cached read/unread changes. QMap m_cachedStatesRead; + + // Map of cached important/unimportant changes. QMap> m_cachedStatesImportant; private: diff --git a/src/librssguard/services/abstract/labelsnode.cpp b/src/librssguard/services/abstract/labelsnode.cpp index e4f0ed269..b6ed94dcd 100755 --- a/src/librssguard/services/abstract/labelsnode.cpp +++ b/src/librssguard/services/abstract/labelsnode.cpp @@ -27,9 +27,11 @@ void LabelsNode::loadLabels(const QList& labels) { } QList LabelsNode::labels() const { - return QList::fromStdList(boolinq::from(childItems()).select([](RootItem* it) { + auto list = boolinq::from(childItems()).select([](RootItem* it) { return static_cast(it); - }).toStdList()); + }).toStdList(); + + return FROM_STD_LIST(QList, list); } QList LabelsNode::contextMenuFeedsList() { diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index 86c19af33..ccf6a354b 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -296,10 +296,6 @@ LabelsNode* ServiceRoot::labelsNode() const { return m_labelsNode; } -void ServiceRoot::setRecycleBin(RecycleBin* recycle_bin) { - m_recycleBin = recycle_bin; -} - void ServiceRoot::syncIn() { QIcon original_icon = icon(); diff --git a/src/librssguard/services/abstract/serviceroot.h b/src/librssguard/services/abstract/serviceroot.h index 6ec4bed8e..cd8d6d985 100644 --- a/src/librssguard/services/abstract/serviceroot.h +++ b/src/librssguard/services/abstract/serviceroot.h @@ -32,19 +32,15 @@ class ServiceRoot : public RootItem { explicit ServiceRoot(RootItem* parent = nullptr); virtual ~ServiceRoot(); - void updateCounts(bool including_total_count); - bool deleteViaGui(); - bool markAsReadUnread(ReadStatus status); - + // These methods bellow are part of "interface". + virtual void updateCounts(bool including_total_count); + virtual bool deleteViaGui(); + virtual bool markAsReadUnread(ReadStatus status); virtual RecycleBin* recycleBin() const; - - void setRecycleBin(RecycleBin* recycle_bin); - virtual ImportantNode* importantNode() const; virtual LabelsNode* labelsNode() const; virtual bool downloadAttachmentOnMyOwn(const QUrl& url) const; - - QList undeletedMessages() const; + virtual QList undeletedMessages() const; virtual bool supportsFeedAdding() const; virtual bool supportsCategoryAdding() const; @@ -53,13 +49,13 @@ class ServiceRoot : public RootItem { // a) Add new feed // b) Add new category // c) ... - // NOTE: Caller does NOT take ownership of created menu! + // NOTE: Caller does NOT take ownership of created menu/actions! virtual QList addItemMenu(); - // NOTE: Caller does NOT take ownership of created menu! + // NOTE: Caller does NOT take ownership of created menu/actions! virtual QList contextMenuFeedsList(); - // NOTE: Caller does NOT take ownership of created menu! + // NOTE: Caller does NOT take ownership of created menu/actions! virtual QList contextMenuMessagesList(const QList& messages); // Returns list of specific actions to be shown in main window menu @@ -67,7 +63,7 @@ class ServiceRoot : public RootItem { // NOTE: Caller does NOT take ownership of created menu! virtual QList serviceMenu(); - // If plugin uses online synchronization, then returns true. + // If plugin uses online synchronization of feeds/labels/etc, then returns true. virtual bool isSyncable() const; // Start/stop services. @@ -80,29 +76,6 @@ class ServiceRoot : public RootItem { virtual void start(bool freshly_activated); virtual void stop(); - // Account ID corresponds with DB attribute Accounts (id). - int accountId() const; - void setAccountId(int account_id); - - // Returns the UNIQUE code of the given service. - // NOTE: Keep in sync with ServiceEntryRoot::code(). - virtual QString code() const = 0; - - // Removes all/read only messages from given underlying feeds. - bool cleanFeeds(QList items, bool clean_read_only); - - void completelyRemoveAllData(); - QStringList customIDSOfMessagesForItem(RootItem* item); - bool markFeedsReadUnread(QList items, ReadStatus read); - - // Obvious methods to wrap signals. - void itemChanged(const QList& items); - void requestReloadMessageList(bool mark_selected_messages_read); - void requestItemExpand(const QList& items, bool expand); - void requestItemExpandStateSave(RootItem* subtree_root); - void requestItemReassignment(RootItem* item, RootItem* new_parent); - void requestItemRemoval(RootItem* item); - // This method should prepare messages for given "item" (download them maybe?) // into predefined "Messages" table // and then use method QSqlTableModel::setFilter(....). @@ -160,6 +133,43 @@ class ServiceRoot : public RootItem { // Selected item is naturally recycle bin. virtual bool onAfterMessagesRestoredFromBin(RootItem* selected_item, const QList& messages); + // Returns the UNIQUE code of the given service. + // NOTE: Keep in sync with ServiceEntryRoot::code(). + virtual QString code() const = 0; + + // These are not part of "interface". + + public: + + // Account ID corresponds with DB attribute Accounts (id). + int accountId() const; + void setAccountId(int account_id); + + // Removes all data associated with this account from DB + // and from model. + void completelyRemoveAllData(); + + // Removes all/read only messages from given underlying feeds. + bool cleanFeeds(QList items, bool clean_read_only); + + // Marks all messages from feeds read/unread. + bool markFeedsReadUnread(QList items, ReadStatus read); + + // Obvious methods to wrap signals. + void itemChanged(const QList& items); + void requestReloadMessageList(bool mark_selected_messages_read); + void requestItemExpand(const QList& items, bool expand); + void requestItemExpandStateSave(RootItem* subtree_root); + void requestItemReassignment(RootItem* item, RootItem* new_parent); + void requestItemRemoval(RootItem* item); + + // Some message/feed attribute selectors. + QStringList textualFeedUrls(const QList& feeds) const; + QStringList textualFeedIds(const QList& feeds) const; + QStringList customIDsOfMessages(const QList& changes); + QStringList customIDsOfMessages(const QList& messages); + QStringList customIDSOfMessagesForItem(RootItem* item); + public slots: virtual void addNewFeed(RootItem* selected_item, const QString& url = QString()); virtual void addNewCategory(RootItem* selected_item); @@ -196,11 +206,6 @@ class ServiceRoot : public RootItem { // assigned from non-existing labels. void removeLeftOverMessageLabelAssignments(); - QStringList textualFeedUrls(const QList& feeds) const; - QStringList textualFeedIds(const QList& feeds) const; - QStringList customIDsOfMessages(const QList& changes); - QStringList customIDsOfMessages(const QList& messages); - // Takes lists of feeds/categories and assembles them into the tree structure. void assembleCategories(Assignment categories); void assembleFeeds(Assignment feeds);