forked from qt-creator/qt-creator
9124833a63introduced an optional parameter to removeChildren to suppress signal emitting, anda61f9162f1reimplemented that in a ABI compatible way with a new removeChildrenSilently method instead. The code now uses removeChildrenSilently, so the parameter to removeChildren is no longer needed. Change-Id: I8411d80f90e83b8746c2553dddb41c3f40a2bfc7 Reviewed-by: David Faure <david.faure@kdab.com> Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
367 lines
13 KiB
C++
367 lines
13 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#pragma once
|
|
|
|
#include "utils_global.h"
|
|
|
|
#include "indexedcontainerproxyconstiterator.h"
|
|
|
|
#include <QSortFilterProxyModel>
|
|
|
|
#include <functional>
|
|
|
|
namespace Utils {
|
|
|
|
class BaseTreeModel;
|
|
|
|
class QTCREATOR_UTILS_EXPORT TreeItem
|
|
{
|
|
public:
|
|
TreeItem();
|
|
virtual ~TreeItem();
|
|
|
|
virtual QVariant data(int column, int role) const;
|
|
virtual bool setData(int column, const QVariant &data, int role);
|
|
virtual Qt::ItemFlags flags(int column) const;
|
|
|
|
virtual bool hasChildren() const;
|
|
virtual bool canFetchMore() const;
|
|
virtual void fetchMore() {}
|
|
|
|
TreeItem *parent() const { return m_parent; }
|
|
|
|
void prependChild(TreeItem *item);
|
|
void appendChild(TreeItem *item);
|
|
void insertChild(int pos, TreeItem *item);
|
|
void insertOrderedChild(TreeItem *item,
|
|
const std::function<bool(const TreeItem *, const TreeItem *)> &cmp);
|
|
|
|
void removeChildAt(int pos);
|
|
void removeChildren();
|
|
void removeChildrenSilently();
|
|
void sortChildren(const std::function<bool(const TreeItem *, const TreeItem *)> &cmp);
|
|
void update();
|
|
void updateAll();
|
|
void updateColumn(int column);
|
|
void expand();
|
|
void collapse();
|
|
TreeItem *firstChild() const;
|
|
TreeItem *lastChild() const;
|
|
int level() const;
|
|
|
|
using const_iterator = QList<TreeItem *>::const_iterator;
|
|
using value_type = TreeItem *;
|
|
int childCount() const { return m_children.size(); }
|
|
int indexInParent() const;
|
|
TreeItem *childAt(int index) const;
|
|
int indexOf(const TreeItem *item) const;
|
|
const_iterator begin() const { return m_children.begin(); }
|
|
const_iterator end() const { return m_children.end(); }
|
|
QModelIndex index() const;
|
|
QAbstractItemModel *model() const;
|
|
|
|
void forSelectedChildren(const std::function<bool(TreeItem *)> &pred) const;
|
|
void forAllChildren(const std::function<void(TreeItem *)> &pred) const;
|
|
TreeItem *findAnyChild(const std::function<bool(TreeItem *)> &pred) const;
|
|
// like findAnyChild() but processes children in exact reverse order
|
|
// (bottom to top, most inner children first)
|
|
TreeItem *reverseFindAnyChild(const std::function<bool(TreeItem *)> &pred) const;
|
|
|
|
// Levels are 1-based: Child at Level 1 is an immediate child.
|
|
void forChildrenAtLevel(int level, const std::function<void(TreeItem *)> &pred) const;
|
|
TreeItem *findChildAtLevel(int level, const std::function<bool(TreeItem *)> &pred) const;
|
|
|
|
private:
|
|
TreeItem(const TreeItem &) = delete;
|
|
void operator=(const TreeItem &) = delete;
|
|
|
|
void clear();
|
|
void removeItemAt(int pos);
|
|
void propagateModel(BaseTreeModel *m);
|
|
|
|
TreeItem *m_parent = nullptr; // Not owned.
|
|
BaseTreeModel *m_model = nullptr; // Not owned.
|
|
QList<TreeItem *> m_children; // Owned.
|
|
friend class BaseTreeModel;
|
|
};
|
|
|
|
// A TreeItem with children all of the same type.
|
|
template <class ChildType, class ParentType = TreeItem>
|
|
class TypedTreeItem : public TreeItem
|
|
{
|
|
public:
|
|
ChildType *childAt(int index) const { return static_cast<ChildType *>(TreeItem::childAt(index)); }
|
|
|
|
void sortChildren(const std::function<bool(const ChildType *, const ChildType *)> &lessThan)
|
|
{
|
|
return TreeItem::sortChildren([lessThan](const TreeItem *a, const TreeItem *b) {
|
|
return lessThan(static_cast<const ChildType *>(a), static_cast<const ChildType *>(b));
|
|
});
|
|
}
|
|
|
|
using value_type = ChildType *;
|
|
using const_iterator = IndexedContainerProxyConstIterator<TypedTreeItem>;
|
|
using size_type = int;
|
|
|
|
ChildType *operator[](int index) const { return childAt(index); }
|
|
int size() const { return childCount(); }
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
|
|
template <typename Predicate>
|
|
void forAllChildren(const Predicate &pred) const {
|
|
const auto pred0 = [pred](TreeItem *treeItem) { pred(static_cast<ChildType *>(treeItem)); };
|
|
TreeItem::forAllChildren(pred0);
|
|
}
|
|
|
|
template <typename Predicate>
|
|
void forFirstLevelChildren(Predicate pred) const {
|
|
const auto pred0 = [pred](TreeItem *treeItem) { pred(static_cast<ChildType *>(treeItem)); };
|
|
TreeItem::forChildrenAtLevel(1, pred0);
|
|
}
|
|
|
|
template <typename Predicate>
|
|
ChildType *findFirstLevelChild(Predicate pred) const {
|
|
const auto pred0 = [pred](TreeItem *treeItem) { return pred(static_cast<ChildType *>(treeItem)); };
|
|
return static_cast<ChildType *>(TreeItem::findChildAtLevel(1, pred0));
|
|
}
|
|
|
|
ParentType *parent() const {
|
|
return static_cast<ParentType *>(TreeItem::parent());
|
|
}
|
|
|
|
void insertOrderedChild(ChildType *item, const std::function<bool(const ChildType *, const ChildType *)> &cmp)
|
|
{
|
|
const auto cmp0 = [cmp](const TreeItem *lhs, const TreeItem *rhs) {
|
|
return cmp(static_cast<const ChildType *>(lhs), static_cast<const ChildType *>(rhs));
|
|
};
|
|
TreeItem::insertOrderedChild(item, cmp0);
|
|
}
|
|
|
|
ChildType *findAnyChild(const std::function<bool(TreeItem *)> &pred) const
|
|
{
|
|
return static_cast<ChildType *>(TreeItem::findAnyChild(pred));
|
|
}
|
|
|
|
ChildType *reverseFindAnyChild(const std::function<bool(TreeItem *)> &pred) const
|
|
{
|
|
return static_cast<ChildType *>(TreeItem::reverseFindAnyChild(pred));
|
|
}
|
|
};
|
|
|
|
class QTCREATOR_UTILS_EXPORT StaticTreeItem : public TreeItem
|
|
{
|
|
public:
|
|
StaticTreeItem(const QStringList &displays);
|
|
StaticTreeItem(const QString &display);
|
|
StaticTreeItem(const QStringList &displays, const QStringList &toolTips);
|
|
|
|
QVariant data(int column, int role) const override;
|
|
Qt::ItemFlags flags(int column) const override;
|
|
|
|
private:
|
|
QStringList m_displays;
|
|
QStringList m_toolTips;
|
|
};
|
|
|
|
// A general purpose multi-level model where each item can have its
|
|
// own (TreeItem-derived) type.
|
|
class QTCREATOR_UTILS_EXPORT BaseTreeModel : public QAbstractItemModel
|
|
{
|
|
Q_OBJECT
|
|
|
|
protected:
|
|
explicit BaseTreeModel(QObject *parent = nullptr);
|
|
explicit BaseTreeModel(TreeItem *root, QObject *parent = nullptr);
|
|
~BaseTreeModel() override;
|
|
|
|
void setHeader(const QStringList &displays);
|
|
void setHeaderToolTip(const QStringList &tips);
|
|
void clear();
|
|
|
|
TreeItem *rootItem() const;
|
|
void setRootItem(TreeItem *item); // resets the model
|
|
void setRootItemInternal(TreeItem *item);
|
|
TreeItem *itemForIndex(const QModelIndex &) const;
|
|
QModelIndex indexForItem(const TreeItem *needle) const;
|
|
|
|
int rowCount(const QModelIndex &idx = QModelIndex()) const override;
|
|
int columnCount(const QModelIndex &idx) const override;
|
|
|
|
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
|
|
QVariant data(const QModelIndex &idx, int role) const override;
|
|
QModelIndex index(int, int, const QModelIndex &idx = QModelIndex()) const override;
|
|
QModelIndex parent(const QModelIndex &idx) const override;
|
|
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override;
|
|
Qt::ItemFlags flags(const QModelIndex &idx) const override;
|
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
|
bool hasChildren(const QModelIndex &idx) const override;
|
|
|
|
bool canFetchMore(const QModelIndex &idx) const override;
|
|
void fetchMore(const QModelIndex &idx) override;
|
|
|
|
TreeItem *takeItem(TreeItem *item); // item is not destroyed.
|
|
void destroyItem(TreeItem *item); // item is destroyed.
|
|
|
|
signals:
|
|
void requestExpansion(const QModelIndex &);
|
|
void requestCollapse(const QModelIndex &);
|
|
|
|
protected:
|
|
friend class TreeItem;
|
|
|
|
TreeItem *m_root; // Owned.
|
|
QStringList m_header;
|
|
QStringList m_headerToolTip;
|
|
int m_columnCount;
|
|
};
|
|
|
|
namespace Internal {
|
|
|
|
// SelectType<N, T0, T1, T2, ...> selects the Nth type from the list
|
|
// If there are not enough types in the list, 'TreeItem' is used.
|
|
template<int N, typename ...All> struct SelectType;
|
|
|
|
template<int N, typename First, typename ...Rest> struct SelectType<N, First, Rest...>
|
|
{
|
|
using Type = typename SelectType<N - 1, Rest...>::Type;
|
|
};
|
|
|
|
template<typename First, typename ...Rest> struct SelectType<0, First, Rest...>
|
|
{
|
|
using Type = First;
|
|
};
|
|
|
|
template<int N> struct SelectType<N>
|
|
{
|
|
using Type = TreeItem;
|
|
};
|
|
|
|
// BestItem<T0, T1, T2, ... > selects T0 if all types are equal and 'TreeItem' otherwise
|
|
template<typename ...All> struct BestItemType;
|
|
|
|
template<typename First, typename Second, typename ...Rest> struct BestItemType<First, Second, Rest...>
|
|
{
|
|
using Type = TreeItem;
|
|
};
|
|
|
|
template<typename First, typename ...Rest> struct BestItemType<First, First, Rest...>
|
|
{
|
|
using Type = typename BestItemType<First, Rest...>::Type;
|
|
};
|
|
|
|
template<typename First> struct BestItemType<First>
|
|
{
|
|
using Type = First;
|
|
};
|
|
|
|
template<> struct BestItemType<>
|
|
{
|
|
using Type = TreeItem;
|
|
|
|
};
|
|
|
|
} // namespace Internal
|
|
|
|
// A multi-level model with possibly uniform types per level.
|
|
template <typename ...LevelItemTypes>
|
|
class TreeModel : public BaseTreeModel
|
|
{
|
|
public:
|
|
using RootItem = typename Internal::SelectType<0, LevelItemTypes...>::Type;
|
|
using BestItem = typename Internal::BestItemType<LevelItemTypes...>::Type;
|
|
|
|
explicit TreeModel(QObject *parent = nullptr) : BaseTreeModel(new RootItem, parent) {}
|
|
explicit TreeModel(RootItem *root, QObject *parent = nullptr) : BaseTreeModel(root, parent) {}
|
|
|
|
using BaseTreeModel::canFetchMore;
|
|
using BaseTreeModel::clear;
|
|
using BaseTreeModel::columnCount;
|
|
using BaseTreeModel::data;
|
|
using BaseTreeModel::destroyItem;
|
|
using BaseTreeModel::fetchMore;
|
|
using BaseTreeModel::hasChildren;
|
|
using BaseTreeModel::index;
|
|
using BaseTreeModel::indexForItem;
|
|
using BaseTreeModel::parent;
|
|
using BaseTreeModel::rowCount;
|
|
using BaseTreeModel::setData;
|
|
using BaseTreeModel::setHeader;
|
|
using BaseTreeModel::setHeaderToolTip;
|
|
using BaseTreeModel::takeItem;
|
|
|
|
template <int Level, class Predicate>
|
|
void forItemsAtLevel(const Predicate &pred) const {
|
|
using ItemType = typename Internal::SelectType<Level, LevelItemTypes...>::Type;
|
|
const auto pred0 = [pred](TreeItem *treeItem) { pred(static_cast<ItemType *>(treeItem)); };
|
|
m_root->forChildrenAtLevel(Level, pred0);
|
|
}
|
|
|
|
template <int Level, class Predicate>
|
|
typename Internal::SelectType<Level, LevelItemTypes...>::Type *findItemAtLevel(const Predicate &pred) const {
|
|
using ItemType = typename Internal::SelectType<Level, LevelItemTypes...>::Type;
|
|
const auto pred0 = [pred](TreeItem *treeItem) { return pred(static_cast<ItemType *>(treeItem)); };
|
|
return static_cast<ItemType *>(m_root->findChildAtLevel(Level, pred0));
|
|
}
|
|
|
|
RootItem *rootItem() const {
|
|
return static_cast<RootItem *>(BaseTreeModel::rootItem());
|
|
}
|
|
|
|
template<int Level>
|
|
typename Internal::SelectType<Level, LevelItemTypes...>::Type *itemForIndexAtLevel(
|
|
const QModelIndex &idx) const {
|
|
TreeItem *item = BaseTreeModel::itemForIndex(idx);
|
|
return item && item->level() == Level
|
|
? static_cast<typename Internal::SelectType<Level, LevelItemTypes...>::Type *>(item)
|
|
: nullptr;
|
|
}
|
|
|
|
BestItem *nonRootItemForIndex(const QModelIndex &idx) const {
|
|
TreeItem *item = BaseTreeModel::itemForIndex(idx);
|
|
return item && item->parent() ? static_cast<BestItem *>(item) : nullptr;
|
|
}
|
|
|
|
template <class Predicate>
|
|
BestItem *findNonRootItem(const Predicate &pred) const {
|
|
const auto pred0 = [pred](TreeItem *treeItem) -> bool { return pred(static_cast<BestItem *>(treeItem)); };
|
|
return static_cast<BestItem *>(m_root->findAnyChild(pred0));
|
|
}
|
|
|
|
template <class Predicate>
|
|
void forSelectedItems(const Predicate &pred) const {
|
|
const auto pred0 = [pred](TreeItem *treeItem) -> bool { return pred(static_cast<BestItem *>(treeItem)); };
|
|
m_root->forSelectedChildren(pred0);
|
|
}
|
|
|
|
template <class Predicate>
|
|
void forAllItems(const Predicate &pred) const {
|
|
const auto pred0 = [pred](TreeItem *treeItem) -> void { pred(static_cast<BestItem *>(treeItem)); };
|
|
m_root->forAllChildren(pred0);
|
|
}
|
|
|
|
BestItem *itemForIndex(const QModelIndex &idx) const {
|
|
return static_cast<BestItem *>(BaseTreeModel::itemForIndex(idx));
|
|
}
|
|
};
|
|
|
|
// By default, does natural sorting by display name. Call setLessThan() to customize.
|
|
class QTCREATOR_UTILS_EXPORT SortModel : public QSortFilterProxyModel
|
|
{
|
|
public:
|
|
using QSortFilterProxyModel::QSortFilterProxyModel;
|
|
using LessThan = std::function<bool(const QModelIndex &, const QModelIndex &)>;
|
|
void setLessThan(const LessThan &lessThan) { m_lessThan = lessThan; }
|
|
|
|
protected:
|
|
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
|
|
|
private:
|
|
LessThan m_lessThan;
|
|
};
|
|
|
|
} // namespace Utils
|
|
|
|
Q_DECLARE_METATYPE(Utils::TreeItem *)
|