Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
selectionmodelsynchronizer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 by the Quassel Project *
3  * devel@quassel-irc.org *
4  * *
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. *
9  * *
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. *
14  * *
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  ***************************************************************************/
20 
22 
23 #include <QAbstractItemModel>
24 #include <QAbstractProxyModel>
25 
26 #include <QDebug>
27 
29  : QObject(parent),
30  _model(parent),
31  _selectionModel(parent),
32  _changeCurrentEnabled(true),
33  _changeSelectionEnabled(true)
34 {
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 &)));
39 }
40 
41 
42 bool SelectionModelSynchronizer::checkBaseModel(QItemSelectionModel *selectionModel)
43 {
44  if (!selectionModel)
45  return false;
46 
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())
52  break;
53  }
54  return baseModel == model();
55 }
56 
57 
58 void SelectionModelSynchronizer::synchronizeSelectionModel(QItemSelectionModel *selectionModel)
59 {
60  if (!checkBaseModel(selectionModel)) {
61  qWarning() << "cannot Synchronize SelectionModel" << selectionModel << "which has a different baseModel()";
62  return;
63  }
64 
65  if (_selectionModels.contains(selectionModel)) {
66  selectionModel->setCurrentIndex(mapFromSource(currentIndex(), selectionModel), QItemSelectionModel::Current);
67  selectionModel->select(mapSelectionFromSource(currentSelection(), selectionModel), QItemSelectionModel::ClearAndSelect);
68  return;
69  }
70 
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)));
75 
76  connect(selectionModel, SIGNAL(destroyed(QObject *)), this, SLOT(selectionModelDestroyed(QObject *)));
77 
79 }
80 
81 
82 void SelectionModelSynchronizer::removeSelectionModel(QItemSelectionModel *model)
83 {
84  disconnect(model, 0, this, 0);
85  disconnect(this, 0, model, 0);
87 }
88 
89 
91 {
92  QItemSelectionModel *model = static_cast<QItemSelectionModel *>(object);
93  QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
94  while (iter != _selectionModels.end()) {
95  if (*iter == model) {
96  iter = _selectionModels.erase(iter);
97  }
98  else {
99  iter++;
100  }
101  }
102 }
103 
104 
105 void SelectionModelSynchronizer::syncedCurrentChanged(const QModelIndex &current, const QModelIndex &previous)
106 {
107  Q_UNUSED(previous);
108 
110  return;
111 
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);
117 }
118 
119 
120 void SelectionModelSynchronizer::syncedSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
121 {
122  Q_UNUSED(selected);
123  Q_UNUSED(deselected);
124 
126  return;
127 
128  QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
129  Q_ASSERT(selectionModel);
130 
131  QItemSelection mappedSelection = selectionModel->selection();
132  QItemSelection currentSelectionMapped = mapSelectionFromSource(currentSelection(), selectionModel);
133 
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
143  return;
144  }
145  }
146  setCurrentSelection(mapSelectionToSource(mappedSelection, selectionModel));
147 }
148 
149 
150 QModelIndex SelectionModelSynchronizer::mapFromSource(const QModelIndex &sourceIndex, const QItemSelectionModel *selectionModel)
151 {
152  Q_ASSERT(selectionModel);
153 
154  QModelIndex mappedIndex = sourceIndex;
155 
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())
162  break;
163  proxyModels << proxyModel;
164  baseModel = proxyModel->sourceModel();
165  }
166 
167  // now traverse it;
168  for (int i = proxyModels.count() - 1; i >= 0; i--) {
169  mappedIndex = proxyModels[i]->mapFromSource(mappedIndex);
170  }
171 
172  return mappedIndex;
173 }
174 
175 
176 QItemSelection SelectionModelSynchronizer::mapSelectionFromSource(const QItemSelection &sourceSelection, const QItemSelectionModel *selectionModel)
177 {
178  Q_ASSERT(selectionModel);
179 
180  QItemSelection mappedSelection = sourceSelection;
181 
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())
188  break;
189  proxyModels << proxyModel;
190  baseModel = proxyModel->sourceModel();
191  }
192 
193  // now traverse it;
194  for (int i = proxyModels.count() - 1; i >= 0; i--) {
195  mappedSelection = proxyModels[i]->mapSelectionFromSource(mappedSelection);
196  }
197  return mappedSelection;
198 }
199 
200 
201 QModelIndex SelectionModelSynchronizer::mapToSource(const QModelIndex &index, QItemSelectionModel *selectionModel)
202 {
203  Q_ASSERT(selectionModel);
204 
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())
212  break;
213  }
214  return sourceIndex;
215 }
216 
217 
218 QItemSelection SelectionModelSynchronizer::mapSelectionToSource(const QItemSelection &selection, QItemSelectionModel *selectionModel)
219 {
220  Q_ASSERT(selectionModel);
221 
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())
229  break;
230  }
231  return sourceSelection;
232 }
233 
234 
235 void SelectionModelSynchronizer::setCurrentIndex(const QModelIndex &index)
236 {
237  _selectionModel.setCurrentIndex(index, QItemSelectionModel::Current);
238 }
239 
240 
241 void SelectionModelSynchronizer::setCurrentSelection(const QItemSelection &selection)
242 {
243  _selectionModel.select(selection, QItemSelectionModel::ClearAndSelect);
244 }
245 
246 
247 void SelectionModelSynchronizer::currentChanged(const QModelIndex &current, const QModelIndex &previous)
248 {
249  Q_UNUSED(previous);
250 
251  _changeCurrentEnabled = false;
252  QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
253  while (iter != _selectionModels.end()) {
254  (*iter)->setCurrentIndex(mapFromSource(current, (*iter)), QItemSelectionModel::Current);
255  iter++;
256  }
257  _changeCurrentEnabled = true;
258 }
259 
260 
261 void SelectionModelSynchronizer::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
262 {
263  Q_UNUSED(selected);
264  Q_UNUSED(deselected);
265 
266  _changeSelectionEnabled = false;
267  QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
268  while (iter != _selectionModels.end()) {
269  (*iter)->select(mapSelectionFromSource(currentSelection(), (*iter)), QItemSelectionModel::ClearAndSelect);
270  iter++;
271  }
273 }