From d5ff059543b1757a9fa3b68646ff7c95ec1b8e8a Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Thu, 12 Nov 2015 10:53:17 +0100 Subject: [PATCH] Many refactorings in model/items, some tiny preparations for messages operation - they moved into feed classes. --- src/core/feedsmodel.cpp | 81 ++-------------- src/core/feedsmodel.h | 13 ++- src/core/rootitem.cpp | 42 +++++++++ src/core/rootitem.h | 53 +++++------ src/gui/feedmessageviewer.cpp | 16 ++-- src/gui/feedsview.cpp | 77 ++++++---------- src/gui/feedsview.h | 42 +++------ src/services/abstract/feed.h | 2 +- src/services/standard/standardcategory.cpp | 8 ++ src/services/standard/standardcategory.h | 3 + src/services/standard/standardfeed.cpp | 16 +++- src/services/standard/standardfeed.h | 7 +- src/services/standard/standardserviceroot.cpp | 92 +++++++++++++++++++ src/services/standard/standardserviceroot.h | 7 ++ 14 files changed, 259 insertions(+), 200 deletions(-) diff --git a/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp index e5b8f93c4..828dac9ef 100755 --- a/src/core/feedsmodel.cpp +++ b/src/core/feedsmodel.cpp @@ -439,85 +439,16 @@ Feed *FeedsModel::feedForIndex(const QModelIndex &index) { } } -bool FeedsModel::markFeedsRead(const QList &feeds, int read) { - QSqlDatabase db_handle = qApp->database()->connection(objectName(), DatabaseFactory::FromSettings); - - if (!db_handle.transaction()) { - qWarning("Starting transaction for feeds read change."); - return false; +bool FeedsModel::markItemRead(RootItem *item, RootItem::ReadStatus read) { + if (item->canBeMarkedAsReadUnread(read)) { + return item->markAsReadUnread(read); } - QSqlQuery query_read_msg(db_handle); - query_read_msg.setForwardOnly(true); - - if (!query_read_msg.prepare(QString("UPDATE Messages SET is_read = :read " - "WHERE feed IN (%1) AND is_deleted = 0;").arg(textualFeedIds(feeds).join(QSL(", "))))) { - qWarning("Query preparation failed for feeds read change."); - - db_handle.rollback(); - return false; - } - - query_read_msg.bindValue(QSL(":read"), read); - - if (!query_read_msg.exec()) { - qDebug("Query execution for feeds read change failed."); - db_handle.rollback(); - } - - // Commit changes. - if (db_handle.commit()) { - return true; - } - else { - return db_handle.rollback(); - } + return false; } -bool FeedsModel::markFeedsDeleted(const QList &feeds, int deleted, bool read_only) { - QSqlDatabase db_handle = qApp->database()->connection(objectName(), DatabaseFactory::FromSettings); - - if (!db_handle.transaction()) { - qWarning("Starting transaction for feeds clearing."); - return false; - } - - QSqlQuery query_delete_msg(db_handle); - query_delete_msg.setForwardOnly(true); - - if (read_only) { - if (!query_delete_msg.prepare(QString("UPDATE Messages SET is_deleted = :deleted " - "WHERE feed IN (%1) AND is_deleted = 0 AND is_read = 1;").arg(textualFeedIds(feeds).join(QSL(", "))))) { - qWarning("Query preparation failed for feeds clearing."); - - db_handle.rollback(); - return false; - } - } - else { - if (!query_delete_msg.prepare(QString("UPDATE Messages SET is_deleted = :deleted " - "WHERE feed IN (%1) AND is_deleted = 0;").arg(textualFeedIds(feeds).join(QSL(", "))))) { - qWarning("Query preparation failed for feeds clearing."); - - db_handle.rollback(); - return false; - } - } - - query_delete_msg.bindValue(QSL(":deleted"), deleted); - - if (!query_delete_msg.exec()) { - qDebug("Query execution for feeds clearing failed."); - db_handle.rollback(); - } - - // Commit changes. - if (db_handle.commit()) { - return true; - } - else { - return db_handle.rollback(); - } +bool FeedsModel::markItemCleared(RootItem *item, bool clean_read_only) { + return item->cleanMessages(clean_read_only); } QList FeedsModel::allFeeds() { diff --git a/src/core/feedsmodel.h b/src/core/feedsmodel.h index 1fdc26b72..48d612b39 100755 --- a/src/core/feedsmodel.h +++ b/src/core/feedsmodel.h @@ -132,8 +132,8 @@ class FeedsModel : public QAbstractItemModel { public slots: // Feeds operations. - bool markFeedsRead(const QList &feeds, int read); - bool markFeedsDeleted(const QList &feeds, int deleted, bool read_only); + bool markItemRead(RootItem *item, RootItem::ReadStatus read); + bool markItemCleared(RootItem *item, bool clean_read_only); // Signals that properties (probably counts) // of ALL items have changed. @@ -147,6 +147,12 @@ class FeedsModel : public QAbstractItemModel { // Invalidates data under index for the item. void reloadChangedItem(RootItem *item); + // Notifies other components about messages + // counts. + inline void notifyWithCounts() { + emit messageCountsChanged(countOfUnreadMessages(), countOfAllMessages(), hasAnyFeedNewMessages()); + } + private slots: // Is executed when next auto-update round could be done. void executeNextAutoUpdate(); @@ -155,6 +161,9 @@ class FeedsModel : public QAbstractItemModel { // Emitted when model requests update of some feeds. void feedsUpdateRequested(const QList feeds); + // Emitted if counts of messages are changed. + void messageCountsChanged(int unread_messages, int total_messages, bool any_feed_has_unread_messages); + private: // Returns converted ids of given feeds // which are suitable as IN clause for SQL queries. diff --git a/src/core/rootitem.cpp b/src/core/rootitem.cpp index e92ef64f4..5d062e320 100755 --- a/src/core/rootitem.cpp +++ b/src/core/rootitem.cpp @@ -46,6 +46,48 @@ QList RootItem::contextMenuActions() { return QList(); } +bool RootItem::canBeEdited() { + return false; +} + +bool RootItem::editViaGui() { + return false; +} + +bool RootItem::canBeDeleted() { + return false; +} + +bool RootItem::deleteViaGui() { + return false; +} + +bool RootItem::canBeMarkedAsReadUnread(ReadStatus status) { + return true; +} + +bool RootItem::markAsReadUnread(ReadStatus status) { + bool result = true; + + foreach (RootItem *child, m_childItems) { + if (child->canBeMarkedAsReadUnread(status)) { + result &= child->markAsReadUnread(status); + } + } + + return result; +} + +bool RootItem::cleanMessages(bool clear_only_read) { + bool result = true; + + foreach (RootItem *child, m_childItems) { + result &= child->cleanMessages(clear_only_read); + } + + return result; +} + void RootItem::setupFonts() { m_normalFont = Application::font("FeedsView"); m_boldFont = m_normalFont; diff --git a/src/core/rootitem.h b/src/core/rootitem.h index 450153e77..4ccc84947 100755 --- a/src/core/rootitem.h +++ b/src/core/rootitem.h @@ -50,6 +50,16 @@ class RootItem : public QObject { Q_OBJECT public: + enum ReadStatus { + Read, + Unread + }; + + enum CleanStatus { + Clean, + Unclean + }; + // Constructors and destructors. explicit RootItem(RootItem *parent_item = NULL); virtual ~RootItem(); @@ -82,39 +92,20 @@ class RootItem : public QObject { // NOTE: Ownership of returned actions is not switched to caller, free them when needed. virtual QList contextMenuActions(); - // TODO: pracovat s těmito věcmi - virtual bool canBeEdited() { - return false; - } + virtual bool canBeEdited(); + virtual bool editViaGui(); + virtual bool canBeDeleted(); + virtual bool deleteViaGui(); - virtual bool editViaGui() { - return false; - } - - virtual bool canBeDeleted() { - return false; - } - - virtual bool deleteViaGui() { - return false; - } - - virtual bool canBeMarkedAsRead() { - return true; - } - - virtual bool markAsRead() { - return true; - } - - virtual bool canBeMarkedAsUnread() { - return true; - } - - virtual bool markAsUnread() { - return true; - } + virtual bool canBeMarkedAsReadUnread(ReadStatus status); + virtual bool markAsReadUnread(ReadStatus status); + // This method should "clean" all messages it contains. + // What "clean" means? It means delete message -> move them to recycle bin + // or eventually remove them completely if there is no recycle bin functionality. + // If this method is called on "recycle bin" instance of your + // service account, it should not do anything. + virtual bool cleanMessages(bool clear_only_read); virtual int row() const; virtual QVariant data(int column, int role) const; diff --git a/src/gui/feedmessageviewer.cpp b/src/gui/feedmessageviewer.cpp index ff15a2382..ae3c18d0c 100755 --- a/src/gui/feedmessageviewer.cpp +++ b/src/gui/feedmessageviewer.cpp @@ -75,7 +75,7 @@ FeedMessageViewer::FeedMessageViewer(QWidget *parent) createConnections(); // Now, update all feeds if user has set it. - m_feedsView->updateAllFeedsOnStartup(); + m_feedsView->updateAllItemsOnStartup(); } FeedMessageViewer::~FeedMessageViewer() { @@ -337,7 +337,7 @@ void FeedMessageViewer::createConnections() { connect(m_feedsView, SIGNAL(feedsNeedToBeReloaded(bool)), m_messagesView, SLOT(reloadSelections(bool))); // If counts of unread/all messages change, update the tray icon. - connect(m_feedsView, SIGNAL(messageCountsChanged(int,int,bool)), this, SLOT(updateTrayIconStatus(int,int,bool))); + connect(m_feedsView->sourceModel(), SIGNAL(messageCountsChanged(int,int,bool)), this, SLOT(updateTrayIconStatus(int,int,bool))); // Message openers. connect(m_messagesView, SIGNAL(openLinkMiniBrowser(QString)), m_messagesBrowser, SLOT(navigateToUrl(QString))); @@ -371,25 +371,25 @@ void FeedMessageViewer::createConnections() { connect(form_main->m_ui->m_actionSendMessageViaEmail, SIGNAL(triggered()), m_messagesView, SLOT(sendSelectedMessageViaEmail())); connect(form_main->m_ui->m_actionMarkAllItemsRead, - SIGNAL(triggered()), m_feedsView, SLOT(markAllFeedsRead())); + SIGNAL(triggered()), m_feedsView, SLOT(markAllItemsRead())); connect(form_main->m_ui->m_actionMarkSelectedItemsAsRead, - SIGNAL(triggered()), m_feedsView, SLOT(markSelectedFeedsRead())); + SIGNAL(triggered()), m_feedsView, SLOT(markSelectedItemsRead())); connect(form_main->m_ui->m_actionExpandCollapseItem, SIGNAL(triggered()), m_feedsView, SLOT(expandCollapseCurrentItem())); connect(form_main->m_ui->m_actionMarkSelectedItemsAsUnread, - SIGNAL(triggered()), m_feedsView, SLOT(markSelectedFeedsUnread())); + SIGNAL(triggered()), m_feedsView, SLOT(markSelectedItemsUnread())); connect(form_main->m_ui->m_actionClearSelectedItems, SIGNAL(triggered()), m_feedsView, SLOT(clearSelectedFeeds())); connect(form_main->m_ui->m_actionClearAllItems, SIGNAL(triggered()), m_feedsView, SLOT(clearAllFeeds())); connect(form_main->m_ui->m_actionUpdateSelectedItems, - SIGNAL(triggered()), m_feedsView, SLOT(updateSelectedFeeds())); + SIGNAL(triggered()), m_feedsView, SLOT(updateSelectedItems())); connect(form_main->m_ui->m_actionUpdateAllItems, - SIGNAL(triggered()), m_feedsView, SLOT(updateAllFeeds())); + SIGNAL(triggered()), m_feedsView, SLOT(updateAllItems())); connect(form_main->m_ui->m_actionEditSelectedItem, SIGNAL(triggered()), m_feedsView, SLOT(editSelectedItem())); connect(form_main->m_ui->m_actionViewSelectedItemsNewspaperMode, - SIGNAL(triggered()), m_feedsView, SLOT(openSelectedFeedsInNewspaperMode())); + SIGNAL(triggered()), m_feedsView, SLOT(openSelectedItemsInNewspaperMode())); connect(form_main->m_ui->m_actionDeleteSelectedItem, SIGNAL(triggered()), m_feedsView, SLOT(deleteSelectedItem())); connect(form_main->m_ui->m_actionSwitchFeedsList, diff --git a/src/gui/feedsview.cpp b/src/gui/feedsview.cpp index 6e60b6d94..25abe83f6 100755 --- a/src/gui/feedsview.cpp +++ b/src/gui/feedsview.cpp @@ -159,43 +159,35 @@ void FeedsView::expandCollapseCurrentItem() { } } -void FeedsView::updateAllFeeds() { +void FeedsView::updateAllItems() { emit feedsUpdateRequested(allFeeds()); } -void FeedsView::updateSelectedFeeds() { +void FeedsView::updateSelectedItems() { emit feedsUpdateRequested(selectedFeeds()); } -void FeedsView::updateAllFeedsOnStartup() { +void FeedsView::updateAllItemsOnStartup() { if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) { qDebug("Requesting update for all feeds on application startup."); - QTimer::singleShot(STARTUP_UPDATE_DELAY, this, SLOT(updateAllFeeds())); + QTimer::singleShot(STARTUP_UPDATE_DELAY, this, SLOT(updateAllItems())); } } -void FeedsView::setSelectedFeedsClearStatus(int clear) { - m_sourceModel->markFeedsDeleted(selectedFeeds(), clear, 0); +void FeedsView::clearSelectedFeeds() { + m_sourceModel->markItemCleared(selectedItem(), false); updateCountsOfSelectedFeeds(true); emit feedsNeedToBeReloaded(true); } -void FeedsView::setAllFeedsClearStatus(int clear) { - m_sourceModel->markFeedsDeleted(allFeeds(), clear, 0); +void FeedsView::clearAllFeeds() { + m_sourceModel->markItemCleared(m_sourceModel->rootItem(), false); updateCountsOfAllFeeds(true); emit feedsNeedToBeReloaded(true); } -void FeedsView::clearSelectedFeeds() { - setSelectedFeedsClearStatus(1); -} - -void FeedsView::clearAllFeeds() { - setAllFeedsClearStatus(1); -} - void FeedsView::receiveMessageCountsChange(FeedsSelection::SelectionMode mode, bool total_msg_count_changed, bool any_msg_restored) { @@ -313,7 +305,7 @@ void FeedsView::deleteSelectedItem() { // a delete selected_item jen volat tady, editViaGui taky obstará všechno, // ale tam je to zas komplexnější. m_sourceModel->removeItem(m_proxyModel->mapToSource(current_index)); - notifyWithCounts(); + m_sourceModel->notifyWithCounts(); } } else { @@ -329,42 +321,42 @@ void FeedsView::deleteSelectedItem() { qApp->feedUpdateLock()->unlock(); } -void FeedsView::markSelectedFeedsReadStatus(int read) { - m_sourceModel->markFeedsRead(selectedFeeds(), read); +void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) { + m_sourceModel->markItemRead(selectedItem(), read); updateCountsOfSelectedFeeds(false); emit feedsNeedToBeReloaded(read == 1); } -void FeedsView::markSelectedFeedsRead() { - markSelectedFeedsReadStatus(1); +void FeedsView::markSelectedItemsRead() { + markSelectedItemReadStatus(RootItem::Read); } -void FeedsView::markSelectedFeedsUnread() { - markSelectedFeedsReadStatus(0); +void FeedsView::markSelectedItemsUnread() { + markSelectedItemReadStatus(RootItem::Unread); } -void FeedsView::markAllFeedsReadStatus(int read) { - m_sourceModel->markFeedsRead(allFeeds(), read); +void FeedsView::markAllItemsReadStatus(RootItem::ReadStatus read) { + m_sourceModel->markItemRead(m_sourceModel->rootItem(), read); updateCountsOfAllFeeds(false); emit feedsNeedToBeReloaded(read == 1); } -void FeedsView::markAllFeedsRead() { - markAllFeedsReadStatus(1); +void FeedsView::markAllItemsRead() { + markAllItemsReadStatus(RootItem::Read); } void FeedsView::clearAllReadMessages() { - m_sourceModel->markFeedsDeleted(allFeeds(), 1, 1); + m_sourceModel->markItemCleared(m_sourceModel->rootItem(), true); } -void FeedsView::openSelectedFeedsInNewspaperMode() { +void FeedsView::openSelectedItemsInNewspaperMode() { QList messages = m_sourceModel->messagesForFeeds(selectedFeeds()); if (!messages.isEmpty()) { emit openMessagesInNewspaperView(messages); - QTimer::singleShot(0, this, SLOT(markSelectedFeedsRead())); + QTimer::singleShot(0, this, SLOT(markSelectedItemsRead())); } } @@ -410,7 +402,7 @@ void FeedsView::updateCountsOfSelectedFeeds(bool update_total_too) { // Make sure that selected view reloads changed indexes. m_sourceModel->reloadChangedLayout(selected_indexes); - notifyWithCounts(); + m_sourceModel->notifyWithCounts(); } void FeedsView::updateCountsOfRecycleBin(bool update_total_too) { @@ -418,7 +410,7 @@ void FeedsView::updateCountsOfRecycleBin(bool update_total_too) { // TODO: pridat metodu cisteni standardniho kose nebo vsech kosu. //m_sourceModel->recycleBin()->updateCounts(update_total_too); //m_sourceModel->reloadChangedLayout(QModelIndexList() << m_sourceModel->indexForItem(m_sourceModel->recycleBin())); - notifyWithCounts(); + m_sourceModel->notifyWithCounts(); } void FeedsView::updateCountsOfAllFeeds(bool update_total_too) { @@ -435,29 +427,22 @@ void FeedsView::updateCountsOfAllFeeds(bool update_total_too) { // Make sure that all views reloads its data. m_sourceModel->reloadWholeLayout(); - notifyWithCounts(); + m_sourceModel->notifyWithCounts(); } void FeedsView::updateCountsOfParticularFeed(Feed *feed, bool update_total_too) { QModelIndex index = m_sourceModel->indexForItem(feed); if (index.isValid()) { - feed->updateCounts(update_total_too, false); + feed->updateCounts(update_total_too); m_sourceModel->reloadChangedLayout(QModelIndexList() << index); } invalidateReadFeedsFilter(); - notifyWithCounts(); + m_sourceModel->notifyWithCounts(); } void FeedsView::selectNextItem() { - // NOTE: Bug #122 requested to not expand in here. - /* - if (!isExpanded(currentIndex())) { - expand(currentIndex()); - } - */ - QModelIndex index_next = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier); if (index_next.isValid()) { @@ -469,14 +454,6 @@ void FeedsView::selectNextItem() { void FeedsView::selectPreviousItem() { QModelIndex index_previous = moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier); - // NOTE: Bug #122 requested to not expand in here. - /* - if (!isExpanded(index_previous)) { - expand(index_previous); - index_previous = moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier); - } - */ - if (index_previous.isValid()) { setCurrentIndex(index_previous); setFocus(); diff --git a/src/gui/feedsview.h b/src/gui/feedsview.h index 1b01477b2..34950f403 100755 --- a/src/gui/feedsview.h +++ b/src/gui/feedsview.h @@ -71,27 +71,23 @@ class FeedsView : public QTreeView { void expandCollapseCurrentItem(); // Feed updating. - void updateAllFeeds(); - void updateAllFeedsOnStartup(); - void updateSelectedFeeds(); + void updateAllItems(); + void updateAllItemsOnStartup(); + void updateSelectedItems(); // Feed read/unread manipulators. - void markSelectedFeedsReadStatus(int read); - void markSelectedFeedsRead(); - void markSelectedFeedsUnread(); - void markAllFeedsReadStatus(int read); - void markAllFeedsRead(); + void markSelectedItemsRead(); + void markSelectedItemsUnread(); + void markAllItemsRead(); // Newspaper accessors. - void openSelectedFeedsInNewspaperMode(); + void openSelectedItemsInNewspaperMode(); // Recycle bin operators. void emptyRecycleBin(); void restoreRecycleBin(); // Feed clearers. - void setSelectedFeedsClearStatus(int clear); - void setAllFeedsClearStatus(int clear); void clearSelectedFeeds(); void clearAllFeeds(); void clearAllReadMessages(); @@ -116,14 +112,6 @@ class FeedsView : public QTreeView { // Reloads counts for particular feed. void updateCountsOfParticularFeed(Feed *feed, bool update_total_too); - // Notifies other components about messages - // counts. - inline void notifyWithCounts() { - emit messageCountsChanged(m_sourceModel->countOfUnreadMessages(), - m_sourceModel->countOfAllMessages(), - m_sourceModel->hasAnyFeedNewMessages()); - } - // Selects next/previous item (feed/category) in the list. void selectNextItem(); void selectPreviousItem(); @@ -133,7 +121,14 @@ class FeedsView : public QTreeView { setVisible(!isVisible()); } - protected: + private slots: + void markSelectedItemReadStatus(RootItem::ReadStatus read); + void markAllItemsReadStatus(RootItem::ReadStatus read); + + void saveSortState(int column, Qt::SortOrder order); + void validateItemAfterDragDrop(const QModelIndex &source_index); + + private: // Initializes context menus. void initializeContextMenuCategories(RootItem *clicked_item); void initializeContextMenuFeeds(RootItem *clicked_item); @@ -151,17 +146,10 @@ class FeedsView : public QTreeView { // Show custom context menu. void contextMenuEvent(QContextMenuEvent *event); - private slots: - void saveSortState(int column, Qt::SortOrder order); - void validateItemAfterDragDrop(const QModelIndex &source_index); - signals: // Emitted if user/application requested updating of some feeds. void feedsUpdateRequested(const QList feeds); - // Emitted if counts of messages are changed. - void messageCountsChanged(int unread_messages, int total_messages, bool any_feed_has_unread_messages); - // Emitted if currently selected feeds needs to be reloaded. void feedsNeedToBeReloaded(bool mark_current_index_read); diff --git a/src/services/abstract/feed.h b/src/services/abstract/feed.h index c3911a0b2..fdfde7f54 100755 --- a/src/services/abstract/feed.h +++ b/src/services/abstract/feed.h @@ -60,7 +60,7 @@ class Feed : public RootItem { virtual int update() = 0; // Updates counts of all/unread messages for this feed. - virtual void updateCounts(bool including_total_count = true, bool update_feed_statuses = true) = 0; + virtual void updateCounts(bool including_total_count) = 0; // Get ALL undeleted messages from this feed in one single list. virtual QList undeletedMessages() const = 0; diff --git a/src/services/standard/standardcategory.cpp b/src/services/standard/standardcategory.cpp index 6fa73df5a..76cb49c0e 100755 --- a/src/services/standard/standardcategory.cpp +++ b/src/services/standard/standardcategory.cpp @@ -102,6 +102,14 @@ bool StandardCategory::deleteViaGui() { return removeItself(); } +bool StandardCategory::markAsReadUnread(ReadStatus status) { + return serviceRoot()->markFeedsReadUnread(getSubTreeFeeds(), status); +} + +bool StandardCategory::cleanMessages(bool clean_read_only) { + return serviceRoot()->cleanFeeds(getSubTreeFeeds(), clean_read_only); +} + bool StandardCategory::removeItself() { bool children_removed = true; diff --git a/src/services/standard/standardcategory.h b/src/services/standard/standardcategory.h index 1edab3bab..d318ec99f 100755 --- a/src/services/standard/standardcategory.h +++ b/src/services/standard/standardcategory.h @@ -56,6 +56,9 @@ class StandardCategory : public Category { bool editViaGui(); bool deleteViaGui(); + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clean_read_only); + // Removes category and all its children from persistent // database. bool removeItself(); diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp index 530b0a939..584a7d3c3 100755 --- a/src/services/standard/standardfeed.cpp +++ b/src/services/standard/standardfeed.cpp @@ -118,6 +118,14 @@ bool StandardFeed::deleteViaGui() { return removeItself(); } +bool StandardFeed::markAsReadUnread(ReadStatus status) { + return serviceRoot()->markFeedsReadUnread(QList() << this, status); +} + +bool StandardFeed::cleanMessages(bool clean_read_only) { + return serviceRoot()->cleanFeeds(QList() << this, clean_read_only); +} + QList StandardFeed::undeletedMessages() const { QList messages; @@ -165,7 +173,7 @@ QString StandardFeed::typeToString(StandardFeed::Type type) { } } -void StandardFeed::updateCounts(bool including_total_count, bool update_feed_statuses) { +void StandardFeed::updateCounts(bool including_total_count) { QSqlDatabase database = qApp->database()->connection(QSL("Feed"), DatabaseFactory::FromSettings); QSqlQuery query_all(database); @@ -181,7 +189,7 @@ void StandardFeed::updateCounts(bool including_total_count, bool update_feed_sta if (query_all.exec(QString("SELECT count(*) FROM Messages WHERE feed = %1 AND is_deleted = 0 AND is_read = 0;").arg(id())) && query_all.next()) { int new_unread_count = query_all.value(0).toInt(); - if (update_feed_statuses && status() == NewMessages && new_unread_count < m_unreadCount) { + if (status() == NewMessages && new_unread_count < m_unreadCount) { setStatus(Normal); } @@ -433,7 +441,7 @@ int StandardFeed::update() { setStatus(NetworkError); return 0; } - else { + else if (status() != NewMessages) { setStatus(Normal); } @@ -731,5 +739,5 @@ StandardFeed::StandardFeed(const QSqlRecord &record) : Feed(NULL) { setPassword(TextFactory::decrypt(record.value(FDS_DB_PASSWORD_INDEX).toString())); setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); - updateCounts(); + updateCounts(true); } diff --git a/src/services/standard/standardfeed.h b/src/services/standard/standardfeed.h index 395710995..24577662b 100755 --- a/src/services/standard/standardfeed.h +++ b/src/services/standard/standardfeed.h @@ -74,6 +74,9 @@ class StandardFeed : public Feed { bool editViaGui(); bool deleteViaGui(); + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clean_read_only); + QList undeletedMessages() const; // Obtains data related to this feed. @@ -83,7 +86,7 @@ class StandardFeed : public Feed { int update(); // Updates counts of all/unread messages for this feed. - void updateCounts(bool including_total_count = true, bool update_feed_statuses = true); + void updateCounts(bool including_total_count); // Removes this standard feed from persistent // storage. @@ -156,7 +159,7 @@ class StandardFeed : public Feed { // Fetches metadata for the feed. void fetchMetadataForItself(); - protected: + private: // Persistently stores given messages into the database // and updates existing messages if newer version is // available. diff --git a/src/services/standard/standardserviceroot.cpp b/src/services/standard/standardserviceroot.cpp index 0137b45b9..7e934bd6d 100755 --- a/src/services/standard/standardserviceroot.cpp +++ b/src/services/standard/standardserviceroot.cpp @@ -128,6 +128,87 @@ QVariant StandardServiceRoot::data(int column, int role) const { } } +bool StandardServiceRoot::markFeedsReadUnread(QList items, ReadStatus read) { + QSqlDatabase db_handle = qApp->database()->connection(QSL("StandardServiceRoot"), DatabaseFactory::FromSettings); + + if (!db_handle.transaction()) { + qWarning("Starting transaction for feeds read change."); + return false; + } + + QSqlQuery query_read_msg(db_handle); + query_read_msg.setForwardOnly(true); + + if (!query_read_msg.prepare(QString("UPDATE Messages SET is_read = :read " + "WHERE feed IN (%1) AND is_deleted = 0;").arg(textualFeedIds(items).join(QSL(", "))))) { + qWarning("Query preparation failed for feeds read change."); + + db_handle.rollback(); + return false; + } + + query_read_msg.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); + + if (!query_read_msg.exec()) { + qDebug("Query execution for feeds read change failed."); + db_handle.rollback(); + } + + // Commit changes. + if (db_handle.commit()) { + return true; + } + else { + return db_handle.rollback(); + } +} + +bool StandardServiceRoot::cleanFeeds(QList items, bool clean_read_only) { + QSqlDatabase db_handle = qApp->database()->connection(QSL("StandardServiceRoot"), DatabaseFactory::FromSettings); + + if (!db_handle.transaction()) { + qWarning("Starting transaction for feeds clearing."); + return false; + } + + QSqlQuery query_delete_msg(db_handle); + query_delete_msg.setForwardOnly(true); + + if (clean_read_only) { + if (!query_delete_msg.prepare(QString("UPDATE Messages SET is_deleted = :deleted " + "WHERE feed IN (%1) AND is_deleted = 0 AND is_read = 1;").arg(textualFeedIds(items).join(QSL(", "))))) { + qWarning("Query preparation failed for feeds clearing."); + + db_handle.rollback(); + return false; + } + } + else { + if (!query_delete_msg.prepare(QString("UPDATE Messages SET is_deleted = :deleted " + "WHERE feed IN (%1) AND is_deleted = 0;").arg(textualFeedIds(items).join(QSL(", "))))) { + qWarning("Query preparation failed for feeds clearing."); + + db_handle.rollback(); + return false; + } + } + + query_delete_msg.bindValue(QSL(":deleted"), 1); + + if (!query_delete_msg.exec()) { + qDebug("Query execution for feeds clearing failed."); + db_handle.rollback(); + } + + // Commit changes. + if (db_handle.commit()) { + return true; + } + else { + return db_handle.rollback(); + } +} + void StandardServiceRoot::loadFromDatabase(){ QSqlDatabase database = qApp->database()->connection("StandardServiceRoot", DatabaseFactory::FromSettings); CategoryAssignment categories; @@ -362,6 +443,17 @@ void StandardServiceRoot::exportFeeds() { delete form.data(); } +QStringList StandardServiceRoot::textualFeedIds(const QList &feeds) { + QStringList stringy_ids; + stringy_ids.reserve(feeds.size()); + + foreach (Feed *feed, feeds) { + stringy_ids.append(QString::number(feed->id())); + } + + return stringy_ids; +} + QList StandardServiceRoot::addItemMenu() { if (m_addItemMenu.isEmpty()) { QAction *action_new_category = new QAction(qApp->icons()->fromTheme("folder-category"), tr("Add new category"), this); diff --git a/src/services/standard/standardserviceroot.h b/src/services/standard/standardserviceroot.h index 08f7ae4fb..cf86bb7cb 100755 --- a/src/services/standard/standardserviceroot.h +++ b/src/services/standard/standardserviceroot.h @@ -74,6 +74,9 @@ class StandardServiceRoot : public ServiceRoot { // NOTE: This is used for import/export of the model. bool mergeImportExportModel(FeedsImportExportModel *model, QString &output_message); + bool markFeedsReadUnread(QList items, ReadStatus read); + bool cleanFeeds(QList items, bool clean_read_only); + public slots: void addNewCategory(); void addNewFeed(); @@ -81,6 +84,10 @@ class StandardServiceRoot : public ServiceRoot { void exportFeeds(); private: + // Returns converted ids of given feeds + // which are suitable as IN clause for SQL queries. + QStringList textualFeedIds(const QList &feeds); + void loadFromDatabase(); // Takes lists of feeds/categories and assembles