diff --git a/resources/scripts/uncrustify/uncrustify.cfg b/resources/scripts/uncrustify/uncrustify.cfg index 1fb944e60..a33d9f3a5 100644 --- a/resources/scripts/uncrustify/uncrustify.cfg +++ b/resources/scripts/uncrustify/uncrustify.cfg @@ -233,10 +233,13 @@ nl_before_c_comment = 2 # unsigned number nl_before_cpp_comment = 2 # unsigned number # The number of newlines before a class definition -nl_before_class = 2 # unsigned number +nl_before_class = 0 # unsigned number # The number of newlines after '}' or ';' of a class definition -nl_after_class = 2 # unsigned number +nl_after_class = 0 # unsigned number + +# The number of newlines after '}' or ';' of a struct/enum/union definition. +nl_after_struct = 0 # unsigned number # The number of newlines after '}' of a multi-line function body nl_after_func_body = 2 # unsigned number @@ -265,7 +268,7 @@ nl_remove_extra_newlines = 0 # unsigned number ## Splitting. # Try to limit code width to N number of columns -code_width = 140 # unsigned number +code_width = 160 # unsigned number # Whether to fully split long function protos/calls at commas ls_func_split_full = true # false/true @@ -288,4 +291,4 @@ cmt_c_group = true # false/true # If True, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. -mod_sort_include = true # false/true \ No newline at end of file +mod_sort_include = true # false/true diff --git a/src/librssguard/core/feedsproxymodel.cpp b/src/librssguard/core/feedsproxymodel.cpp index 20ed777af..919956a87 100644 --- a/src/librssguard/core/feedsproxymodel.cpp +++ b/src/librssguard/core/feedsproxymodel.cpp @@ -20,6 +20,18 @@ FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent) setFilterRole(Qt::EditRole); setDynamicSortFilter(true); setSourceModel(m_sourceModel); + + // Describes priorities of node types for sorting. + // Smaller index means that item is "smaller" which + // means it should be more on top when sorting + // in ascending order. + m_priorities = { + RootItemKind::Kind::Category, + RootItemKind::Kind::Feed, + RootItemKind::Kind::Labels, + RootItemKind::Kind::Important, + RootItemKind::Kind::Bin + }; } FeedsProxyModel::~FeedsProxyModel() { @@ -65,7 +77,7 @@ QModelIndexList FeedsProxyModel::match(const QModelIndex& start, int role, const QString item_text = item_value.toString(); switch (match_type) { - case Qt::MatchRegExp: + case Qt::MatchRegularExpression: if (QRegularExpression(entered_text, QRegularExpression::PatternOption::CaseInsensitiveOption | QRegularExpression::PatternOption::UseUnicodePropertiesOption).match(item_text).hasMatch()) { @@ -141,13 +153,13 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right // by item counts, depending on the sort column. if (left_item->keepOnTop()) { - return sortOrder() == Qt::AscendingOrder; + return sortOrder() == Qt::SortOrder::AscendingOrder; } else if (right_item->keepOnTop()) { - return sortOrder() == Qt::DescendingOrder; + return sortOrder() == Qt::SortOrder::DescendingOrder; } else if (left_item->kind() == right_item->kind()) { - // Both items are feeds or both items are categories. + // Both items are of the same type. if (left.column() == FDS_MODEL_COUNTS_INDEX) { // User wants to sort according to counts. return left_item->countOfUnreadMessages() < right_item->countOfUnreadMessages(); @@ -157,24 +169,14 @@ bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right return QString::localeAwareCompare(left_item->title(), right_item->title()) < 0; } } - else if (left_item->kind() == RootItemKind::Bin) { - // Left item is recycle bin. Make sure it is "biggest" item if we have selected ascending order. - return sortOrder() == Qt::DescendingOrder; - } - else if (right_item->kind() == RootItemKind::Bin) { - // Right item is recycle bin. Make sure it is "smallest" item if we have selected descending order. - return sortOrder() == Qt::AscendingOrder; - } - else if (left_item->kind() == RootItemKind::Feed) { - // Left item is feed, right item is category. - return false; - } else { - // Left item is category, right item is feed. - // NOTE: Category is in fact "more" than feed but we consider it to be "less" because it should be "placed" - // above the "smalles" feed when ascending sort is used. - // NOTE: We need to keep recycle bin in first position. - return true; + // We sort using priorities. + auto left_priority = m_priorities.indexOf(left_item->kind()); + auto right_priority = m_priorities.indexOf(right_item->kind()); + + return sortOrder() == Qt::SortOrder::AscendingOrder + ? left_priority < right_priority + : right_priority < left_priority; } } else { diff --git a/src/librssguard/core/feedsproxymodel.h b/src/librssguard/core/feedsproxymodel.h index cecde7e6d..9787b6059 100644 --- a/src/librssguard/core/feedsproxymodel.h +++ b/src/librssguard/core/feedsproxymodel.h @@ -5,16 +5,15 @@ #include +#include "services/abstract/rootitem.h" + class FeedsModel; -class RootItem; class FeedsProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - - // Constructors and destructors. - explicit FeedsProxyModel(FeedsModel* source_model, QObject* parent = 0); + explicit FeedsProxyModel(FeedsModel* source_model, QObject* parent = nullptr); virtual ~FeedsProxyModel(); // Returns index list of items which "match" given value. @@ -51,8 +50,8 @@ class FeedsProxyModel : public QSortFilterProxyModel { FeedsModel* m_sourceModel; const RootItem* m_selectedItem; bool m_showUnreadOnly; - QList> m_hiddenIndices; + QList m_priorities; }; #endif // FEEDSPROXYMODEL_H diff --git a/src/librssguard/core/messagesproxymodel.cpp b/src/librssguard/core/messagesproxymodel.cpp index b2946a773..c3e81d277 100644 --- a/src/librssguard/core/messagesproxymodel.cpp +++ b/src/librssguard/core/messagesproxymodel.cpp @@ -113,7 +113,7 @@ QModelIndexList MessagesProxyModel::match(const QModelIndex& start, int role, QString item_text = item_value.toString(); switch (match_type) { - case Qt::MatchRegExp: + case Qt::MatchRegularExpression: if (QRegularExpression(entered_text, QRegularExpression::PatternOption::CaseInsensitiveOption | QRegularExpression::PatternOption::UseUnicodePropertiesOption).match(item_text).hasMatch()) { diff --git a/src/librssguard/gui/labelwithstatus.cpp b/src/librssguard/gui/labelwithstatus.cpp index 8305f0d65..9653a8fa2 100644 --- a/src/librssguard/gui/labelwithstatus.cpp +++ b/src/librssguard/gui/labelwithstatus.cpp @@ -11,7 +11,7 @@ LabelWithStatus::LabelWithStatus(QWidget* parent) m_wdgInput = new QLabel(this); // Set correct size for the tool button. - int label_height = m_wdgInput->sizeHint().height(); + int label_height = m_wdgInput->sizeHint().height() * 1.2; m_btnStatus->setFixedSize(label_height, label_height); diff --git a/src/librssguard/gui/settings/settingsgui.ui b/src/librssguard/gui/settings/settingsgui.ui index 7e7fc0928..74529d78d 100644 --- a/src/librssguard/gui/settings/settingsgui.ui +++ b/src/librssguard/gui/settings/settingsgui.ui @@ -29,7 +29,7 @@ QTabWidget::North - 2 + 0 diff --git a/src/librssguard/librssguard.pro b/src/librssguard/librssguard.pro index da5a93171..ec243ca05 100644 --- a/src/librssguard/librssguard.pro +++ b/src/librssguard/librssguard.pro @@ -123,6 +123,7 @@ HEADERS += core/feeddownloader.h \ services/abstract/category.h \ services/abstract/feed.h \ services/abstract/gui/formfeeddetails.h \ + services/abstract/importantnode.h \ services/abstract/recyclebin.h \ services/abstract/rootitem.h \ services/abstract/serviceentrypoint.h \ @@ -263,6 +264,7 @@ SOURCES += core/feeddownloader.cpp \ services/abstract/category.cpp \ services/abstract/feed.cpp \ services/abstract/gui/formfeeddetails.cpp \ + services/abstract/importantnode.cpp \ services/abstract/recyclebin.cpp \ services/abstract/rootitem.cpp \ services/abstract/serviceentrypoint.cpp \ diff --git a/src/librssguard/miscellaneous/databasequeries.cpp b/src/librssguard/miscellaneous/databasequeries.cpp index d12dff8ea..9190f3e46 100755 --- a/src/librssguard/miscellaneous/databasequeries.cpp +++ b/src/librssguard/miscellaneous/databasequeries.cpp @@ -171,14 +171,14 @@ bool DatabaseQueries::purgeRecycleBin(const QSqlDatabase& db) { QMap> DatabaseQueries::getMessageCountsForCategory(const QSqlDatabase& db, const QString& custom_id, int account_id, - bool including_total_counts, + bool only_total_counts, bool* ok) { QMap> counts; QSqlQuery q(db); q.setForwardOnly(true); - if (including_total_counts) { + if (only_total_counts) { q.prepare("SELECT feed, sum((is_read + 1) % 2), count(*) FROM Messages " "WHERE feed IN (SELECT custom_id FROM Feeds WHERE category = :category AND account_id = :account_id) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " "GROUP BY feed;"); @@ -197,7 +197,7 @@ QMap> DatabaseQueries::getMessageCountsForCategory(cons QString feed_custom_id = q.value(0).toString(); int unread_count = q.value(1).toInt(); - if (including_total_counts) { + if (only_total_counts) { int total_count = q.value(2).toInt(); counts.insert(feed_custom_id, QPair(unread_count, total_count)); @@ -221,13 +221,13 @@ QMap> DatabaseQueries::getMessageCountsForCategory(cons } QMap> DatabaseQueries::getMessageCountsForAccount(const QSqlDatabase& db, int account_id, - bool including_total_counts, bool* ok) { + bool only_total_counts, bool* ok) { QMap> counts; QSqlQuery q(db); q.setForwardOnly(true); - if (including_total_counts) { + if (only_total_counts) { q.prepare("SELECT feed, sum((is_read + 1) % 2), count(*) FROM Messages " "WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " "GROUP BY feed;"); @@ -245,7 +245,7 @@ QMap> DatabaseQueries::getMessageCountsForAccount(const QString feed_id = q.value(0).toString(); int unread_count = q.value(1).toInt(); - if (including_total_counts) { + if (only_total_counts) { int total_count = q.value(2).toInt(); counts.insert(feed_id, QPair(unread_count, total_count)); @@ -269,12 +269,12 @@ QMap> DatabaseQueries::getMessageCountsForAccount(const } int DatabaseQueries::getMessageCountsForFeed(const QSqlDatabase& db, const QString& feed_custom_id, - int account_id, bool including_total_counts, bool* ok) { + int account_id, bool only_total_counts, bool* ok) { QSqlQuery q(db); q.setForwardOnly(true); - if (including_total_counts) { + if (only_total_counts) { q.prepare("SELECT count(*) FROM Messages " "WHERE feed = :feed AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); } @@ -302,6 +302,38 @@ int DatabaseQueries::getMessageCountsForFeed(const QSqlDatabase& db, const QStri } } +int DatabaseQueries::getImportantMessageCounts(const QSqlDatabase& db, int account_id, bool only_total_counts, bool* ok) { + QSqlQuery q(db); + + q.setForwardOnly(true); + + if (only_total_counts) { + q.prepare("SELECT count(*) FROM Messages " + "WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); + } + else { + q.prepare("SELECT count(*) FROM Messages " + "WHERE is_read = 0 AND is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); + } + + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec() && q.next()) { + if (ok != nullptr) { + *ok = true; + } + + return q.value(0).toInt(); + } + else { + if (ok != nullptr) { + *ok = false; + } + + return 0; + } +} + int DatabaseQueries::getMessageCountsForBin(const QSqlDatabase& db, int account_id, bool including_total_counts, bool* ok) { QSqlQuery q(db); diff --git a/src/librssguard/miscellaneous/databasequeries.h b/src/librssguard/miscellaneous/databasequeries.h index 1077d8fca..26c9094f6 100644 --- a/src/librssguard/miscellaneous/databasequeries.h +++ b/src/librssguard/miscellaneous/databasequeries.h @@ -33,12 +33,15 @@ class DatabaseQueries { static bool purgeLeftoverMessages(const QSqlDatabase& db, int account_id); // Obtain counts of unread/all messages. - static QMap> getMessageCountsForCategory(const QSqlDatabase& db, const QString& custom_id, int account_id, - bool including_total_counts, bool* ok = nullptr); + static QMap> getMessageCountsForCategory(const QSqlDatabase& db, const QString& custom_id, + int account_id, bool only_total_counts, + bool* ok = nullptr); static QMap> getMessageCountsForAccount(const QSqlDatabase& db, int account_id, - bool including_total_counts, bool* ok = nullptr); + bool only_total_counts, bool* ok = nullptr); static int getMessageCountsForFeed(const QSqlDatabase& db, const QString& feed_custom_id, int account_id, - bool including_total_counts, bool* ok = nullptr); + bool only_total_counts, bool* ok = nullptr); + static int getImportantMessageCounts(const QSqlDatabase& db, int account_id, + bool only_total_counts, bool* ok = nullptr); static int getMessageCountsForBin(const QSqlDatabase& db, int account_id, bool including_total_counts, bool* ok = nullptr); // Get messages (for newspaper view for example). diff --git a/src/librssguard/network-web/googlesuggest.h b/src/librssguard/network-web/googlesuggest.h index c5e364995..621f799b1 100644 --- a/src/librssguard/network-web/googlesuggest.h +++ b/src/librssguard/network-web/googlesuggest.h @@ -39,7 +39,6 @@ #include "network-web/downloader.h" class LocationLineEdit; - class QTimer; class GoogleSuggest : public QObject { diff --git a/src/librssguard/services/abstract/importantnode.cpp b/src/librssguard/services/abstract/importantnode.cpp new file mode 100755 index 000000000..05fc280e4 --- /dev/null +++ b/src/librssguard/services/abstract/importantnode.cpp @@ -0,0 +1,41 @@ +// For license of this file, see /LICENSE.md. + +#include "services/abstract/importantnode.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/databasequeries.h" +#include "miscellaneous/iconfactory.h" +#include "services/abstract/serviceroot.h" + +#include + +ImportantNode::ImportantNode(RootItem* parent_item) : RootItem(parent_item) { + setKind(RootItemKind::Important); + setId(ID_RECYCLE_BIN); + setIcon(qApp->icons()->fromTheme(QSL("mail-mark-important"))); + setTitle(tr("Important messages")); + setDescription(tr("You can find all important messages here.")); + setCreationDate(QDateTime::currentDateTime()); +} + +void ImportantNode::updateCounts(bool including_total_count) { + bool is_main_thread = QThread::currentThread() == qApp->thread(); + QSqlDatabase database = is_main_thread ? + qApp->database()->connection(metaObject()->className()) : + qApp->database()->connection(QSL("feed_upd")); + int account_id = getParentServiceRoot()->accountId(); + + if (including_total_count) { + m_totalCount = DatabaseQueries::getImportantMessageCounts(database, account_id, true); + } + + m_unreadCount = DatabaseQueries::getImportantMessageCounts(database, account_id, false); +} + +int ImportantNode::countOfUnreadMessages() const { + return m_unreadCount; +} + +int ImportantNode::countOfAllMessages() const { + return m_totalCount; +} diff --git a/src/librssguard/services/abstract/importantnode.h b/src/librssguard/services/abstract/importantnode.h new file mode 100755 index 000000000..42e662885 --- /dev/null +++ b/src/librssguard/services/abstract/importantnode.h @@ -0,0 +1,24 @@ +// For license of this file, see /LICENSE.md. + +#ifndef IMPORTANTNODE_H +#define IMPORTANTNODE_H + +#include "services/abstract/rootitem.h" + +class ImportantNode : public RootItem { + Q_OBJECT + + public: + explicit ImportantNode(RootItem* parent_item = nullptr); + virtual ~ImportantNode() = default; + + void updateCounts(bool including_total_count); + int countOfUnreadMessages() const; + int countOfAllMessages() const; + + private: + int m_totalCount{}; + int m_unreadCount{}; +}; + +#endif // IMPORTANTNODE_H diff --git a/src/librssguard/services/abstract/recyclebin.cpp b/src/librssguard/services/abstract/recyclebin.cpp index ad75210bc..525ef2529 100644 --- a/src/librssguard/services/abstract/recyclebin.cpp +++ b/src/librssguard/services/abstract/recyclebin.cpp @@ -21,8 +21,6 @@ RecycleBin::RecycleBin(RootItem* parent_item) : RootItem(parent_item), m_totalCo setCreationDate(QDateTime::currentDateTime()); } -RecycleBin::~RecycleBin() = default; - QString RecycleBin::additionalTooltip() const { return tr("%n deleted message(s).", nullptr, countOfAllMessages()); } diff --git a/src/librssguard/services/abstract/recyclebin.h b/src/librssguard/services/abstract/recyclebin.h index 356b10af5..78c1014fb 100644 --- a/src/librssguard/services/abstract/recyclebin.h +++ b/src/librssguard/services/abstract/recyclebin.h @@ -10,7 +10,7 @@ class RecycleBin : public RootItem { public: explicit RecycleBin(RootItem* parent_item = nullptr); - virtual ~RecycleBin(); + virtual ~RecycleBin() = default; QString additionalTooltip() const; @@ -32,7 +32,6 @@ class RecycleBin : public RootItem { private: int m_totalCount; int m_unreadCount; - QList m_contextMenu; }; diff --git a/src/librssguard/services/abstract/rootitem.cpp b/src/librssguard/services/abstract/rootitem.cpp index 3ec9b9916..c2c29450f 100644 --- a/src/librssguard/services/abstract/rootitem.cpp +++ b/src/librssguard/services/abstract/rootitem.cpp @@ -209,11 +209,25 @@ bool RootItem::performDragDropChange(RootItem* target_item) { return false; } +int RootItem::countOfUnreadMessages() const { + int total_count = 0; + + for (RootItem* child_item : m_childItems) { + if (child_item->kind() != RootItemKind::Kind::Important) { + total_count += child_item->countOfUnreadMessages(); + } + } + + return total_count; +} + int RootItem::countOfAllMessages() const { int total_count = 0; for (RootItem* child_item : m_childItems) { - total_count += child_item->countOfAllMessages(); + if (child_item->kind() != RootItemKind::Kind::Important) { + total_count += child_item->countOfAllMessages(); + } } return total_count; @@ -250,6 +264,7 @@ bool RootItem::isParentOf(const RootItem* child) const { QList RootItem::getSubTree() const { QList children; QList traversable_items; + traversable_items.append(const_cast(this)); // Iterate all nested items. @@ -266,6 +281,7 @@ QList RootItem::getSubTree() const { QList RootItem::getSubTree(RootItemKind::Kind kind_of_item) const { QList children; QList traversable_items; + traversable_items.append(const_cast(this)); // Iterate all nested items. @@ -285,6 +301,7 @@ QList RootItem::getSubTree(RootItemKind::Kind kind_of_item) const { QList RootItem::getSubTreeCategories() const { QList children; QList traversable_items; + traversable_items.append(const_cast(this)); // Iterate all nested items. @@ -304,6 +321,7 @@ QList RootItem::getSubTreeCategories() const { QHash RootItem::getHashedSubTreeCategories() const { QHash children; QList traversable_items; + traversable_items.append(const_cast(this)); // Iterate all nested items. @@ -323,6 +341,7 @@ QHash RootItem::getHashedSubTreeCategories() const { QHash RootItem::getHashedSubTreeFeeds() const { QHash children; QList traversable_items; + traversable_items.append(const_cast(this)); // Iterate all nested items. @@ -342,6 +361,7 @@ QHash RootItem::getHashedSubTreeFeeds() const { QList RootItem::getSubTreeFeeds() const { QList children; QList traversable_items; + traversable_items.append(const_cast(this)); // Iterate all nested items. @@ -457,16 +477,6 @@ void RootItem::setKeepOnTop(bool keep_on_top) { m_keepOnTop = keep_on_top; } -int RootItem::countOfUnreadMessages() const { - int total_count = 0; - - for (RootItem* child_item : m_childItems) { - total_count += child_item->countOfUnreadMessages(); - } - - return total_count; -} - bool RootItem::removeChild(int index) { if (index >= 0 && index < m_childItems.size()) { m_childItems.removeAt(index); diff --git a/src/librssguard/services/abstract/rootitem.h b/src/librssguard/services/abstract/rootitem.h index 4b174e1d1..b8e7bc64a 100644 --- a/src/librssguard/services/abstract/rootitem.h +++ b/src/librssguard/services/abstract/rootitem.h @@ -22,8 +22,10 @@ namespace RootItemKind { Feed = 4, Category = 8, ServiceRoot = 16, - LabelsRoot = 32 + Labels = 32, + Important = 64 }; + inline Kind operator|(Kind a, Kind b) { return static_cast(static_cast(a) | static_cast(b)); } @@ -92,8 +94,6 @@ class RSSGUARD_DLLSPEC RootItem : public QObject { // If this method is called on "recycle bin" instance of your // service account, it should "empty" the recycle bin. virtual bool cleanMessages(bool clear_only_read); - - // Updates counts of all/unread messages for this feed. virtual void updateCounts(bool including_total_count); virtual int row() const; virtual QVariant data(int column, int role) const; @@ -104,6 +104,7 @@ class RSSGUARD_DLLSPEC RootItem : public QObject { // Returns counts of messages of all child items summed up. virtual int countOfUnreadMessages() const; virtual int countOfAllMessages() const; + inline RootItem* parent() const { return m_parentItem; } @@ -215,7 +216,6 @@ class RSSGUARD_DLLSPEC RootItem : public QObject { QIcon m_icon; QDateTime m_creationDate; bool m_keepOnTop; - QList m_childItems; RootItem* m_parentItem; }; diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index 776f057b6..86a0e6a70 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -11,9 +11,11 @@ #include "services/abstract/cacheforserviceroot.h" #include "services/abstract/category.h" #include "services/abstract/feed.h" +#include "services/abstract/importantnode.h" #include "services/abstract/recyclebin.h" -ServiceRoot::ServiceRoot(RootItem* parent) : RootItem(parent), m_recycleBin(new RecycleBin(this)), m_accountId(NO_PARENT_CATEGORY) { +ServiceRoot::ServiceRoot(RootItem* parent) + : RootItem(parent), m_recycleBin(new RecycleBin(this)), m_importantNode(new ImportantNode(this)), m_accountId(NO_PARENT_CATEGORY) { setKind(RootItemKind::ServiceRoot); setCreationDate(QDateTime::currentDateTime()); } @@ -98,7 +100,6 @@ void ServiceRoot::updateCounts(bool including_total_count) { QSqlDatabase database = qApp->database()->connection(metaObject()->className()); bool ok; - QMap> counts = DatabaseQueries::getMessageCountsForAccount(database, accountId(), including_total_count, &ok); if (ok) { @@ -163,6 +164,13 @@ bool ServiceRoot::cleanFeeds(QList items, bool clean_read_only) { itemss.append(bin); } + ImportantNode* imp = importantNode(); + + if (imp != nullptr) { + imp->updateCounts(true); + itemss << imp; + } + itemChanged(itemss); requestReloadMessageList(true); return true; @@ -267,6 +275,10 @@ void ServiceRoot::restoreCustomFeedsData(const QMap& data, co } } +ImportantNode* ServiceRoot::importantNode() const { + return m_importantNode; +} + void ServiceRoot::setRecycleBin(RecycleBin* recycle_bin) { m_recycleBin = recycle_bin; } @@ -282,6 +294,7 @@ void ServiceRoot::syncIn() { // Purge old data from SQL and clean all model items. requestItemExpandStateSave(this); QMap feed_custom_data = storeCustomFeedsData(); + removeOldFeedTree(false); cleanAllItems(); restoreCustomFeedsData(feed_custom_data, new_tree->getHashedSubTreeFeeds()); @@ -303,6 +316,7 @@ void ServiceRoot::syncIn() { new_tree->clearChildren(); new_tree->deleteLater(); QList all_items = getSubTree(); + itemChanged(all_items); requestReloadMessageList(true); @@ -388,6 +402,13 @@ bool ServiceRoot::markFeedsReadUnread(QList items, RootItem::ReadStatus r itemss.append(feed); } + ImportantNode* imp = importantNode(); + + if (imp != nullptr) { + imp->updateCounts(true); + itemss << imp; + } + itemChanged(itemss); requestReloadMessageList(read == RootItem::Read); return true; @@ -424,7 +445,7 @@ QStringList ServiceRoot::textualFeedIds(const QList& feeds) const { QStringList ServiceRoot::customIDsOfMessages(const QList& changes) { QStringList list; - for (const auto & change : changes) { + for (const auto& change : changes) { list.append(change.first.m_customId); } @@ -454,6 +475,10 @@ bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) { model->setFilter(QString("Messages.is_deleted = 1 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1") .arg(QString::number(accountId()))); } + else if (item->kind() == RootItemKind::Kind::Important) { + model->setFilter(QString("Messages.is_important = 1 AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1") + .arg(QString::number(accountId()))); + } else { QList children = item->getSubTreeFeeds(); QString filter_clause = textualFeedIds(children).join(QSL(", ")); @@ -485,8 +510,25 @@ bool ServiceRoot::onBeforeSetMessagesRead(RootItem* selected_item, const QList& messages, RootItem::ReadStatus read) { Q_UNUSED(messages) Q_UNUSED(read) - selected_item->updateCounts(false); - itemChanged(QList() << selected_item); + + QList items_to_reload; + ImportantNode* imp = importantNode(); + + if (imp == selected_item) { + updateCounts(true); + items_to_reload << getSubTree(); + } + else { + selected_item->updateCounts(false); + items_to_reload << selected_item; + } + + if (imp != nullptr && imp != selected_item) { + imp->updateCounts(true); + items_to_reload << imp; + } + + itemChanged(items_to_reload); return true; } @@ -524,6 +566,16 @@ bool ServiceRoot::onBeforeSwitchMessageImportance(RootItem* selected_item, const bool ServiceRoot::onAfterSwitchMessageImportance(RootItem* selected_item, const QList& changes) { Q_UNUSED(selected_item) Q_UNUSED(changes) + + QList items_to_reload; + ImportantNode* imp = importantNode(); + + if (imp != nullptr) { + imp->updateCounts(true); + items_to_reload << imp; + } + + itemChanged(items_to_reload); return true; } @@ -536,24 +588,26 @@ bool ServiceRoot::onBeforeMessagesDelete(RootItem* selected_item, const QList& messages) { Q_UNUSED(messages) - // User deleted some messages he selected in message list. + QList items_to_reload; + selected_item->updateCounts(true); + items_to_reload << selected_item; - if (selected_item->kind() == RootItemKind::Bin) { - itemChanged(QList() << selected_item); - } - else { - RecycleBin* bin = recycleBin(); + RecycleBin* bin = recycleBin(); - if (bin != nullptr) { - bin->updateCounts(true); - itemChanged(QList() << selected_item << bin); - } - else { - itemChanged(QList() << selected_item); - } + if (bin != nullptr && bin != selected_item) { + bin->updateCounts(true); + items_to_reload << bin; } + ImportantNode* imp = importantNode(); + + if (imp != nullptr && imp != selected_item) { + imp->updateCounts(true); + items_to_reload << imp; + } + + itemChanged(items_to_reload); return true; } @@ -591,6 +645,7 @@ void ServiceRoot::assembleFeeds(Assignment feeds) { void ServiceRoot::assembleCategories(Assignment categories) { QHash assignments; + assignments.insert(NO_PARENT_CATEGORY, this); // Add top-level categories. diff --git a/src/librssguard/services/abstract/serviceroot.h b/src/librssguard/services/abstract/serviceroot.h index 634a96a0e..631254a38 100644 --- a/src/librssguard/services/abstract/serviceroot.h +++ b/src/librssguard/services/abstract/serviceroot.h @@ -11,6 +11,7 @@ class FeedsModel; class RecycleBin; +class ImportantNode; class QAction; class MessagesModel; @@ -37,6 +38,7 @@ class ServiceRoot : public RootItem { void setRecycleBin(RecycleBin* recycle_bin); + virtual ImportantNode* importantNode() const; virtual bool downloadAttachmentOnMyOwn(const QUrl& url) const; QList undeletedMessages() const; @@ -80,6 +82,18 @@ class ServiceRoot : public RootItem { // 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(....). @@ -137,18 +151,6 @@ class ServiceRoot : public RootItem { // Selected item is naturally recycle bin. virtual bool onAfterMessagesRestoredFromBin(RootItem* selected_item, const QList& messages); - 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); - public slots: virtual void addNewFeed(const QString& url = QString()); virtual void addNewCategory(); @@ -197,6 +199,7 @@ class ServiceRoot : public RootItem { private: RecycleBin* m_recycleBin; + ImportantNode* m_importantNode; int m_accountId; }; diff --git a/src/librssguard/services/standard/standardserviceroot.cpp b/src/librssguard/services/standard/standardserviceroot.cpp index 3fcd47523..755741f72 100644 --- a/src/librssguard/services/standard/standardserviceroot.cpp +++ b/src/librssguard/services/standard/standardserviceroot.cpp @@ -11,6 +11,7 @@ #include "miscellaneous/iconfactory.h" #include "miscellaneous/mutex.h" #include "miscellaneous/settings.h" +#include "services/abstract/importantnode.h" #include "services/abstract/recyclebin.h" #include "services/standard/gui/formstandardcategorydetails.h" #include "services/standard/gui/formstandardfeeddetails.h" @@ -120,6 +121,7 @@ void StandardServiceRoot::addNewFeed(const QString& url) { } QScopedPointer form_pointer(new FormStandardFeedDetails(this, qApp->mainFormWidget())); + form_pointer.data()->addEditFeed(nullptr, nullptr, url); qApp->feedUpdateLock()->unlock(); } @@ -139,6 +141,7 @@ void StandardServiceRoot::loadFromDatabase() { // As the last item, add recycle bin, which is needed. appendChild(recycleBin()); + appendChild(importantNode()); updateCounts(true); } @@ -185,8 +188,10 @@ QList StandardServiceRoot::getContextMenuForFeed(StandardFeed* feed) { bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model, RootItem* target_root_node, QString& output_message) { QStack original_parents; + original_parents.push(target_root_node); QStack new_parents; + new_parents.push(model->rootItem()); bool some_feed_category_error = false; @@ -280,18 +285,21 @@ void StandardServiceRoot::addNewCategory() { } QScopedPointer form_pointer(new FormStandardCategoryDetails(this, qApp->mainFormWidget())); + form_pointer.data()->addEditCategory(nullptr, nullptr); qApp->feedUpdateLock()->unlock(); } void StandardServiceRoot::importFeeds() { QScopedPointer form(new FormStandardImportExport(this, qApp->mainFormWidget())); + form.data()->setMode(FeedsImportExportModel::Import); form.data()->exec(); } void StandardServiceRoot::exportFeeds() { QScopedPointer form(new FormStandardImportExport(this, qApp->mainFormWidget())); + form.data()->setMode(FeedsImportExportModel::Export); form.data()->exec(); } diff --git a/src/librssguard/services/tt-rss/gui/formeditttrssaccount.ui b/src/librssguard/services/tt-rss/gui/formeditttrssaccount.ui index 133426807..687ab428b 100644 --- a/src/librssguard/services/tt-rss/gui/formeditttrssaccount.ui +++ b/src/librssguard/services/tt-rss/gui/formeditttrssaccount.ui @@ -7,7 +7,7 @@ 0 0 604 - 442 + 517 @@ -192,21 +192,21 @@ + + + + Qt::Vertical + + + + 20 + 68 + + + + - - - - Qt::Vertical - - - - 20 - 68 - - - -