Many refactorings in model/items, some tiny preparations for messages operation - they moved into feed classes.

This commit is contained in:
Martin Rotter 2015-11-12 10:53:17 +01:00
parent d0b00915f3
commit d5ff059543
14 changed files with 259 additions and 200 deletions

View File

@ -439,85 +439,16 @@ Feed *FeedsModel::feedForIndex(const QModelIndex &index) {
} }
} }
bool FeedsModel::markFeedsRead(const QList<Feed*> &feeds, int read) { bool FeedsModel::markItemRead(RootItem *item, RootItem::ReadStatus read) {
QSqlDatabase db_handle = qApp->database()->connection(objectName(), DatabaseFactory::FromSettings); if (item->canBeMarkedAsReadUnread(read)) {
return item->markAsReadUnread(read);
if (!db_handle.transaction()) {
qWarning("Starting transaction for feeds read change.");
return false;
} }
QSqlQuery query_read_msg(db_handle); return false;
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();
}
} }
bool FeedsModel::markFeedsDeleted(const QList<Feed*> &feeds, int deleted, bool read_only) { bool FeedsModel::markItemCleared(RootItem *item, bool clean_read_only) {
QSqlDatabase db_handle = qApp->database()->connection(objectName(), DatabaseFactory::FromSettings); return item->cleanMessages(clean_read_only);
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();
}
} }
QList<Feed*> FeedsModel::allFeeds() { QList<Feed*> FeedsModel::allFeeds() {

View File

@ -132,8 +132,8 @@ class FeedsModel : public QAbstractItemModel {
public slots: public slots:
// Feeds operations. // Feeds operations.
bool markFeedsRead(const QList<Feed*> &feeds, int read); bool markItemRead(RootItem *item, RootItem::ReadStatus read);
bool markFeedsDeleted(const QList<Feed*> &feeds, int deleted, bool read_only); bool markItemCleared(RootItem *item, bool clean_read_only);
// Signals that properties (probably counts) // Signals that properties (probably counts)
// of ALL items have changed. // of ALL items have changed.
@ -147,6 +147,12 @@ class FeedsModel : public QAbstractItemModel {
// Invalidates data under index for the item. // Invalidates data under index for the item.
void reloadChangedItem(RootItem *item); void reloadChangedItem(RootItem *item);
// Notifies other components about messages
// counts.
inline void notifyWithCounts() {
emit messageCountsChanged(countOfUnreadMessages(), countOfAllMessages(), hasAnyFeedNewMessages());
}
private slots: private slots:
// Is executed when next auto-update round could be done. // Is executed when next auto-update round could be done.
void executeNextAutoUpdate(); void executeNextAutoUpdate();
@ -155,6 +161,9 @@ class FeedsModel : public QAbstractItemModel {
// Emitted when model requests update of some feeds. // Emitted when model requests update of some feeds.
void feedsUpdateRequested(const QList<Feed*> feeds); void feedsUpdateRequested(const QList<Feed*> feeds);
// Emitted if counts of messages are changed.
void messageCountsChanged(int unread_messages, int total_messages, bool any_feed_has_unread_messages);
private: private:
// Returns converted ids of given feeds // Returns converted ids of given feeds
// which are suitable as IN clause for SQL queries. // which are suitable as IN clause for SQL queries.

View File

@ -46,6 +46,48 @@ QList<QAction*> RootItem::contextMenuActions() {
return QList<QAction*>(); return QList<QAction*>();
} }
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() { void RootItem::setupFonts() {
m_normalFont = Application::font("FeedsView"); m_normalFont = Application::font("FeedsView");
m_boldFont = m_normalFont; m_boldFont = m_normalFont;

View File

@ -50,6 +50,16 @@ class RootItem : public QObject {
Q_OBJECT Q_OBJECT
public: public:
enum ReadStatus {
Read,
Unread
};
enum CleanStatus {
Clean,
Unclean
};
// Constructors and destructors. // Constructors and destructors.
explicit RootItem(RootItem *parent_item = NULL); explicit RootItem(RootItem *parent_item = NULL);
virtual ~RootItem(); virtual ~RootItem();
@ -82,39 +92,20 @@ class RootItem : public QObject {
// NOTE: Ownership of returned actions is not switched to caller, free them when needed. // NOTE: Ownership of returned actions is not switched to caller, free them when needed.
virtual QList<QAction*> contextMenuActions(); virtual QList<QAction*> contextMenuActions();
// TODO: pracovat s těmito věcmi virtual bool canBeEdited();
virtual bool canBeEdited() { virtual bool editViaGui();
return false; virtual bool canBeDeleted();
} virtual bool deleteViaGui();
virtual bool editViaGui() { virtual bool canBeMarkedAsReadUnread(ReadStatus status);
return false; virtual bool markAsReadUnread(ReadStatus status);
}
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;
}
// 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 int row() const;
virtual QVariant data(int column, int role) const; virtual QVariant data(int column, int role) const;

View File

@ -75,7 +75,7 @@ FeedMessageViewer::FeedMessageViewer(QWidget *parent)
createConnections(); createConnections();
// Now, update all feeds if user has set it. // Now, update all feeds if user has set it.
m_feedsView->updateAllFeedsOnStartup(); m_feedsView->updateAllItemsOnStartup();
} }
FeedMessageViewer::~FeedMessageViewer() { FeedMessageViewer::~FeedMessageViewer() {
@ -337,7 +337,7 @@ void FeedMessageViewer::createConnections() {
connect(m_feedsView, SIGNAL(feedsNeedToBeReloaded(bool)), m_messagesView, SLOT(reloadSelections(bool))); connect(m_feedsView, SIGNAL(feedsNeedToBeReloaded(bool)), m_messagesView, SLOT(reloadSelections(bool)));
// If counts of unread/all messages change, update the tray icon. // 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. // Message openers.
connect(m_messagesView, SIGNAL(openLinkMiniBrowser(QString)), m_messagesBrowser, SLOT(navigateToUrl(QString))); 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, connect(form_main->m_ui->m_actionSendMessageViaEmail,
SIGNAL(triggered()), m_messagesView, SLOT(sendSelectedMessageViaEmail())); SIGNAL(triggered()), m_messagesView, SLOT(sendSelectedMessageViaEmail()));
connect(form_main->m_ui->m_actionMarkAllItemsRead, 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, 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, connect(form_main->m_ui->m_actionExpandCollapseItem,
SIGNAL(triggered()), m_feedsView, SLOT(expandCollapseCurrentItem())); SIGNAL(triggered()), m_feedsView, SLOT(expandCollapseCurrentItem()));
connect(form_main->m_ui->m_actionMarkSelectedItemsAsUnread, 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, connect(form_main->m_ui->m_actionClearSelectedItems,
SIGNAL(triggered()), m_feedsView, SLOT(clearSelectedFeeds())); SIGNAL(triggered()), m_feedsView, SLOT(clearSelectedFeeds()));
connect(form_main->m_ui->m_actionClearAllItems, connect(form_main->m_ui->m_actionClearAllItems,
SIGNAL(triggered()), m_feedsView, SLOT(clearAllFeeds())); SIGNAL(triggered()), m_feedsView, SLOT(clearAllFeeds()));
connect(form_main->m_ui->m_actionUpdateSelectedItems, 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, 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, connect(form_main->m_ui->m_actionEditSelectedItem,
SIGNAL(triggered()), m_feedsView, SLOT(editSelectedItem())); SIGNAL(triggered()), m_feedsView, SLOT(editSelectedItem()));
connect(form_main->m_ui->m_actionViewSelectedItemsNewspaperMode, 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, connect(form_main->m_ui->m_actionDeleteSelectedItem,
SIGNAL(triggered()), m_feedsView, SLOT(deleteSelectedItem())); SIGNAL(triggered()), m_feedsView, SLOT(deleteSelectedItem()));
connect(form_main->m_ui->m_actionSwitchFeedsList, connect(form_main->m_ui->m_actionSwitchFeedsList,

View File

@ -159,43 +159,35 @@ void FeedsView::expandCollapseCurrentItem() {
} }
} }
void FeedsView::updateAllFeeds() { void FeedsView::updateAllItems() {
emit feedsUpdateRequested(allFeeds()); emit feedsUpdateRequested(allFeeds());
} }
void FeedsView::updateSelectedFeeds() { void FeedsView::updateSelectedItems() {
emit feedsUpdateRequested(selectedFeeds()); emit feedsUpdateRequested(selectedFeeds());
} }
void FeedsView::updateAllFeedsOnStartup() { void FeedsView::updateAllItemsOnStartup() {
if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) { if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) {
qDebug("Requesting update for all feeds on application startup."); 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) { void FeedsView::clearSelectedFeeds() {
m_sourceModel->markFeedsDeleted(selectedFeeds(), clear, 0); m_sourceModel->markItemCleared(selectedItem(), false);
updateCountsOfSelectedFeeds(true); updateCountsOfSelectedFeeds(true);
emit feedsNeedToBeReloaded(true); emit feedsNeedToBeReloaded(true);
} }
void FeedsView::setAllFeedsClearStatus(int clear) { void FeedsView::clearAllFeeds() {
m_sourceModel->markFeedsDeleted(allFeeds(), clear, 0); m_sourceModel->markItemCleared(m_sourceModel->rootItem(), false);
updateCountsOfAllFeeds(true); updateCountsOfAllFeeds(true);
emit feedsNeedToBeReloaded(true); emit feedsNeedToBeReloaded(true);
} }
void FeedsView::clearSelectedFeeds() {
setSelectedFeedsClearStatus(1);
}
void FeedsView::clearAllFeeds() {
setAllFeedsClearStatus(1);
}
void FeedsView::receiveMessageCountsChange(FeedsSelection::SelectionMode mode, void FeedsView::receiveMessageCountsChange(FeedsSelection::SelectionMode mode,
bool total_msg_count_changed, bool total_msg_count_changed,
bool any_msg_restored) { bool any_msg_restored) {
@ -313,7 +305,7 @@ void FeedsView::deleteSelectedItem() {
// a delete selected_item jen volat tady, editViaGui taky obstará všechno, // a delete selected_item jen volat tady, editViaGui taky obstará všechno,
// ale tam je to zas komplexnější. // ale tam je to zas komplexnější.
m_sourceModel->removeItem(m_proxyModel->mapToSource(current_index)); m_sourceModel->removeItem(m_proxyModel->mapToSource(current_index));
notifyWithCounts(); m_sourceModel->notifyWithCounts();
} }
} }
else { else {
@ -329,42 +321,42 @@ void FeedsView::deleteSelectedItem() {
qApp->feedUpdateLock()->unlock(); qApp->feedUpdateLock()->unlock();
} }
void FeedsView::markSelectedFeedsReadStatus(int read) { void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) {
m_sourceModel->markFeedsRead(selectedFeeds(), read); m_sourceModel->markItemRead(selectedItem(), read);
updateCountsOfSelectedFeeds(false); updateCountsOfSelectedFeeds(false);
emit feedsNeedToBeReloaded(read == 1); emit feedsNeedToBeReloaded(read == 1);
} }
void FeedsView::markSelectedFeedsRead() { void FeedsView::markSelectedItemsRead() {
markSelectedFeedsReadStatus(1); markSelectedItemReadStatus(RootItem::Read);
} }
void FeedsView::markSelectedFeedsUnread() { void FeedsView::markSelectedItemsUnread() {
markSelectedFeedsReadStatus(0); markSelectedItemReadStatus(RootItem::Unread);
} }
void FeedsView::markAllFeedsReadStatus(int read) { void FeedsView::markAllItemsReadStatus(RootItem::ReadStatus read) {
m_sourceModel->markFeedsRead(allFeeds(), read); m_sourceModel->markItemRead(m_sourceModel->rootItem(), read);
updateCountsOfAllFeeds(false); updateCountsOfAllFeeds(false);
emit feedsNeedToBeReloaded(read == 1); emit feedsNeedToBeReloaded(read == 1);
} }
void FeedsView::markAllFeedsRead() { void FeedsView::markAllItemsRead() {
markAllFeedsReadStatus(1); markAllItemsReadStatus(RootItem::Read);
} }
void FeedsView::clearAllReadMessages() { void FeedsView::clearAllReadMessages() {
m_sourceModel->markFeedsDeleted(allFeeds(), 1, 1); m_sourceModel->markItemCleared(m_sourceModel->rootItem(), true);
} }
void FeedsView::openSelectedFeedsInNewspaperMode() { void FeedsView::openSelectedItemsInNewspaperMode() {
QList<Message> messages = m_sourceModel->messagesForFeeds(selectedFeeds()); QList<Message> messages = m_sourceModel->messagesForFeeds(selectedFeeds());
if (!messages.isEmpty()) { if (!messages.isEmpty()) {
emit openMessagesInNewspaperView(messages); 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. // Make sure that selected view reloads changed indexes.
m_sourceModel->reloadChangedLayout(selected_indexes); m_sourceModel->reloadChangedLayout(selected_indexes);
notifyWithCounts(); m_sourceModel->notifyWithCounts();
} }
void FeedsView::updateCountsOfRecycleBin(bool update_total_too) { 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. // TODO: pridat metodu cisteni standardniho kose nebo vsech kosu.
//m_sourceModel->recycleBin()->updateCounts(update_total_too); //m_sourceModel->recycleBin()->updateCounts(update_total_too);
//m_sourceModel->reloadChangedLayout(QModelIndexList() << m_sourceModel->indexForItem(m_sourceModel->recycleBin())); //m_sourceModel->reloadChangedLayout(QModelIndexList() << m_sourceModel->indexForItem(m_sourceModel->recycleBin()));
notifyWithCounts(); m_sourceModel->notifyWithCounts();
} }
void FeedsView::updateCountsOfAllFeeds(bool update_total_too) { 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. // Make sure that all views reloads its data.
m_sourceModel->reloadWholeLayout(); m_sourceModel->reloadWholeLayout();
notifyWithCounts(); m_sourceModel->notifyWithCounts();
} }
void FeedsView::updateCountsOfParticularFeed(Feed *feed, bool update_total_too) { void FeedsView::updateCountsOfParticularFeed(Feed *feed, bool update_total_too) {
QModelIndex index = m_sourceModel->indexForItem(feed); QModelIndex index = m_sourceModel->indexForItem(feed);
if (index.isValid()) { if (index.isValid()) {
feed->updateCounts(update_total_too, false); feed->updateCounts(update_total_too);
m_sourceModel->reloadChangedLayout(QModelIndexList() << index); m_sourceModel->reloadChangedLayout(QModelIndexList() << index);
} }
invalidateReadFeedsFilter(); invalidateReadFeedsFilter();
notifyWithCounts(); m_sourceModel->notifyWithCounts();
} }
void FeedsView::selectNextItem() { 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); QModelIndex index_next = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier);
if (index_next.isValid()) { if (index_next.isValid()) {
@ -469,14 +454,6 @@ void FeedsView::selectNextItem() {
void FeedsView::selectPreviousItem() { void FeedsView::selectPreviousItem() {
QModelIndex index_previous = moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier); 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()) { if (index_previous.isValid()) {
setCurrentIndex(index_previous); setCurrentIndex(index_previous);
setFocus(); setFocus();

View File

@ -71,27 +71,23 @@ class FeedsView : public QTreeView {
void expandCollapseCurrentItem(); void expandCollapseCurrentItem();
// Feed updating. // Feed updating.
void updateAllFeeds(); void updateAllItems();
void updateAllFeedsOnStartup(); void updateAllItemsOnStartup();
void updateSelectedFeeds(); void updateSelectedItems();
// Feed read/unread manipulators. // Feed read/unread manipulators.
void markSelectedFeedsReadStatus(int read); void markSelectedItemsRead();
void markSelectedFeedsRead(); void markSelectedItemsUnread();
void markSelectedFeedsUnread(); void markAllItemsRead();
void markAllFeedsReadStatus(int read);
void markAllFeedsRead();
// Newspaper accessors. // Newspaper accessors.
void openSelectedFeedsInNewspaperMode(); void openSelectedItemsInNewspaperMode();
// Recycle bin operators. // Recycle bin operators.
void emptyRecycleBin(); void emptyRecycleBin();
void restoreRecycleBin(); void restoreRecycleBin();
// Feed clearers. // Feed clearers.
void setSelectedFeedsClearStatus(int clear);
void setAllFeedsClearStatus(int clear);
void clearSelectedFeeds(); void clearSelectedFeeds();
void clearAllFeeds(); void clearAllFeeds();
void clearAllReadMessages(); void clearAllReadMessages();
@ -116,14 +112,6 @@ class FeedsView : public QTreeView {
// Reloads counts for particular feed. // Reloads counts for particular feed.
void updateCountsOfParticularFeed(Feed *feed, bool update_total_too); 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. // Selects next/previous item (feed/category) in the list.
void selectNextItem(); void selectNextItem();
void selectPreviousItem(); void selectPreviousItem();
@ -133,7 +121,14 @@ class FeedsView : public QTreeView {
setVisible(!isVisible()); 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. // Initializes context menus.
void initializeContextMenuCategories(RootItem *clicked_item); void initializeContextMenuCategories(RootItem *clicked_item);
void initializeContextMenuFeeds(RootItem *clicked_item); void initializeContextMenuFeeds(RootItem *clicked_item);
@ -151,17 +146,10 @@ class FeedsView : public QTreeView {
// Show custom context menu. // Show custom context menu.
void contextMenuEvent(QContextMenuEvent *event); void contextMenuEvent(QContextMenuEvent *event);
private slots:
void saveSortState(int column, Qt::SortOrder order);
void validateItemAfterDragDrop(const QModelIndex &source_index);
signals: signals:
// Emitted if user/application requested updating of some feeds. // Emitted if user/application requested updating of some feeds.
void feedsUpdateRequested(const QList<Feed*> feeds); void feedsUpdateRequested(const QList<Feed*> 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. // Emitted if currently selected feeds needs to be reloaded.
void feedsNeedToBeReloaded(bool mark_current_index_read); void feedsNeedToBeReloaded(bool mark_current_index_read);

View File

@ -60,7 +60,7 @@ class Feed : public RootItem {
virtual int update() = 0; virtual int update() = 0;
// Updates counts of all/unread messages for this feed. // 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. // Get ALL undeleted messages from this feed in one single list.
virtual QList<Message> undeletedMessages() const = 0; virtual QList<Message> undeletedMessages() const = 0;

View File

@ -102,6 +102,14 @@ bool StandardCategory::deleteViaGui() {
return removeItself(); 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 StandardCategory::removeItself() {
bool children_removed = true; bool children_removed = true;

View File

@ -56,6 +56,9 @@ class StandardCategory : public Category {
bool editViaGui(); bool editViaGui();
bool deleteViaGui(); bool deleteViaGui();
bool markAsReadUnread(ReadStatus status);
bool cleanMessages(bool clean_read_only);
// Removes category and all its children from persistent // Removes category and all its children from persistent
// database. // database.
bool removeItself(); bool removeItself();

View File

@ -118,6 +118,14 @@ bool StandardFeed::deleteViaGui() {
return removeItself(); return removeItself();
} }
bool StandardFeed::markAsReadUnread(ReadStatus status) {
return serviceRoot()->markFeedsReadUnread(QList<Feed*>() << this, status);
}
bool StandardFeed::cleanMessages(bool clean_read_only) {
return serviceRoot()->cleanFeeds(QList<Feed*>() << this, clean_read_only);
}
QList<Message> StandardFeed::undeletedMessages() const { QList<Message> StandardFeed::undeletedMessages() const {
QList<Message> messages; QList<Message> 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); QSqlDatabase database = qApp->database()->connection(QSL("Feed"), DatabaseFactory::FromSettings);
QSqlQuery query_all(database); 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()) { 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(); 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); setStatus(Normal);
} }
@ -433,7 +441,7 @@ int StandardFeed::update() {
setStatus(NetworkError); setStatus(NetworkError);
return 0; return 0;
} }
else { else if (status() != NewMessages) {
setStatus(Normal); setStatus(Normal);
} }
@ -731,5 +739,5 @@ StandardFeed::StandardFeed(const QSqlRecord &record) : Feed(NULL) {
setPassword(TextFactory::decrypt(record.value(FDS_DB_PASSWORD_INDEX).toString())); setPassword(TextFactory::decrypt(record.value(FDS_DB_PASSWORD_INDEX).toString()));
setAutoUpdateType(static_cast<StandardFeed::AutoUpdateType>(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); setAutoUpdateType(static_cast<StandardFeed::AutoUpdateType>(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt());
updateCounts(); updateCounts(true);
} }

View File

@ -74,6 +74,9 @@ class StandardFeed : public Feed {
bool editViaGui(); bool editViaGui();
bool deleteViaGui(); bool deleteViaGui();
bool markAsReadUnread(ReadStatus status);
bool cleanMessages(bool clean_read_only);
QList<Message> undeletedMessages() const; QList<Message> undeletedMessages() const;
// Obtains data related to this feed. // Obtains data related to this feed.
@ -83,7 +86,7 @@ class StandardFeed : public Feed {
int update(); int update();
// Updates counts of all/unread messages for this feed. // 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 // Removes this standard feed from persistent
// storage. // storage.
@ -156,7 +159,7 @@ class StandardFeed : public Feed {
// Fetches metadata for the feed. // Fetches metadata for the feed.
void fetchMetadataForItself(); void fetchMetadataForItself();
protected: private:
// Persistently stores given messages into the database // Persistently stores given messages into the database
// and updates existing messages if newer version is // and updates existing messages if newer version is
// available. // available.

View File

@ -128,6 +128,87 @@ QVariant StandardServiceRoot::data(int column, int role) const {
} }
} }
bool StandardServiceRoot::markFeedsReadUnread(QList<Feed*> 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<Feed*> 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(){ void StandardServiceRoot::loadFromDatabase(){
QSqlDatabase database = qApp->database()->connection("StandardServiceRoot", DatabaseFactory::FromSettings); QSqlDatabase database = qApp->database()->connection("StandardServiceRoot", DatabaseFactory::FromSettings);
CategoryAssignment categories; CategoryAssignment categories;
@ -362,6 +443,17 @@ void StandardServiceRoot::exportFeeds() {
delete form.data(); delete form.data();
} }
QStringList StandardServiceRoot::textualFeedIds(const QList<Feed*> &feeds) {
QStringList stringy_ids;
stringy_ids.reserve(feeds.size());
foreach (Feed *feed, feeds) {
stringy_ids.append(QString::number(feed->id()));
}
return stringy_ids;
}
QList<QAction*> StandardServiceRoot::addItemMenu() { QList<QAction*> StandardServiceRoot::addItemMenu() {
if (m_addItemMenu.isEmpty()) { if (m_addItemMenu.isEmpty()) {
QAction *action_new_category = new QAction(qApp->icons()->fromTheme("folder-category"), tr("Add new category"), this); QAction *action_new_category = new QAction(qApp->icons()->fromTheme("folder-category"), tr("Add new category"), this);

View File

@ -74,6 +74,9 @@ class StandardServiceRoot : public ServiceRoot {
// NOTE: This is used for import/export of the model. // NOTE: This is used for import/export of the model.
bool mergeImportExportModel(FeedsImportExportModel *model, QString &output_message); bool mergeImportExportModel(FeedsImportExportModel *model, QString &output_message);
bool markFeedsReadUnread(QList<Feed*> items, ReadStatus read);
bool cleanFeeds(QList<Feed*> items, bool clean_read_only);
public slots: public slots:
void addNewCategory(); void addNewCategory();
void addNewFeed(); void addNewFeed();
@ -81,6 +84,10 @@ class StandardServiceRoot : public ServiceRoot {
void exportFeeds(); void exportFeeds();
private: private:
// Returns converted ids of given feeds
// which are suitable as IN clause for SQL queries.
QStringList textualFeedIds(const QList<Feed *> &feeds);
void loadFromDatabase(); void loadFromDatabase();
// Takes lists of feeds/categories and assembles // Takes lists of feeds/categories and assembles