LeechCraft  %{LEECHCRAFT_VERSION}
Modular cross-platform feature rich live environment.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 LeechCraft
38 {
39 namespace Util
40 {
41  MergeModel::MergeModel (const QStringList& headers, QObject *parent)
42  : QAbstractItemModel (parent)
43  , DefaultAcceptsRowImpl_ (false)
44  , Headers_ (headers)
45  , Root_ (new ModelItem)
46  {
47  }
48 
49  int MergeModel::columnCount (const QModelIndex& index) const
50  {
51  if (index.isValid ())
52  {
53  const auto& mapped = mapToSource (index);
54  return mapped.model ()->columnCount (mapped);
55  }
56  else
57  return Headers_.size ();
58  }
59 
60  QVariant MergeModel::headerData (int column, Qt::Orientation orient, int role) const
61  {
62  if (orient != Qt::Horizontal || role != Qt::DisplayRole)
63  return QVariant ();
64 
65  return Headers_.at (column);
66  }
67 
68  QVariant MergeModel::data (const QModelIndex& index, int role) const
69  {
70  if (!index.isValid ())
71  return QVariant ();
72 
73  try
74  {
75  return mapToSource (index).data (role);
76  }
77  catch (const std::exception& e)
78  {
79  qWarning () << Q_FUNC_INFO
80  << e.what ();
81  return {};
82  }
83  }
84 
85  Qt::ItemFlags MergeModel::flags (const QModelIndex& index) const
86  {
87  try
88  {
89  return mapToSource (index).flags ();
90  }
91  catch (const std::exception& e)
92  {
93  qWarning () << Q_FUNC_INFO
94  << e.what ();
95  return {};
96  }
97  }
98 
99  QModelIndex MergeModel::index (int row, int column, const QModelIndex& parent) const
100  {
101  if (!hasIndex (row, column, parent))
102  return {};
103 
104  auto parentItem = parent.isValid () ?
105  static_cast<ModelItem*> (parent.internalPointer ()) :
106  Root_.get ();
107 
108  return createIndex (row, column, parentItem->EnsureChild (row));
109  }
110 
111  QModelIndex MergeModel::parent (const QModelIndex& index) const
112  {
113  if (!index.isValid () || index.internalPointer () == Root_.get ())
114  return {};
115 
116  auto item = static_cast<ModelItem*> (index.internalPointer ());
117  auto parent = item->GetParent ();
118  if (parent == Root_)
119  return {};
120 
121  return createIndex (parent->GetRow (), 0, parent.get ());
122  }
123 
124  int MergeModel::rowCount (const QModelIndex& parent) const
125  {
126  if (!parent.isValid ())
127  return Root_->GetRowCount ();
128 
129  const auto item = static_cast<ModelItem*> (parent.internalPointer ());
130  return item->GetModel ()->rowCount (item->GetIndex ());
131  }
132 
133  QStringList MergeModel::mimeTypes () const
134  {
135  QStringList result;
136  for (const auto model : GetAllModels ())
137  for (const auto& type : model->mimeTypes ())
138  if (!result.contains (type))
139  result << type;
140  return result;
141  }
142 
143  namespace
144  {
145  void Merge (QMimeData *out, const QMimeData *sub)
146  {
147  for (const auto& format : sub->formats ())
148  if (format != "text/uri-list" && !out->hasFormat (format))
149  out->setData (format, sub->data (format));
150 
151  out->setUrls (out->urls () + sub->urls ());
152  }
153  }
154 
155  QMimeData* MergeModel::mimeData (const QModelIndexList& indexes) const
156  {
157  QMimeData *result = nullptr;
158 
159  for (const auto& index : indexes)
160  {
161  const auto& src = mapToSource (index);
162 
163  const auto subresult = src.model ()->mimeData ({ src });
164 
165  if (!subresult)
166  continue;
167 
168  if (!result)
169  result = subresult;
170  else
171  {
172  Merge (result, subresult);
173  delete subresult;
174  }
175  }
176 
177  return result;
178  }
179 
180  QModelIndex MergeModel::mapFromSource (const QModelIndex& sourceIndex) const
181  {
182  if (!sourceIndex.isValid ())
183  return {};
184 
185  QList<QModelIndex> hier;
186  auto parent = sourceIndex;
187  while (parent.isValid ())
188  {
189  hier.prepend (parent);
190  parent = parent.parent ();
191  }
192 
193  auto currentItem = Root_;
194  for (const auto& idx : hier)
195  {
196  currentItem = currentItem->FindChild (idx);
197  if (!currentItem)
198  {
199  qWarning () << Q_FUNC_INFO
200  << "no next item for"
201  << idx
202  << hier;
203  return {};
204  }
205  }
206 
207  return createIndex (currentItem->GetRow (), sourceIndex.column (), currentItem.get ());
208  }
209 
210  QModelIndex MergeModel::mapToSource (const QModelIndex& proxyIndex) const
211  {
212  const auto item = proxyIndex.isValid () ?
213  static_cast<ModelItem*> (proxyIndex.internalPointer ()) :
214  Root_.get ();
215 
216  const auto& srcIdx = item->GetIndex ();
217  return srcIdx.sibling (srcIdx.row (), proxyIndex.column ());
218  }
219 
220  void MergeModel::setSourceModel (QAbstractItemModel*)
221  {
222  throw std::runtime_error ("You should not set source model via setSourceModel()");
223  }
224 
225  void MergeModel::SetHeaders (const QStringList& headers)
226  {
227  Headers_ = headers;
228  }
229 
230  void MergeModel::AddModel (QAbstractItemModel *model)
231  {
232  if (!model)
233  return;
234 
235  Models_.push_back (model);
236 
237  connect (model,
238  SIGNAL (columnsAboutToBeInserted (const QModelIndex&, int, int)),
239  this,
240  SLOT (handleColumnsAboutToBeInserted (const QModelIndex&, int, int)));
241  connect (model,
242  SIGNAL (columnsAboutToBeRemoved (const QModelIndex&, int, int)),
243  this,
244  SLOT (handleColumnsAboutToBeRemoved (const QModelIndex&, int, int)));
245  connect (model,
246  SIGNAL (columnsInserted (const QModelIndex&, int, int)),
247  this,
248  SLOT (handleColumnsInserted (const QModelIndex&, int, int)));
249  connect (model,
250  SIGNAL (columnsRemoved (const QModelIndex&, int, int)),
251  this,
252  SLOT (handleColumnsRemoved (const QModelIndex&, int, int)));
253  connect (model,
254  SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
255  this,
256  SLOT (handleDataChanged (const QModelIndex&, const QModelIndex&)));
257  connect (model,
258  SIGNAL (layoutAboutToBeChanged ()),
259  this,
260  SLOT (handleModelAboutToBeReset ()));
261  connect (model,
262  SIGNAL (layoutChanged ()),
263  this,
264  SLOT (handleModelReset ()));
265  connect (model,
266  SIGNAL (modelAboutToBeReset ()),
267  this,
268  SLOT (handleModelAboutToBeReset ()));
269  connect (model,
270  SIGNAL (modelReset ()),
271  this,
272  SLOT (handleModelReset ()));
273  connect (model,
274  SIGNAL (rowsAboutToBeInserted (const QModelIndex&, int, int)),
275  this,
276  SLOT (handleRowsAboutToBeInserted (const QModelIndex&, int, int)));
277  connect (model,
278  SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
279  this,
280  SLOT (handleRowsAboutToBeRemoved (const QModelIndex&, int, int)));
281  connect (model,
282  SIGNAL (rowsInserted (const QModelIndex&, int, int)),
283  this,
284  SLOT (handleRowsInserted (const QModelIndex&, int, int)));
285  connect (model,
286  SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
287  this,
288  SLOT (handleRowsRemoved (const QModelIndex&, int, int)));
289 
290  if (const auto rc = model->rowCount ())
291  {
292  beginInsertRows ({}, rowCount ({}), rowCount ({}) + rc - 1);
293 
294  for (auto i = 0; i < rc; ++i)
295  Root_->AppendChild (model, model->index (i, 0), Root_);
296 
297  endInsertRows ();
298  }
299  }
300 
301  MergeModel::const_iterator MergeModel::FindModel (const QAbstractItemModel *model) const
302  {
303  return std::find (Models_.begin (), Models_.end (), model);
304  }
305 
306  MergeModel::iterator MergeModel::FindModel (const QAbstractItemModel *model)
307  {
308  return std::find (Models_.begin (), Models_.end (), model);
309  }
310 
311  void MergeModel::RemoveModel (QAbstractItemModel *model)
312  {
313  auto i = FindModel (model);
314 
315  if (i == Models_.end ())
316  {
317  qWarning () << Q_FUNC_INFO << "not found model" << model;
318  return;
319  }
320 
321  for (auto r = Root_->begin (); r != Root_->end (); )
322  if ((*r)->GetModel () == model)
323  {
324  const auto idx = std::distance (Root_->begin (), r);
325 
326  beginRemoveRows ({}, idx, idx);
327  r = Root_->EraseChild (r);
328  endRemoveRows ();
329  }
330  else
331  ++r;
332  }
333 
334  size_t MergeModel::Size () const
335  {
336  return Models_.size ();
337  }
338 
340  {
341  int result = 0;
342  for (auto i = Models_.begin (); i != it; ++i)
343  result += (*i)->rowCount ({});
344  return result;
345  }
346 
348  {
349  const auto child = Root_->GetChild (row);
350  const auto it = FindModel (child->GetModel ());
351 
352  if (starting)
353  *starting = GetStartingRow (it);
354 
355  return it;
356  }
357 
359  {
360  const auto child = Root_->GetChild (row);
361  const auto it = FindModel (child->GetModel ());
362 
363  if (starting)
364  *starting = GetStartingRow (it);
365 
366  return it;
367  }
368 
370  {
372  for (auto p : Models_)
373  if (p)
374  result << p.data ();
375  return result;
376  }
377 
378  void MergeModel::handleColumnsAboutToBeInserted (const QModelIndex&, int, int)
379  {
380  }
381 
382  void MergeModel::handleColumnsAboutToBeRemoved (const QModelIndex&, int, int)
383  {
384  qWarning () << "model" << sender ()
385  << "called handleColumnsAboutToBeRemoved, ignoring it";
386  }
387 
388  void MergeModel::handleColumnsInserted (const QModelIndex&, int, int)
389  {
390  }
391 
392  void MergeModel::handleColumnsRemoved (const QModelIndex&, int, int)
393  {
394  qWarning () << "model" << sender ()
395  << "called handleColumnsRemoved, ignoring it";
396  }
397 
398  void MergeModel::handleDataChanged (const QModelIndex& topLeft,
399  const QModelIndex& bottomRight)
400  {
401  emit dataChanged (mapFromSource (topLeft), mapFromSource (bottomRight));
402  }
403 
404  void MergeModel::handleRowsAboutToBeInserted (const QModelIndex& parent,
405  int first, int last)
406  {
407  const auto model = static_cast<QAbstractItemModel*> (sender ());
408 
409  const auto startingRow = parent.isValid () ?
410  0 :
411  GetStartingRow (FindModel (model));
412  beginInsertRows (mapFromSource (parent),
413  first + startingRow, last + startingRow);
414  }
415 
416  void MergeModel::handleRowsAboutToBeRemoved (const QModelIndex& parent,
417  int first, int last)
418  {
419  auto model = static_cast<QAbstractItemModel*> (sender ());
420 
421  const auto startingRow = parent.isValid () ?
422  0 :
423  GetStartingRow (FindModel (model));
424  beginRemoveRows (mapFromSource (parent),
425  first + startingRow, last + startingRow);
426 
427  const auto rawItem = parent.isValid () ?
428  static_cast<ModelItem*> (mapFromSource (parent).internalPointer ()) :
429  Root_.get ();
430  const auto& item = rawItem->shared_from_this ();
431 
432  auto it = item->EraseChildren (item->begin () + startingRow + first,
433  item->begin () + startingRow + last + 1);
434 
435  for ( ; it < item->end (); ++it)
436  {
437  if ((*it)->GetModel () != model)
438  break;
439 
440  (*it)->RefreshIndex (startingRow);
441  }
442  }
443 
444  void MergeModel::handleRowsInserted (const QModelIndex& parent, int first, int last)
445  {
446  const auto model = static_cast<QAbstractItemModel*> (sender ());
447 
448  const auto startingRow = parent.isValid () ?
449  0 :
450  GetStartingRow (FindModel (model));
451 
452  const auto rawItem = parent.isValid () ?
453  static_cast<ModelItem*> (mapFromSource (parent).internalPointer ()) :
454  Root_.get ();
455  const auto& item = rawItem->shared_from_this ();
456 
457  for ( ; first <= last; ++first)
458  {
459  const auto& srcIdx = model->index (first, 0, parent);
460  item->InsertChild (startingRow + first, model, srcIdx, item);
461  }
462 
463  ++last;
464  last += startingRow;
465 
466  for (int rc = item->GetRowCount (); last < rc; ++last)
467  {
468  const auto child = item->GetChild (last);
469  if (child->GetModel () != model)
470  break;
471 
472  child->RefreshIndex (startingRow);
473  }
474 
475  endInsertRows ();
476  }
477 
478  void MergeModel::handleRowsRemoved (const QModelIndex&, int, int)
479  {
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 QModelIndex mapToSource(const QModelIndex &index) const
Returns the source model index corresponding to the given index from the sorting filter model...
Definition: mergemodel.cpp:210
const_iterator GetModelForRow(int row, int *starting=0) const
Returns the model for the given row.
Definition: mergemodel.cpp:347
QAbstractItemModel * GetModel() const
Definition: modelitem.cpp:118
virtual void setSourceModel(QAbstractItemModel *)
Definition: mergemodel.cpp:220
virtual void handleModelAboutToBeReset()
Definition: mergemodel.cpp:483
virtual bool AcceptsRow(QAbstractItemModel *model, int row) const
Allows to filter rows from the resulting model.
Definition: mergemodel.cpp:511
virtual void handleColumnsRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:392
virtual QVariant headerData(int, Qt::Orientation, int=Qt::DisplayRole) const
Definition: mergemodel.cpp:60
virtual QVariant data(const QModelIndex &, int=Qt::DisplayRole) const
Definition: mergemodel.cpp:68
virtual void handleColumnsAboutToBeRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:382
virtual int columnCount(const QModelIndex &=QModelIndex()) const
Definition: mergemodel.cpp:49
virtual void handleColumnsAboutToBeInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:378
size_t Size() const
Returns the number of child models in the merger.
Definition: mergemodel.cpp:334
virtual void handleRowsRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:478
virtual void handleRowsAboutToBeInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:404
QList< QAbstractItemModel * > GetAllModels() const
Returns all models intalled into this one.
Definition: mergemodel.cpp:369
virtual Qt::ItemFlags flags(const QModelIndex &) const
Definition: mergemodel.cpp:85
models_t::const_iterator const_iterator
Definition: mergemodel.h:70
models_t::iterator iterator
Definition: mergemodel.h:69
MergeModel(const QStringList &headers, QObject *parent=0)
Constructs the merge model.
Definition: mergemodel.cpp:41
virtual void handleRowsAboutToBeRemoved(const QModelIndex &, int, int)
Definition: mergemodel.cpp:416
virtual void handleDataChanged(const QModelIndex &, const QModelIndex &)
Definition: mergemodel.cpp:398
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:301
virtual void handleRowsInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:444
virtual QModelIndex parent(const QModelIndex &) const
Definition: mergemodel.cpp:111
int GetStartingRow(const_iterator it) const
Finds starting row for the model pointed by it.
Definition: mergemodel.cpp:339
virtual int rowCount(const QModelIndex &=QModelIndex()) const
Definition: mergemodel.cpp:124
virtual QModelIndex mapFromSource(const QModelIndex &index) const
Returns the model index in the MergeModel given the index from the source model.
Definition: mergemodel.cpp:180
void AddModel(QAbstractItemModel *model)
Adds a model to the list of source models.
Definition: mergemodel.cpp:230
QStringList mimeTypes() const
Returns the union of MIME types of the models.
Definition: mergemodel.cpp:133
void SetHeaders(const QStringList &headers)
Sets the new headers for this model.
Definition: mergemodel.cpp:225
virtual void handleColumnsInserted(const QModelIndex &, int, int)
Definition: mergemodel.cpp:388
virtual QModelIndex index(int, int, const QModelIndex &=QModelIndex()) const
Definition: mergemodel.cpp:99
QMimeData * mimeData(const QModelIndexList &indices) const
Returns the MIME data for the given indices.
Definition: mergemodel.cpp:155
virtual void handleModelReset()
Definition: mergemodel.cpp:495
void RemoveModel(QAbstractItemModel *model)
Removes a model from the list of source models.
Definition: mergemodel.cpp:311