18 enum class Type : std::uint8_t
37 ,
Key_ { std::move (key) }
47 const auto pos = std::ranges::find_if (siblings, [
this] (
const auto& sibling) {
return sibling.get () ==
this; });
48 return pos - siblings.begin ();
53 return C_.emplaceBack (std::make_shared<FlatTreeItem> (
Type::Folder, *
this, key));
58 const auto& leaf =
C_.emplaceBack (std::make_shared<FlatTreeItem> (
Type::Item, *
this,
Key_));
66 FlatTreeItem* ToFlat (
const QModelIndex& idx)
68 return static_cast<FlatTreeItem*
> (idx.internalPointer ());
73 : QAbstractItemModel {
parent }
74 , Config_ { std::move (config) }
83 SourceModel_->columnCount ({}) :
89 if (!
index.isValid ())
92 const auto fti = ToFlat (
index);
96 const auto& source = fti->Index_;
97 return source.sibling (source.row (),
index.column ()).data (role);
101 return fti->Key_.isEmpty () ? Config_.UnkeyedLabel_ : fti->Key_;
111 return SourceModel_->headerData (section, orient, role);
116 if (!
index.isValid ())
117 return Qt::ItemIsDropEnabled;
119 if (
const auto fti = ToFlat (
index);
122 const auto& source = fti->Index_;
123 return source.sibling (source.row (),
index.column ()).flags ();
126 return Qt::ItemIsSelectable |
128 Qt::ItemIsDragEnabled |
129 Qt::ItemIsDropEnabled;
134 if (!
index.isValid ())
137 const auto fti = ToFlat (
index);
141 const auto& source = fti->Index_;
142 return SourceModel_->setData (source.sibling (source.row (),
index.column ()), value, role);
147 if (!hasIndex (row, column,
parent))
150 return createIndex (row, column, ToFlatOrRoot (
parent).C_.at (row).get ());
164 if (
index.column () > 0)
167 return ToFlatOrRoot (
index).C_.size ();
172 return SourceModel_ ?
173 SourceModel_->supportedDropActions () :
174 QAbstractItemModel::supportedDropActions ();
179 return SourceModel_ ?
180 SourceModel_->mimeTypes () :
181 QAbstractItemModel::mimeTypes ();
187 return QAbstractItemModel::mimeData (indexes);
189 QModelIndexList sourceIdxs;
190 for (
const auto&
index : indexes)
199 for (
const auto& subItem : item->C_)
200 sourceIdxs << subItem->Index_;
207 return SourceModel_->mimeData (sourceIdxs);
216 for (
const auto& format :
data->formats ())
217 modified.setData (format,
data->data (format));
220 modified.setData (Config_.DropFormat_, ToFlat (
parent)->Key_.toUtf8 ());
222 return SourceModel_->dropMimeData (&modified, action, -1, -1, QModelIndex ());
228 disconnect (SourceModel_,
233 SourceModel_ = model;
242 &QAbstractItemModel::headerDataChanged,
244 &QAbstractItemModel::headerDataChanged);
246 &QAbstractItemModel::dataChanged,
248 &FlatToFoldersProxyModel::HandleDataChanged);
250 &QAbstractItemModel::modelReset,
252 &FlatToFoldersProxyModel::HandleModelReset);
254 &QAbstractItemModel::rowsInserted,
256 &FlatToFoldersProxyModel::HandleRowsInserted);
258 &QAbstractItemModel::rowsAboutToBeRemoved,
260 &FlatToFoldersProxyModel::HandleRowsAboutToBeRemoved);
276 if (!proxy.isValid ())
279 const auto item = ToFlat (proxy);
290 for (
const auto& leaf : Items_.values (QPersistentModelIndex { source }))
291 result << IndexOf (*leaf);
297 const auto item = ToFlat (
index);
306 const auto& children = ToFlat (
index)->C_;
308 result.reserve (children.size ());
309 for (
const auto& child : children)
310 result << child->Index_;
319 result.reserve (children.size ());
320 for (
const auto& child : children)
321 result.push_back (child.data (role));
325 QStringList FlatToFoldersProxyModel::KeysOf (
const QModelIndex& index)
const
328 keys.removeDuplicates ();
334 FlatTreeItem_ptr FlatToFoldersProxyModel::FindFolder (
const QString& key)
const
336 const auto& folders = Root_->C_;
338 return pos == folders.end () ? nullptr : *pos;
343 if (
const auto& folder = FindFolder (key))
346 const auto row = Root_->C_.size ();
347 beginInsertRows ({}, row, row);
348 const auto& folder = Root_->AddFolder (key);
353 const FlatTreeItem& FlatToFoldersProxyModel::ToFlatOrRoot (
const QModelIndex& idx)
const
355 return idx.isValid () ? *ToFlat (idx) : *Root_;
358 QModelIndex FlatToFoldersProxyModel::IndexOf (
const FlatTreeItem& item,
int column)
const
360 return createIndex (item.Row (), column, &item);
363 void FlatToFoldersProxyModel::AddForKey (
const QString& key,
const QPersistentModelIndex& pidx)
365 const auto& folder = GetFolder (key);
366 const auto row = folder->C_.size ();
367 beginInsertRows (IndexOf (*folder), row, row);
368 Items_.insert (pidx, folder->AddLeaf (pidx));
374 const auto folder = leaf->Parent_;
375 const auto leafRow = leaf->Row ();
376 beginRemoveRows (IndexOf (*folder), leafRow, leafRow);
377 Items_.remove (leaf->Index_, leaf);
378 folder->C_.removeAt (leafRow);
381 if (!folder->C_.isEmpty ())
384 const auto folderRow = folder->Row ();
385 beginRemoveRows ({}, folderRow, folderRow);
386 Root_->C_.removeAt (folderRow);
390 void FlatToFoldersProxyModel::UpdateLeaves (
const QPersistentModelIndex& pidx,
const QList<int>& roles)
392 auto keys = KeysOf (pidx);
393 for (
const auto& leaf : Items_.values (pidx))
394 if (keys.removeOne (leaf->Key_))
395 emit dataChanged (IndexOf (*leaf), IndexOf (*leaf,
columnCount () - 1), roles);
399 for (
const auto& key : keys)
400 AddForKey (key, pidx);
403 void FlatToFoldersProxyModel::RenameFolder (
FlatTreeItem& folder,
const QString& key)
406 for (
const auto& leaf : folder.C_)
409 const auto&
index = IndexOf (folder);
410 emit dataChanged (
index,
index, { Qt::DisplayRole });
415 const auto& folder = FindFolder (old);
419 if (newKeys.size () == 1 && !FindFolder (newKeys.first ()))
421 RenameFolder (*folder, newKeys.first ());
425 for (
const auto& leaf :
auto { folder->C_ })
426 UpdateLeaves (leaf->Index_, {});
429 void FlatToFoldersProxyModel::HandleDataChanged (
const QModelIndex& topLeft,
const QModelIndex& bottomRight,
const QList<int>& roles)
431 for (
int row = topLeft.row (); row <= bottomRight.row (); ++row)
432 UpdateLeaves (topLeft.sibling (row, 0), roles);
435 void FlatToFoldersProxyModel::HandleModelReset ()
441 for (
int i = 0, size = SourceModel_->rowCount (); i < size; ++i)
443 const QPersistentModelIndex pidx { SourceModel_->index (i, 0) };
444 for (
const auto& key : KeysOf (pidx))
446 auto folder = FindFolder (key);
448 folder = Root_->AddFolder (key);
449 Items_.insert (pidx, folder->AddLeaf (pidx));
455 void FlatToFoldersProxyModel::HandleRowsInserted (
const QModelIndex&,
int start,
int end)
457 for (
int i = start; i <= end; ++i)
459 const QPersistentModelIndex pidx { SourceModel_->index (i, 0) };
460 for (
const auto& key : KeysOf (pidx))
461 AddForKey (key, pidx);
465 void FlatToFoldersProxyModel::HandleRowsAboutToBeRemoved (
const QModelIndex&,
int start,
int end)
467 for (
int i = start; i <= end; ++i)
468 for (
const auto& leaf : Items_.values (QPersistentModelIndex { SourceModel_->index (i, 0) }))
QList< QModelIndex > GetChildren(const QModelIndex &) const
QVariant headerData(int, Qt::Orientation, int) const override
Qt::DropActions supportedDropActions() const override
void ReplaceKey(const QString &old, const QStringList &newKeys)
Updates the folders after the key old was replaced with newKeys on every row that had it,...
int rowCount(const QModelIndex &={}) const override
QAbstractItemModel * GetSourceModel() const
void SetSourceModel(QAbstractItemModel *)
FlatToFoldersProxyModel(Config, QObject *=nullptr)
bool IsFolder(const QModelIndex &) const
QModelIndex parent(const QModelIndex &) const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
QList< QModelIndex > MapFromSource(const QModelIndex &) const
QList< QVariant > GetChildrenData(const QModelIndex &index, int role) const
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
int columnCount(const QModelIndex &={}) const override
QVariant data(const QModelIndex &, int=Qt::DisplayRole) const override
bool setData(const QModelIndex &, const QVariant &, int=Qt::EditRole) override
QModelIndex MapToSource(const QModelIndex &) const
QModelIndex index(int, int, const QModelIndex &={}) const override
Qt::ItemFlags flags(const QModelIndex &) const override
QStringList mimeTypes() const override
void InstallModelSanitizer(QAbstractItemModel &model)
Attaches a QAbstractItemModelTester to model for the rest of its lifetime.
std::shared_ptr< FlatTreeItem > FlatTreeItem_ptr
std::function< QStringList(const QModelIndex &)> KeysOf_
QPersistentModelIndex Index_
QList< FlatTreeItem_ptr > C_
FlatTreeItem *const Parent_
FlatTreeItem_ptr AddFolder(const QString &key)
FlatTreeItem_ptr AddLeaf(const QPersistentModelIndex &index)
FlatTreeItem(Type type, FlatTreeItem &parent, QString key)