From ccba649f622497b8463930b9ad5b7a1cafe6c976 Mon Sep 17 00:00:00 2001 From: Jim Broadus Date: Mon, 14 Dec 2020 23:07:59 -0800 Subject: [PATCH] Clear cached indexes in InternetModel when rows are removed When opening a context menu on an internet item, the selected items are stored in the InternetModel instance. In cases when the items are removed, certain menu options can cause a crash. A specific case is downloading a podcast when the user has chosen to limit the number of visible episodes. The subtree for the podcast is rebuilt after the download completes, so if a context menu was opened during the download time, selecting the append to playlist option will attempt to operate on bad indexes. This fix uses the rowsAboutToBeRemoved signal to remove these stored indexes. There are likely another rare cases where the indexes can become invalid. For example, sibling items within a subtree may be removed, causing the stored indexes to become incorrect or out of range. --- src/internet/core/internetmodel.cpp | 44 +++++++++++++++++++++++++++++ src/internet/core/internetmodel.h | 6 ++++ 2 files changed, 50 insertions(+) diff --git a/src/internet/core/internetmodel.cpp b/src/internet/core/internetmodel.cpp index 2015ae1b6..8718cd8b5 100644 --- a/src/internet/core/internetmodel.cpp +++ b/src/internet/core/internetmodel.cpp @@ -81,6 +81,8 @@ InternetModel::InternetModel(Application* app, QObject* parent) Q_ASSERT(sServices->isEmpty()); merged_model_->setSourceModel(this); + connect(merged_model_, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), + SLOT(RowsAboutToBeRemoved(QModelIndex, int, int))); AddService(new ClassicalRadioService(app, this)); AddService(new DigitallyImportedService(app, this)); @@ -234,6 +236,48 @@ int InternetModel::rowCount(const QModelIndex& parent) const { return QStandardItemModel::rowCount(parent); } +void InternetModel::RowsAboutToBeRemoved(const QModelIndex& parent, int first, + int last) { + for (int i = first; i <= last; i++) { + // Assuming we're always looking at column 0 + QModelIndex removing = + merged_model_->mapToSource(merged_model_->index(first, 0, parent)); + IndexAboutToBeRemoved(removing); + } +} + +void InternetModel::IndexAboutToBeRemoved(const QModelIndex& index) { + if (IsInLineage(current_index_, index)) { + qLog(Debug) << "Removing current index"; + // Invalidate index + current_index_ = QModelIndex(); + } + + QModelIndexList::iterator iter = selected_indexes_.begin(); + while (iter != selected_indexes_.end()) { + if (IsInLineage(*iter, index)) { + qLog(Debug) << "Removing selected item"; + iter = selected_indexes_.erase(iter); + } else { + iter++; + } + } +} + +bool InternetModel::IsInLineage(QModelIndex d, const QModelIndex& a) { + if (!a.isValid()) { + return false; + } + + while (d.isValid()) { + if (d == a) { + return true; + } + d = d.parent(); + } + return false; +} + bool InternetModel::IsPlayable(const QModelIndex& index) const { QVariant behaviour = index.data(Role_PlayBehaviour); if (!behaviour.isValid()) return false; diff --git a/src/internet/core/internetmodel.h b/src/internet/core/internetmodel.h index 91040adcb..94aa05145 100644 --- a/src/internet/core/internetmodel.h +++ b/src/internet/core/internetmodel.h @@ -186,8 +186,14 @@ class InternetModel : public QStandardItemModel { private slots: void ServiceDeleted(); + void RowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); private: + // Index is about to be removed from the merged model + void IndexAboutToBeRemoved(const QModelIndex& index); + // Determine if d or one of its ancestors is equal to a. + bool IsInLineage(QModelIndex d, const QModelIndex& a); + QMap shown_services_; static QMap* sServices;