Commit dee7ad38 authored by hjk's avatar hjk

TreeModel: Rework tree iteration

Use function objects to apply directly during a depth-first
walk of the tree instead of faking a flat container of tree
nodes. Less code, and allows even some non-const operations.

Change-Id: I804ab11df358fe937b40809cbcb772e6f3ff8dc5
Reviewed-by: default avatarDavid Schulz <david.schulz@theqtcompany.com>
Reviewed-by: Eike Ziller's avatarEike Ziller <eike.ziller@qt.io>
parent aa2e1353
......@@ -33,6 +33,7 @@
#include <utils/categorysortfiltermodel.h>
#include <utils/icon.h>
#include <utils/itemviews.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <QDebug>
......
......@@ -1074,41 +1074,4 @@ TreeItem *TreeModel::takeItem(TreeItem *item)
return item;
}
//
// TreeLevelItems
//
UntypedTreeLevelItems::UntypedTreeLevelItems(TreeItem *item, int level)
: m_item(item), m_level(level)
{}
UntypedTreeLevelItems::const_iterator::const_iterator(TreeItem *base, int level)
: m_level(level)
{
QTC_ASSERT(level > 0, return);
if (base) {
// "begin()"
m_depth = 0;
// Level x: The item m_item[x] is the m_pos[x]'th child of its
// parent, out of m_size[x].
m_pos[0] = 0;
m_size[0] = 1;
m_item[0] = base;
goDown();
} else {
// "end()"
m_depth = -1;
}
}
UntypedTreeLevelItems::const_iterator UntypedTreeLevelItems::begin() const
{
return const_iterator(m_item, m_level);
}
UntypedTreeLevelItems::const_iterator UntypedTreeLevelItems::end() const
{
return const_iterator(0, m_level);
}
} // namespace Utils
......@@ -27,13 +27,9 @@
#include "utils_global.h"
#include "algorithm.h"
#include "qtcassert.h"
#include <QAbstractItemModel>
#include <functional>
#include <iterator>
namespace Utils {
......@@ -99,6 +95,36 @@ public:
void walkTree(TreeItemVisitor *visitor);
void walkTree(std::function<void(TreeItem *)> f);
// Levels are 1-based: Child at Level 1 is an immediate child.
template <class T, typename Function>
void forEachChildAtLevel(int n, Function func) {
foreach (auto item, m_children) {
if (n == 1)
func(static_cast<T>(item));
else
item->forEachChildAtLevel<T>(n - 1, func);
}
}
template <class T, typename Function>
void forEachChild(Function func) const {
forEachChildAtLevel<T>(1, func);
}
template <class T, typename Predicate>
T findChildAtLevel(int n, Predicate func) const {
if (n == 1) {
foreach (auto item, m_children)
if (func(static_cast<T>(item)))
return static_cast<T>(item);
} else {
foreach (auto item, m_children)
if (T found = item->findChildAtLevel<T>(n - 1, func))
return found;
}
return 0;
}
private:
TreeItem(const TreeItem &) Q_DECL_EQ_DELETE;
void operator=(const TreeItem &) Q_DECL_EQ_DELETE;
......@@ -115,130 +141,6 @@ private:
friend class TreeModel;
};
class QTCREATOR_UTILS_EXPORT UntypedTreeLevelItems
{
public:
enum { MaxSearchDepth = 12 }; // FIXME.
explicit UntypedTreeLevelItems(TreeItem *item, int level = 1);
typedef TreeItem *value_type;
class const_iterator
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef TreeItem *value_type;
typedef std::ptrdiff_t difference_type;
typedef const value_type *pointer;
typedef const value_type &reference;
const_iterator(TreeItem *base, int level);
TreeItem *operator*() { return m_item[m_depth]; }
void operator++()
{
QTC_ASSERT(m_depth == m_level, return);
int pos = ++m_pos[m_depth];
if (pos < m_size[m_depth])
m_item[m_depth] = m_item[m_depth - 1]->child(pos);
else
goUpNextDown();
}
bool operator==(const const_iterator &other) const
{
if (m_depth != other.m_depth)
return false;
for (int i = 0; i <= m_depth; ++i)
if (m_item[i] != other.m_item[i])
return false;
return true;
}
bool operator!=(const const_iterator &other) const
{
return !operator==(other);
}
private:
// Result is either an item of the target level, or 'end'.
void goDown()
{
QTC_ASSERT(m_depth != -1, return);
QTC_ASSERT(m_depth < m_level, return);
do {
TreeItem *curr = m_item[m_depth];
++m_depth;
int size = curr->rowCount();
if (size == 0) {
// This is a dead end not reaching to the desired level.
goUpNextDown();
return;
}
m_size[m_depth] = size;
m_pos[m_depth] = 0;
m_item[m_depth] = curr->child(0);
} while (m_depth < m_level);
// Did not reach the required level? Set to end().
if (m_depth != m_level)
m_depth = -1;
}
void goUpNextDown()
{
// Go up until we can move sidewards.
do {
--m_depth;
if (m_depth < 0)
return; // Solid end.
} while (++m_pos[m_depth] >= m_size[m_depth]);
m_item[m_depth] = m_item[m_depth - 1]->child(m_pos[m_depth]);
goDown();
}
int m_level;
int m_depth;
TreeItem *m_item[MaxSearchDepth];
int m_pos[MaxSearchDepth];
int m_size[MaxSearchDepth];
};
const_iterator begin() const;
const_iterator end() const;
private:
TreeItem *m_item;
int m_level;
};
template <class T>
class TreeLevelItems
{
public:
typedef T value_type;
explicit TreeLevelItems(const UntypedTreeLevelItems &items) : m_items(items) {}
struct const_iterator : public UntypedTreeLevelItems::const_iterator
{
typedef std::forward_iterator_tag iterator_category;
typedef T value_type;
typedef std::ptrdiff_t difference_type;
typedef const value_type *pointer;
typedef const value_type &reference;
const_iterator(UntypedTreeLevelItems::const_iterator it) : UntypedTreeLevelItems::const_iterator(it) {}
T operator*() { return static_cast<T>(UntypedTreeLevelItems::const_iterator::operator*()); }
};
const_iterator begin() const { return const_iterator(m_items.begin()); }
const_iterator end() const { return const_iterator(m_items.end()); }
private:
UntypedTreeLevelItems m_items;
};
class QTCREATOR_UTILS_EXPORT TreeModel : public QAbstractItemModel
{
Q_OBJECT
......@@ -272,16 +174,14 @@ public:
bool canFetchMore(const QModelIndex &idx) const override;
void fetchMore(const QModelIndex &idx) override;
template <class T>
TreeLevelItems<T> itemsAtLevel(int level, TreeItem *start = 0) const
{
return TreeLevelItems<T>(UntypedTreeLevelItems(start ? start : m_root, level));
template <class T, typename Function>
void forEachItemAtLevel(int n, Function func) const {
m_root->forEachChildAtLevel<T>(n, func);
}
template <class T>
T findItemAtLevel(int level, std::function<bool(T)> f, TreeItem *start = 0) const
{
return Utils::findOrDefault(itemsAtLevel<T>(level, start), f);
template <class T, typename Predicate>
T findItemAtLevel(int n, Predicate func) const {
return m_root->findChildAtLevel<T>(n, func);
}
TreeItem *takeItem(TreeItem *item); // item is not destroyed.
......
......@@ -46,6 +46,7 @@
#include <qmljs/qmljsdialect.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <utils/textfileformat.h>
......
......@@ -26,6 +26,8 @@
#include "testresultdelegate.h"
#include "testresultmodel.h"
#include <utils/qtcassert.h>
#include <QFontMetrics>
#include <QIcon>
......
......@@ -241,7 +241,7 @@ void CMakeToolItemModel::apply()
foreach (const Core::Id &id, m_removedItems)
CMakeToolManager::deregisterCMakeTool(id);
foreach (auto item, itemsAtLevel<CMakeToolTreeItem *>(2)) {
forEachItemAtLevel<CMakeToolTreeItem *>(2, [](CMakeToolTreeItem *item) {
item->m_changed = false;
bool isNew = false;
......@@ -262,7 +262,7 @@ void CMakeToolItemModel::apply()
item->m_changed = true;
}
}
}
});
CMakeToolManager::setDefaultCMakeTool(defaultItemId());
}
......@@ -293,8 +293,9 @@ void CMakeToolItemModel::setDefaultItemId(const Core::Id &id)
QString CMakeToolItemModel::uniqueDisplayName(const QString &base) const
{
QStringList names;
foreach (CMakeToolTreeItem *item, itemsAtLevel<CMakeToolTreeItem *>(2))
forEachItemAtLevel<CMakeToolTreeItem *>(2, [&names](CMakeToolTreeItem *item) {
names << item->m_name;
});
return ProjectExplorer::Project::makeUnique(base, names);
}
......
......@@ -180,10 +180,10 @@ void DebuggerItemModel::apply()
foreach (const QVariant &id, m_removedItems)
DebuggerItemManager::deregisterDebugger(id);
foreach (auto item, itemsAtLevel<DebuggerTreeItem *>(2)) {
forEachItemAtLevel<DebuggerTreeItem *>(2, [](DebuggerTreeItem *item) {
item->m_changed = false;
DebuggerItemManager::updateOrAddDebugger(item->m_item);
}
});
}
void DebuggerItemModel::setCurrentIndex(const QModelIndex &index)
......
......@@ -45,6 +45,7 @@
#include <texteditor/texteditor.h>
#include <texteditor/textdocument.h>
#include <utils/algorithm.h>
#include <utils/tooltip/tooltip.h>
#include <utils/treemodel.h>
#include <utils/qtcassert.h>
......
......@@ -321,6 +321,11 @@ void ThreadsHandler::notifyGroupCreated(const QByteArray &groupId, const QByteAr
m_pidForGroupId[groupId] = pid;
}
void ThreadsHandler::foreachThread(const std::function<void (ThreadItem *)> &func)
{
forEachItemAtLevel<ThreadItem *>(1, func);
}
void ThreadsHandler::updateThread(const ThreadData &threadData)
{
if (ThreadItem *item = itemForThreadId(this, threadData.id))
......@@ -349,9 +354,9 @@ void ThreadsHandler::setThreads(const Threads &threads)
void ThreadsHandler::updateThreadBox()
{
QStringList list;
auto items = itemsAtLevel<ThreadItem *>(1);
foreach (ThreadItem *item, items)
foreachThread([&list](ThreadItem *item) {
list.append(QString::fromLatin1("#%1 %2").arg(item->threadData.id.raw()).arg(item->threadData.name));
});
Internal::setThreadBoxContents(list, indexForThreadId(this, m_currentId));
}
......@@ -370,10 +375,10 @@ void ThreadsHandler::removeAll()
bool ThreadsHandler::notifyGroupExited(const QByteArray &groupId)
{
QList<ThreadItem *> list;
auto items = itemsAtLevel<ThreadItem *>(1);
foreach (ThreadItem *item, items)
foreachThread([&list, groupId](ThreadItem *item) {
if (item->threadData.groupId == groupId)
list.append(item);
});
foreach (ThreadItem *item, list)
delete takeItem(item);
......@@ -397,9 +402,7 @@ void ThreadsHandler::notifyRunning(const QByteArray &data)
void ThreadsHandler::notifyAllRunning()
{
auto items = itemsAtLevel<ThreadItem *>(1);
foreach (ThreadItem *item, items)
item->notifyRunning();
foreachThread([](ThreadItem *item) { item->notifyRunning(); });
}
void ThreadsHandler::notifyRunning(ThreadId threadId)
......@@ -424,9 +427,7 @@ void ThreadsHandler::notifyStopped(const QByteArray &data)
void ThreadsHandler::notifyAllStopped()
{
auto items = itemsAtLevel<ThreadItem *>(1);
foreach (ThreadItem *item, items)
item->notifyStopped();
foreachThread([](ThreadItem *item) { item->notifyStopped(); });
}
void ThreadsHandler::notifyStopped(ThreadId threadId)
......
......@@ -39,6 +39,7 @@ namespace Debugger {
namespace Internal {
class GdbMi;
class ThreadItem;
class ThreadsHandler : public Utils::TreeModel
{
......@@ -77,6 +78,8 @@ public:
void resetLocation();
void scheduleResetLocation();
void foreachThread(const std::function<void(ThreadItem *item)> &func);
private:
void updateThreadBox();
......
......@@ -1361,8 +1361,9 @@ void WatchHandler::notifyUpdateStarted(const QList<QByteArray> &inames)
auto marker = [](TreeItem *it) { static_cast<WatchItem *>(it)->outdated = true; };
if (inames.isEmpty()) {
foreach (auto item, m_model->itemsAtLevel<WatchItem *>(2))
m_model->forEachItemAtLevel<WatchItem *>(2, [marker](WatchItem *item) {
item->walkTree(marker);
});
} else {
foreach (auto iname, inames) {
if (WatchItem *item = m_model->findItem(iname))
......
......@@ -195,29 +195,29 @@ void KitModel::isAutoDetectedChanged()
void KitModel::validateKitNames()
{
QHash<QString, int> nameHash;
foreach (KitNode *n, itemsAtLevel<KitNode *>(2)) {
forEachItemAtLevel<KitNode *>(2, [&nameHash](KitNode *n) {
const QString displayName = n->widget->displayName();
if (nameHash.contains(displayName))
++nameHash[displayName];
else
nameHash.insert(displayName, 1);
}
});
foreach (KitNode *n, itemsAtLevel<KitNode *>(2)) {
forEachItemAtLevel<KitNode *>(2, [&nameHash](KitNode *n) {
const QString displayName = n->widget->displayName();
n->widget->setHasUniqueName(nameHash.value(displayName) == 1);
}
});
}
void KitModel::apply()
{
// Add/update dirty nodes before removing kits. This ensures the right kit ends up as default.
foreach (KitNode *n, itemsAtLevel<KitNode *>(2)) {
forEachItemAtLevel<KitNode *>(2, [](KitNode *n) {
if (n->widget->isDirty()) {
n->widget->apply();
n->update();
}
}
});
// Remove unused kits:
foreach (KitNode *n, m_toRemoveList)
......@@ -272,11 +272,7 @@ Kit *KitModel::markForAddition(Kit *baseKit)
KitNode *KitModel::findWorkingCopy(Kit *k) const
{
foreach (KitNode *n, itemsAtLevel<KitNode *>(2)) {
if (n->widget->workingCopy() == k)
return n;
}
return 0;
return findItemAtLevel<KitNode *>(2, [k](KitNode *n) { return n->widget->workingCopy() == k; });
}
KitNode *KitModel::createNode(Kit *k)
......@@ -341,13 +337,9 @@ void KitModel::removeKit(Kit *k)
}
}
KitNode *node = 0;
foreach (KitNode *n, itemsAtLevel<KitNode *>(2)) {
if (n->widget->configures(k)) {
node = n;
break;
}
}
KitNode *node = findItemAtLevel<KitNode *>(2, [k](KitNode *n) {
return n->widget->configures(k);
});
if (node == m_defaultNode)
setDefaultNode(findItemAtLevel<KitNode *>(2, [node](KitNode *kn) { return kn != node; }));
......@@ -361,12 +353,10 @@ void KitModel::removeKit(Kit *k)
void KitModel::changeDefaultKit()
{
Kit *defaultKit = KitManager::defaultKit();
foreach (KitNode *n, itemsAtLevel<KitNode *>(2)) {
if (n->widget->configures(defaultKit)) {
setDefaultNode(n);
return;
}
}
KitNode *node = findItemAtLevel<KitNode *>(2, [defaultKit](KitNode *n) {
return n->widget->configures(defaultKit);
});
setDefaultNode(node);
}
} // namespace Internal
......
......@@ -252,13 +252,10 @@ void ToolChainOptionsWidget::removeToolChain(ToolChain *tc)
}
}
TreeItem *parent = m_model.rootItem()->child(tc->isAutoDetected() ? 0 : 1);
foreach (ToolChainTreeItem *item, m_model.itemsAtLevel<ToolChainTreeItem *>(1, parent)) {
if (item->toolChain == tc) {
delete m_model.takeItem(item);
break;
}
}
auto item = m_model.findItemAtLevel<ToolChainTreeItem *>(1, [tc](ToolChainTreeItem *item) {
return tc->isAutoDetected() && item->toolChain == tc;
});
delete m_model.takeItem(item);
updateState();
}
......@@ -287,15 +284,15 @@ void ToolChainOptionsWidget::apply()
Q_ASSERT(m_toRemoveList.isEmpty());
// Update tool chains:
foreach (ToolChainTreeItem *item, m_model.itemsAtLevel<ToolChainTreeItem *>(1, m_manualRoot)) {
if (item->changed) {
m_model.forEachItemAtLevel<ToolChainTreeItem *>(1, [this](ToolChainTreeItem *item) {
if (item->parent() == m_manualRoot && item->changed) {
Q_ASSERT(item->toolChain);
if (item->widget)
item->widget->apply();
item->changed = false;
item->update();
}
}
});
// Add new (and already updated) tool chains
QStringList removedTcs;
......
......@@ -42,10 +42,7 @@ private slots:
static int countLevelItems(TreeItem *base, int level)
{
int n = 0;
foreach (TreeItem *item, UntypedTreeLevelItems(base, level)) {
Q_UNUSED(item);
++n;
}
base->forEachChildAtLevel<TreeItem *>(level, [&n](TreeItem *) { ++n; });
return n;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment