1 /***************************************************************************
2 * Copyright (C) 2005-09 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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "selectionmodelsynchronizer.h"
24 #include <QAbstractItemModel>
25 #include <QAbstractProxyModel>
29 SelectionModelSynchronizer::SelectionModelSynchronizer(QAbstractItemModel *parent)
32 _selectionModel(parent),
33 _changeCurrentEnabled(true),
34 _changeSelectionEnabled(true)
36 connect(&_selectionModel, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
37 this, SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
38 connect(&_selectionModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
39 this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
42 bool SelectionModelSynchronizer::checkBaseModel(QItemSelectionModel *selectionModel) {
46 const QAbstractItemModel *baseModel = selectionModel->model();
47 const QAbstractProxyModel *proxyModel = 0;
48 while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
49 baseModel = proxyModel->sourceModel();
50 if(baseModel == model())
53 return baseModel == model();
56 void SelectionModelSynchronizer::synchronizeSelectionModel(QItemSelectionModel *selectionModel) {
57 if(!checkBaseModel(selectionModel)) {
58 qWarning() << "cannot Syncronize SelectionModel" << selectionModel << "which has a different baseModel()";
62 if(_selectionModels.contains(selectionModel)) {
63 selectionModel->setCurrentIndex(mapFromSource(currentIndex(), selectionModel), QItemSelectionModel::Current);
64 selectionModel->select(mapSelectionFromSource(currentSelection(), selectionModel), QItemSelectionModel::ClearAndSelect);
68 connect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
69 this, SLOT(syncedCurrentChanged(QModelIndex, QModelIndex)));
70 connect(selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
71 this, SLOT(syncedSelectionChanged(QItemSelection, QItemSelection)));
73 connect(selectionModel, SIGNAL(destroyed(QObject *)), this, SLOT(selectionModelDestroyed(QObject *)));
75 _selectionModels << selectionModel;
78 void SelectionModelSynchronizer::removeSelectionModel(QItemSelectionModel *model) {
79 disconnect(model, 0, this, 0);
80 disconnect(this, 0, model, 0);
81 selectionModelDestroyed(model);
84 void SelectionModelSynchronizer::selectionModelDestroyed(QObject *object) {
85 QItemSelectionModel *model = static_cast<QItemSelectionModel *>(object);
86 QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
87 while(iter != _selectionModels.end()) {
89 iter = _selectionModels.erase(iter);
96 void SelectionModelSynchronizer::syncedCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) {
99 if(!_changeCurrentEnabled)
102 QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
103 Q_ASSERT(selectionModel);
104 QModelIndex newSourceCurrent = mapToSource(current, selectionModel);
105 if(newSourceCurrent.isValid() && newSourceCurrent != currentIndex())
106 setCurrentIndex(newSourceCurrent);
109 void SelectionModelSynchronizer::syncedSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
111 Q_UNUSED(deselected);
113 if(!_changeSelectionEnabled)
116 QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
117 Q_ASSERT(selectionModel);
119 QItemSelection mappedSelection = selectionModel->selection();
120 QItemSelection currentSelectionMapped = mapSelectionFromSource(currentSelection(), selectionModel);
122 QItemSelection checkSelection = currentSelectionMapped;
123 checkSelection.merge(mappedSelection, QItemSelectionModel::Deselect);
124 if(checkSelection.isEmpty()) {
125 // that means the new selection contains the current selection (currentSel - newSel = {})
126 checkSelection = mappedSelection;
127 checkSelection.merge(currentSelectionMapped, QItemSelectionModel::Deselect);
128 if(checkSelection.isEmpty()) {
129 // that means the current selection contains the new selection (newSel - currentSel = {})
130 // -> currentSel == newSel
134 setCurrentSelection(mapSelectionToSource(mappedSelection, selectionModel));
137 QModelIndex SelectionModelSynchronizer::mapFromSource(const QModelIndex &sourceIndex, const QItemSelectionModel *selectionModel) {
138 Q_ASSERT(selectionModel);
140 QModelIndex mappedIndex = sourceIndex;
142 // make a list of all involved proxies, wie have to traverse backwards
143 QList<const QAbstractProxyModel *> proxyModels;
144 const QAbstractItemModel *baseModel = selectionModel->model();
145 const QAbstractProxyModel *proxyModel = 0;
146 while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
147 if(baseModel == model())
149 proxyModels << proxyModel;
150 baseModel = proxyModel->sourceModel();
154 for(int i = proxyModels.count() - 1; i >= 0; i--) {
155 mappedIndex = proxyModels[i]->mapFromSource(mappedIndex);
161 QItemSelection SelectionModelSynchronizer::mapSelectionFromSource(const QItemSelection &sourceSelection, const QItemSelectionModel *selectionModel) {
162 Q_ASSERT(selectionModel);
164 QItemSelection mappedSelection = sourceSelection;
166 // make a list of all involved proxies, wie have to traverse backwards
167 QList<const QAbstractProxyModel *> proxyModels;
168 const QAbstractItemModel *baseModel = selectionModel->model();
169 const QAbstractProxyModel *proxyModel = 0;
170 while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
171 if(baseModel == model())
173 proxyModels << proxyModel;
174 baseModel = proxyModel->sourceModel();
178 for(int i = proxyModels.count() - 1; i >= 0; i--) {
179 mappedSelection = proxyModels[i]->mapSelectionFromSource(mappedSelection);
181 return mappedSelection;
184 QModelIndex SelectionModelSynchronizer::mapToSource(const QModelIndex &index, QItemSelectionModel *selectionModel) {
185 Q_ASSERT(selectionModel);
187 QModelIndex sourceIndex = index;
188 const QAbstractItemModel *baseModel = selectionModel->model();
189 const QAbstractProxyModel *proxyModel = 0;
190 while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
191 sourceIndex = proxyModel->mapToSource(sourceIndex);
192 baseModel = proxyModel->sourceModel();
193 if(baseModel == model())
199 QItemSelection SelectionModelSynchronizer::mapSelectionToSource(const QItemSelection &selection, QItemSelectionModel *selectionModel) {
200 Q_ASSERT(selectionModel);
202 QItemSelection sourceSelection = selection;
203 const QAbstractItemModel *baseModel = selectionModel->model();
204 const QAbstractProxyModel *proxyModel = 0;
205 while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
206 sourceSelection = proxyModel->mapSelectionToSource(sourceSelection);
207 baseModel = proxyModel->sourceModel();
208 if(baseModel == model())
211 return sourceSelection;
214 void SelectionModelSynchronizer::setCurrentIndex(const QModelIndex &index) {
215 _selectionModel.setCurrentIndex(index, QItemSelectionModel::Current);
217 void SelectionModelSynchronizer::setCurrentSelection(const QItemSelection &selection) {
218 _selectionModel.select(selection, QItemSelectionModel::ClearAndSelect);
221 void SelectionModelSynchronizer::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) {
224 _changeCurrentEnabled = false;
225 QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
226 while(iter != _selectionModels.end()) {
227 (*iter)->setCurrentIndex(mapFromSource(current, (*iter)), QItemSelectionModel::Current);
230 _changeCurrentEnabled = true;
233 void SelectionModelSynchronizer::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
235 Q_UNUSED(deselected);
237 _changeSelectionEnabled = false;
238 QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
239 while(iter != _selectionModels.end()) {
240 (*iter)->select(mapSelectionFromSource(currentSelection(), (*iter)), QItemSelectionModel::ClearAndSelect);
243 _changeSelectionEnabled = true;