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

/kadu-core/model/merged-proxy-model.cpp

https://gitlab.com/mziab/kadu
C++ | 442 lines | 322 code | 98 blank | 22 comment | 31 complexity | c3c5d18b3623250d3172781a9d47a30c MD5 | raw file
Possible License(s): LGPL-2.1, GPL-3.0, BSD-3-Clause, CC-BY-3.0, GPL-2.0
  1. /*
  2. * %kadu copyright begin%
  3. * Copyright 2012, 2013 Bartosz Brachaczek (b.brachaczek@gmail.com)
  4. * Copyright 2011, 2012, 2013, 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
  5. * %kadu copyright end%
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include <QtCore/QMimeData>
  22. #include <QtCore/QStringList>
  23. #include "merged-proxy-model.h"
  24. MergedProxyModel::MergedProxyModel(QObject *parent) :
  25. QAbstractItemModel(parent)
  26. {
  27. setSupportedDragActions(Qt::LinkAction);
  28. }
  29. MergedProxyModel::~MergedProxyModel()
  30. {
  31. }
  32. void MergedProxyModel::connectModels()
  33. {
  34. foreach (const QAbstractItemModel *model, Models)
  35. {
  36. Q_ASSERT(model);
  37. connect(model, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged()), Qt::DirectConnection);
  38. connect(model, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()), Qt::DirectConnection);
  39. connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChangedSlot(QModelIndex,QModelIndex)), Qt::DirectConnection);
  40. connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
  41. this, SLOT(rowsAboutToBeInsertedSlot(QModelIndex,int,int)), Qt::DirectConnection);
  42. connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
  43. this, SLOT(rowsInsertedSlot(QModelIndex,int,int)), Qt::DirectConnection);
  44. connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
  45. this, SLOT(rowsAboutToBeRemovedSlot(QModelIndex,int,int)), Qt::DirectConnection);
  46. connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
  47. this, SLOT(rowsRemovedSlot(QModelIndex,int,int)), Qt::DirectConnection);
  48. connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
  49. this, SLOT(rowsAboutToBeMovedSlot(QModelIndex,int,int,QModelIndex,int)), Qt::DirectConnection);
  50. connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
  51. this, SLOT(rowsMovedSlot(QModelIndex,int,int,QModelIndex,int)), Qt::DirectConnection);
  52. connect(model, SIGNAL(modelAboutToBeReset()), this, SLOT(modelAboutToBeResetSlot()), Qt::DirectConnection);
  53. connect(model, SIGNAL(modelReset()), this, SLOT(modelResetSlot()), Qt::DirectConnection);
  54. connect(model, SIGNAL(destroyed(QObject*)), this, SLOT(modelDestroyedSlot(QObject*)), Qt::DirectConnection);
  55. }
  56. }
  57. void MergedProxyModel::disconnectModels()
  58. {
  59. foreach (const QAbstractItemModel *model, Models)
  60. {
  61. Q_ASSERT(model);
  62. disconnect(model,0, this, 0);
  63. }
  64. }
  65. void MergedProxyModel::setModels(QList<QAbstractItemModel *> models)
  66. {
  67. beginResetModel();
  68. disconnectModels();
  69. Models = models;
  70. connectModels();
  71. updateBoundaries();
  72. endResetModel();
  73. }
  74. void MergedProxyModel::updateBoundaries() const
  75. {
  76. Boundaries.clear();
  77. int index = 0;
  78. foreach (const QAbstractItemModel *model, Models)
  79. {
  80. Q_ASSERT(model);
  81. const int count = model->rowCount();
  82. Boundaries.insert(model, qMakePair(index, index + count - 1));
  83. index += model->rowCount();
  84. }
  85. }
  86. int MergedProxyModel::modelRowOffset(QAbstractItemModel *model) const
  87. {
  88. Q_ASSERT(model);
  89. Q_ASSERT(Boundaries.contains(model));
  90. return Boundaries.value(model).first;
  91. }
  92. void MergedProxyModel::dataChangedSlot(const QModelIndex &topLeft, const QModelIndex &bottomRight)
  93. {
  94. const QModelIndex &proxyTopLeft = mapFromSource(topLeft);
  95. const QModelIndex &proxyBottomRight = mapFromSource(bottomRight);
  96. emit dataChanged(proxyTopLeft, proxyBottomRight);
  97. }
  98. void MergedProxyModel::rowsAboutToBeInsertedSlot(const QModelIndex &parent, int first, int last)
  99. {
  100. QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(sender());
  101. Q_UNUSED(model);
  102. Q_ASSERT(model);
  103. Q_ASSERT(Boundaries.contains(model));
  104. const QModelIndex &proxyParent = mapFromSource(parent);
  105. const int offset = proxyParent.isValid()
  106. ? 0
  107. : modelRowOffset(model);
  108. beginInsertRows(proxyParent, first + offset, last + offset);
  109. }
  110. void MergedProxyModel::rowsInsertedSlot(const QModelIndex &parent, int first, int last)
  111. {
  112. Q_UNUSED(parent);
  113. Q_UNUSED(first);
  114. Q_UNUSED(last);
  115. QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(sender());
  116. Q_UNUSED(model);
  117. Q_ASSERT(model);
  118. Q_ASSERT(Boundaries.contains(model));
  119. updateBoundaries();
  120. endInsertRows();
  121. }
  122. void MergedProxyModel::rowsAboutToBeRemovedSlot(const QModelIndex &parent, int first, int last)
  123. {
  124. QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(sender());
  125. Q_ASSERT(model);
  126. Q_ASSERT(Boundaries.contains(model));
  127. const QModelIndex &proxyParent = mapFromSource(parent);
  128. const int offset = proxyParent.isValid()
  129. ? 0
  130. : modelRowOffset(model);
  131. beginRemoveRows(proxyParent, first + offset, last + offset);
  132. for (int i = first; i <= last; i++)
  133. IndexesToRemove.append(model->index(i, 0, parent));
  134. }
  135. void MergedProxyModel::rowsRemovedSlot(const QModelIndex &parent, int first, int last)
  136. {
  137. Q_UNUSED(parent);
  138. Q_UNUSED(first);
  139. Q_UNUSED(last);
  140. QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(sender());
  141. Q_UNUSED(model);
  142. Q_ASSERT(model);
  143. Q_ASSERT(Boundaries.contains(model));
  144. foreach (const QModelIndex &sourceIndex, IndexesToRemove)
  145. removeMapping(sourceIndex);
  146. IndexesToRemove.clear();
  147. updateBoundaries();
  148. endRemoveRows();
  149. }
  150. void MergedProxyModel::rowsAboutToBeMovedSlot(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
  151. {
  152. Q_UNUSED(parent);
  153. Q_UNUSED(start);
  154. Q_UNUSED(end);
  155. Q_UNUSED(destination);
  156. Q_UNUSED(row);
  157. // we do not support moving yet
  158. Q_ASSERT(false);
  159. }
  160. void MergedProxyModel::rowsMovedSlot(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
  161. {
  162. Q_UNUSED(parent);
  163. Q_UNUSED(start);
  164. Q_UNUSED(end);
  165. Q_UNUSED(destination);
  166. Q_UNUSED(row);
  167. // we do not support moving yet
  168. Q_ASSERT(false);
  169. }
  170. void MergedProxyModel::modelAboutToBeResetSlot()
  171. {
  172. beginResetModel();
  173. }
  174. void MergedProxyModel::modelResetSlot()
  175. {
  176. updateBoundaries();
  177. endResetModel();
  178. }
  179. void MergedProxyModel::modelDestroyedSlot(QObject *model)
  180. {
  181. Models.removeAll(static_cast<QAbstractItemModel *>(model));
  182. }
  183. QModelIndex MergedProxyModel::mapFirstLevelToSource(const QModelIndex &proxyIndex) const
  184. {
  185. if (!proxyIndex.isValid()) // invalid maps to invalid
  186. return QModelIndex();
  187. Q_ASSERT(this == proxyIndex.model());
  188. ModelMap::const_iterator i = Boundaries.constBegin();
  189. ModelMap::const_iterator end = Boundaries.constEnd();
  190. int row = proxyIndex.row();
  191. while (i != end)
  192. {
  193. if (row >= i.value().first && row <= i.value().second)
  194. return i.key()->index(row - i.value().first, proxyIndex.column());
  195. i++;
  196. }
  197. Q_ASSERT(false); // we should never be here
  198. return QModelIndex();
  199. }
  200. QModelIndex MergedProxyModel::mapFirstLevelFromSource(const QModelIndex &sourceIndex) const
  201. {
  202. Q_ASSERT(Boundaries.contains(sourceIndex.model()));
  203. return index(Boundaries.value(sourceIndex.model()).first + sourceIndex.row(), sourceIndex.column());
  204. }
  205. QModelIndex MergedProxyModel::mapToSource(const QModelIndex &proxyIndex) const
  206. {
  207. if (!proxyIndex.isValid()) // invalid maps to invalid
  208. return QModelIndex();
  209. Q_ASSERT(this == proxyIndex.model());
  210. const QModelIndex &proxyParentIndex = parent(proxyIndex);
  211. if (!proxyParentIndex.isValid())
  212. return mapFirstLevelToSource(proxyIndex);
  213. const QModelIndex &sourceParentIndex = mapToSource(proxyParentIndex);
  214. return sourceParentIndex.model()->index(proxyIndex.row(), proxyIndex.column(), sourceParentIndex);
  215. }
  216. QModelIndex MergedProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
  217. {
  218. if (!sourceIndex.isValid()) // invalid maps to invalid
  219. return QModelIndex();
  220. Q_ASSERT(Boundaries.contains(sourceIndex.model()));
  221. const QModelIndex &sourceParentIndex = sourceIndex.parent();
  222. if (!sourceParentIndex.isValid())
  223. return mapFirstLevelFromSource(sourceIndex);
  224. const QModelIndex &proxyParentIndex = mapFromSource(sourceParentIndex);
  225. return index(sourceIndex.row(), sourceIndex.column(), proxyParentIndex);
  226. }
  227. QModelIndex * MergedProxyModel::createMapping(const QModelIndex &sourceParent) const
  228. {
  229. IndexMapping::const_iterator it = Mappings.constFind(sourceParent);
  230. if (it != Mappings.constEnd())
  231. return it.value();
  232. QModelIndex *mapping = new QModelIndex(sourceParent);
  233. Mappings.insert(sourceParent, mapping);
  234. return mapping;
  235. }
  236. void MergedProxyModel::removeMapping(const QModelIndex &sourceParent) const
  237. {
  238. QModelIndex *mapping = Mappings.take(sourceParent);
  239. if (mapping)
  240. delete mapping;
  241. IndexMapping::const_iterator i = Mappings.constBegin();
  242. IndexMapping::const_iterator end = Mappings.constEnd();
  243. QModelIndexList indexesToRemove;
  244. while (i != end)
  245. {
  246. if (*i.value() == sourceParent)
  247. indexesToRemove.append(i.key());
  248. i++;
  249. }
  250. foreach (const QModelIndex &index, indexesToRemove)
  251. removeMapping(index);
  252. }
  253. QModelIndex MergedProxyModel::mappedSourceParent(const QModelIndex &proxyIndex) const
  254. {
  255. Q_ASSERT(proxyIndex.isValid());
  256. Q_ASSERT(proxyIndex.model() == this);
  257. const void *p = proxyIndex.internalPointer();
  258. if (!p)
  259. return QModelIndex();
  260. const QModelIndex *mapping = static_cast<const QModelIndex *>(p);
  261. return *mapping;
  262. }
  263. QModelIndex MergedProxyModel::index(int row, int column, const QModelIndex &parent) const
  264. {
  265. if (row < 0 || column < 0)
  266. return QModelIndex();
  267. if (!parent.isValid())
  268. return createIndex(row, column);
  269. const QModelIndex &sourceParent = mapToSource(parent); // parent is already mapped
  270. QModelIndex *mapping = createMapping(sourceParent); // map children for this parent
  271. if (!mapping) // something went wrong
  272. return QModelIndex();
  273. return createIndex(row, column, mapping);
  274. }
  275. QModelIndex MergedProxyModel::parent(const QModelIndex &proxyChild) const
  276. {
  277. if (!proxyChild.isValid())
  278. return QModelIndex();
  279. return mapFromSource(mappedSourceParent(proxyChild));
  280. }
  281. int MergedProxyModel::rowCount(const QModelIndex &parent) const
  282. {
  283. if (parent.isValid())
  284. {
  285. Q_ASSERT(parent.model() == this);
  286. const QModelIndex &sourceParent = mapToSource(parent);
  287. Q_ASSERT(sourceParent.model());
  288. return sourceParent.model()->rowCount(sourceParent);
  289. }
  290. int count = 0;
  291. foreach (const QAbstractItemModel *model, Models)
  292. {
  293. Q_ASSERT(model);
  294. count += model->rowCount();
  295. }
  296. return count;
  297. }
  298. int MergedProxyModel::columnCount(const QModelIndex &parent) const
  299. {
  300. Q_UNUSED(parent)
  301. return 1;
  302. }
  303. QFlags<Qt::ItemFlag> MergedProxyModel::flags(const QModelIndex &index) const
  304. {
  305. return mapToSource(index).flags();
  306. }
  307. QVariant MergedProxyModel::data(const QModelIndex &index, int role) const
  308. {
  309. return mapToSource(index).data(role);
  310. }
  311. QStringList MergedProxyModel::mimeTypes() const
  312. {
  313. QStringList result;
  314. foreach (const QAbstractItemModel *model, Models)
  315. result += model->mimeTypes();
  316. return result;
  317. }
  318. QMimeData * MergedProxyModel::mimeData(const QModelIndexList &proxyIndexes) const
  319. {
  320. if (proxyIndexes.isEmpty())
  321. return 0;
  322. QMap<const QAbstractItemModel *, QModelIndexList> sourceIndexes;
  323. foreach (const QModelIndex &proxyIndex, proxyIndexes)
  324. {
  325. const QModelIndex &sourceIndex = mapToSource(proxyIndex);
  326. const QAbstractItemModel *sourceModel = sourceIndex.model();
  327. if (!sourceIndexes.contains(sourceModel))
  328. sourceIndexes.insert(sourceModel, QModelIndexList());
  329. sourceIndexes.find(sourceModel)->append(sourceIndex);
  330. }
  331. QMimeData *mergedMimeData = new QMimeData();
  332. QList<const QAbstractItemModel *> sourceModels = sourceIndexes.keys();
  333. foreach (const QAbstractItemModel *sourceModel, sourceModels)
  334. {
  335. QMimeData *sourceMimeData = sourceModel->mimeData(sourceIndexes.value(sourceModel));
  336. if (!sourceMimeData)
  337. continue;
  338. foreach (const QString &sourceMimeDataFormat, sourceMimeData->formats())
  339. mergedMimeData->setData(sourceMimeDataFormat, sourceMimeData->data(sourceMimeDataFormat));
  340. delete sourceMimeData;
  341. }
  342. return mergedMimeData;
  343. }
  344. #include "moc_merged-proxy-model.cpp"