1 /***************************************************************************
2 * Copyright (C) 2005-2014 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "selectionmodelsynchronizer.h"
23 #include <QAbstractItemModel>
24 #include <QAbstractProxyModel>
28 SelectionModelSynchronizer::SelectionModelSynchronizer(QAbstractItemModel *parent)
31 _selectionModel(parent),
32 _changeCurrentEnabled(true),
33 _changeSelectionEnabled(true)
35 connect(&_selectionModel, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
36 this, SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
37 connect(&_selectionModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
38 this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
42 bool SelectionModelSynchronizer::checkBaseModel(QItemSelectionModel *selectionModel)
47 const QAbstractItemModel *baseModel = selectionModel->model();
48 const QAbstractProxyModel *proxyModel = 0;
49 while ((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
50 baseModel = proxyModel->sourceModel();
51 if (baseModel == model())
54 return baseModel == model();
58 void SelectionModelSynchronizer::synchronizeSelectionModel(QItemSelectionModel *selectionModel)
60 if (!checkBaseModel(selectionModel)) {
61 qWarning() << "cannot Synchronize SelectionModel" << selectionModel << "which has a different baseModel()";
65 if (_selectionModels.contains(selectionModel)) {
66 selectionModel->setCurrentIndex(mapFromSource(currentIndex(), selectionModel), QItemSelectionModel::Current);
67 selectionModel->select(mapSelectionFromSource(currentSelection(), selectionModel), QItemSelectionModel::ClearAndSelect);
71 connect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
72 this, SLOT(syncedCurrentChanged(QModelIndex, QModelIndex)));
73 connect(selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
74 this, SLOT(syncedSelectionChanged(QItemSelection, QItemSelection)));
76 connect(selectionModel, SIGNAL(destroyed(QObject *)), this, SLOT(selectionModelDestroyed(QObject *)));
78 _selectionModels << selectionModel;
82 void SelectionModelSynchronizer::removeSelectionModel(QItemSelectionModel *model)
84 disconnect(model, 0, this, 0);
85 disconnect(this, 0, model, 0);
86 selectionModelDestroyed(model);
90 void SelectionModelSynchronizer::selectionModelDestroyed(QObject *object)
92 QItemSelectionModel *model = static_cast<QItemSelectionModel *>(object);
93 QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
94 while (iter != _selectionModels.end()) {
96 iter = _selectionModels.erase(iter);
105 void SelectionModelSynchronizer::syncedCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
109 if (!_changeCurrentEnabled)
112 QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
113 Q_ASSERT(selectionModel);
114 QModelIndex newSourceCurrent = mapToSource(current, selectionModel);
115 if (newSourceCurrent.isValid() && newSourceCurrent != currentIndex())
116 setCurrentIndex(newSourceCurrent);
120 void SelectionModelSynchronizer::syncedSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
123 Q_UNUSED(deselected);
125 if (!_changeSelectionEnabled)
128 QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
129 Q_ASSERT(selectionModel);
131 QItemSelection mappedSelection = selectionModel->selection();
132 QItemSelection currentSelectionMapped = mapSelectionFromSource(currentSelection(), selectionModel);
134 QItemSelection checkSelection = currentSelectionMapped;
135 checkSelection.merge(mappedSelection, QItemSelectionModel::Deselect);
136 if (checkSelection.isEmpty()) {
137 // that means the new selection contains the current selection (currentSel - newSel = {})
138 checkSelection = mappedSelection;
139 checkSelection.merge(currentSelectionMapped, QItemSelectionModel::Deselect);
140 if (checkSelection.isEmpty()) {
141 // that means the current selection contains the new selection (newSel - currentSel = {})
142 // -> currentSel == newSel
146 setCurrentSelection(mapSelectionToSource(mappedSelection, selectionModel));
150 QModelIndex SelectionModelSynchronizer::mapFromSource(const QModelIndex &sourceIndex, const QItemSelectionModel *selectionModel)
152 Q_ASSERT(selectionModel);
154 QModelIndex mappedIndex = sourceIndex;
156 // make a list of all involved proxies, wie have to traverse backwards
157 QList<const QAbstractProxyModel *> proxyModels;
158 const QAbstractItemModel *baseModel = selectionModel->model();
159 const QAbstractProxyModel *proxyModel = 0;
160 while ((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
161 if (baseModel == model())
163 proxyModels << proxyModel;
164 baseModel = proxyModel->sourceModel();
168 for (int i = proxyModels.count() - 1; i >= 0; i--) {
169 mappedIndex = proxyModels[i]->mapFromSource(mappedIndex);
176 QItemSelection SelectionModelSynchronizer::mapSelectionFromSource(const QItemSelection &sourceSelection, const QItemSelectionModel *selectionModel)
178 Q_ASSERT(selectionModel);
180 QItemSelection mappedSelection = sourceSelection;
182 // make a list of all involved proxies, wie have to traverse backwards
183 QList<const QAbstractProxyModel *> proxyModels;
184 const QAbstractItemModel *baseModel = selectionModel->model();
185 const QAbstractProxyModel *proxyModel = 0;
186 while ((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
187 if (baseModel == model())
189 proxyModels << proxyModel;
190 baseModel = proxyModel->sourceModel();
194 for (int i = proxyModels.count() - 1; i >= 0; i--) {
195 mappedSelection = proxyModels[i]->mapSelectionFromSource(mappedSelection);
197 return mappedSelection;
201 QModelIndex SelectionModelSynchronizer::mapToSource(const QModelIndex &index, QItemSelectionModel *selectionModel)
203 Q_ASSERT(selectionModel);
205 QModelIndex sourceIndex = index;
206 const QAbstractItemModel *baseModel = selectionModel->model();
207 const QAbstractProxyModel *proxyModel = 0;
208 while ((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
209 sourceIndex = proxyModel->mapToSource(sourceIndex);
210 baseModel = proxyModel->sourceModel();
211 if (baseModel == model())
218 QItemSelection SelectionModelSynchronizer::mapSelectionToSource(const QItemSelection &selection, QItemSelectionModel *selectionModel)
220 Q_ASSERT(selectionModel);
222 QItemSelection sourceSelection = selection;
223 const QAbstractItemModel *baseModel = selectionModel->model();
224 const QAbstractProxyModel *proxyModel = 0;
225 while ((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
226 sourceSelection = proxyModel->mapSelectionToSource(sourceSelection);
227 baseModel = proxyModel->sourceModel();
228 if (baseModel == model())
231 return sourceSelection;
235 void SelectionModelSynchronizer::setCurrentIndex(const QModelIndex &index)
237 _selectionModel.setCurrentIndex(index, QItemSelectionModel::Current);
241 void SelectionModelSynchronizer::setCurrentSelection(const QItemSelection &selection)
243 _selectionModel.select(selection, QItemSelectionModel::ClearAndSelect);
247 void SelectionModelSynchronizer::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
251 _changeCurrentEnabled = false;
252 QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
253 while (iter != _selectionModels.end()) {
254 (*iter)->setCurrentIndex(mapFromSource(current, (*iter)), QItemSelectionModel::Current);
257 _changeCurrentEnabled = true;
261 void SelectionModelSynchronizer::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
264 Q_UNUSED(deselected);
266 _changeSelectionEnabled = false;
267 QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
268 while (iter != _selectionModels.end()) {
269 (*iter)->select(mapSelectionFromSource(currentSelection(), (*iter)), QItemSelectionModel::ClearAndSelect);
272 _changeSelectionEnabled = true;