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.
This commit is contained in:
Jim Broadus 2020-12-14 23:07:59 -08:00 committed by John Maguire
parent fe88d8f8b0
commit ccba649f62
2 changed files with 50 additions and 0 deletions

View File

@ -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;

View File

@ -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<InternetService*, ServiceItem> shown_services_;
static QMap<QString, InternetService*>* sServices;