LeechCraft  0.6.70-15082-g543737046d
Modular cross-platform feature rich live environment.
flattofoldersproxymodel.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 
10 #include <QSet>
11 #include <QMimeData>
12 #include <QItemSelectionRange>
14 #include <util/sll/prelude.h>
15 #include <interfaces/iinfo.h>
17 
18 namespace LC::Util
19 {
20  struct FlatTreeItem
21  {
24 
25  enum class Type
26  {
27  Root,
28  Folder,
29  Item
30  };
31 
33 
34  QPersistentModelIndex Index_;
35  QString Tag_;
36 
37  int Row () const
38  {
39  if (Parent_)
40  {
41  const auto& c = Parent_->C_;
42  for (int i = 0, size = c.size (); i < size; ++i)
43  if (c.at (i).get () == this)
44  return i;
45  }
46  return 0;
47  }
48  };
49 
50  FlatTreeItem* ToFlat (const QModelIndex& idx)
51  {
52  return static_cast<FlatTreeItem*> (idx.internalPointer ());
53  }
54 
56  : QAbstractItemModel { parent }
57  , TM_ { itm }
58  , Root_ { std::make_shared<FlatTreeItem> () }
59  {
60  }
61 
62  int FlatToFoldersProxyModel::columnCount (const QModelIndex&) const
63  {
64  return SourceModel_ ?
65  SourceModel_->columnCount ({}) :
66  0;
67  }
68 
69  QVariant FlatToFoldersProxyModel::data (const QModelIndex& index, int role) const
70  {
71  const auto fti = ToFlat (index);
72 
73  if (fti->Type_ == FlatTreeItem::Type::Item)
74  {
75  const auto& source = fti->Index_;
76  return source.sibling (source.row (), index.column ()).data (role);
77  }
78 
79  if (fti->Type_ == FlatTreeItem::Type::Folder && index.column () == 0)
80  {
81  switch (role)
82  {
83  case Qt::DisplayRole:
84  {
85  if (fti->Tag_.isEmpty ())
86  return tr ("untagged");
87 
88  const auto& ut = TM_->GetTag (fti->Tag_);
89  return ut.isEmpty () ? tr ("<unknown tag>") : ut;
90  }
91  case RoleTags:
92  return fti->Tag_;
93  default:
94  return {};
95  }
96  }
97 
98  return {};
99  }
100 
101  QVariant FlatToFoldersProxyModel::headerData (int section, Qt::Orientation orient, int role) const
102  {
103  if (!SourceModel_)
104  return {};
105 
106  return SourceModel_->headerData (section, orient, role);
107  }
108 
109  Qt::ItemFlags FlatToFoldersProxyModel::flags (const QModelIndex& index) const
110  {
111  if (const auto fti = ToFlat (index);
112  fti && fti->Type_ == FlatTreeItem::Type::Item)
113  return fti->Index_.flags ();
114 
115  return Qt::ItemIsSelectable |
116  Qt::ItemIsEnabled |
117  Qt::ItemIsDragEnabled |
118  Qt::ItemIsDropEnabled;
119  }
120 
121  QModelIndex FlatToFoldersProxyModel::index (int row, int column, const QModelIndex& parent) const
122  {
123  if (!hasIndex (row, column, parent))
124  return {};
125 
126  const auto& fti = ToFlatOrRoot (parent);
127  return fti.Type_ == FlatTreeItem::Type::Item ?
128  QModelIndex {} :
129  createIndex (row, column, fti.C_.at (row).get ());
130  }
131 
132  QModelIndex FlatToFoldersProxyModel::parent (const QModelIndex& index) const
133  {
134  const auto& parent = ToFlatOrRoot (index).Parent_;
135  if (!parent || parent->Type_ == FlatTreeItem::Type::Root)
136  return {};
137 
138  return createIndex (parent->Row (), 0, parent.get ());
139  }
140 
141  int FlatToFoldersProxyModel::rowCount (const QModelIndex& index) const
142  {
143  return ToFlatOrRoot (index).C_.size ();
144  }
145 
146  Qt::DropActions FlatToFoldersProxyModel::supportedDropActions() const
147  {
148  return SourceModel_ ?
149  SourceModel_->supportedDropActions () :
150  QAbstractItemModel::supportedDropActions ();
151  }
152 
153  QStringList FlatToFoldersProxyModel::mimeTypes() const
154  {
155  return SourceModel_ ?
156  SourceModel_->mimeTypes () :
157  QAbstractItemModel::mimeTypes ();
158  }
159 
160  QMimeData* FlatToFoldersProxyModel::mimeData (const QModelIndexList& indexes) const
161  {
162  if (!SourceModel_)
163  return QAbstractItemModel::mimeData (indexes);
164 
165  QModelIndexList sourceIdxs;
166  for (const auto& index : indexes)
167  {
168  auto item = static_cast<FlatTreeItem*> (index.internalPointer ());
169  switch (item->Type_)
170  {
172  sourceIdxs << MapToSource (index);
173  break;
175  for (const auto& subItem : item->C_)
176  sourceIdxs << subItem->Index_;
177  break;
178  default:
179  break;
180  }
181  }
182 
183  return SourceModel_->mimeData (sourceIdxs);
184  }
185 
186  bool FlatToFoldersProxyModel::dropMimeData (const QMimeData* data, Qt::DropAction action, int, int, const QModelIndex& parent)
187  {
188  if (!SourceModel_)
189  return false;
190 
191  QMimeData modified;
192  for (const auto& format : data->formats ())
193  modified.setData (format, data->data (format));
194 
195  switch (const auto ptr = static_cast<FlatTreeItem*> (parent.internalPointer ());
196  ptr->Type_)
197  {
200  modified.setData (QStringLiteral ("x-leechcraft/tag"), ptr->Tag_.toLatin1 ());
201  break;
202  default:
203  break;
204  }
205 
206  return SourceModel_->dropMimeData (&modified, action, -1, -1, QModelIndex ());
207  }
208 
209  void FlatToFoldersProxyModel::SetSourceModel (QAbstractItemModel *model)
210  {
211  if (SourceModel_)
212  disconnect (SourceModel_,
213  nullptr,
214  this,
215  nullptr);
216 
217  SourceModel_ = model;
218 
219  if (model)
220  {
221  // We don't support changing columns (yet) so don't connect
222  // to columns* signals.
223  connect (model,
224  &QAbstractItemModel::headerDataChanged,
225  this,
226  &QAbstractItemModel::headerDataChanged);
227  connect (model,
228  &QAbstractItemModel::dataChanged,
229  this,
230  &FlatToFoldersProxyModel::HandleDataChanged);
231  connect (model,
232  &QAbstractItemModel::layoutAboutToBeChanged,
233  this,
234  &QAbstractItemModel::layoutAboutToBeChanged);
235  connect (model,
236  &QAbstractItemModel::layoutChanged,
237  this,
238  &QAbstractItemModel::layoutChanged);
239  connect (model,
240  &QAbstractItemModel::modelReset,
241  this,
242  &FlatToFoldersProxyModel::HandleModelReset);
243  connect (model,
244  &QAbstractItemModel::rowsInserted,
245  this,
246  &FlatToFoldersProxyModel::HandleRowsInserted);
247  connect (model,
248  &QAbstractItemModel::rowsAboutToBeRemoved,
249  this,
250  &FlatToFoldersProxyModel::HandleRowsAboutToBeRemoved);
251  }
252 
253  HandleModelReset ();
254  }
255 
256  QAbstractItemModel* FlatToFoldersProxyModel::GetSourceModel () const
257  {
258  return SourceModel_;
259  }
260 
261  QModelIndex FlatToFoldersProxyModel::MapToSource (const QModelIndex& proxy) const
262  {
263  if (!GetSourceModel ())
264  return {};
265 
266  if (!proxy.isValid ())
267  return {};
268 
269  const auto item = ToFlat (proxy);
270 
271  if (item->Type_ != FlatTreeItem::Type::Item)
272  return {};
273 
274  return item->Index_;
275  }
276 
277  QList<QModelIndex> FlatToFoldersProxyModel::MapFromSource (const QModelIndex& source) const
278  {
279  auto tags = source.data (RoleTags).toStringList ();
280  if (tags.isEmpty ())
281  tags << QString ();
282 
284  for (const auto& tag : tags)
285  {
286  const auto& folder = FindFolder (tag);
287  if (!folder)
288  {
289  qWarning () << Q_FUNC_INFO
290  << "could not find folder for tag"
291  << tag
292  << GetSourceModel ();
293  continue;
294  }
295 
296  const auto& folderIdx = index (folder->Row (), 0, {});
297 
298  for (int i = 0; i < folder->C_.size (); ++i)
299  {
300  const auto& child = folder->C_.at (i);
301  if (child->Index_ != source)
302  continue;
303 
304  result << index (i, 0, folderIdx);
305  break;
306  }
307  }
308  return result;
309  }
310 
311  FlatTreeItem_ptr FlatToFoldersProxyModel::FindFolder (const QString& tag) const
312  {
313  for (const auto& item : Root_->C_)
314  if (item->Tag_ == tag)
315  return item;
316 
317  return {};
318  }
319 
320  FlatTreeItem_ptr FlatToFoldersProxyModel::GetFolder (const QString& tag)
321  {
322  auto& c = Root_->C_;
323  for (const auto& item : c)
324  if (item->Tag_ == tag)
325  return item;
326 
327  const auto& item = std::make_shared<FlatTreeItem> ();
328  item->Type_ = FlatTreeItem::Type::Folder;
329  item->Tag_ = tag;
330  item->Parent_ = Root_;
331 
332  int size = c.size ();
333  beginInsertRows (QModelIndex (), size, size);
334  c.append (item);
335  endInsertRows ();
336 
337  return item;
338  }
339 
340  const FlatTreeItem& FlatToFoldersProxyModel::ToFlatOrRoot (const QModelIndex& idx) const
341  {
342  return idx.isValid () ? *ToFlat (idx) : *Root_;
343  }
344 
345  void FlatToFoldersProxyModel::HandleRowInserted (int i)
346  {
347  const auto& idx = SourceModel_->index (i, 0);
348 
349  auto tags = idx.data (RoleTags).toStringList ();
350 
351  if (tags.isEmpty ())
352  tags << QString ();
353 
354  QPersistentModelIndex pidx (idx);
355 
356  for (const auto& tag : qAsConst (tags))
357  AddForTag (tag, pidx);
358  }
359 
360  void FlatToFoldersProxyModel::HandleRowRemoved (int i)
361  {
362  QAbstractItemModel *model = SourceModel_;
363  QModelIndex idx = model->index (i, 0);
364 
365  QStringList tags = idx.data (RoleTags).toStringList ();
366 
367  if (tags.isEmpty ())
368  tags << QString ();
369 
370  QPersistentModelIndex pidx (idx);
371 
372  for (const auto& tag : tags)
373  RemoveFromTag (tag, pidx);
374  }
375 
376  void FlatToFoldersProxyModel::AddForTag (const QString& tag,
377  const QPersistentModelIndex& pidx)
378  {
379  FlatTreeItem_ptr folder = GetFolder (tag);
380 
381  const auto& item = std::make_shared<FlatTreeItem> ();
382  item->Type_ = FlatTreeItem::Type::Item;
383  item->Index_ = pidx;
384  item->Parent_ = folder;
385  item->Tag_ = tag;
386 
387  int size = folder->C_.size ();
388  QModelIndex iidx = index (Root_->C_.indexOf (folder), 0);
389  beginInsertRows (iidx, size, size);
390  folder->C_.append (item);
391  Items_.insert (pidx, item);
392  endInsertRows ();
393  }
394 
395  void FlatToFoldersProxyModel::RemoveFromTag (const QString& tag,
396  const QPersistentModelIndex& pidx)
397  {
398  const auto& folder = GetFolder (tag);
399  auto& c = folder->C_;
400  int findex = Root_->C_.indexOf (folder);
401  for (int i = 0, size = c.size ();
402  i < size; ++i)
403  {
404  if (c.at (i)->Index_ != pidx)
405  continue;
406 
407  beginRemoveRows (index (findex, 0), i, i);
408  Items_.remove (pidx, c.at (i));
409  c.removeAt (i);
410  endRemoveRows ();
411  break;
412  }
413 
414  if (c.isEmpty ())
415  {
416  beginRemoveRows (QModelIndex (), findex, findex);
417  Root_->C_.removeAt (findex);
418  endRemoveRows ();
419  }
420  }
421 
422  void FlatToFoldersProxyModel::HandleChanged (const QModelIndex& idx)
423  {
424  auto newTags = Util::AsSet (idx.data (RoleTags).toStringList ());
425  if (newTags.isEmpty ())
426  newTags << QString {};
427 
428  QPersistentModelIndex pidx (idx);
429 
430  const auto& oldTags = Util::MapAs<QSet> (Items_.values (pidx), [] (const auto& item) { return item->Tag_; });
431 
432  const auto added = QSet<QString> (newTags).subtract (oldTags);
433  const auto removed = QSet<QString> (oldTags).subtract (newTags);
434  const auto changed = QSet<QString> (newTags).intersect (oldTags);
435 
436  for (const auto& ch : changed)
437  {
438  FlatTreeItem_ptr folder = GetFolder (ch);
439 
440  QList<FlatTreeItem_ptr>& c = folder->C_;
441  int findex = Root_->C_.indexOf (folder);
442  QModelIndex fmi = index (findex, 0);
443  for (int i = 0, size = c.size ();
444  i < size; ++i)
445  {
446  if (c.at (i)->Index_ != pidx)
447  continue;
448 
449  emit dataChanged (index (i, 0, fmi),
450  index (i, columnCount () - 1, fmi));
451  break;
452  }
453  }
454 
455  for (const auto& rem : removed)
456  RemoveFromTag (rem, pidx);
457 
458  for (const auto& add : added)
459  AddForTag (add, pidx);
460  }
461 
462  void FlatToFoldersProxyModel::HandleDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
463  {
464  const QItemSelectionRange range
465  {
466  topLeft.sibling (topLeft.row (), 0),
467  bottomRight.sibling (bottomRight.row (), 0)
468  };
469  for (const auto& index : range.indexes ())
470  HandleChanged (index);
471  }
472 
473  void FlatToFoldersProxyModel::HandleModelReset ()
474  {
475  if (const int size = Root_->C_.size ())
476  {
477  beginRemoveRows (QModelIndex (), 0, size - 1);
478  Root_->C_.clear ();
479  Items_.clear ();
480  endRemoveRows ();
481  }
482 
483  if (SourceModel_)
484  for (int i = 0, size = SourceModel_->rowCount (); i < size; ++i)
485  HandleRowInserted (i);
486  }
487 
488  void FlatToFoldersProxyModel::HandleRowsInserted (const QModelIndex&, int start, int end)
489  {
490  for (int i = start; i <= end; ++i)
491  HandleRowInserted (i);
492  }
493 
494  void FlatToFoldersProxyModel::HandleRowsAboutToBeRemoved (const QModelIndex&, int start, int end)
495  {
496  for (int i = start; i <= end; ++i)
497  HandleRowRemoved (i);
498  }
499 }
500 
LC::Util::FlatToFoldersProxyModel::mimeData
QMimeData * mimeData(const QModelIndexList &indexes) const override
Definition: flattofoldersproxymodel.cpp:166
LC::Util::FlatToFoldersProxyModel::MapToSource
QModelIndex MapToSource(const QModelIndex &) const
Definition: flattofoldersproxymodel.cpp:267
ITagsManager::GetTag
virtual QString GetTag(tag_id id) const =0
Returns the tag with the given id.
LC::Util::FlatToFoldersProxyModel::SetSourceModel
void SetSourceModel(QAbstractItemModel *)
Definition: flattofoldersproxymodel.cpp:215
LC::Util::FlatTreeItem::Type
Type
Definition: flattofoldersproxymodel.cpp:37
LC::Util::FlatToFoldersProxyModel::rowCount
int rowCount(const QModelIndex &={}) const override
Definition: flattofoldersproxymodel.cpp:147
QList< FlatTreeItem_ptr >
LC::Util::FlatTreeItem::Tag_
QString Tag_
Definition: flattofoldersproxymodel.cpp:47
LC::Util
Definition: icoreproxy.h:33
LC::Util::FlatToFoldersProxyModel::headerData
QVariant headerData(int, Qt::Orientation, int) const override
Definition: flattofoldersproxymodel.cpp:107
LC::Util::FlatTreeItem::Parent_
FlatTreeItem_ptr Parent_
Definition: flattofoldersproxymodel.cpp:35
LC::Util::FlatToFoldersProxyModel::parent
QModelIndex parent(const QModelIndex &) const override
Definition: flattofoldersproxymodel.cpp:138
itagsmanager.h
iinfo.h
LC::Util::FlatToFoldersProxyModel::index
QModelIndex index(int, int, const QModelIndex &={}) const override
Definition: flattofoldersproxymodel.cpp:127
LC::Util::FlatToFoldersProxyModel::mimeTypes
QStringList mimeTypes() const override
Definition: flattofoldersproxymodel.cpp:159
LC::Util::AsSet
auto AsSet(const T &cont)
Definition: containerconversions.h:22
ITagsManager
Tags manager's interface.
Definition: itagsmanager.h:22
LC::Util::FlatTreeItem_ptr
std::shared_ptr< FlatTreeItem > FlatTreeItem_ptr
Definition: flattofoldersproxymodel.h:22
LC::Util::ToFlat
FlatTreeItem * ToFlat(const QModelIndex &idx)
Definition: flattofoldersproxymodel.cpp:56
LC::Util::FlatTreeItem
Definition: flattofoldersproxymodel.cpp:26
LC::Util::FlatToFoldersProxyModel::supportedDropActions
Qt::DropActions supportedDropActions() const override
Definition: flattofoldersproxymodel.cpp:152
LC::RoleTags
@ RoleTags
Definition: structures.h:178
LC::Util::FlatTreeItem::Type::Item
@ Item
LC::Util::FlatTreeItem::Type_
Type Type_
Definition: flattofoldersproxymodel.cpp:44
flattofoldersproxymodel.h
LC::Util::FlatTreeItem::Type::Root
@ Root
LC::Util::FlatToFoldersProxyModel::FlatToFoldersProxyModel
FlatToFoldersProxyModel(const ITagsManager *, QObject *=nullptr)
Definition: flattofoldersproxymodel.cpp:61
LC::Util::FlatTreeItem::Type::Folder
@ Folder
prelude.h
containerconversions.h
LC::Util::FlatToFoldersProxyModel::columnCount
int columnCount(const QModelIndex &={}) const override
Definition: flattofoldersproxymodel.cpp:68
LC::Util::FlatToFoldersProxyModel::data
QVariant data(const QModelIndex &, int=Qt::DisplayRole) const override
Definition: flattofoldersproxymodel.cpp:75
LC::Util::FlatTreeItem::Index_
QPersistentModelIndex Index_
Definition: flattofoldersproxymodel.cpp:46
LC::Util::FlatTreeItem::C_
QList< FlatTreeItem_ptr > C_
Definition: flattofoldersproxymodel.cpp:34
LC::Util::FlatToFoldersProxyModel::dropMimeData
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Definition: flattofoldersproxymodel.cpp:192
LC::Util::FlatToFoldersProxyModel::MapFromSource
QList< QModelIndex > MapFromSource(const QModelIndex &) const
Definition: flattofoldersproxymodel.cpp:283
LC::Util::FlatToFoldersProxyModel::GetSourceModel
QAbstractItemModel * GetSourceModel() const
Definition: flattofoldersproxymodel.cpp:262
LC::Util::FlatTreeItem::Row
int Row() const
Definition: flattofoldersproxymodel.cpp:49
LC::Util::FlatToFoldersProxyModel::flags
Qt::ItemFlags flags(const QModelIndex &) const override
Definition: flattofoldersproxymodel.cpp:115