File indexing completed on 2024-05-05 03:56:40
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"