diff --git a/src/gui/torrentcontentfiltermodel.cpp b/src/gui/torrentcontentfiltermodel.cpp index 59e2e4771..9ff5043a1 100644 --- a/src/gui/torrentcontentfiltermodel.cpp +++ b/src/gui/torrentcontentfiltermodel.cpp @@ -110,17 +110,13 @@ bool TorrentContentFilterModel::lessThan(const QModelIndex &left, const QModelIn void TorrentContentFilterModel::selectAll() { for (int i = 0; i < rowCount(); ++i) - setData(index(i, 0), Qt::Checked, Qt::CheckStateRole); - - emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1))); + setData(index(i, TorrentContentModelItem::COL_NAME), Qt::Checked, Qt::CheckStateRole); } void TorrentContentFilterModel::selectNone() { for (int i = 0; i < rowCount(); ++i) - setData(index(i, 0), Qt::Unchecked, Qt::CheckStateRole); - - emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1))); + setData(index(i, TorrentContentModelItem::COL_NAME), Qt::Unchecked, Qt::CheckStateRole); } bool TorrentContentFilterModel::hasFiltered(const QModelIndex &folder) const diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 664b73256..ff4d23ce2 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -220,7 +220,7 @@ void TorrentContentModel::updateFilesProgress(const QVector &fp) // Update folders progress in the tree m_rootItem->recalculateProgress(); m_rootItem->recalculateAvailability(); - emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1))); + notifyModelUpdate(index(0, 0)); } void TorrentContentModel::updateFilesPriorities(const QVector &fprio) @@ -233,7 +233,7 @@ void TorrentContentModel::updateFilesPriorities(const QVectorsetPriority(static_cast(fprio[i])); - emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1))); + notifyModelUpdate(index(0, 0)); } void TorrentContentModel::updateFilesAvailability(const QVector &fa) @@ -247,7 +247,7 @@ void TorrentContentModel::updateFilesAvailability(const QVector &fa) m_filesIndex[i]->setAvailability(fa[i]); // Update folders progress in the tree m_rootItem->recalculateProgress(); - emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1))); + notifyModelUpdate(index(0, 0)); } QVector TorrentContentModel::getFilePriorities() const @@ -269,66 +269,83 @@ bool TorrentContentModel::allFiltered() const int TorrentContentModel::columnCount(const QModelIndex &parent) const { - if (parent.isValid()) - return static_cast(parent.internalPointer())->columnCount(); - - return m_rootItem->columnCount(); + Q_UNUSED(parent); + return TorrentContentModelItem::NB_COL; } -bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &value, const int role) { if (!index.isValid()) return false; if ((index.column() == TorrentContentModelItem::COL_NAME) && (role == Qt::CheckStateRole)) { - auto *item = static_cast(index.internalPointer()); - qDebug("setData(%s, %d)", qUtf8Printable(item->name()), value.toInt()); + auto *item = static_cast(index.internalPointer()); - BitTorrent::DownloadPriority prio = BitTorrent::DownloadPriority::Normal; - if (value.toInt() == Qt::PartiallyChecked) - prio = BitTorrent::DownloadPriority::Mixed; - else if (value.toInt() == Qt::Unchecked) - prio = BitTorrent::DownloadPriority::Ignored; + const BitTorrent::DownloadPriority currentPrio = item->priority(); + const auto checkState = static_cast(value.toInt()); + const BitTorrent::DownloadPriority newPrio = (checkState == Qt::PartiallyChecked) + ? BitTorrent::DownloadPriority::Mixed + : ((checkState == Qt::Unchecked) + ? BitTorrent::DownloadPriority::Ignored + : BitTorrent::DownloadPriority::Normal); - if (item->priority() != prio) + if (currentPrio != newPrio) { - item->setPriority(prio); + item->setPriority(newPrio); // Update folders progress in the tree m_rootItem->recalculateProgress(); m_rootItem->recalculateAvailability(); - emit dataChanged(this->index(0, 0), this->index((rowCount() - 1), (columnCount() - 1))); + + notifyModelUpdate(index); emit filteredFilesChanged(); + + return true; } - return true; } if (role == Qt::EditRole) { - Q_ASSERT(index.isValid()); - auto *item = static_cast(index.internalPointer()); + auto *item = static_cast(index.internalPointer()); + switch (index.column()) { case TorrentContentModelItem::COL_NAME: - item->setName(value.toString()); + { + const QString currentName = item->name(); + const QString newName = value.toString(); + if (currentName != newName) + { + item->setName(newName); + emit dataChanged(index, index); + return true; + } + } break; + case TorrentContentModelItem::COL_PRIO: { - const BitTorrent::DownloadPriority previousPrio = item->priority(); + const BitTorrent::DownloadPriority currentPrio = item->priority(); const auto newPrio = static_cast(value.toInt()); - item->setPriority(newPrio); - if ((newPrio != previousPrio) && ((newPrio == BitTorrent::DownloadPriority::Ignored) - || (previousPrio == BitTorrent::DownloadPriority::Ignored))) + if (currentPrio != newPrio) { - emit filteredFilesChanged(); + item->setPriority(newPrio); + + notifyModelUpdate(index); + if ((newPrio == BitTorrent::DownloadPriority::Ignored) + || (currentPrio == BitTorrent::DownloadPriority::Ignored)) + { + emit filteredFilesChanged(); + } + + return true; } } break; + default: - return false; + break; } - emit dataChanged(index, index); - return true; } return false; @@ -336,14 +353,14 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu TorrentContentModelItem::ItemType TorrentContentModel::itemType(const QModelIndex &index) const { - return static_cast(index.internalPointer())->itemType(); + return static_cast(index.internalPointer())->itemType(); } int TorrentContentModel::getFileIndex(const QModelIndex &index) { - auto *item = static_cast(index.internalPointer()); + auto *item = static_cast(index.internalPointer()); if (item->itemType() == TorrentContentModelItem::FileType) - return static_cast(item)->fileIndex(); + return static_cast(item)->fileIndex(); Q_ASSERT(item->itemType() == TorrentContentModelItem::FileType); return -1; @@ -354,7 +371,7 @@ QVariant TorrentContentModel::data(const QModelIndex &index, const int role) con if (!index.isValid()) return {}; - auto *item = static_cast(index.internalPointer()); + auto *item = static_cast(index.internalPointer()); switch (role) { @@ -392,8 +409,10 @@ QVariant TorrentContentModel::data(const QModelIndex &index, const int role) con return item->underlyingData(index.column()); default: - return {}; + break; } + + return {}; } Qt::ItemFlags TorrentContentModel::flags(const QModelIndex &index) const @@ -431,19 +450,14 @@ QVariant TorrentContentModel::headerData(int section, Qt::Orientation orientatio } } -QModelIndex TorrentContentModel::index(int row, int column, const QModelIndex &parent) const +QModelIndex TorrentContentModel::index(const int row, const int column, const QModelIndex &parent) const { - if (parent.isValid() && (parent.column() != 0)) - return {}; - - if (column >= TorrentContentModelItem::NB_COL) + if (column >= columnCount()) return {}; - TorrentContentModelFolder *parentItem = nullptr; - if (!parent.isValid()) - parentItem = m_rootItem; - else - parentItem = static_cast(parent.internalPointer()); + const TorrentContentModelFolder *parentItem = parent.isValid() + ? static_cast(parent.internalPointer()) + : m_rootItem; Q_ASSERT(parentItem); if (row >= parentItem->childCount()) @@ -452,6 +466,7 @@ QModelIndex TorrentContentModel::index(int row, int column, const QModelIndex &p TorrentContentModelItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); + return {}; } @@ -460,28 +475,26 @@ QModelIndex TorrentContentModel::parent(const QModelIndex &index) const if (!index.isValid()) return {}; - auto *childItem = static_cast(index.internalPointer()); - if (!childItem) + const auto *item = static_cast(index.internalPointer()); + if (!item) return {}; - TorrentContentModelItem *parentItem = childItem->parent(); + TorrentContentModelItem *parentItem = item->parent(); if (parentItem == m_rootItem) return {}; + // From https://doc.qt.io/qt-6/qabstractitemmodel.html#parent: + // A common convention used in models that expose tree data structures is that only items + // in the first column have children. For that case, when reimplementing this function in + // a subclass the column of the returned QModelIndex would be 0. return createIndex(parentItem->row(), 0, parentItem); } int TorrentContentModel::rowCount(const QModelIndex &parent) const { - if (parent.column() > 0) - return 0; - - TorrentContentModelFolder *parentItem = nullptr; - if (!parent.isValid()) - parentItem = m_rootItem; - else - parentItem = dynamic_cast(static_cast(parent.internalPointer())); - + const TorrentContentModelFolder *parentItem = parent.isValid() + ? dynamic_cast(static_cast(parent.internalPointer())) + : m_rootItem; return parentItem ? parentItem->childCount() : 0; } @@ -551,20 +564,47 @@ void TorrentContentModel::setupModelData(const BitTorrent::AbstractFileStorage & emit layoutChanged(); } -void TorrentContentModel::selectAll() +void TorrentContentModel::notifyModelUpdate(const QModelIndex &index) { - for (int i = 0; i < m_rootItem->childCount(); ++i) + Q_ASSERT(index.isValid()); + + const int lastColumnIndex = columnCount(index) - 1; + + // emit itself + emit dataChanged(index.siblingAtColumn(0), index.siblingAtColumn(lastColumnIndex)); + + // propagate up the model + QModelIndex parentIndex = parent(index); + while (parentIndex.isValid()) { - TorrentContentModelItem* child = m_rootItem->child(i); - if (child->priority() == BitTorrent::DownloadPriority::Ignored) - child->setPriority(BitTorrent::DownloadPriority::Normal); + emit dataChanged(parentIndex.siblingAtColumn(0), parentIndex.siblingAtColumn(lastColumnIndex)); + parentIndex = parent(parentIndex); } - emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1))); -} -void TorrentContentModel::selectNone() -{ - for (int i = 0; i < m_rootItem->childCount(); ++i) - m_rootItem->child(i)->setPriority(BitTorrent::DownloadPriority::Ignored); - emit dataChanged(index(0, 0), index((rowCount() - 1), (columnCount() - 1))); + // propagate down the model + QVector parentIndexes; + + if (hasChildren(index)) + parentIndexes.push_back(index); + + while (!parentIndexes.isEmpty()) + { + const QModelIndex parent = parentIndexes.takeLast(); + + const int childCount = rowCount(parent); + const QModelIndex childTopLeft = this->index(0, 0, parent); + const QModelIndex childBottomRight = this->index((childCount - 1), lastColumnIndex, parent); + + // emit this generation + emit dataChanged(childTopLeft, childBottomRight); + + // check generations further down + parentIndexes.reserve(childCount); + for (int i = 0; i < childCount; ++i) + { + const QModelIndex child = childTopLeft.siblingAtRow(i); + if (hasChildren(child)) + parentIndexes.push_back(child); + } + } } diff --git a/src/gui/torrentcontentmodel.h b/src/gui/torrentcontentmodel.h index a1baf0666..6505fc679 100644 --- a/src/gui/torrentcontentmodel.h +++ b/src/gui/torrentcontentmodel.h @@ -79,11 +79,9 @@ public: signals: void filteredFilesChanged(); -public slots: - void selectAll(); - void selectNone(); - private: + void notifyModelUpdate(const QModelIndex &index); + TorrentContentModelFolder *m_rootItem = nullptr; QVector m_filesIndex; QFileIconProvider *m_fileIconProvider = nullptr;