Big refactoring, marking read/unread, clearing now uses newer approach.

This commit is contained in:
Martin Rotter 2015-11-20 13:29:32 +01:00
parent 8c7079de26
commit 68d3e0a53f
13 changed files with 130 additions and 215 deletions

View File

@ -206,6 +206,12 @@ int FeedsModel::rowCount(const QModelIndex &parent) const {
}
}
void FeedsModel::reloadCountsOfWholeModel() {
m_rootItem->updateCounts(true);
reloadWholeLayout();
notifyWithCounts();
}
void FeedsModel::removeItem(const QModelIndex &index) {
if (index.isValid()) {
QModelIndex parent_index = index.parent();
@ -395,8 +401,11 @@ void FeedsModel::reloadChangedItem(RootItem *item) {
reloadChangedLayout(QModelIndexList() << index_item);
}
void FeedsModel::onItemDataChanged(RootItem *item) {
void FeedsModel::onItemDataChanged(QList<RootItem*> items) {
foreach (RootItem *item, items) {
reloadChangedItem(item);
}
notifyWithCounts();
}
@ -421,7 +430,7 @@ bool FeedsModel::addServiceAccount(ServiceRoot *root) {
// Connect.
connect(root, SIGNAL(readFeedsFilterInvalidationRequested()), this, SIGNAL(readFeedsFilterInvalidationRequested()));
connect(root, SIGNAL(dataChanged(RootItem*)), this, SLOT(onItemDataChanged(RootItem*)));
connect(root, SIGNAL(dataChanged(QList<RootItem*>)), this, SLOT(onItemDataChanged(QList<RootItem*>)));
root->start();
return true;

View File

@ -59,6 +59,8 @@ class FeedsModel : public QAbstractItemModel {
return m_rootItem->countOfUnreadMessages();
}
void reloadCountsOfWholeModel();
// Removes item with given index.
// NOTE: Also deletes item from memory.
void removeItem(const QModelIndex &index);
@ -156,7 +158,7 @@ class FeedsModel : public QAbstractItemModel {
}
private slots:
void onItemDataChanged(RootItem *item);
void onItemDataChanged(QList<RootItem*> items);
// Is executed when next auto-update round could be done.
void executeNextAutoUpdate();

View File

@ -361,12 +361,9 @@ bool MessagesModel::switchBatchMessageImportance(const QModelIndexList &messages
}
bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList &messages, int deleted) {
QSqlQuery query_read_msg(database());
QStringList message_ids;
QList<int> message_ids_num;
query_read_msg.setForwardOnly(true);
// Obtain IDs of all desired messages.
foreach (const QModelIndex &message, messages) {
int message_id = messageId(message.row());
@ -375,8 +372,15 @@ bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList &messages, int
message_ids.append(QString::number(message_id));
}
if (!m_selectedItem->getParentServiceRoot()->onBeforeMessagesDelete(m_selectedItem, message_ids_num)) {
return false;
}
QSqlQuery query_read_msg(database());
QString sql_delete_query;
query_read_msg.setForwardOnly(true);
if (m_selectedItem->kind() != RootItemKind::Bin) {
sql_delete_query = QString(QSL("UPDATE Messages SET is_deleted = %2 WHERE id IN (%1);")).arg(message_ids.join(QSL(", ")),
QString::number(deleted));
@ -388,13 +392,7 @@ bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList &messages, int
if (query_read_msg.exec(sql_delete_query)) {
fetchAllData();
//emit messageCountsChanged();
// TODO: counts changed - zde pokracovat podle metod setbarchmessageread
//emit messageCountsChanged(m_selectedItem.mode(), true, false);
return true;
return m_selectedItem->getParentServiceRoot()->onAfterMessagesDelete(m_selectedItem, message_ids_num);
}
else {
return false;

View File

@ -235,7 +235,6 @@ void FeedMessageViewer::onFeedUpdatesStarted() {
void FeedMessageViewer::onFeedUpdatesProgress(Feed *feed, int current, int total) {
// Some feed got updated.
m_feedsView->updateCountsOfParticularFeed(feed, true);
qApp->mainForm()->statusBar()->showProgressFeeds((current * 100.0) / total,
//: Text display in status bar when particular feed is updated.
tr("Updated feed '%1'").arg(feed->title()));
@ -328,9 +327,6 @@ void FeedMessageViewer::createConnections() {
// If user selects feeds, load their messages.
connect(m_feedsView, SIGNAL(itemSelected(RootItem*)), m_messagesView, SLOT(loadFeeds(RootItem*)));
// If user changes status of some messages, recalculate message counts.
connect(m_messagesView->sourceModel(), SIGNAL(messageCountsChanged()), m_feedsView, SLOT(receiveMessageCountsChange()));
// State of many messages is changed, then we need
// to reload selections.
connect(m_feedsView, SIGNAL(feedsNeedToBeReloaded(bool)), m_messagesView, SLOT(reloadSelections(bool)));
@ -498,7 +494,7 @@ void FeedMessageViewer::showDbCleanupAssistant() {
qApp->feedUpdateLock()->unlock();
m_messagesView->reloadSelections(false);
m_feedsView->updateCountsOfAllFeeds(true);
m_feedsView->sourceModel()->reloadCountsOfWholeModel();
}
else {
qApp->showGuiMessage(tr("Cannot cleanup database"),

View File

@ -169,60 +169,14 @@ void FeedsView::updateAllItemsOnStartup() {
void FeedsView::clearSelectedFeeds() {
m_sourceModel->markItemCleared(selectedItem(), false);
updateCountsOfSelectedFeeds(true);
emit feedsNeedToBeReloaded(true);
}
void FeedsView::clearAllFeeds() {
m_sourceModel->markItemCleared(m_sourceModel->rootItem(), false);
updateCountsOfAllFeeds(true);
emit feedsNeedToBeReloaded(true);
}
void FeedsView::receiveMessageCountsChange() {
// TODO: toto vymazat, prepocitani cisel unread/all
// a upozorneni na zmenu itemu provede ten item
// zde jen nechat tu invalidaci read filteru
// If the change came from recycle bin mode, then:
// a) total count of message was changed AND no message was restored - some messages
// were permanently deleted from recycle bin --> we need to update counts of
// just recycle bin, including total counts.
// b) total count of message was changed AND some message was restored - some messages
// were restored --> we need to update counts from all items and bin, including total counts.
// c) total count of message was not changed - state of some messages was switched, no
// deletings or restorings were made --> update counts of just recycle bin, excluding total counts.
//
// If the change came from feed mode, then:
// a) total count of message was changed - some messages were deleted --> we need to update
// counts of recycle bin, including total counts and update counts of selected feeds, including
// total counts.
// b) total count of message was not changed - some messages switched state --> we need to update
// counts of just selected feeds.
/*
if (mode == FeedsSelection::MessagesFromRecycleBin) {
if (total_msg_count_changed) {
if (any_msg_restored) {
updateCountsOfAllFeeds(true);
}
else {
updateCountsOfRecycleBin(true);
}
}
else {
updateCountsOfRecycleBin(false);
}
}
else {
updateCountsOfSelectedFeeds(total_msg_count_changed);
}*/
m_proxyModel->invalidateReadFeedsFilter();
}
void FeedsView::editSelectedItem() {
if (!qApp->feedUpdateLock()->tryLock()) {
// Lock was not obtained because
@ -320,8 +274,6 @@ void FeedsView::deleteSelectedItem() {
void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) {
m_sourceModel->markItemRead(selectedItem(), read);
updateCountsOfSelectedFeeds(false);
emit feedsNeedToBeReloaded(read == 1);
}
@ -335,8 +287,6 @@ void FeedsView::markSelectedItemsUnread() {
void FeedsView::markAllItemsReadStatus(RootItem::ReadStatus read) {
m_sourceModel->markItemRead(m_sourceModel->rootItem(), read);
updateCountsOfAllFeeds(false);
emit feedsNeedToBeReloaded(read == 1);
}
@ -357,88 +307,6 @@ void FeedsView::openSelectedItemsInNewspaperMode() {
}
}
void FeedsView::emptyRecycleBin() {
if (MessageBox::show(qApp->mainForm(), QMessageBox::Question, tr("Permanently delete messages"),
tr("You are about to permanenty delete all messages from your recycle bin."),
tr("Do you really want to empty your recycle bin?"),
QString(), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
// TODO: pridat metodu cisteni standardniho kose nebo vsech kosu.
//m_sourceModel->recycleBin()->empty();
updateCountsOfSelectedFeeds(true);
emit feedsNeedToBeReloaded(true);
}
}
void FeedsView::restoreRecycleBin() {
// TODO: pridat metodu cisteni standardniho kose nebo vsech kosu.
//m_sourceModel->recycleBin()->restore();
updateCountsOfAllFeeds(true);
emit feedsNeedToBeReloaded(true);
}
void FeedsView::updateCountsOfSelectedFeeds(bool update_total_too) {
foreach (Feed *feed, selectedFeeds()) {
feed->updateCounts(update_total_too);
}
QModelIndexList selected_indexes = m_proxyModel->mapListToSource(selectionModel()->selectedRows());
if (update_total_too) {
// Number of items in recycle bin has changed.
// TODO: pridat metodu cisteni standardniho kose nebo vsech kosu.
//m_sourceModel->recycleBin()->updateCounts(true);
// We need to refresh data for recycle bin too.
// TODO: pridat metodu cisteni standardniho kose nebo vsech kosu.
//selected_indexes.append(m_sourceModel->indexForItem(m_sourceModel->recycleBin()));
}
// Make sure that selected view reloads changed indexes.
m_sourceModel->reloadChangedLayout(selected_indexes);
m_sourceModel->notifyWithCounts();
}
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()));
m_sourceModel->notifyWithCounts();
}
void FeedsView::updateCountsOfAllFeeds(bool update_total_too) {
foreach (Feed *feed, allFeeds()) {
feed->updateCounts(update_total_too);
}
if (update_total_too) {
// Number of items in recycle bin has changed.
// TODO: pridat metodu cisteni standardniho kose nebo vsech kosu.
//m_sourceModel->recycleBin()->updateCounts(true);
}
// Make sure that all views reloads its data.
m_sourceModel->reloadWholeLayout();
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);
m_sourceModel->reloadChangedLayout(QModelIndexList() << index);
}
m_proxyModel->invalidateReadFeedsFilter();
m_sourceModel->notifyWithCounts();
}
void FeedsView::selectNextItem() {
QModelIndex index_next = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier);
@ -457,6 +325,10 @@ void FeedsView::selectPreviousItem() {
}
}
void FeedsView::switchVisibility() {
setVisible(!isVisible());
}
QMenu *FeedsView::initializeContextMenuCategories(RootItem *clicked_item) {
if (m_contextMenuCategories == NULL) {
m_contextMenuCategories = new QMenu(tr("Context menu for categories"), this);

View File

@ -79,10 +79,6 @@ class FeedsView : public QTreeView {
// Newspaper accessors.
void openSelectedItemsInNewspaperMode();
// Recycle bin operators.
void emptyRecycleBin();
void restoreRecycleBin();
// Feed clearers.
void clearSelectedFeeds();
void clearAllFeeds();
@ -92,30 +88,25 @@ class FeedsView : public QTreeView {
void editSelectedItem();
void deleteSelectedItem();
// Is called when counts of messages are changed externally,
// typically from message view.
void receiveMessageCountsChange();
// Reloads counts for selected feeds.
void updateCountsOfSelectedFeeds(bool update_total_too);
// Reloads counts of recycle bin.
void updateCountsOfRecycleBin(bool update_total_too);
// Reloads counts for all feeds.
void updateCountsOfAllFeeds(bool update_total_too);
// Reloads counts for particular feed.
void updateCountsOfParticularFeed(Feed *feed, bool update_total_too);
// Selects next/previous item (feed/category) in the list.
void selectNextItem();
void selectPreviousItem();
// Switches visibility of the widget.
void switchVisibility() {
setVisible(!isVisible());
}
void switchVisibility();
signals:
// Emitted if user/application requested updating of some feeds.
void feedsUpdateRequested(const QList<Feed*> feeds);
// Emitted if currently selected feeds needs to be reloaded.
void feedsNeedToBeReloaded(bool mark_current_index_read);
// Emitted if user selects new feeds.
void itemSelected(RootItem *item);
// Requests opening of given messages in newspaper mode.
void openMessagesInNewspaperView(const QList<Message> &messages);
private slots:
void markSelectedItemReadStatus(RootItem::ReadStatus read);
@ -143,20 +134,6 @@ class FeedsView : public QTreeView {
// Show custom context menu.
void contextMenuEvent(QContextMenuEvent *event);
signals:
// Emitted if user/application requested updating of some feeds.
void feedsUpdateRequested(const QList<Feed*> feeds);
// Emitted if currently selected feeds needs to be reloaded.
void feedsNeedToBeReloaded(bool mark_current_index_read);
// Emitted if user selects new feeds.
void itemSelected(RootItem *item);
// Requests opening of given messages in newspaper mode.
void openMessagesInNewspaperView(const QList<Message> &messages);
private:
QMenu *m_contextMenuCategories;
QMenu *m_contextMenuFeeds;
QMenu *m_contextMenuEmptySpace;

View File

@ -72,6 +72,9 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
// Register needed metatypes.
qRegisterMetaType<QList<RootItem*> >("QList<RootItem*>");
// Add an extra path for non-system icon themes and set current icon theme
// and skin.
qApp->icons()->setupSearchPaths();

View File

@ -31,6 +31,6 @@ FeedsModel *ServiceRoot::feedsModel() const {
return m_feedsModel;
}
void ServiceRoot::itemChanged(RootItem *item) {
emit dataChanged(item);
void ServiceRoot::itemChanged(QList<RootItem*> items) {
emit dataChanged(items);
}

View File

@ -67,7 +67,7 @@ class ServiceRoot : public RootItem {
// "loading" dialog přes view a toto zavolat, nasledně signalovat
virtual bool loadMessagesForItem(RootItem *item, QSqlTableModel *model) = 0;
// Called BEFORE this read status update is stored in DB,
// Called BEFORE this read status update (triggered by user in message list) is stored in DB,
// when false is returned, change is aborted.
// This is the place to make some other changes like updating
// some ONLINE service or something.
@ -75,7 +75,7 @@ class ServiceRoot : public RootItem {
// "read" is status which is ABOUT TO BE SET.
virtual bool onBeforeSetMessagesRead(RootItem *selected_item, QList<int> message_db_ids, ReadStatus read) = 0;
// Called AFTER this read status update is stored in DB,
// Called AFTER this read status update (triggered by user in message list) is stored in DB,
// when false is returned, change is aborted.
// Here service root should inform (via signals)
// which items are actually changed.
@ -99,17 +99,25 @@ class ServiceRoot : public RootItem {
// "changes" - list of pairs - <message (integer id), new status>
virtual bool onAfterSwitchMessageImportance(RootItem *selected_item, QList<QPair<int,RootItem::Importance> > changes) = 0;
// Called BEFORE the list of messages is about to be deleted
// by the user from message list.
virtual bool onBeforeMessagesDelete(RootItem *selected_item, QList<int> message_db_ids) = 0;
// Called AFTER the list of messages is about to be deleted
// by the user from message list.
virtual bool onAfterMessagesDelete(RootItem *selected_item, QList<int> message_db_ids) = 0;
// Access to feed model.
FeedsModel *feedsModel() const;
// Obvious method to wrap dataChanged(...) signal
// which can be used by items themselves or any
// other component.
void itemChanged(RootItem *item);
void itemChanged(QList<RootItem*> items);
signals:
// Emitted if data in any item belonging to this root are changed.
void dataChanged(RootItem *item);
void dataChanged(QList<RootItem*> items);
void readFeedsFilterInvalidationRequested();
private:

View File

@ -215,7 +215,7 @@ void StandardFeed::fetchMetadataForItself() {
// Notify the model about fact, that it needs to reload new information about
// this item, particularly the icon.
serviceRoot()->itemChanged(this);
serviceRoot()->itemChanged(QList<RootItem*>() << this);
}
else {
qApp->showGuiMessage(tr("Metadata not fetched"),
@ -731,6 +731,10 @@ int StandardFeed::updateMessages(const QList<Message> &messages) {
qDebug("Transaction commit for message downloader failed.");
}
else {
updateCounts(true);
serviceRoot()->itemChanged(QList<RootItem*>() << this);
}
return updated_messages;
}

View File

@ -157,6 +157,15 @@ bool StandardServiceRoot::markFeedsReadUnread(QList<Feed*> items, ReadStatus rea
// Commit changes.
if (db_handle.commit()) {
// Messages are cleared, now inform model about need to reload data.
QList<RootItem*> itemss;
foreach (Feed *feed, items) {
feed->updateCounts(true);
itemss.append(feed);
}
emit dataChanged(itemss);
return true;
}
else {
@ -191,6 +200,9 @@ bool StandardServiceRoot::markRecycleBinReadUnread(RootItem::ReadStatus read) {
// Commit changes.
if (db_handle.commit()) {
m_recycleBin->updateCounts(true);
emit dataChanged(QList<RootItem*>() << m_recycleBin);
return true;
}
else {
@ -200,12 +212,6 @@ bool StandardServiceRoot::markRecycleBinReadUnread(RootItem::ReadStatus read) {
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);
@ -213,8 +219,6 @@ bool StandardServiceRoot::cleanFeeds(QList<Feed*> items, bool 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;
}
}
@ -222,8 +226,6 @@ bool StandardServiceRoot::cleanFeeds(QList<Feed*> items, bool clean_read_only) {
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;
}
}
@ -232,15 +234,22 @@ bool StandardServiceRoot::cleanFeeds(QList<Feed*> items, bool clean_read_only) {
if (!query_delete_msg.exec()) {
qDebug("Query execution for feeds clearing failed.");
db_handle.rollback();
}
// Commit changes.
if (db_handle.commit()) {
return true;
return false;
}
else {
return db_handle.rollback();
// Messages are cleared, now inform model about need to reload data.
QList<RootItem*> itemss;
foreach (Feed *feed, items) {
feed->updateCounts(true);
itemss.append(feed);
}
m_recycleBin->updateCounts(true);
itemss.append(m_recycleBin);
emit dataChanged(itemss);
return true;
}
}
@ -533,7 +542,7 @@ bool StandardServiceRoot::loadMessagesForItem(RootItem *item, QSqlTableModel *mo
QString filter_clause = stringy_ids.join(QSL(", "));
model->setFilter(QString(QSL("feed IN (%1) AND is_deleted = 0")).arg(filter_clause));
model->setFilter(QString(QSL("feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0")).arg(filter_clause));
qDebug("Loading messages from feeds: %s.", qPrintable(filter_clause));
}
@ -554,7 +563,7 @@ bool StandardServiceRoot::onAfterSetMessagesRead(RootItem *selected_item, QList<
selected_item->updateCounts(false);
emit dataChanged(selected_item);
emit dataChanged(QList<RootItem*>() << selected_item);
emit readFeedsFilterInvalidationRequested();
return true;
}
@ -575,8 +584,34 @@ bool StandardServiceRoot::onAfterSwitchMessageImportance(RootItem *selected_item
return true;
}
bool StandardServiceRoot::onBeforeMessagesDelete(RootItem *selected_item, QList<int> message_db_ids) {
Q_UNUSED(selected_item)
Q_UNUSED(message_db_ids)
return true;
}
bool StandardServiceRoot::onAfterMessagesDelete(RootItem *selected_item, QList<int> message_db_ids) {
Q_UNUSED(message_db_ids)
// User deleted some messages he selected in message list.
selected_item->updateCounts(true);
if (selected_item->kind() == RootItemKind::Bin) {
emit dataChanged(QList<RootItem*>() << m_recycleBin);
}
else {
m_recycleBin->updateCounts(true);
emit dataChanged(QList<RootItem*>() << selected_item << m_recycleBin);
}
emit readFeedsFilterInvalidationRequested();
return true;
}
void StandardServiceRoot::assembleCategories(CategoryAssignment categories) {
QHash<int, RootItem*> assignments;
QHash<int,RootItem*> assignments;
assignments.insert(NO_PARENT_CATEGORY, this);
// Add top-level categories.

View File

@ -66,6 +66,9 @@ class StandardServiceRoot : public ServiceRoot {
bool onBeforeSwitchMessageImportance(RootItem *selected_item, QList<QPair<int,RootItem::Importance> > changes);
bool onAfterSwitchMessageImportance(RootItem *selected_item, QList<QPair<int,RootItem::Importance> > changes);
bool onBeforeMessagesDelete(RootItem *selected_item, QList<int> message_db_ids);
bool onAfterMessagesDelete(RootItem *selected_item, QList<int> message_db_ids);
// Returns all standard categories which are lying under given root node.
// This does NOT include the root node even if the node is category.
QHash<int,StandardCategory*> categoriesForItem(RootItem *root);

View File

@ -52,6 +52,14 @@ class TtRssServiceRoot : public ServiceRoot {
bool onAfterSwitchMessageImportance(RootItem *selected_item, QList<QPair<int,RootItem::Importance> > changes) {
return false;
}
bool onBeforeMessagesDelete(RootItem *selected_item, QList<int> message_db_ids) {
return false;
}
bool onAfterMessagesDelete(RootItem *selected_item, QList<int> message_db_ids) {
return false;
}
};
#endif // TTRSSSERVICEROOT_H