Fix #endif. AGAIN. -.-
[quassel.git] / src / client / selectionmodelsynchronizer.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "selectionmodelsynchronizer.h"
22
23
24 #include <QAbstractItemModel>
25 #include <QAbstractProxyModel>
26
27 #include <QDebug>
28
29 SelectionModelSynchronizer::SelectionModelSynchronizer(QAbstractItemModel *parent)
30   : QObject(parent),
31     _model(parent),
32     _selectionModel(parent),
33     _changeCurrentEnabled(true),
34     _changeSelectionEnabled(true)
35 {
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 &)));
40 }
41
42 bool SelectionModelSynchronizer::checkBaseModel(QItemSelectionModel *selectionModel) {
43   if(!selectionModel)
44     return false;
45
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())
51       break;
52   }
53   return baseModel == model();
54 }
55
56 void SelectionModelSynchronizer::synchronizeSelectionModel(QItemSelectionModel *selectionModel) {
57   if(!checkBaseModel(selectionModel)) {
58     qWarning() << "cannot Syncronize SelectionModel" << selectionModel << "which has a different baseModel()";
59     return;
60   }
61
62   if(_selectionModels.contains(selectionModel)) {
63     selectionModel->setCurrentIndex(mapFromSource(currentIndex(), selectionModel), QItemSelectionModel::Current);
64     selectionModel->select(mapSelectionFromSource(currentSelection(), selectionModel), QItemSelectionModel::ClearAndSelect);
65     return;
66   }
67
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)));
72
73   connect(selectionModel, SIGNAL(destroyed(QObject *)), this, SLOT(selectionModelDestroyed(QObject *)));
74
75   _selectionModels << selectionModel;
76 }
77
78 void SelectionModelSynchronizer::removeSelectionModel(QItemSelectionModel *model) {
79   disconnect(model, 0, this, 0);
80   disconnect(this, 0, model, 0);
81   selectionModelDestroyed(model);
82 }
83
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()) {
88     if(*iter == model) {
89       iter = _selectionModels.erase(iter);
90     } else {
91       iter++;
92     }
93   }
94 }
95
96 void SelectionModelSynchronizer::syncedCurrentChanged(const QModelIndex &current, const QModelIndex &previous) {
97   Q_UNUSED(previous);
98
99   if(!_changeCurrentEnabled)
100     return;
101
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);
107 }
108
109 void SelectionModelSynchronizer::syncedSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
110   Q_UNUSED(selected);
111   Q_UNUSED(deselected);
112
113   if(!_changeSelectionEnabled)
114     return;
115
116   QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
117   Q_ASSERT(selectionModel);
118
119   QItemSelection mappedSelection = selectionModel->selection();
120   QItemSelection currentSelectionMapped = mapSelectionFromSource(currentSelection(), selectionModel);
121
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
131       return;
132     }
133   }
134   setCurrentSelection(mapSelectionToSource(mappedSelection, selectionModel));
135 }
136
137 QModelIndex SelectionModelSynchronizer::mapFromSource(const QModelIndex &sourceIndex, const QItemSelectionModel *selectionModel) {
138   Q_ASSERT(selectionModel);
139
140   QModelIndex mappedIndex = sourceIndex;
141
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())
148       break;
149     proxyModels << proxyModel;
150     baseModel = proxyModel->sourceModel();
151   }
152
153   // now traverse it;
154   for(int i = proxyModels.count() - 1; i >= 0; i--) {
155     mappedIndex = proxyModels[i]->mapFromSource(mappedIndex);
156   }
157   
158   return mappedIndex;
159 }
160
161 QItemSelection SelectionModelSynchronizer::mapSelectionFromSource(const QItemSelection &sourceSelection, const QItemSelectionModel *selectionModel) {
162   Q_ASSERT(selectionModel);
163
164   QItemSelection mappedSelection = sourceSelection;
165
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())
172       break;
173     proxyModels << proxyModel;
174     baseModel = proxyModel->sourceModel();
175   }
176
177   // now traverse it;
178   for(int i = proxyModels.count() - 1; i >= 0; i--) {
179     mappedSelection = proxyModels[i]->mapSelectionFromSource(mappedSelection);
180   }
181   return mappedSelection;
182 }
183
184 QModelIndex SelectionModelSynchronizer::mapToSource(const QModelIndex &index, QItemSelectionModel *selectionModel) {
185   Q_ASSERT(selectionModel);
186
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())
194       break;
195   }
196   return sourceIndex;
197 }
198
199 QItemSelection SelectionModelSynchronizer::mapSelectionToSource(const QItemSelection &selection, QItemSelectionModel *selectionModel) {
200   Q_ASSERT(selectionModel);
201
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())
209       break;
210   }
211   return sourceSelection;
212 }
213
214 void SelectionModelSynchronizer::setCurrentIndex(const QModelIndex &index) {
215   _selectionModel.setCurrentIndex(index, QItemSelectionModel::Current);
216 }
217 void SelectionModelSynchronizer::setCurrentSelection(const QItemSelection &selection) {
218   _selectionModel.select(selection, QItemSelectionModel::ClearAndSelect);
219 }
220
221 void SelectionModelSynchronizer::currentChanged(const QModelIndex &current, const QModelIndex &previous) {
222   Q_UNUSED(previous);
223
224   _changeCurrentEnabled = false;
225   QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
226   while(iter != _selectionModels.end()) {
227     (*iter)->setCurrentIndex(mapFromSource(current, (*iter)), QItemSelectionModel::Current);
228     iter++;
229   }
230   _changeCurrentEnabled = true;
231 }
232
233 void SelectionModelSynchronizer::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
234   Q_UNUSED(selected);
235   Q_UNUSED(deselected);
236
237   _changeSelectionEnabled = false;
238   QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
239   while(iter != _selectionModels.end()) {
240     (*iter)->select(mapSelectionFromSource(currentSelection(), (*iter)), QItemSelectionModel::ClearAndSelect);
241     iter++;
242   }
243   _changeSelectionEnabled = true;
244 }