diff --git a/src/librssguard/core/feedsmodel.cpp b/src/librssguard/core/feedsmodel.cpp index 21434dac1..bb5b0c9c3 100644 --- a/src/librssguard/core/feedsmodel.cpp +++ b/src/librssguard/core/feedsmodel.cpp @@ -365,7 +365,7 @@ QListFeedsModel::feedsForScheduledUpdate(bool auto_update_now) { return feeds_for_update; } -QListFeedsModel::messagesForItem(RootItem* item) const { +QList FeedsModel::messagesForItem(RootItem* item) const { return item->undeletedMessages(); } diff --git a/src/librssguard/core/messagesmodel.cpp b/src/librssguard/core/messagesmodel.cpp index 675611e05..090e7d4f4 100644 --- a/src/librssguard/core/messagesmodel.cpp +++ b/src/librssguard/core/messagesmodel.cpp @@ -362,6 +362,8 @@ bool MessagesModel::setMessageRead(int row_index, RootItem::ReadStatus read) { } bool MessagesModel::setMessageReadById(int id, RootItem::ReadStatus read) { + int a = 5; + for (int i = 0; i < rowCount(); i++) { int found_id = data(i, MSG_DB_ID_INDEX, Qt::EditRole).toInt(); @@ -415,7 +417,6 @@ bool MessagesModel::switchMessageImportance(int row_index) { bool MessagesModel::switchBatchMessageImportance(const QModelIndexList& messages) { QStringList message_ids; - QList> message_states; // Obtain IDs of all desired messages. @@ -423,6 +424,7 @@ bool MessagesModel::switchBatchMessageImportance(const QModelIndexList& messages const Message msg = messageAt(message.row()); RootItem::Importance message_importance = messageImportance((message.row())); + message_states.append(QPair(msg, message_importance == RootItem::Important ? RootItem::NotImportant : RootItem::Important)); @@ -450,7 +452,6 @@ bool MessagesModel::switchBatchMessageImportance(const QModelIndexList& messages bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList& messages) { QStringList message_ids; - QList msgs; // Obtain IDs of all desired messages. @@ -493,7 +494,6 @@ bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList& messages) { bool MessagesModel::setBatchMessagesRead(const QModelIndexList& messages, RootItem::ReadStatus read) { QStringList message_ids; - QList msgs; // Obtain IDs of all desired messages. @@ -521,7 +521,6 @@ bool MessagesModel::setBatchMessagesRead(const QModelIndexList& messages, RootIt bool MessagesModel::setBatchMessagesRestored(const QModelIndexList& messages) { QStringList message_ids; - QList msgs; // Obtain IDs of all desired messages. diff --git a/src/librssguard/core/messagesmodel.h b/src/librssguard/core/messagesmodel.h index 9ad7bd052..ddbf4d83f 100644 --- a/src/librssguard/core/messagesmodel.h +++ b/src/librssguard/core/messagesmodel.h @@ -85,10 +85,8 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer { MessageHighlighter m_messageHighlighter; QString m_customDateFormat; RootItem* m_selectedItem; - QList m_headerData; QList m_tooltipData; - QFont m_normalFont; QFont m_boldFont; QFont m_normalStrikedFont; diff --git a/src/librssguard/gui/feedsview.cpp b/src/librssguard/gui/feedsview.cpp index ac0606689..bafffeda8 100755 --- a/src/librssguard/gui/feedsview.cpp +++ b/src/librssguard/gui/feedsview.cpp @@ -29,7 +29,7 @@ FeedsView::FeedsView(QWidget* parent) : QTreeView(parent), m_contextMenuService(nullptr), m_contextMenuBin(nullptr), m_contextMenuCategories(nullptr), - m_contextMenuFeeds(nullptr), m_contextMenuEmptySpace(nullptr), m_contextMenuOtherItems(nullptr) { + m_contextMenuFeeds(nullptr), m_contextMenuImportant(nullptr), m_contextMenuEmptySpace(nullptr), m_contextMenuOtherItems(nullptr) { setObjectName(QSL("FeedsView")); // Allocate models. @@ -95,7 +95,6 @@ void FeedsView::saveAllExpandStates() { void FeedsView::saveExpandStates(RootItem* item) { Settings* settings = qApp->settings(); - QList items = item->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot); // Iterate all categories and save their expand statuses. @@ -112,8 +111,8 @@ void FeedsView::saveExpandStates(RootItem* item) { void FeedsView::loadAllExpandStates() { const Settings* settings = qApp->settings(); - QList expandable_items; + expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot)); // Iterate all categories and save their expand statuses. @@ -432,6 +431,7 @@ QMenu* FeedsView::initializeContextMenuBin(RootItem* clicked_item) { } QList specific_actions = clicked_item->contextMenu(); + m_contextMenuBin->addActions(QList() << qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode << qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead << @@ -454,6 +454,7 @@ QMenu* FeedsView::initializeContextMenuService(RootItem* clicked_item) { } QList specific_actions = clicked_item->contextMenu(); + m_contextMenuService->addActions(QList() << qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << qApp->mainForm()->m_ui->m_actionEditSelectedItem << @@ -484,7 +485,7 @@ void FeedsView::focusInEvent(QFocusEvent* event) { } void FeedsView::expandItemDelayed(const QModelIndex& idx) { - QTimer::singleShot(100, this, [ = ] { + QTimer::singleShot(100, this, [=] { setExpanded(m_proxyModel->mapFromSource(idx), true); }); } @@ -498,6 +499,7 @@ QMenu* FeedsView::initializeContextMenuCategories(RootItem* clicked_item) { } QList specific_actions = clicked_item->contextMenu(); + m_contextMenuCategories->addActions(QList() << qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << qApp->mainForm()->m_ui->m_actionEditSelectedItem << @@ -524,6 +526,7 @@ QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) { } QList specific_actions = clicked_item->contextMenu(); + m_contextMenuFeeds->addActions(QList() << qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << qApp->mainForm()->m_ui->m_actionEditSelectedItem << @@ -541,6 +544,29 @@ QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) { return m_contextMenuFeeds; } +QMenu* FeedsView::initializeContextMenuImportant(RootItem* clicked_item) { + if (m_contextMenuImportant == nullptr) { + m_contextMenuImportant = new QMenu(tr("Context menu for important messages"), this); + } + else { + m_contextMenuImportant->clear(); + } + + QList specific_actions = clicked_item->contextMenu(); + + m_contextMenuImportant->addActions(QList() << + qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode << + qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead << + qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread); + + if (!specific_actions.isEmpty()) { + m_contextMenuImportant->addSeparator(); + m_contextMenuImportant->addActions(specific_actions); + } + + return m_contextMenuImportant; +} + QMenu* FeedsView::initializeContextMenuEmptySpace() { if (m_contextMenuEmptySpace == nullptr) { m_contextMenuEmptySpace = new QMenu(tr("Context menu for empty space"), this); @@ -628,6 +654,9 @@ void FeedsView::contextMenuEvent(QContextMenuEvent* event) { // Display context menu for feeds. initializeContextMenuFeeds(clicked_item)->exec(event->globalPos()); } + else if (clicked_item->kind() == RootItemKind::Important) { + initializeContextMenuImportant(clicked_item)->exec(event->globalPos()); + } else if (clicked_item->kind() == RootItemKind::Bin) { initializeContextMenuBin(clicked_item)->exec(event->globalPos()); } diff --git a/src/librssguard/gui/feedsview.h b/src/librssguard/gui/feedsview.h index a63e1a63c..d1f28f26a 100755 --- a/src/librssguard/gui/feedsview.h +++ b/src/librssguard/gui/feedsview.h @@ -114,18 +114,18 @@ class RSSGUARD_DLLSPEC FeedsView : public QTreeView { QMenu* initializeContextMenuService(RootItem* clicked_item); QMenu* initializeContextMenuCategories(RootItem* clicked_item); QMenu* initializeContextMenuFeeds(RootItem* clicked_item); + QMenu* initializeContextMenuImportant(RootItem* clicked_item); QMenu* initializeContextMenuEmptySpace(); QMenu* initializeContextMenuOtherItem(RootItem* clicked_item); - // Sets up appearance of this widget. void setupAppearance(); - void saveExpandStates(RootItem* item); QMenu* m_contextMenuService; QMenu* m_contextMenuBin; QMenu* m_contextMenuCategories; QMenu* m_contextMenuFeeds; + QMenu* m_contextMenuImportant; QMenu* m_contextMenuEmptySpace; QMenu* m_contextMenuOtherItems; FeedsModel* m_sourceModel; diff --git a/src/librssguard/gui/tabwidget.cpp b/src/librssguard/gui/tabwidget.cpp index 3b61b80a6..0129fc0df 100644 --- a/src/librssguard/gui/tabwidget.cpp +++ b/src/librssguard/gui/tabwidget.cpp @@ -184,15 +184,27 @@ void TabWidget::closeAllTabs() { int TabWidget::addNewspaperView(RootItem* root, const QList& messages) { #if defined(USE_WEBENGINE) WebBrowser* prev = new WebBrowser(this); + + connect(prev, &WebBrowser::markMessageRead, + m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageReadById); + connect(prev, &WebBrowser::markMessageImportant, + m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageImportantById); #else NewspaperPreviewer* prev = new NewspaperPreviewer(root, messages, this); -#endif - int index = addTab(prev, qApp->icons()->fromTheme(QSL("format-justify-fill")), tr("Newspaper view"), TabBar::Closable); + connect(prev, &MessagePreviewer::markMessageRead, + m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageReadById); + connect(prev, &MessagePreviewer::markMessageImportant, + m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageImportantById); +#endif + + int index = addTab(prev, qApp->icons()->fromTheme(QSL("format-justify-fill")), tr("Newspaper view"), TabBar::Closable); setCurrentIndex(index); + #if defined(USE_WEBENGINE) prev->loadMessages(messages, root); #endif + return index; } @@ -210,7 +222,6 @@ int TabWidget::addLinkedBrowser(const QString& initial_url) { int TabWidget::addBrowser(bool move_after_current, bool make_active, const QUrl& initial_url) { #if defined(USE_WEBENGINE) - // Create new WebBrowser. WebBrowser* browser = new WebBrowser(this); int final_index; diff --git a/src/librssguard/miscellaneous/databasequeries.cpp b/src/librssguard/miscellaneous/databasequeries.cpp index 9190f3e46..f00fcb003 100755 --- a/src/librssguard/miscellaneous/databasequeries.cpp +++ b/src/librssguard/miscellaneous/databasequeries.cpp @@ -30,6 +30,17 @@ #include #include +bool DatabaseQueries::markImportantMessagesReadUnread(const QSqlDatabase& db, int account_id, RootItem::ReadStatus read) { + QSqlQuery q(db); + + q.setForwardOnly(true); + q.prepare("UPDATE Messages SET is_read = :read " + "WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); + q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); + q.bindValue(QSL(":account_id"), account_id); + return q.exec(); +} + bool DatabaseQueries::markMessagesReadUnread(const QSqlDatabase& db, const QStringList& ids, RootItem::ReadStatus read) { QSqlQuery q(db); @@ -366,6 +377,39 @@ int DatabaseQueries::getMessageCountsForBin(const QSqlDatabase& db, int account_ } } +QList DatabaseQueries::getUndeletedImportantMessages(const QSqlDatabase& db, int account_id, bool* ok) { + QList messages; + QSqlQuery q(db); + + q.setForwardOnly(true); + q.prepare("SELECT id, is_read, is_deleted, is_important, custom_id, title, url, author, date_created, contents, is_pdeleted, enclosures, account_id, custom_id, custom_hash, feed, CASE WHEN length(Messages.enclosures) > 10 THEN 'true' ELSE 'false' END AS has_enclosures " + "FROM Messages " + "WHERE 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()) { + while (q.next()) { + bool decoded; + Message message = Message::fromSqlRecord(q.record(), &decoded); + + if (decoded) { + messages.append(message); + } + } + + if (ok != nullptr) { + *ok = true; + } + } + else { + if (ok != nullptr) { + *ok = false; + } + } + + return messages; +} + QList DatabaseQueries::getUndeletedMessagesForFeed(const QSqlDatabase& db, const QString& feed_custom_id, int account_id, bool* ok) { QList messages; @@ -766,6 +810,32 @@ bool DatabaseQueries::deleteAccountData(const QSqlDatabase& db, int account_id, return result; } +bool DatabaseQueries::cleanImportantMessages(const QSqlDatabase& db, bool clean_read_only, int account_id) { + QSqlQuery q(db); + + q.setForwardOnly(true); + + if (clean_read_only) { + q.prepare(QSL("UPDATE Messages SET is_deleted = :deleted " + "WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND is_read = 1 AND account_id = :account_id;")); + } + else { + q.prepare(QSL("UPDATE Messages SET is_deleted = :deleted " + "WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;")); + } + + q.bindValue(QSL(":deleted"), 1); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qDebug("Cleaning of important messages failed: '%s'.", qPrintable(q.lastError().text())); + return false; + } + else { + return true; + } +} + bool DatabaseQueries::cleanFeeds(const QSqlDatabase& db, const QStringList& ids, bool clean_read_only, int account_id) { QSqlQuery q(db); @@ -883,6 +953,29 @@ QStringList DatabaseQueries::customIdsOfMessagesFromAccount(const QSqlDatabase& return ids; } +QStringList DatabaseQueries::customIdsOfImportantMessages(const QSqlDatabase& db, int account_id, bool* ok) { + QSqlQuery q(db); + QStringList ids; + + q.setForwardOnly(true); + q.prepare(QSL("SELECT custom_id FROM Messages " + "WHERE is_important = 1 AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + + if (ok != nullptr) { + *ok = q.exec(); + } + else { + q.exec(); + } + + while (q.next()) { + ids.append(q.value(0).toString()); + } + + return ids; +} + QStringList DatabaseQueries::customIdsOfMessagesFromBin(const QSqlDatabase& db, int account_id, bool* ok) { QSqlQuery q(db); QStringList ids; diff --git a/src/librssguard/miscellaneous/databasequeries.h b/src/librssguard/miscellaneous/databasequeries.h index 26c9094f6..3c5839b76 100644 --- a/src/librssguard/miscellaneous/databasequeries.h +++ b/src/librssguard/miscellaneous/databasequeries.h @@ -14,6 +14,7 @@ class DatabaseQueries { public: // Mark read/unread/starred/delete messages. + static bool markImportantMessagesReadUnread(const QSqlDatabase& db, int account_id, RootItem::ReadStatus read); static bool markMessagesReadUnread(const QSqlDatabase& db, const QStringList& ids, RootItem::ReadStatus read); static bool markMessageImportant(const QSqlDatabase& db, int id, RootItem::Importance importance); static bool markFeedsReadUnread(const QSqlDatabase& db, const QStringList& ids, int account_id, RootItem::ReadStatus read); @@ -45,6 +46,9 @@ class DatabaseQueries { static int getMessageCountsForBin(const QSqlDatabase& db, int account_id, bool including_total_counts, bool* ok = nullptr); // Get messages (for newspaper view for example). + static QList getUndeletedImportantMessages(const QSqlDatabase& db, + int account_id, + bool* ok = nullptr); static QList getUndeletedMessagesForFeed(const QSqlDatabase& db, const QString& feed_custom_id, int account_id, @@ -53,6 +57,7 @@ class DatabaseQueries { static QList getUndeletedMessagesForAccount(const QSqlDatabase& db, int account_id, bool* ok = nullptr); // Custom ID accumulators. + static QStringList customIdsOfImportantMessages(const QSqlDatabase& db, int account_id, bool* ok = nullptr); static QStringList customIdsOfMessagesFromAccount(const QSqlDatabase& db, int account_id, bool* ok = nullptr); static QStringList customIdsOfMessagesFromBin(const QSqlDatabase& db, int account_id, bool* ok = nullptr); static QStringList customIdsOfMessagesFromFeed(const QSqlDatabase& db, const QString& feed_custom_id, int account_id, @@ -63,6 +68,7 @@ class DatabaseQueries { int account_id, const QString& url, bool* any_message_changed, bool* ok = nullptr); static bool deleteAccount(const QSqlDatabase& db, int account_id); static bool deleteAccountData(const QSqlDatabase& db, int account_id, bool delete_messages_too); + static bool cleanImportantMessages(const QSqlDatabase& db, bool clean_read_only, int account_id); static bool cleanFeeds(const QSqlDatabase& db, const QStringList& ids, bool clean_read_only, int account_id); static bool storeAccountTree(const QSqlDatabase& db, RootItem* tree_root, int account_id); static bool editBaseFeed(const QSqlDatabase& db, int feed_id, Feed::AutoUpdateType auto_update_type, diff --git a/src/librssguard/services/abstract/importantnode.cpp b/src/librssguard/services/abstract/importantnode.cpp index 05fc280e4..032bc6493 100755 --- a/src/librssguard/services/abstract/importantnode.cpp +++ b/src/librssguard/services/abstract/importantnode.cpp @@ -5,6 +5,7 @@ #include "miscellaneous/application.h" #include "miscellaneous/databasequeries.h" #include "miscellaneous/iconfactory.h" +#include "services/abstract/cacheforserviceroot.h" #include "services/abstract/serviceroot.h" #include @@ -18,6 +19,12 @@ ImportantNode::ImportantNode(RootItem* parent_item) : RootItem(parent_item) { setCreationDate(QDateTime::currentDateTime()); } +QList ImportantNode::undeletedMessages() const { + QSqlDatabase database = qApp->database()->connection(metaObject()->className()); + + return DatabaseQueries::getUndeletedImportantMessages(database, getParentServiceRoot()->accountId()); +} + void ImportantNode::updateCounts(bool including_total_count) { bool is_main_thread = QThread::currentThread() == qApp->thread(); QSqlDatabase database = is_main_thread ? @@ -32,6 +39,42 @@ void ImportantNode::updateCounts(bool including_total_count) { m_unreadCount = DatabaseQueries::getImportantMessageCounts(database, account_id, false); } +bool ImportantNode::cleanMessages(bool clean_read_only) { + ServiceRoot* service = getParentServiceRoot(); + QSqlDatabase database = qApp->database()->connection(metaObject()->className()); + + if (DatabaseQueries::cleanImportantMessages(database, clean_read_only, service->accountId())) { + service->updateCounts(true); + service->itemChanged(getSubTree()); + service->requestReloadMessageList(true); + return true; + } + else { + return false; + } +} + +bool ImportantNode::markAsReadUnread(RootItem::ReadStatus status) { + ServiceRoot* service = getParentServiceRoot(); + auto* cache = dynamic_cast(service); + + if (cache != nullptr) { + cache->addMessageStatesToCache(service->customIDSOfMessagesForItem(this), status); + } + + QSqlDatabase database = qApp->database()->connection(metaObject()->className()); + + if (DatabaseQueries::markImportantMessagesReadUnread(database, service->accountId(), status)) { + service->updateCounts(true); + service->itemChanged(getSubTree()); + service->requestReloadMessageList(status == RootItem::Read); + return true; + } + else { + return false; + } +} + int ImportantNode::countOfUnreadMessages() const { return m_unreadCount; } diff --git a/src/librssguard/services/abstract/importantnode.h b/src/librssguard/services/abstract/importantnode.h index 42e662885..2b11dd6f1 100755 --- a/src/librssguard/services/abstract/importantnode.h +++ b/src/librssguard/services/abstract/importantnode.h @@ -12,7 +12,10 @@ class ImportantNode : public RootItem { explicit ImportantNode(RootItem* parent_item = nullptr); virtual ~ImportantNode() = default; + QList undeletedMessages() const; + bool cleanMessages(bool clean_read_only); void updateCounts(bool including_total_count); + bool markAsReadUnread(ReadStatus status); int countOfUnreadMessages() const; int countOfAllMessages() const; diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index 86a0e6a70..aafac4a66 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -382,6 +382,13 @@ QStringList ServiceRoot::customIDSOfMessagesForItem(RootItem* item) { break; } + case RootItemKind::Important: { + QSqlDatabase database = qApp->database()->connection(metaObject()->className()); + + list = DatabaseQueries::customIdsOfImportantMessages(database, accountId()); + break; + } + default: break; }