162 lines
5.3 KiB
C++
162 lines
5.3 KiB
C++
#include "ContextModel.h"
|
|
#include <QString>
|
|
#include <typeinfo>
|
|
#include <cxxabi.h>
|
|
#include <functional>
|
|
#include "Core/Object.h"
|
|
|
|
ContextModel::ContextModel(QObject* parent)
|
|
: QAbstractItemModel(parent), m_rootContext(nullptr) {}
|
|
|
|
ContextModel::~ContextModel() {}
|
|
|
|
void ContextModel::setContext(uLib::ObjectsContext* context) {
|
|
beginResetModel();
|
|
m_rootContext = context;
|
|
if (m_rootContext) {
|
|
uLib::Object::connect(m_rootContext, &uLib::Object::Updated, [this]() {
|
|
this->beginResetModel();
|
|
this->endResetModel();
|
|
});
|
|
}
|
|
endResetModel();
|
|
}
|
|
|
|
QModelIndex ContextModel::index(int row, int column, const QModelIndex& parent) const {
|
|
if (!hasIndex(row, column, parent) || !m_rootContext) {
|
|
return QModelIndex();
|
|
}
|
|
|
|
if (!parent.isValid()) {
|
|
if (row < m_rootContext->GetCount()) {
|
|
return createIndex(row, column, m_rootContext->GetObject(row));
|
|
}
|
|
} else {
|
|
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
|
uLib::ObjectsContext* parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj);
|
|
if (parentCtx && row < parentCtx->GetCount()) {
|
|
return createIndex(row, column, parentCtx->GetObject(row));
|
|
}
|
|
}
|
|
return QModelIndex();
|
|
}
|
|
|
|
QModelIndex ContextModel::parent(const QModelIndex& child) const {
|
|
if (!child.isValid() || !m_rootContext) {
|
|
return QModelIndex();
|
|
}
|
|
|
|
uLib::Object* childObj = static_cast<uLib::Object*>(child.internalPointer());
|
|
|
|
// Finding the parent of childObj is O(N) since there is no parent pointer.
|
|
// We just do a recursive search starting from root context.
|
|
std::function<uLib::ObjectsContext*(uLib::ObjectsContext*, uLib::Object*)> findParent =
|
|
[&findParent](uLib::ObjectsContext* ctx, uLib::Object* target) -> uLib::ObjectsContext* {
|
|
for (const auto& obj : ctx->GetObjects()) {
|
|
if (obj == target) return ctx;
|
|
if (auto subCtx = dynamic_cast<uLib::ObjectsContext*>(obj)) {
|
|
if (auto p = findParent(subCtx, target)) return p;
|
|
}
|
|
}
|
|
return nullptr;
|
|
};
|
|
|
|
uLib::ObjectsContext* parentCtx = findParent(m_rootContext, childObj);
|
|
if (!parentCtx || parentCtx == m_rootContext) {
|
|
return QModelIndex(); // Root items have invalid parent index
|
|
}
|
|
|
|
// Now need to find the row of parentCtx in its own parent Context.
|
|
uLib::ObjectsContext* grandParentCtx = findParent(m_rootContext, parentCtx);
|
|
if (!grandParentCtx) grandParentCtx = m_rootContext;
|
|
|
|
int row = -1;
|
|
for (size_t i = 0; i < grandParentCtx->GetCount(); ++i) {
|
|
if (grandParentCtx->GetObject(i) == parentCtx) {
|
|
row = (int)i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (row != -1) {
|
|
return createIndex(row, 0, parentCtx);
|
|
}
|
|
return QModelIndex();
|
|
}
|
|
|
|
int ContextModel::rowCount(const QModelIndex& parent) const {
|
|
if (!m_rootContext) return 0;
|
|
|
|
if (!parent.isValid()) {
|
|
return m_rootContext->GetCount();
|
|
}
|
|
|
|
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
|
if (auto parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj)) {
|
|
return parentCtx->GetCount();
|
|
}
|
|
return 0; // leaf node
|
|
}
|
|
|
|
int ContextModel::columnCount(const QModelIndex& parent) const {
|
|
return 1;
|
|
}
|
|
|
|
static QString getDemangledName(const std::type_info& info) {
|
|
int status = -4;
|
|
char* demangled = abi::__cxa_demangle(info.name(), nullptr, nullptr, &status);
|
|
QString res = (status == 0 && demangled) ? QString::fromUtf8(demangled) : QString::fromUtf8(info.name());
|
|
if (demangled) free(demangled);
|
|
|
|
// Remove namespaces
|
|
int lastColon = res.lastIndexOf("::");
|
|
if (lastColon != -1) {
|
|
res = res.mid(lastColon + 2);
|
|
}
|
|
|
|
// Remove "class " prefix if any
|
|
if (res.startsWith("class ")) res = res.mid(6);
|
|
return res;
|
|
}
|
|
|
|
QVariant ContextModel::data(const QModelIndex& index, int role) const {
|
|
if (!index.isValid()) return QVariant();
|
|
|
|
uLib::Object* obj = static_cast<uLib::Object*>(index.internalPointer());
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
QString typeName = getDemangledName(typeid(*obj));
|
|
std::string instName = obj->GetInstanceName();
|
|
if (instName.empty()) return typeName;
|
|
return QString("%1 (%2)").arg(QString::fromStdString(instName)).arg(typeName);
|
|
}
|
|
|
|
if (role == Qt::EditRole) {
|
|
return QString::fromStdString(obj->GetInstanceName());
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
QVariant ContextModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
|
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) {
|
|
return "Object Context";
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags ContextModel::flags(const QModelIndex& index) const {
|
|
if (!index.isValid()) return Qt::NoItemFlags;
|
|
return Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
}
|
|
|
|
bool ContextModel::setData(const QModelIndex& index, const QVariant& value, int role) {
|
|
if (index.isValid() && role == Qt::EditRole) {
|
|
uLib::Object* obj = static_cast<uLib::Object*>(index.internalPointer());
|
|
obj->SetInstanceName(value.toString().toStdString());
|
|
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|