LeechCraft  0.6.70-15082-g543737046d
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  * Distributed under the Boost Software License, Version 1.0.
6  * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7  **********************************************************************/
8 
9 #include <algorithm>
10 #include <stdexcept>
11 #include <QMimeData>
12 #include <QUrl>
13 #include <QtDebug>
14 #include <util/sll/qtutil.h>
15 #include "mergemodel.h"
16 
17 namespace LC::Util
18 {
19  MergeModel::MergeModel (QStringList headers, QObject *parent)
20  : QAbstractItemModel (parent)
21  , Headers_ (std::move (headers))
22  , Root_ (std::make_shared<ModelItem> ())
23  {
24  }
25 
26  int MergeModel::columnCount (const QModelIndex& index) const
27  {
28  if (!index.isValid ())
29  return Headers_.size ();
30  const auto& mapped = mapToSource (index);
31  return mapped.model ()->columnCount (mapped);
32  }
33 
34  QVariant MergeModel::headerData (int column, Qt::Orientation orient, int role) const
35  {
36  if (orient != Qt::Horizontal || role != Qt::DisplayRole)
37  return QVariant ();
38 
39  return Headers_.at (column);
40  }
41 
42  QVariant MergeModel::data (const QModelIndex& index, int role) const
43  {
44  if (!index.isValid ())
45  return QVariant ();
46 
47  try
48  {
49  return mapToSource (index).data (role);
50  }
51  catch (const std::exception& e)
52  {
53  qWarning () << Q_FUNC_INFO
54  << e.what ();
55  return {};
56  }
57  }
58 
59  Qt::ItemFlags MergeModel::flags (const QModelIndex& index) const
60  {
61  try
62  {
63  return mapToSource (index).flags ();
64  }
65  catch (const std::exception& e)
66  {
67  qWarning () << Q_FUNC_INFO
68  << e.what ();
69  return {};
70  }
71  }
72 
73  QModelIndex MergeModel::index (int row, int column, const QModelIndex& parent) const
74  {
75  if (!hasIndex (row, column, parent))
76  return {};
77 
78  auto parentItem = parent.isValid () ?
79  static_cast<ModelItem*> (parent.internalPointer ()) :
80  Root_.get ();
81 
82  return createIndex (row, column, parentItem->EnsureChild (row));
83  }
84 
85  QModelIndex MergeModel::parent (const QModelIndex& index) const
86  {
87  if (!index.isValid () || index.internalPointer () == Root_.get ())
88  return {};
89 
90  auto item = static_cast<ModelItem*> (index.internalPointer ());
91  auto parent = item->GetParent ();
92  if (parent == Root_)
93  return {};
94 
95  return createIndex (parent->GetRow (), 0, parent.get ());
96  }
97 
98  int MergeModel::rowCount (const QModelIndex& parent) const
99  {
100  if (!parent.isValid ())
101  return Root_->GetRowCount ();
102 
103  const auto item = static_cast<ModelItem*> (parent.internalPointer ());
104  return item->GetModel ()->rowCount (item->GetIndex ());
105  }
106 
107  QStringList MergeModel::mimeTypes () const
108  {
109  QStringList result;
110  for (const auto model : GetAllModels ())
111  for (const auto& type : model->mimeTypes ())
112  if (!result.contains (type))
113  result << type;
114  return result;
115  }
116 
117  namespace
118  {
119  void Merge (QMimeData& out, const QMimeData& sub)
120  {
121  for (const auto& format : sub.formats ())
122  if (format != "text/uri-list"_ql && !out.hasFormat (format))
123  out.setData (format, sub.data (format));
124 
125  out.setUrls (out.urls () + sub.urls ());
126  }
127  }
128 
129  QMimeData* MergeModel::mimeData (const QModelIndexList& indexes) const
130  {
131  std::unique_ptr<QMimeData> result;
132 
133  for (const auto& index : indexes)
134  {
135  const auto& src = mapToSource (index);
136 
137  std::unique_ptr<QMimeData> subresult { src.model ()->mimeData ({ src }) };
138  if (!subresult)
139  continue;
140 
141  if (!result)
142  result = std::move (subresult);
143  else
144  Merge (*result, *subresult);
145  }
146 
147  return result.release ();
148  }
149 
150  QModelIndex MergeModel::mapFromSource (const QModelIndex& sourceIndex) const
151  {
152  if (!sourceIndex.isValid ())
153  return {};
154 
155  QList<QModelIndex> hier;
156  auto parent = sourceIndex;
157  while (parent.isValid ())
158  {
159  hier.prepend (parent);
160  parent = parent.parent ();
161  }
162 
163  auto currentItem = Root_;
164  for (const auto& idx : hier)
165  {
166  currentItem = currentItem->FindChild (idx);
167  if (!currentItem)
168  {
169  qWarning () << Q_FUNC_INFO
170  << "no next item for"
171  << idx
172  << hier;
173  return {};
174  }
175  }
176 
177  return createIndex (currentItem->GetRow (), sourceIndex.column (), currentItem.get ());
178  }
179 
180  QModelIndex MergeModel::mapToSource (const QModelIndex& proxyIndex) const
181  {
182  const auto item = proxyIndex.isValid () ?
183  static_cast<ModelItem*> (proxyIndex.internalPointer ()) :
184  Root_.get ();
185 
186  const auto& srcIdx = item->GetIndex ();
187  return srcIdx.sibling (srcIdx.row (), proxyIndex.column ());
188  }
189 
190  void MergeModel::setSourceModel (QAbstractItemModel*)
191  {
192  throw std::runtime_error ("You should not set source model via setSourceModel()");
193  }
194 
195  void MergeModel::SetHeaders (const QStringList& headers)
196  {
197  Headers_ = headers;
198  }
199 
200  void MergeModel::AddModel (QAbstractItemModel *model)
201  {
202  if (!model)
203  return;
204 
205  Models_.push_back (model);
206 
207  auto withModel = [this, model]<typename... Args> (void (MergeModel::*method) (QAbstractItemModel*, Args...))
208  {
209  return [this, model, method] (Args... args) { (this->*method) (model, args...); };
210  };
211 
212  connect (model,
213  &QAbstractItemModel::dataChanged,
214  this,
215  [this] (const QModelIndex& topLeft, const QModelIndex& bottomRight)
216  {
217  emit dataChanged (mapFromSource (topLeft), mapFromSource (bottomRight));
218  });
219  connect (model,
220  &QAbstractItemModel::layoutAboutToBeChanged,
221  this,
223  connect (model,
224  &QAbstractItemModel::layoutChanged,
225  this,
226  withModel (&MergeModel::HandleModelReset));
227  connect (model,
228  &QAbstractItemModel::modelAboutToBeReset,
229  this,
231  connect (model,
232  &QAbstractItemModel::modelReset,
233  this,
234  withModel (&MergeModel::HandleModelReset));
235  connect (model,
236  &QAbstractItemModel::rowsAboutToBeInserted,
237  this,
239  connect (model,
240  &QAbstractItemModel::rowsAboutToBeRemoved,
241  this,
243  connect (model,
244  &QAbstractItemModel::rowsInserted,
245  this,
246  withModel (&MergeModel::HandleRowsInserted));
247  connect (model,
248  &QAbstractItemModel::rowsRemoved,
249  this,
250  withModel (&MergeModel::HandleRowsRemoved));
251 
252  if (const auto rc = model->rowCount ())
253  {
254  beginInsertRows ({}, rowCount ({}), rowCount ({}) + rc - 1);
255 
256  for (auto i = 0; i < rc; ++i)
257  Root_->AppendChild (model, model->index (i, 0), Root_);
258 
259  endInsertRows ();
260  }
261  }
262 
263  MergeModel::const_iterator MergeModel::FindModel (const QAbstractItemModel *model) const
264  {
265  return std::find (Models_.begin (), Models_.end (), model);
266  }
267 
268  MergeModel::iterator MergeModel::FindModel (const QAbstractItemModel *model)
269  {
270  return std::find (Models_.begin (), Models_.end (), model);
271  }
272 
273  void MergeModel::RemoveModel (QAbstractItemModel *model)
274  {
275  auto i = FindModel (model);
276 
277  if (i == Models_.end ())
278  {
279  qWarning () << Q_FUNC_INFO << "not found model" << model;
280  return;
281  }
282 
283  for (auto r = Root_->begin (); r != Root_->end (); )
284  if ((*r)->GetModel () == model)
285  {
286  const auto idx = static_cast<int> (std::distance (Root_->begin (), r));
287 
288  beginRemoveRows ({}, idx, idx);
289  r = Root_->EraseChild (r);
290  endRemoveRows ();
291  }
292  else
293  ++r;
294 
295  Models_.erase (i);
296  }
297 
298  size_t MergeModel::Size () const
299  {
300  return Models_.size ();
301  }
302 
304  {
305  int result = 0;
306  for (auto i = Models_.begin (); i != it; ++i)
307  result += (*i)->rowCount ({});
308  return result;
309  }
310 
312  {
313  int result = 0;
314  for (auto i = Models_.begin (); i != it; ++i)
315  result += (*i)->rowCount ({});
316  return result;
317  }
318 
319  MergeModel::const_iterator MergeModel::GetModelForRow (int row, int *starting) const
320  {
321  const auto child = Root_->GetChild (row);
322  const auto it = FindModel (child->GetModel ());
323 
324  if (starting)
325  *starting = GetStartingRow (it);
326 
327  return it;
328  }
329 
330  MergeModel::iterator MergeModel::GetModelForRow (int row, int *starting)
331  {
332  const auto child = Root_->GetChild (row);
333  const auto it = FindModel (child->GetModel ());
334 
335  if (starting)
336  *starting = GetStartingRow (it);
337 
338  return it;
339  }
340 
342  {
344  for (auto p : Models_)
345  if (p)
346  result << p.data ();
347  return result;
348  }
349 
350  void MergeModel::HandleRowsAboutToBeInserted (QAbstractItemModel *model, const QModelIndex& parent, int first, int last)
351  {
352  const auto startingRow = parent.isValid () ?
353  0 :
354  GetStartingRow (FindModel (model));
355  beginInsertRows (mapFromSource (parent),
356  first + startingRow, last + startingRow);
357  }
358 
359  void MergeModel::HandleRowsAboutToBeRemoved (QAbstractItemModel *model, const QModelIndex& parent, int first, int last)
360  {
361  const auto startingRow = parent.isValid () ?
362  0 :
363  GetStartingRow (FindModel (model));
364  beginRemoveRows (mapFromSource (parent),
365  first + startingRow, last + startingRow);
366 
367  const auto rawItem = parent.isValid () ?
368  static_cast<ModelItem*> (mapFromSource (parent).internalPointer ()) :
369  Root_.get ();
370  const auto& item = rawItem->shared_from_this ();
371 
372  auto it = item->EraseChildren (item->begin () + startingRow + first,
373  item->begin () + startingRow + last + 1);
374 
375  RemovalRefreshers_.push ([=] () mutable
376  {
377  for ( ; it != item->end () && (*it)->GetModel () == model; ++it)
378  (*it)->RefreshIndex (startingRow);
379  });
380  }
381 
382  void MergeModel::HandleRowsInserted (QAbstractItemModel *model, const QModelIndex& parent, int first, int last)
383  {
384  const auto startingRow = parent.isValid () ?
385  0 :
386  GetStartingRow (FindModel (model));
387 
388  const auto rawItem = parent.isValid () ?
389  static_cast<ModelItem*> (mapFromSource (parent).internalPointer ()) :
390  Root_.get ();
391  const auto& item = rawItem->shared_from_this ();
392 
393  for ( ; first <= last; ++first)
394  {
395  const auto& srcIdx = model->index (first, 0, parent);
396  item->InsertChild (startingRow + first, model, srcIdx, item);
397  }
398 
399  ++last;
400  last += startingRow;
401 
402  for (int rc = item->GetRowCount (); last < rc; ++last)
403  {
404  const auto child = item->GetChild (last);
405  if (child->GetModel () != model)
406  break;
407 
408  child->RefreshIndex (startingRow);
409  }
410 
411  endInsertRows ();
412  }
413 
414  void MergeModel::HandleRowsRemoved (QAbstractItemModel*, const QModelIndex&, int, int)
415  {
416  RemovalRefreshers_.pop () ();
417  endRemoveRows ();
418  }
419 
420  void MergeModel::HandleModelAboutToBeReset (QAbstractItemModel *model)
421  {
422  if (const auto rc = model->rowCount ())
423  {
424  const auto startingRow = GetStartingRow (FindModel (model));
425  beginRemoveRows ({}, startingRow, rc + startingRow - 1);
426  Root_->EraseChildren (Root_->begin () + startingRow, Root_->begin () + startingRow + rc);
427  endRemoveRows ();
428  }
429  }
430 
431  void MergeModel::HandleModelReset (QAbstractItemModel *model)
432  {
433  if (const auto rc = model->rowCount ())
434  {
435  const auto startingRow = GetStartingRow (FindModel (model));
436 
437  beginInsertRows ({}, startingRow, rc + startingRow - 1);
438 
439  for (int i = 0; i < rc; ++i)
440  Root_->InsertChild (startingRow + i, model, model->index (i, 0, {}), Root_);
441 
442  endInsertRows ();
443  }
444  }
445 
446  bool MergeModel::AcceptsRow (QAbstractItemModel*, int) const
447  {
448  DefaultAcceptsRowImpl_ = true;
449  return true;
450  }
451 
452  int MergeModel::RowCount (QAbstractItemModel *model) const
453  {
454  if (!model)
455  return 0;
456 
457  int orig = model->rowCount ();
458  if (DefaultAcceptsRowImpl_)
459  return orig;
460 
461  int result = 0;
462  for (int i = 0; i < orig; ++i)
463  result += AcceptsRow (model, i) ? 1 : 0;
464  return result;
465  }
466 }
LC::Util::MergeModel::GetModelForRow
const_iterator GetModelForRow(int row, int *starting=nullptr) const
Returns the model for the given row.
Definition: mergemodel.cpp:325
LC::Util::MergeModel::GetStartingRow
int GetStartingRow(const_iterator it) const
Finds starting row for the model pointed by it.
Definition: mergemodel.cpp:309
LC::Util::MergeModel::AcceptsRow
virtual bool AcceptsRow(QAbstractItemModel *model, int row) const
Allows to filter rows from the resulting model.
Definition: mergemodel.cpp:452
LC::Util::MergeModel::mapFromSource
virtual QModelIndex mapFromSource(const QModelIndex &index) const
Returns the model index in the MergeModel given the index from the source model.
Definition: mergemodel.cpp:156
QList
Definition: ianrulesstorage.h:14
LC::Util::MergeModel::columnCount
int columnCount(const QModelIndex &=QModelIndex()) const override
Definition: mergemodel.cpp:32
LC::Util::MergeModel::flags
Qt::ItemFlags flags(const QModelIndex &) const override
Definition: mergemodel.cpp:65
LC::Util
Definition: icoreproxy.h:33
LC::Util::MergeModel::MergeModel
MergeModel(QStringList headers, QObject *parent=nullptr)
Constructs the merge model.
Definition: mergemodel.cpp:25
LC::Util::MergeModel::parent
QModelIndex parent(const QModelIndex &) const override
Definition: mergemodel.cpp:91
LC::Util::MergeModel::Size
size_t Size() const
Returns the number of child models in the merger.
Definition: mergemodel.cpp:304
LC::Util::MergeModel::iterator
models_t::iterator iterator
Definition: mergemodel.h:59
LC::Util::ModelItem
Provides a proxying API on top of an QAbstractItemModel.
Definition: modelitem.h:37
LC::Util::MergeModel::HandleModelAboutToBeReset
virtual void HandleModelAboutToBeReset(QAbstractItemModel *)
Definition: mergemodel.cpp:426
LC::Util::MergeModel::index
QModelIndex index(int, int, const QModelIndex &=QModelIndex()) const override
Definition: mergemodel.cpp:79
LC::Util::MergeModel::SetHeaders
void SetHeaders(const QStringList &headers)
Sets the new headers for this model.
Definition: mergemodel.cpp:201
LC::Util::MergeModel::rowCount
int rowCount(const QModelIndex &=QModelIndex()) const override
Definition: mergemodel.cpp:104
LC::Util::MergeModel::mimeTypes
QStringList mimeTypes() const override
Returns the union of MIME types of the models.
Definition: mergemodel.cpp:113
LC::Util::MergeModel::AddModel
void AddModel(QAbstractItemModel *model)
Adds a model to the list of source models.
Definition: mergemodel.cpp:206
LC::Util::MergeModel::headerData
QVariant headerData(int, Qt::Orientation, int=Qt::DisplayRole) const override
Definition: mergemodel.cpp:40
LC::Util::MergeModel::HandleModelReset
virtual void HandleModelReset(QAbstractItemModel *)
Definition: mergemodel.cpp:437
LC::Util::MergeModel::FindModel
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:269
qtutil.h
LC::Util::MergeModel::HandleRowsAboutToBeInserted
virtual void HandleRowsAboutToBeInserted(QAbstractItemModel *, const QModelIndex &, int, int)
Definition: mergemodel.cpp:356
LC::Util::MergeModel::mapToSource
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:186
LC::Util::MergeModel::setSourceModel
virtual void setSourceModel(QAbstractItemModel *)
Definition: mergemodel.cpp:196
LC::Util::MergeModel::HandleRowsAboutToBeRemoved
virtual void HandleRowsAboutToBeRemoved(QAbstractItemModel *, const QModelIndex &, int, int)
Definition: mergemodel.cpp:365
LC::Util::MergeModel::data
QVariant data(const QModelIndex &, int=Qt::DisplayRole) const override
Definition: mergemodel.cpp:48
mergemodel.h
LC::Util::MergeModel::Models_
models_t Models_
Definition: mergemodel.h:51
LC::Util::MergeModel::mimeData
QMimeData * mimeData(const QModelIndexList &indices) const override
Returns the MIME data for the given indices.
Definition: mergemodel.cpp:135
LC::Util::MergeModel::HandleRowsRemoved
virtual void HandleRowsRemoved(QAbstractItemModel *, const QModelIndex &, int, int)
Definition: mergemodel.cpp:420
LC::Util::MergeModel::RemoveModel
void RemoveModel(QAbstractItemModel *model)
Removes a model from the list of source models.
Definition: mergemodel.cpp:279
LC::Util::MergeModel
Definition: mergemodel.h:40
LC::Util::MergeModel::HandleRowsInserted
virtual void HandleRowsInserted(QAbstractItemModel *, const QModelIndex &, int, int)
Definition: mergemodel.cpp:388
LC::Util::MergeModel::GetAllModels
QList< QAbstractItemModel * > GetAllModels() const
Returns all models intalled into this one.
Definition: mergemodel.cpp:347
LC::Util::MergeModel::const_iterator
models_t::const_iterator const_iterator
Definition: mergemodel.h:60