PageRenderTime 39ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/quassel-0.7.3/src/client/selectionmodelsynchronizer.cpp

#
C++ | 244 lines | 178 code | 40 blank | 26 comment | 34 complexity | d07b2183d2e3c3d0fb3fb7a656786bd6 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0
  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. #include "selectionmodelsynchronizer.h"
  21. #include <QAbstractItemModel>
  22. #include <QAbstractProxyModel>
  23. #include <QDebug>
  24. SelectionModelSynchronizer::SelectionModelSynchronizer(QAbstractItemModel *parent)
  25. : QObject(parent),
  26. _model(parent),
  27. _selectionModel(parent),
  28. _changeCurrentEnabled(true),
  29. _changeSelectionEnabled(true)
  30. {
  31. connect(&_selectionModel, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
  32. this, SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
  33. connect(&_selectionModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
  34. this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
  35. }
  36. bool SelectionModelSynchronizer::checkBaseModel(QItemSelectionModel *selectionModel) {
  37. if(!selectionModel)
  38. return false;
  39. const QAbstractItemModel *baseModel = selectionModel->model();
  40. const QAbstractProxyModel *proxyModel = 0;
  41. while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
  42. baseModel = proxyModel->sourceModel();
  43. if(baseModel == model())
  44. break;
  45. }
  46. return baseModel == model();
  47. }
  48. void SelectionModelSynchronizer::synchronizeSelectionModel(QItemSelectionModel *selectionModel) {
  49. if(!checkBaseModel(selectionModel)) {
  50. qWarning() << "cannot Synchronize SelectionModel" << selectionModel << "which has a different baseModel()";
  51. return;
  52. }
  53. if(_selectionModels.contains(selectionModel)) {
  54. selectionModel->setCurrentIndex(mapFromSource(currentIndex(), selectionModel), QItemSelectionModel::Current);
  55. selectionModel->select(mapSelectionFromSource(currentSelection(), selectionModel), QItemSelectionModel::ClearAndSelect);
  56. return;
  57. }
  58. connect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
  59. this, SLOT(syncedCurrentChanged(QModelIndex, QModelIndex)));
  60. connect(selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
  61. this, SLOT(syncedSelectionChanged(QItemSelection, QItemSelection)));
  62. connect(selectionModel, SIGNAL(destroyed(QObject *)), this, SLOT(selectionModelDestroyed(QObject *)));
  63. _selectionModels << selectionModel;
  64. }
  65. void SelectionModelSynchronizer::removeSelectionModel(QItemSelectionModel *model) {
  66. disconnect(model, 0, this, 0);
  67. disconnect(this, 0, model, 0);
  68. selectionModelDestroyed(model);
  69. }
  70. void SelectionModelSynchronizer::selectionModelDestroyed(QObject *object) {
  71. QItemSelectionModel *model = static_cast<QItemSelectionModel *>(object);
  72. QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
  73. while(iter != _selectionModels.end()) {
  74. if(*iter == model) {
  75. iter = _selectionModels.erase(iter);
  76. } else {
  77. iter++;
  78. }
  79. }
  80. }
  81. void SelectionModelSynchronizer::syncedCurrentChanged(const QModelIndex &current, const QModelIndex &previous) {
  82. Q_UNUSED(previous);
  83. if(!_changeCurrentEnabled)
  84. return;
  85. QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
  86. Q_ASSERT(selectionModel);
  87. QModelIndex newSourceCurrent = mapToSource(current, selectionModel);
  88. if(newSourceCurrent.isValid() && newSourceCurrent != currentIndex())
  89. setCurrentIndex(newSourceCurrent);
  90. }
  91. void SelectionModelSynchronizer::syncedSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
  92. Q_UNUSED(selected);
  93. Q_UNUSED(deselected);
  94. if(!_changeSelectionEnabled)
  95. return;
  96. QItemSelectionModel *selectionModel = qobject_cast<QItemSelectionModel *>(sender());
  97. Q_ASSERT(selectionModel);
  98. QItemSelection mappedSelection = selectionModel->selection();
  99. QItemSelection currentSelectionMapped = mapSelectionFromSource(currentSelection(), selectionModel);
  100. QItemSelection checkSelection = currentSelectionMapped;
  101. checkSelection.merge(mappedSelection, QItemSelectionModel::Deselect);
  102. if(checkSelection.isEmpty()) {
  103. // that means the new selection contains the current selection (currentSel - newSel = {})
  104. checkSelection = mappedSelection;
  105. checkSelection.merge(currentSelectionMapped, QItemSelectionModel::Deselect);
  106. if(checkSelection.isEmpty()) {
  107. // that means the current selection contains the new selection (newSel - currentSel = {})
  108. // -> currentSel == newSel
  109. return;
  110. }
  111. }
  112. setCurrentSelection(mapSelectionToSource(mappedSelection, selectionModel));
  113. }
  114. QModelIndex SelectionModelSynchronizer::mapFromSource(const QModelIndex &sourceIndex, const QItemSelectionModel *selectionModel) {
  115. Q_ASSERT(selectionModel);
  116. QModelIndex mappedIndex = sourceIndex;
  117. // make a list of all involved proxies, wie have to traverse backwards
  118. QList<const QAbstractProxyModel *> proxyModels;
  119. const QAbstractItemModel *baseModel = selectionModel->model();
  120. const QAbstractProxyModel *proxyModel = 0;
  121. while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
  122. if(baseModel == model())
  123. break;
  124. proxyModels << proxyModel;
  125. baseModel = proxyModel->sourceModel();
  126. }
  127. // now traverse it;
  128. for(int i = proxyModels.count() - 1; i >= 0; i--) {
  129. mappedIndex = proxyModels[i]->mapFromSource(mappedIndex);
  130. }
  131. return mappedIndex;
  132. }
  133. QItemSelection SelectionModelSynchronizer::mapSelectionFromSource(const QItemSelection &sourceSelection, const QItemSelectionModel *selectionModel) {
  134. Q_ASSERT(selectionModel);
  135. QItemSelection mappedSelection = sourceSelection;
  136. // make a list of all involved proxies, wie have to traverse backwards
  137. QList<const QAbstractProxyModel *> proxyModels;
  138. const QAbstractItemModel *baseModel = selectionModel->model();
  139. const QAbstractProxyModel *proxyModel = 0;
  140. while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
  141. if(baseModel == model())
  142. break;
  143. proxyModels << proxyModel;
  144. baseModel = proxyModel->sourceModel();
  145. }
  146. // now traverse it;
  147. for(int i = proxyModels.count() - 1; i >= 0; i--) {
  148. mappedSelection = proxyModels[i]->mapSelectionFromSource(mappedSelection);
  149. }
  150. return mappedSelection;
  151. }
  152. QModelIndex SelectionModelSynchronizer::mapToSource(const QModelIndex &index, QItemSelectionModel *selectionModel) {
  153. Q_ASSERT(selectionModel);
  154. QModelIndex sourceIndex = index;
  155. const QAbstractItemModel *baseModel = selectionModel->model();
  156. const QAbstractProxyModel *proxyModel = 0;
  157. while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
  158. sourceIndex = proxyModel->mapToSource(sourceIndex);
  159. baseModel = proxyModel->sourceModel();
  160. if(baseModel == model())
  161. break;
  162. }
  163. return sourceIndex;
  164. }
  165. QItemSelection SelectionModelSynchronizer::mapSelectionToSource(const QItemSelection &selection, QItemSelectionModel *selectionModel) {
  166. Q_ASSERT(selectionModel);
  167. QItemSelection sourceSelection = selection;
  168. const QAbstractItemModel *baseModel = selectionModel->model();
  169. const QAbstractProxyModel *proxyModel = 0;
  170. while((proxyModel = qobject_cast<const QAbstractProxyModel *>(baseModel)) != 0) {
  171. sourceSelection = proxyModel->mapSelectionToSource(sourceSelection);
  172. baseModel = proxyModel->sourceModel();
  173. if(baseModel == model())
  174. break;
  175. }
  176. return sourceSelection;
  177. }
  178. void SelectionModelSynchronizer::setCurrentIndex(const QModelIndex &index) {
  179. _selectionModel.setCurrentIndex(index, QItemSelectionModel::Current);
  180. }
  181. void SelectionModelSynchronizer::setCurrentSelection(const QItemSelection &selection) {
  182. _selectionModel.select(selection, QItemSelectionModel::ClearAndSelect);
  183. }
  184. void SelectionModelSynchronizer::currentChanged(const QModelIndex &current, const QModelIndex &previous) {
  185. Q_UNUSED(previous);
  186. _changeCurrentEnabled = false;
  187. QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
  188. while(iter != _selectionModels.end()) {
  189. (*iter)->setCurrentIndex(mapFromSource(current, (*iter)), QItemSelectionModel::Current);
  190. iter++;
  191. }
  192. _changeCurrentEnabled = true;
  193. }
  194. void SelectionModelSynchronizer::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
  195. Q_UNUSED(selected);
  196. Q_UNUSED(deselected);
  197. _changeSelectionEnabled = false;
  198. QSet<QItemSelectionModel *>::iterator iter = _selectionModels.begin();
  199. while(iter != _selectionModels.end()) {
  200. (*iter)->select(mapSelectionFromSource(currentSelection(), (*iter)), QItemSelectionModel::ClearAndSelect);
  201. iter++;
  202. }
  203. _changeSelectionEnabled = true;
  204. }