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