File indexing completed on 2024-07-21 06:38:21

0001 /*
0002     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0003     SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kbreadcrumbselectionmodel.h"
0009 
0010 class KBreadcrumbSelectionModelPrivate
0011 {
0012     Q_DECLARE_PUBLIC(KBreadcrumbSelectionModel)
0013     KBreadcrumbSelectionModel *const q_ptr;
0014 
0015 public:
0016     KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector,
0017                                      QItemSelectionModel *selectionModel,
0018                                      KBreadcrumbSelectionModel::BreadcrumbTarget direction);
0019 
0020     /**
0021       Returns a selection containing the breadcrumbs for @p index
0022     */
0023     QItemSelection getBreadcrumbSelection(const QModelIndex &index);
0024 
0025     /**
0026       Returns a selection containing the breadcrumbs for @p selection
0027     */
0028     QItemSelection getBreadcrumbSelection(const QItemSelection &selection);
0029 
0030     void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
0031 
0032     void syncBreadcrumbs();
0033 
0034     bool m_includeActualSelection = true;
0035     bool m_showHiddenAscendantData = false;
0036     bool m_ignoreCurrentChanged = false;
0037     int m_selectionDepth = -1;
0038     KBreadcrumbSelectionModel::BreadcrumbTarget m_direction = KBreadcrumbSelectionModel::MakeBreadcrumbSelectionInSelf;
0039     QItemSelectionModel *m_selectionModel = nullptr;
0040 };
0041 
0042 KBreadcrumbSelectionModelPrivate::KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector,
0043                                                                    QItemSelectionModel *selectionModel,
0044                                                                    KBreadcrumbSelectionModel::BreadcrumbTarget direction)
0045     : q_ptr(breadcrumbSelector)
0046     , m_direction(direction)
0047     , m_selectionModel(selectionModel)
0048 {
0049     Q_Q(KBreadcrumbSelectionModel);
0050 
0051     if (direction != KBreadcrumbSelectionModel::MakeBreadcrumbSelectionInSelf) {
0052         q->connect(selectionModel, &QItemSelectionModel::selectionChanged, q, [this](const QItemSelection &selected, const QItemSelection &deselected) {
0053             sourceSelectionChanged(selected, deselected);
0054         });
0055     }
0056 
0057     q->connect(m_selectionModel->model(), &QAbstractItemModel::layoutChanged, q, [this]() {
0058         syncBreadcrumbs();
0059     });
0060     q->connect(m_selectionModel->model(), &QAbstractItemModel::modelReset, q, [this]() {
0061         syncBreadcrumbs();
0062     });
0063     q->connect(m_selectionModel->model(), &QAbstractItemModel::rowsMoved, q, [this]() {
0064         syncBreadcrumbs();
0065     });
0066 
0067     // Don't need to handle insert & remove because they can't change the breadcrumbs on their own.
0068 }
0069 
0070 KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject *parent)
0071     : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent)
0072     , d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, MakeBreadcrumbSelectionInSelf))
0073 {
0074 }
0075 
0076 KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget direction, QObject *parent)
0077     : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent)
0078     , d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, direction))
0079 {
0080 }
0081 
0082 KBreadcrumbSelectionModel::~KBreadcrumbSelectionModel() = default;
0083 
0084 bool KBreadcrumbSelectionModel::isActualSelectionIncluded() const
0085 {
0086     Q_D(const KBreadcrumbSelectionModel);
0087     return d->m_includeActualSelection;
0088 }
0089 
0090 void KBreadcrumbSelectionModel::setActualSelectionIncluded(bool includeActualSelection)
0091 {
0092     Q_D(KBreadcrumbSelectionModel);
0093     d->m_includeActualSelection = includeActualSelection;
0094 }
0095 
0096 int KBreadcrumbSelectionModel::breadcrumbLength() const
0097 {
0098     Q_D(const KBreadcrumbSelectionModel);
0099     return d->m_selectionDepth;
0100 }
0101 
0102 void KBreadcrumbSelectionModel::setBreadcrumbLength(int breadcrumbLength)
0103 {
0104     Q_D(KBreadcrumbSelectionModel);
0105     d->m_selectionDepth = breadcrumbLength;
0106 }
0107 
0108 QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QModelIndex &index)
0109 {
0110     QItemSelection breadcrumbSelection;
0111 
0112     if (m_includeActualSelection) {
0113         breadcrumbSelection.append(QItemSelectionRange(index));
0114     }
0115 
0116     QModelIndex parent = index.parent();
0117     int sumBreadcrumbs = 0;
0118     bool includeAll = m_selectionDepth < 0;
0119     while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) {
0120         breadcrumbSelection.append(QItemSelectionRange(parent));
0121         parent = parent.parent();
0122     }
0123     return breadcrumbSelection;
0124 }
0125 
0126 QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QItemSelection &selection)
0127 {
0128     QItemSelection breadcrumbSelection;
0129 
0130     if (m_includeActualSelection) {
0131         breadcrumbSelection = selection;
0132     }
0133 
0134     QItemSelection::const_iterator it = selection.constBegin();
0135     const QItemSelection::const_iterator end = selection.constEnd();
0136 
0137     for (; it != end; ++it) {
0138         QModelIndex parent = it->parent();
0139 
0140         if (breadcrumbSelection.contains(parent)) {
0141             continue;
0142         }
0143 
0144         int sumBreadcrumbs = 0;
0145         bool includeAll = m_selectionDepth < 0;
0146 
0147         while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) {
0148             breadcrumbSelection.append(QItemSelectionRange(parent));
0149             parent = parent.parent();
0150 
0151             if (breadcrumbSelection.contains(parent)) {
0152                 break;
0153             }
0154 
0155             ++sumBreadcrumbs;
0156         }
0157     }
0158     return breadcrumbSelection;
0159 }
0160 
0161 void KBreadcrumbSelectionModelPrivate::sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
0162 {
0163     Q_Q(KBreadcrumbSelectionModel);
0164     const QItemSelection deselectedCrumbs = getBreadcrumbSelection(deselected);
0165     const QItemSelection selectedCrumbs = getBreadcrumbSelection(selected);
0166 
0167     QItemSelection removed = deselectedCrumbs;
0168     for (const QItemSelectionRange &range : selectedCrumbs) {
0169         removed.removeAll(range);
0170     }
0171 
0172     QItemSelection added = selectedCrumbs;
0173     for (const QItemSelectionRange &range : deselectedCrumbs) {
0174         added.removeAll(range);
0175     }
0176 
0177     if (!removed.isEmpty()) {
0178         q->QItemSelectionModel::select(removed, QItemSelectionModel::Deselect);
0179     }
0180     if (!added.isEmpty()) {
0181         q->QItemSelectionModel::select(added, QItemSelectionModel::Select);
0182     }
0183 }
0184 
0185 void KBreadcrumbSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
0186 {
0187     Q_D(KBreadcrumbSelectionModel);
0188     // When an item is removed, the current index is set to the top index in the model.
0189     // That causes a selectionChanged signal with a selection which we do not want.
0190     if (d->m_ignoreCurrentChanged) {
0191         d->m_ignoreCurrentChanged = false;
0192         return;
0193     }
0194     if (d->m_direction == MakeBreadcrumbSelectionInOther) {
0195         d->m_selectionModel->select(d->getBreadcrumbSelection(index), command);
0196         QItemSelectionModel::select(index, command);
0197     } else {
0198         d->m_selectionModel->select(index, command);
0199         QItemSelectionModel::select(d->getBreadcrumbSelection(index), command);
0200     }
0201 }
0202 
0203 void KBreadcrumbSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
0204 {
0205     Q_D(KBreadcrumbSelectionModel);
0206     QItemSelection bcc = d->getBreadcrumbSelection(selection);
0207     if (d->m_direction == MakeBreadcrumbSelectionInOther) {
0208         d->m_selectionModel->select(selection, command);
0209         QItemSelectionModel::select(bcc, command);
0210     } else {
0211         d->m_selectionModel->select(bcc, command);
0212         QItemSelectionModel::select(selection, command);
0213     }
0214 }
0215 
0216 void KBreadcrumbSelectionModelPrivate::syncBreadcrumbs()
0217 {
0218     Q_Q(KBreadcrumbSelectionModel);
0219     q->select(m_selectionModel->selection(), QItemSelectionModel::ClearAndSelect);
0220 }
0221 
0222 #include "moc_kbreadcrumbselectionmodel.cpp"