LeechCraft  0.6.70-13729-g7046a9d2a7
Modular cross-platform feature rich live environment.
mergemodel.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * LeechCraft - modular cross-platform feature rich internet client.
3  * Copyright (C) 2006-2014 Georg Rudoy
4  *
5  * Boost Software License - Version 1.0 - August 17th, 2003
6  *
7  * Permission is hereby granted, free of charge, to any person or organization
8  * obtaining a copy of the software and accompanying documentation covered by
9  * this license (the "Software") to use, reproduce, display, distribute,
10  * execute, and transmit the Software, and to prepare derivative works of the
11  * Software, and to permit third-parties to whom the Software is furnished to
12  * do so, all subject to the following:
13  *
14  * The copyright notices in the Software and this entire statement, including
15  * the above license grant, this restriction and the following disclaimer,
16  * must be included in all copies of the Software, in whole or in part, and
17  * all derivative works of the Software, unless such copies or derivative
18  * works are solely in the form of machine-executable object code generated by
19  * a source language processor.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  **********************************************************************/
29 
30 #include <algorithm>
31 #include <stdexcept>
32 #include <QMimeData>
33 #include <QUrl>
34 #include <QtDebug>
35 #include "mergemodel.h"
36 
37 namespace LC
38 {
39 namespace Util
40 {
41  MergeModel::MergeModel (const QStringList& headers, QObject *parent)
42  : QAbstractItemModel (parent)
43  , Headers_ (headers)
44  , Root_ (std::make_shared<ModelItem> ())
45  {
46  }
47 
48  int MergeModel::columnCount (const QModelIndex& index) const
49  {
50  if (index.isValid ())
51  {
52  const auto& mapped = mapToSource (index);
53  return mapped.model ()->columnCount (mapped);
54  }
55  else
56  return Headers_.size ();
57  }
58 
59  QVariant MergeModel::headerData (int column, Qt::Orientation orient, int role) const
60  {
61  if (orient != Qt::Horizontal || role != Qt::DisplayRole)
62  return QVariant ();
63 
64  return Headers_.at (column);
65  }
66 
67  QVariant MergeModel::data (const QModelIndex& index, int role) const
68  {
69  if (!index.isValid ())
70  return QVariant ();
71 
72  try
73  {
74  return mapToSource (index).data (role);
75  }
76  catch (const std::exception& e)
77  {
78  qWarning () << Q_FUNC_INFO
79  << e.what ();
80  return {};
81  }
82  }
83 
84  Qt::ItemFlags MergeModel::flags (const QModelIndex& index) const
85  {
86  try
87  {
88  return mapToSource (index).flags ();
89  }
90  catch (const std::exception& e)
91  {
92  qWarning () << Q_FUNC_INFO
93  << e.what ();
94  return {};
95  }
96  }
97 
98  QModelIndex MergeModel::index (int row, int column, const QModelIndex& parent) const
99  {
100  if (!hasIndex (row, column, parent))
101  return {};
102 
103  auto parentItem = parent.isValid () ?
104  static_cast<ModelItem*> (parent.internalPointer ()) :
105  Root_.get ();
106 
107  return createIndex (row, column, parentItem->EnsureChild (row));
108  }
109 
110  QModelIndex MergeModel::parent (const QModelIndex& index) const
111  {
112  if (!index.isValid () || index.internalPointer () == Root_.get ())
113  return {};
114 
115  auto item = static_cast<ModelItem*> (index.internalPointer ());
116  auto parent = item->GetParent ();
117  if (parent == Root_)
118  return {};
119 
120  return createIndex (parent->GetRow (), 0, parent.get ());
121  }
122 
123  int MergeModel::rowCount (const QModelIndex& parent) const
124  {
125  if (!parent.isValid ())
126  return Root_->GetRowCount ();
127 
128  const auto item = static_cast<ModelItem*> (parent.internalPointer ());
129  return item->GetModel ()->rowCount (item->GetIndex ());
130  }
131 
132  QStringList MergeModel::mimeTypes () const
133  {
134  QStringList result;
135  for (const auto model : GetAllModels ())
136  for (const auto& type : model->mimeTypes ())
137  if (!result.contains (type))
138  result << type;
139  return result;
140  }
141 
142  namespace
143  {
144  void Merge (QMimeData *out, const QMimeData *sub)
145  {
146  for (const auto& format : sub->formats ())
147  if (format != "text/uri-list" && !out->hasFormat (format))
148  out->setData (format, sub->data (format));
149 
150  out->setUrls (out->urls () + sub->urls ());
151  }
152  }
153 
154  QMimeData* MergeModel::mimeData (const QModelIndexList& indexes) const
155  {
156  QMimeData *result = nullptr;
157 
158  for (const auto& index : indexes)
159  {
160  const auto& src = mapToSource (index);
161 
162  const auto subresult = src.model ()->mimeData ({ src });
163 
164  if (!subresult)
165  continue;
166 
167  if (!result)
168  result = subresult;
169  else
170  {
171  Merge (result, subresult);
172  delete subresult;
173  }
174  }
175 
176  return result;
177  }
178 
179  QModelIndex MergeModel::mapFromSource (const QModelIndex& sourceIndex) const
180  {
181  if (!sourceIndex.isValid ())
182  return {};
183 
184  QList<QModelIndex> hier;
185  auto parent = sourceIndex;
186  while (parent.isValid ())
187  {
188  hier.prepend (parent);
189  parent = parent.parent ();
190  }
191 
192  auto currentItem = Root_;
193  for (const auto& idx : hier)
194  {
195  currentItem = currentItem->FindChild (idx);
196  if (!currentItem)
197  {
198  qWarning () << Q_FUNC_INFO
199  << "no next item for"
200  << idx
201  << hier;
202  return {};
203  }
204  }
205 
206  return createIndex (currentItem->GetRow (), sourceIndex.column (), currentItem.get ());
207  }
208 
209  QModelIndex MergeModel::mapToSource (const QModelIndex& proxyIndex) const
210  {
211  const auto item = proxyIndex.isValid () ?
212  static_cast<ModelItem*> (proxyIndex.internalPointer ()) :
213  Root_.get ();
214 
215  const auto& srcIdx = item->GetIndex ();
216  return srcIdx.sibling (srcIdx.row (), proxyIndex.column ());
217  }
218 
219  void MergeModel::setSourceModel (QAbstractItemModel*)
220  {
221  throw std::runtime_error ("You should not set source model via setSourceModel()");
222  }
223 
224  void MergeModel::SetHeaders (const QStringList& headers)
225  {
226  Headers_ = headers;
227  }
228 
229  void MergeModel::AddModel (QAbstractItemModel *model)
230  {
231  if (!model)
232  return;
233 
234  Models_.push_back (model);
235 
236  connect (model,
237  SIGNAL (columnsAboutToBeInserted (const QModelIndex&, int, int)),
238  this,
239  SLOT (handleColumnsAboutToBeInserted (const QModelIndex&, int, int)));
240  connect (model,
241  SIGNAL (columnsAboutToBeRemoved (const QModelIndex&, int, int)),
242  this,
243  SLOT (handleColumnsAboutToBeRemoved (const QModelIndex&, int, int)));
244  connect (model,
245  SIGNAL (columnsInserted (const QModelIndex&, int, int)),
246  this,
247  SLOT (handleColumnsInserted (const QModelIndex&, int, int)));
248  connect (model,
249  SIGNAL (columnsRemoved (const QModelIndex&, int, int)),
250  this,
251  SLOT (handleColumnsRemoved (const QModelIndex&, int, int)));
252  connect (model,
253  SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
254  this,
255  SLOT (handleDataChanged (const QModelIndex&, const QModelIndex&)));
256  connect (model,
257  SIGNAL (layoutAboutToBeChanged ()),
258  this,
259  SLOT (handleModelAboutToBeReset ()));
260  connect (model,
261  SIGNAL (layoutChanged ()),
262  this,
263  SLOT (handleModelReset ()));
264  connect (model,
265  SIGNAL (modelAboutToBeReset ()),
266  this,
267  SLOT (handleModelAboutToBeReset ()));
268  connect (model,
269  SIGNAL (modelReset ()),
270  this,
271  SLOT (handleModelReset ()));
272  connect (model,
273  SIGNAL (rowsAboutToBeInserted (const QModelIndex&, int, int)),
274  this,
275  SLOT (handleRowsAboutToBeInserted (const QModelIndex&, int, int)));
276  connect (model,
277  SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
278  this,
279  SLOT (handleRowsAboutToBeRemoved (const QModelIndex&, int, int)));
280  connect (model,
281  SIGNAL (rowsInserted (const QModelIndex&, int, int)),
282  this,
283  SLOT (handleRowsInserted (const QModelIndex&, int, int)));
284  connect (model,
285  SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
286  this,
287  SLOT (handleRowsRemoved (const QModelIndex&, int, int)));
288 
289  if (const auto rc = model->rowCount ())
290  {
291  beginInsertRows ({}, rowCount ({}), rowCount ({}) + rc - 1);
292 
293  for (auto i = 0; i < rc; ++i)
294  Root_->AppendChild (model, model->index (i, 0), Root_);
295 
296  endInsertRows ();
297  }
298  }
299 
300  MergeModel::const_iterator MergeModel::FindModel (const QAbstractItemModel *model) const
301  {
302  return std::find (Models_.begin (), Models_.end (), model);
303  }
304 
305  MergeModel::iterator MergeModel::FindModel (const QAbstractItemModel *model)
306  {
307  return std::find (Models_.begin (), Models_.end (), model);
308  }
309 
310  void MergeModel::RemoveModel (QAbstractItemModel *model)
311  {
312  auto i = FindModel (model);
313 
314  if (i == Models_.end ())
315  {
316  qWarning () << Q_FUNC_INFO << "not found model" << model;
317  return;
318  }
319 
320  for (auto r = Root_->begin (); r != Root_->end (); )
321  if ((*r)->GetModel () == model)
322  {
323  const auto idx = std::distance (Root_->begin (), r);
324 
325  beginRemoveRows ({}, idx, idx);
326  r = Root_->EraseChild (r);
327  endRemoveRows ();
328  }
329  else
330  ++r;
331 
332  Models_.erase (i);
333  }
334 
335  size_t MergeModel::Size () const
336  {
337  return Models_.size ();
338  }
339 
341  {
342  int result = 0;
343  for (auto i = Models_.begin (); i != it; ++i)
344  result += (*i)->rowCount ({});
345  return result;
346  }
347 
349  {
350  const auto child = Root_->GetChild (row);
351  const auto it = FindModel (child->GetModel ());
352 
353  if (starting)
354  *starting = GetStartingRow (it);
355 
356  return it;
357  }
358 
360  {
361  const auto child = Root_->GetChild (row);
362  const auto it = FindModel (child->GetModel ());
363 
364  if (starting)
365  *starting = GetStartingRow (it);
366 
367  return it;
368  }
369 
371  {
373  for (auto p : Models_)
374  if (p)
375  result << p.data ();
376  return result;
377  }
378 
379  void MergeModel::handleColumnsAboutToBeInserted (const QModelIndex&, int, int)
380  {
381  }
382 
383  void MergeModel::handleColumnsAboutToBeRemoved (const QModelIndex&, int, int)
384  {
385  qWarning () << "model" << sender ()
386  << "called handleColumnsAboutToBeRemoved, ignoring it";
387  }
388 
389  void MergeModel::handleColumnsInserted (const QModelIndex&, int, int)
390  {
391  }
392 
393  void MergeModel::handleColumnsRemoved (const QModelIndex&, int, int)
394  {
395  qWarning () << "model" << sender ()
396  << "called handleColumnsRemoved, ignoring it";
397  }
398 
399  void MergeModel::handleDataChanged (const QModelIndex& topLeft,
400  const QModelIndex& bottomRight)
401  {
402  emit dataChanged (mapFromSource (topLeft), mapFromSource (bottomRight));
403  }
404 
405  void MergeModel::handleRowsAboutToBeInserted (const QModelIndex& parent,
406  int first, int last)
407  {
408  const auto model = static_cast<QAbstractItemModel*> (sender ());
409 
410  const auto startingRow = parent.isValid () ?
411  0 :
412  GetStartingRow (FindModel (model));
413  beginInsertRows (mapFromSource (parent),
414  first + startingRow, last + startingRow);
415  }
416 
417  void MergeModel::handleRowsAboutToBeRemoved (const QModelIndex& parent,
418  int first, int last)
419  {
420  auto model = static_cast<QAbstractItemModel*> (sender ());
421 
422  const auto startingRow = parent.isValid () ?
423  0 :
424  GetStartingRow (FindModel (model));
425  beginRemoveRows (mapFromSource (parent),
426  first + startingRow, last + startingRow);
427 
428  const auto rawItem = parent.isValid () ?
429  static_cast<ModelItem*> (mapFromSource (parent).internalPointer ()) :
430  Root_.get ();
431  const auto& item = rawItem->shared_from_this ();
432 
433  auto it = item->EraseChildren (item->begin () + startingRow + first,
434  item->begin () + startingRow + last + 1);
435 
436  RemovalRefreshers_.push ([=] () mutable
437  {
438  for ( ; it != item->end () && (*it)->GetModel () == model; ++it)
439  (*it)->RefreshIndex (startingRow);
440  });
441  }
442 
443  void MergeModel::handleRowsInserted (const QModelIndex& parent, int first, int last)
444  {
445  const auto model = static_cast<QAbstractItemModel*> (sender ());
446 
447  const auto startingRow = parent.isValid () ?
448  0 :
449  GetStartingRow (FindModel (model));
450 
451  const auto rawItem = parent.isValid () ?
452  static_cast<ModelItem*> (mapFromSource (parent).internalPointer ()) :
453  Root_.get ();
454  const auto& item = rawItem->shared_from_this ();
455 
456  for ( ; first <= last; ++first)
457  {
458  const auto& srcIdx = model->index (first, 0, parent);
459  item->InsertChild (startingRow + first, model, srcIdx, item);
460  }
461 
462  ++last;
463  last += startingRow;
464 
465  for (int rc = item->GetRowCount (); last < rc; ++last)
466  {
467  const auto child = item->GetChild (last);
468  if (child->GetModel () != model)
469  break;
470 
471  child->RefreshIndex (startingRow);
472  }
473 
474  endInsertRows ();
475  }
476 
477  void MergeModel::handleRowsRemoved (const QModelIndex&, int, int)
478  {
479  RemovalRefreshers_.pop () ();
480  endRemoveRows ();
481  }
482 
484  {
485  const auto model = static_cast<QAbstractItemModel*> (sender ());
486  if (const auto rc = model->rowCount ())
487  {
488  const auto startingRow = GetStartingRow (FindModel (model));
489  beginRemoveRows ({}, startingRow, rc + startingRow - 1);
490  Root_->EraseChildren (Root_->begin () + startingRow, Root_->begin () + startingRow + rc);
491  endRemoveRows ();
492  }
493  }
494 
496  {
497  const auto model = static_cast<QAbstractItemModel*> (sender ());
498  if (const auto rc = model->rowCount ())
499  {
500  const auto startingRow = GetStartingRow (FindModel (model));
501 
502  beginInsertRows ({}, startingRow, rc + startingRow - 1);
503 
504  for (int i = 0; i < rc; ++i)
505  Root_->InsertChild (startingRow + i, model, model->index (i, 0, {}), Root_);
506 
507  endInsertRows ();
508  }
509  }
510 
511  bool MergeModel::AcceptsRow (QAbstractItemModel*, int) const
512  {
513  DefaultAcceptsRowImpl_ = true;
514  return true;
515  }
516 
517  int MergeModel::RowCount (QAbstractItemModel *model) const
518  {
519  if (!model)
520  return 0;
521 
522  int orig = model->rowCount ();
523  if (DefaultAcceptsRowImpl_)
524  return orig;
525 
526  int result = 0;
527  for (int i = 0; i < orig; ++i)
528  result += AcceptsRow (model, i) ? 1 : 0;
529  return result;
530  }
531 }
532 }
virtual void handleColumnsAboutToBeInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:379
virtual void handleModelReset()
Definition: mergemodel.cpp:495
virtual void handleRowsInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:443
virtual void handleRowsAboutToBeInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:405
virtual void handleDataChanged(const QModelIndex &, const QModelIndex &)
Definition: mergemodel.cpp:399
QVariant data(const QModelIndex &, int=Qt::DisplayRole) const override
Definition: mergemodel.cpp:67
QVariant headerData(int, Qt::Orientation, int=Qt::DisplayRole) const override
Definition: mergemodel.cpp:59
virtual void handleModelAboutToBeReset()
Definition: mergemodel.cpp:483
models_t::const_iterator const_iterator
Definition: mergemodel.h:74
STL namespace.
QModelIndex index(int, int, const QModelIndex &=QModelIndex()) const override
Definition: mergemodel.cpp:98
void AddModel(QAbstractItemModel *model)
Adds a model to the list of source models.
Definition: mergemodel.cpp:229
QList< QAbstractItemModel * > GetAllModels() const
Returns all models intalled into this one.
Definition: mergemodel.cpp:370
virtual QModelIndex mapToSource(const QModelIndex &index) const
Returns the source model index corresponding to the given index from the sorting filter model...
Definition: mergemodel.cpp:209
virtual void setSourceModel(QAbstractItemModel *)
Definition: mergemodel.cpp:219
int GetStartingRow(const_iterator it) const
Finds starting row for the model pointed by it.
Definition: mergemodel.cpp:340
int columnCount(const QModelIndex &=QModelIndex()) const override
Definition: mergemodel.cpp:48
virtual bool AcceptsRow(QAbstractItemModel *model, int row) const
Allows to filter rows from the resulting model.
Definition: mergemodel.cpp:511
virtual QModelIndex mapFromSource(const QModelIndex &index) const
Returns the model index in the MergeModel given the index from the source model.
Definition: mergemodel.cpp:179
QModelIndex parent(const QModelIndex &) const override
Definition: mergemodel.cpp:110
virtual void handleColumnsRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:393
models_t::iterator iterator
Definition: mergemodel.h:73
virtual void handleColumnsInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:389
MergeModel(const QStringList &headers, QObject *parent=0)
Constructs the merge model.
Definition: mergemodel.cpp:41
size_t Size() const
Returns the number of child models in the merger.
Definition: mergemodel.cpp:335
Provides a proxying API on top of an QAbstractItemModel.
Definition: modelitem.h:54
const_iterator FindModel(const QAbstractItemModel *model) const
Returns a const_iterator corresponding to the passed model, or one-past-end if no such model is found...
Definition: mergemodel.cpp:300
void SetHeaders(const QStringList &headers)
Sets the new headers for this model.
Definition: mergemodel.cpp:224
QStringList mimeTypes() const override
Returns the union of MIME types of the models.
Definition: mergemodel.cpp:132
virtual void handleColumnsAboutToBeRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:383
virtual void handleRowsAboutToBeRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:417
QAbstractItemModel * GetModel() const
Returns the wrapped model.
Definition: modelitem.cpp:68
Definition: constants.h:35
Qt::ItemFlags flags(const QModelIndex &) const override
Definition: mergemodel.cpp:84
int rowCount(const QModelIndex &=QModelIndex()) const override
Definition: mergemodel.cpp:123
const_iterator GetModelForRow(int row, int *starting=0) const
Returns the model for the given row.
Definition: mergemodel.cpp:348
QMimeData * mimeData(const QModelIndexList &indices) const override
Returns the MIME data for the given indices.
Definition: mergemodel.cpp:154
virtual void handleRowsRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:477
void RemoveModel(QAbstractItemModel *model)
Removes a model from the list of source models.
Definition: mergemodel.cpp:310