current work on editor widges and data loading
This commit is contained in:
parent
bd1e40685b
commit
cc5b9dfa46
|
@ -6,7 +6,8 @@ set(SOURCES mainwindow.cpp
|
|||
|
||||
add_subdirectory(model)
|
||||
add_subdirectory(editor)
|
||||
add_subdirectory(protocol)
|
||||
|
||||
add_library(src ${HEADERS} ${SOURCES})
|
||||
target_link_libraries(src model editor)
|
||||
target_link_libraries(src model editor protocol)
|
||||
qt5_use_modules(src Widgets)
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
|
||||
set(HEADERS editormodel.h
|
||||
editorelementdata.h
|
||||
set(HEADERS editorelementdata.h
|
||||
guidededitorelementview.h
|
||||
guidededitorgroupview.h
|
||||
editorgroup.h
|
||||
flowlayout.h
|
||||
editortreenode.h
|
||||
EditorNode.h
|
||||
EditorTree.h
|
||||
EditorElementValue.h
|
||||
)
|
||||
|
||||
set(SOURCES guidededitorelementview.cpp
|
||||
guidededitorgroupview.cpp
|
||||
editorelementdata.cpp
|
||||
editormodel.cpp
|
||||
editorgroup.cpp
|
||||
guidededitorview.cpp
|
||||
flowlayout.cpp
|
||||
editortreenode.cpp
|
||||
EditorNode.cpp
|
||||
EditorTree.cpp
|
||||
EditorElementValue.cpp
|
||||
|
||||
)
|
||||
|
||||
add_library(editor ${HEADERS} ${SOURCES})
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#include "EditorElementValue.h"
|
||||
|
||||
EditorElementValue::EditorElementValue()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString EditorElementValue::getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void EditorElementValue::setValue(const QString &value)
|
||||
{
|
||||
this->value = value;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef EDITORELEMENTVALUE_H
|
||||
#define EDITORELEMENTVALUE_H
|
||||
#include <QString>
|
||||
|
||||
class EditorElementValue
|
||||
{
|
||||
public:
|
||||
EditorElementValue();
|
||||
|
||||
QString getValue() const;
|
||||
void setValue(const QString &value);
|
||||
|
||||
private:
|
||||
QString value;
|
||||
};
|
||||
|
||||
#endif // EDITORELEMENTVALUE_H
|
|
@ -0,0 +1,209 @@
|
|||
#include "EditorNode.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
EditorNode::EditorNode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
EditorNode::EditorNode(std::shared_ptr<EditorElementData> data)
|
||||
{
|
||||
this->data = data;
|
||||
}
|
||||
|
||||
EditorNode::~EditorNode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const std::shared_ptr<EditorNode> EditorNode::nextSibling()
|
||||
{
|
||||
if (parent != nullptr) {
|
||||
int i = parent->getChildren().indexOf(std::shared_ptr<EditorNode>(this));
|
||||
if (i + 1 < parent->getChildren().size())
|
||||
return parent->getChildren().at(i + 1); //next sibling...
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
const std::shared_ptr<EditorNode> EditorNode::next(TreeWalkStrategy walkStrategy)
|
||||
{
|
||||
if (walkStrategy == TreeWalkStrategy::mixture)
|
||||
// choose node, work through all siblings, choose next node, but don't change branches more than one parent away, except branch is at an end.
|
||||
if (parent != nullptr) {
|
||||
if (nextSibling() != nullptr) // next sibling
|
||||
return nextSibling();
|
||||
else // find next children
|
||||
{
|
||||
for (auto n : parent->getChildren())
|
||||
if (n->getChildren().size() > 0)
|
||||
return n->getChildren().first();
|
||||
auto siblingOfParent = parent->nextSibling();
|
||||
while (siblingOfParent != nullptr && siblingOfParent->getChildren().size() == 0) {
|
||||
if (siblingOfParent->nextSibling() == nullptr)
|
||||
siblingOfParent = siblingOfParent->getParent();
|
||||
else
|
||||
siblingOfParent = siblingOfParent->nextSibling();
|
||||
}
|
||||
if (siblingOfParent != nullptr)
|
||||
return siblingOfParent->getChildren().first();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (getChildren().size() > 0)
|
||||
return getChildren().first();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
else if (walkStrategy == TreeWalkStrategy::depthFirst)
|
||||
if (getChildren().size() > 0)
|
||||
return getChildren().first();
|
||||
else {
|
||||
auto node = std::shared_ptr<EditorNode>(this);
|
||||
while (node->nextSibling() == nullptr && node->getParent() != nullptr)
|
||||
node = node->getParent();
|
||||
return node->nextSibling();
|
||||
}
|
||||
else if (walkStrategy == TreeWalkStrategy::breadthFirst)
|
||||
if (parent != nullptr) {
|
||||
auto nextOnLvl = nextOnSameLevel();
|
||||
if (nextOnLvl == nullptr){
|
||||
for (auto n : this->getChildren())
|
||||
if (n->getChildren().size() > 0)
|
||||
return n->getChildren().first();
|
||||
}
|
||||
else return nextOnLvl;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::shared_ptr<EditorNode> EditorNode::getRoot()
|
||||
{
|
||||
auto node = std::shared_ptr<EditorNode>(this);
|
||||
while (node->getParent() != nullptr)
|
||||
node = node->getParent();
|
||||
return node;
|
||||
}
|
||||
|
||||
void EditorNode::move(std::shared_ptr<EditorNode> to)
|
||||
{
|
||||
this->getParent()->getChildren().removeOne(std::shared_ptr<EditorNode>(this));
|
||||
to->getChildren().append(std::shared_ptr<EditorNode>(this));
|
||||
fireTreeChanged();
|
||||
}
|
||||
|
||||
void EditorNode::moveChildren(std::shared_ptr<EditorNode> to)
|
||||
{
|
||||
for (auto child : getChildren())
|
||||
child->move(to);
|
||||
fireTreeChanged();
|
||||
}
|
||||
|
||||
void EditorNode::addChild(std::shared_ptr<EditorNode> child)
|
||||
{
|
||||
this->getChildren().append(child);
|
||||
child->setParent(std::shared_ptr<EditorNode>(this));
|
||||
fireTreeChanged();
|
||||
}
|
||||
|
||||
bool EditorNode::removeChild(std::shared_ptr<EditorNode> child)
|
||||
{
|
||||
bool ret = this->getChildren().removeOne(child);
|
||||
if (ret) fireTreeChanged();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorNode::remove()
|
||||
{
|
||||
bool worked = true;
|
||||
if (children.size() > 0) {
|
||||
for (auto node : children) {
|
||||
worked &= node->remove();
|
||||
}
|
||||
}
|
||||
if (worked && parent != nullptr) {
|
||||
worked &= parent->getChildren().removeOne(std::shared_ptr<EditorNode>(this));
|
||||
}
|
||||
if (worked) fireTreeChanged();
|
||||
return worked;
|
||||
}
|
||||
|
||||
std::shared_ptr<EditorNode> EditorNode::getParent() const
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
void EditorNode::setParent(const std::shared_ptr<EditorNode> &value)
|
||||
{
|
||||
parent = value;
|
||||
}
|
||||
|
||||
std::shared_ptr<EditorElementData> EditorNode::getData() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
void EditorNode::setData(const std::shared_ptr<EditorElementData> &value)
|
||||
{
|
||||
fireNodeDataChanged();
|
||||
data = value;
|
||||
}
|
||||
|
||||
std::shared_ptr<EditorElementValue> EditorNode::getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void EditorNode::setValue(const std::shared_ptr<EditorElementValue> &value)
|
||||
{
|
||||
fireNodeDataChanged();
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<EditorNode> > EditorNode::getChildren() const
|
||||
{
|
||||
return children;
|
||||
}
|
||||
|
||||
void EditorNode::fireNodeDataChanged()
|
||||
{
|
||||
if(this->getRoot()->getDataChangedSignal() != nullptr)
|
||||
this->getRoot()->getDataChangedSignal()(std::shared_ptr<EditorNode>(this));
|
||||
}
|
||||
|
||||
void EditorNode::fireTreeChanged()
|
||||
{
|
||||
if(this->getRoot()->getTreeChangedSignal() != nullptr)
|
||||
this->getRoot()->getTreeChangedSignal()(std::shared_ptr<EditorNode>(this));
|
||||
}
|
||||
|
||||
std::shared_ptr<EditorNode> EditorNode::nextOnSameLevel()
|
||||
{
|
||||
if (nextSibling() != nullptr)
|
||||
return nextSibling();
|
||||
uint lvl = 1;
|
||||
auto node = this->getParent();
|
||||
auto pnode = std::shared_ptr<EditorNode>(this);
|
||||
while (lvl > 0 && node != nullptr) {
|
||||
if (node != pnode->getParent() && node->getChildren().size() > 0) {
|
||||
node = node->getChildren().first();
|
||||
--lvl;
|
||||
} else {
|
||||
pnode = node;
|
||||
if (node->nextSibling() != nullptr)
|
||||
node = node->nextSibling();
|
||||
else {
|
||||
if (node->getParent() != nullptr) {
|
||||
node = node->getParent();
|
||||
++lvl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef EDITORNODE_H
|
||||
#define EDITORNODE_H
|
||||
|
||||
#include <memory>
|
||||
#include <QList>
|
||||
#include <functional>
|
||||
|
||||
#include "editorelementdata.h"
|
||||
#include "EditorElementValue.h"
|
||||
|
||||
class EditorNode;
|
||||
|
||||
enum class TreeWalkStrategy {
|
||||
depthFirst,
|
||||
breadthFirst,
|
||||
mixture
|
||||
};
|
||||
|
||||
class EditorNode
|
||||
{
|
||||
public:
|
||||
explicit EditorNode();
|
||||
explicit EditorNode(std::shared_ptr<EditorElementData> data);
|
||||
virtual ~EditorNode();
|
||||
|
||||
const std::shared_ptr<EditorNode> nextSibling();
|
||||
|
||||
const std::shared_ptr<EditorNode> next(TreeWalkStrategy walkStrategy = TreeWalkStrategy::depthFirst);
|
||||
const std::shared_ptr<EditorNode> getRoot();
|
||||
void move(std::shared_ptr<EditorNode> to); //as child
|
||||
void moveChildren(std::shared_ptr<EditorNode> to); //all
|
||||
|
||||
void addChild(std::shared_ptr<EditorNode> child);
|
||||
bool removeChild(std::shared_ptr<EditorNode> child);
|
||||
bool remove();
|
||||
|
||||
std::shared_ptr<EditorNode> getParent() const;
|
||||
void setParent(const std::shared_ptr<EditorNode> &value);
|
||||
|
||||
std::shared_ptr<EditorElementData> getData() const;
|
||||
void setData(const std::shared_ptr<EditorElementData> &value);
|
||||
|
||||
std::shared_ptr<EditorElementValue> getValue() const;
|
||||
void setValue(const std::shared_ptr<EditorElementValue> &value);
|
||||
|
||||
QList<std::shared_ptr<EditorNode> > getChildren() const;
|
||||
|
||||
void setDataChangedSignal(std::function<void(std::shared_ptr<EditorNode>)> f)
|
||||
{
|
||||
nodeDataChanged = f;
|
||||
}
|
||||
void setTreeChangedSignal(std::function<void(std::shared_ptr<EditorNode>)> f)
|
||||
{
|
||||
treeChanged = f;
|
||||
}
|
||||
|
||||
std::function<void(std::shared_ptr<EditorNode>)> getTreeChangedSignal(){ return treeChanged; }
|
||||
std::function<void(std::shared_ptr<EditorNode>)> getDataChangedSignal(){ return nodeDataChanged; }
|
||||
|
||||
private:
|
||||
void fireNodeDataChanged();
|
||||
void fireTreeChanged();
|
||||
//signal callbacks only set in the root node
|
||||
std::function<void(std::shared_ptr<EditorNode>)> nodeDataChanged;
|
||||
std::function<void(std::shared_ptr<EditorNode>)> treeChanged;
|
||||
|
||||
std::shared_ptr<EditorNode> nextOnSameLevel();
|
||||
std::shared_ptr<EditorNode> parent;
|
||||
std::shared_ptr<EditorElementData> data;
|
||||
std::shared_ptr<EditorElementValue> value;
|
||||
QList<std::shared_ptr<EditorNode>> children;
|
||||
};
|
||||
|
||||
#endif // EDITORNODE_H
|
|
@ -0,0 +1,15 @@
|
|||
#include "EditorTree.h"
|
||||
#include <memory>
|
||||
|
||||
EditorTree::EditorTree(QObject *parent) : QObject(parent)
|
||||
{
|
||||
root = std::make_shared<EditorNode>(nullptr);
|
||||
|
||||
root->setDataChangedSignal([this](std::shared_ptr<EditorNode> node){ emit this->nodeDataChanged(node);});
|
||||
root->setTreeChangedSignal([this](std::shared_ptr<EditorNode> node){ emit this->treeChanged(node);});
|
||||
}
|
||||
|
||||
EditorTree::~EditorTree()
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef EDITORTREE_H
|
||||
#define EDITORTREE_H
|
||||
#include <memory>
|
||||
#include <QObject>
|
||||
|
||||
#include "EditorNode.h"
|
||||
|
||||
class EditorTree : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EditorTree(QObject *parent = 0);
|
||||
virtual ~EditorTree();
|
||||
|
||||
const std::shared_ptr<EditorNode> getRoot() { return root; }
|
||||
|
||||
signals:
|
||||
void nodeDataChanged(std::shared_ptr<EditorNode> node);
|
||||
void treeChanged(std::shared_ptr<EditorNode> node);
|
||||
public slots:
|
||||
private:
|
||||
std::shared_ptr<EditorNode> root;
|
||||
};
|
||||
|
||||
#endif // EDITORTREE_H
|
|
@ -13,3 +13,123 @@ EditorElementData::~EditorElementData()
|
|||
|
||||
}
|
||||
|
||||
QString EditorElementData::getTitle() const
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
void EditorElementData::setTitle(const QString &value)
|
||||
{
|
||||
title = value;
|
||||
}
|
||||
|
||||
QString EditorElementData::getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
void EditorElementData::setId(const QString &value)
|
||||
{
|
||||
id = value;
|
||||
}
|
||||
|
||||
QList<QString> EditorElementData::getSyntaxes() const
|
||||
{
|
||||
return syntaxes;
|
||||
}
|
||||
|
||||
void EditorElementData::setSyntaxes(const QList<QString> &value)
|
||||
{
|
||||
syntaxes = value;
|
||||
}
|
||||
|
||||
bool EditorElementData::isMultiLine() const
|
||||
{
|
||||
return multiline;
|
||||
}
|
||||
|
||||
void EditorElementData::setIsMultiLine(bool value)
|
||||
{
|
||||
multiline = value;
|
||||
}
|
||||
|
||||
ValueType EditorElementData::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
void EditorElementData::setType(const ValueType &value)
|
||||
{
|
||||
type = value;
|
||||
}
|
||||
|
||||
QString EditorElementData::getRegex() const
|
||||
{
|
||||
return regex;
|
||||
}
|
||||
|
||||
void EditorElementData::setRegex(const QString &value)
|
||||
{
|
||||
regex = value;
|
||||
}
|
||||
|
||||
QMap<QString, QString> EditorElementData::getHints() const
|
||||
{
|
||||
return hints;
|
||||
}
|
||||
|
||||
void EditorElementData::setHint(const QString &key, const QString &value)
|
||||
{
|
||||
hints.insert(key, value);
|
||||
}
|
||||
|
||||
ulong EditorElementData::getBytes() const
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void EditorElementData::setBytes(const ulong &value)
|
||||
{
|
||||
bytes = value;
|
||||
}
|
||||
|
||||
ulong EditorElementData::getBits() const
|
||||
{
|
||||
return bits;
|
||||
}
|
||||
|
||||
void EditorElementData::setBits(const ulong &value)
|
||||
{
|
||||
bits = value;
|
||||
}
|
||||
|
||||
bool EditorElementData::isData() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
void EditorElementData::setData(bool value)
|
||||
{
|
||||
data = value;
|
||||
}
|
||||
|
||||
bool EditorElementData::isNextLayer() const
|
||||
{
|
||||
return nextLayer;
|
||||
}
|
||||
|
||||
void EditorElementData::setNextLayer(bool value)
|
||||
{
|
||||
nextLayer = value;
|
||||
}
|
||||
|
||||
bool EditorElementData::isOptional() const
|
||||
{
|
||||
return optional;
|
||||
}
|
||||
|
||||
void EditorElementData::setOptional(bool value)
|
||||
{
|
||||
optional = value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,71 @@
|
|||
#ifndef EDITORELEMENT_H
|
||||
#define EDITORELEMENT_H
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QList>
|
||||
|
||||
enum class ValueType {
|
||||
BIN,OCT,DEC,HEX,TXT
|
||||
};
|
||||
|
||||
|
||||
|
||||
class EditorElementData : public QObject
|
||||
class EditorElementData
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit EditorElementData();
|
||||
~EditorElementData();
|
||||
virtual ~EditorElementData();
|
||||
|
||||
|
||||
QString getTitle() const;
|
||||
void setTitle(const QString &value);
|
||||
|
||||
QString getId() const;
|
||||
void setId(const QString &value);
|
||||
|
||||
QList<QString> getSyntaxes() const;
|
||||
void setSyntaxes(const QList<QString> &value);
|
||||
|
||||
bool isMultiLine() const;
|
||||
void setIsMultiLine(bool value);
|
||||
|
||||
ValueType getType() const;
|
||||
void setType(const ValueType &value);
|
||||
|
||||
QString getRegex() const;
|
||||
void setRegex(const QString &value);
|
||||
|
||||
QMap<QString, QString> getHints() const;
|
||||
void setHint(const QString &key, const QString &value);
|
||||
|
||||
ulong getBytes() const;
|
||||
void setBytes(const ulong &value);
|
||||
|
||||
ulong getBits() const;
|
||||
void setBits(const ulong &value);
|
||||
|
||||
bool isData() const;
|
||||
void setData(bool value);
|
||||
|
||||
bool isNextLayer() const;
|
||||
void setNextLayer(bool value);
|
||||
|
||||
bool isOptional() const;
|
||||
void setOptional(bool value);
|
||||
|
||||
private:
|
||||
QMap<QString, QString> elements;
|
||||
QString title;
|
||||
QString id;
|
||||
QList<QString> syntaxes;
|
||||
bool multiline;
|
||||
ValueType type;
|
||||
ulong bytes;
|
||||
ulong bits;
|
||||
QString regex;
|
||||
QMap<QString, QString> hints;
|
||||
bool data;
|
||||
bool nextLayer;
|
||||
bool optional;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#include "editorgroup.h"
|
||||
#include "cstdlib"
|
||||
#include <QColor>
|
||||
|
||||
EditorGroup::EditorGroup(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
#ifndef GUIDEDEDITORLAYER_H
|
||||
#define GUIDEDEDITORLAYER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QColor>
|
||||
|
||||
#include "editorelementdata.h"
|
||||
|
||||
class EditorGroup : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EditorGroup(QObject *parent = 0);
|
||||
|
||||
private:
|
||||
QMap<QString, QString> attributes;
|
||||
QList<EditorElementData*> elements;
|
||||
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // GUIDEDEDITORLAYER_H
|
|
@ -1,7 +0,0 @@
|
|||
#include "editormodel.h"
|
||||
|
||||
EditorModel::EditorModel(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#ifndef ABSTRACTGUIDEDEDITORMODEL_H
|
||||
#define ABSTRACTGUIDEDEDITORMODEL_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include "editorelementdata.h"
|
||||
#include "editorgroup.h"
|
||||
|
||||
class EditorModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EditorModel(QObject *parent = 0);
|
||||
virtual ~EditorModel();
|
||||
|
||||
virtual uint getCount()=0;
|
||||
virtual EditorElementData& getElement(uint index) = 0;
|
||||
virtual void setElementValue(const uint index, const QString value)= 0;
|
||||
|
||||
virtual const std::list<EditorElementData>& getAllElements() = 0;
|
||||
|
||||
signals:
|
||||
void dataChanged();
|
||||
void dataChanged(uint index);
|
||||
|
||||
public slots:
|
||||
private:
|
||||
QList<EditorElementData*> data;
|
||||
QList<EditorGroup*> groups;
|
||||
|
||||
};
|
||||
|
||||
#endif // ABSTRACTGUIDEDEDITORMODEL_H
|
|
@ -1,7 +1,9 @@
|
|||
#include "editortreenode.h"
|
||||
|
||||
template <typename T>
|
||||
EditorTreeNode<T>::EditorTreeNode() {}
|
||||
EditorTreeNode<T>::EditorTreeNode() {
|
||||
parentNode = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EditorTreeNode<T>::EditorTreeNode(T v)
|
||||
|
@ -18,7 +20,7 @@ EditorTreeNode<T>::~EditorTreeNode()
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
const EditorTreeNode<T>* EditorTreeNode<T>::nextSibling()
|
||||
const std::shared_ptr<EditorTreeNode<T> > EditorTreeNode<T>::nextSibling()
|
||||
{
|
||||
if (parent() != nullptr) {
|
||||
uint i = parent()->children().indexOf(this);
|
||||
|
@ -31,7 +33,7 @@ const EditorTreeNode<T>* EditorTreeNode<T>::nextSibling()
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
const EditorTreeNode<T>* EditorTreeNode<T>::next(TreeWalkStrategy walkStrategy)
|
||||
const std::shared_ptr<EditorTreeNode<T>> EditorTreeNode<T>::next(TreeWalkStrategy walkStrategy)
|
||||
{
|
||||
if (walkStrategy == TreeWalkStrategy::mixture)
|
||||
// choose node, work through all siblings, choose next node, but don't change branches more than one parent away, except branch is at an end.
|
||||
|
@ -84,37 +86,37 @@ const EditorTreeNode<T>* EditorTreeNode<T>::next(TreeWalkStrategy walkStrategy)
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
const EditorTreeNode<T>* EditorTreeNode<T>::getRoot()
|
||||
const std::shared_ptr<EditorTreeNode<T>> EditorTreeNode<T>::getRoot()
|
||||
{
|
||||
auto node = this;
|
||||
auto node = std::shared_ptr<EditorTreeNode<T>>(this);
|
||||
while (node->parent() != nullptr)
|
||||
node = node->parent();
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EditorTreeNode<T>::move(EditorTreeNode<T>* to)
|
||||
void EditorTreeNode<T>::move(std::shared_ptr<EditorTreeNode<T>> to)
|
||||
{
|
||||
this->parent()->children().removeOne(this);
|
||||
to->children().append(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EditorTreeNode<T>::moveChildren(EditorTreeNode<T>* to)
|
||||
void EditorTreeNode<T>::moveChildren(std::shared_ptr<EditorTreeNode<T>> to)
|
||||
{
|
||||
for (auto child : childrenNodes)
|
||||
child->move(to);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EditorTreeNode<T>::addChild(EditorTreeNode<T>* child)
|
||||
void EditorTreeNode<T>::addChild(std::shared_ptr<EditorTreeNode<T>> child)
|
||||
{
|
||||
this->children().append(child);
|
||||
child->setParen(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool EditorTreeNode<T>::removeChild(EditorTreeNode<T>* child)
|
||||
bool EditorTreeNode<T>::removeChild(std::shared_ptr<EditorTreeNode<T>> child)
|
||||
{
|
||||
return this->children().removeOne(child);
|
||||
}
|
||||
|
@ -136,7 +138,7 @@ bool EditorTreeNode<T>::remove()
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
EditorTreeNode<T>* EditorTreeNode<T>::nextOnSameLevel()
|
||||
std::shared_ptr<EditorTreeNode<T>> EditorTreeNode<T>::nextOnSameLevel()
|
||||
{
|
||||
if (nextSibling() != nullptr)
|
||||
return nextSibling();
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
#define EDITORTREENODE_H
|
||||
|
||||
#include <QList>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
enum class TreeWalkStrategy {
|
||||
depthFirst,
|
||||
breadthFirst,
|
||||
mixture
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class EditorTreeNode;
|
||||
|
@ -17,34 +14,43 @@ class EditorTreeNode {
|
|||
public:
|
||||
explicit EditorTreeNode();
|
||||
explicit EditorTreeNode(T v);
|
||||
~EditorTreeNode();
|
||||
virtual ~EditorTreeNode();
|
||||
|
||||
const EditorTreeNode<T>* nextSibling();
|
||||
const EditorTreeNode<T>* parent() { return parentNode; }
|
||||
QList<EditorTreeNode<T>*>& children() { return childrenNodes; }
|
||||
void setParen(EditorTreeNode<T>* parent) { this->parentNode = parent; }
|
||||
|
||||
const std::shared_ptr<EditorTreeNode<T>> nextSibling();
|
||||
const std::shared_ptr<EditorTreeNode<T>> parent() { return parentNode; }
|
||||
QList<std::shared_ptr<EditorTreeNode<T>>>& children() { return childrenNodes; }
|
||||
void setParen(std::shared_ptr<EditorTreeNode<T>> parent) { this->parentNode = parent; }
|
||||
|
||||
T& value() { return val; }
|
||||
void setValue(const T &val);
|
||||
|
||||
const EditorTreeNode<T>* next(TreeWalkStrategy depth_first = TreeWalkStrategy::depthFirst);
|
||||
const EditorTreeNode<T>* getRoot();
|
||||
void move(EditorTreeNode<T>* to); // as child!
|
||||
void moveChildren(EditorTreeNode<T>* to); // move all children
|
||||
const std::shared_ptr<EditorTreeNode<T>> next(TreeWalkStrategy depth_first = TreeWalkStrategy::depthFirst);
|
||||
const std::shared_ptr<EditorTreeNode<T>> getRoot();
|
||||
void move(std::shared_ptr<EditorTreeNode<T>> to); // as child!
|
||||
void moveChildren(std::shared_ptr<EditorTreeNode<T>> to); // move all children
|
||||
|
||||
//signals:
|
||||
void nodeDataChanged(EditorTreeNode<T>* node);
|
||||
void treeChanged(EditorTreeNode<T>* node);
|
||||
|
||||
//public slots:
|
||||
void addChild(EditorTreeNode<T>* child);
|
||||
bool removeChild(EditorTreeNode<T>* child);
|
||||
void addChild(std::shared_ptr<EditorTreeNode<T>> child);
|
||||
bool removeChild(std::shared_ptr<EditorTreeNode<T>> child);
|
||||
bool remove();
|
||||
//*/
|
||||
|
||||
void setDataChangedSignal(std::function<void(std::shared_ptr<EditorTreeNode<T>>)> f)
|
||||
{
|
||||
nodeDataChanged = f;
|
||||
}
|
||||
void setTreeChangedSignal(std::function<void(std::shared_ptr<EditorTreeNode<T>>)> f)
|
||||
{
|
||||
treeChanged = f;
|
||||
}
|
||||
|
||||
private:
|
||||
//signal callbacks only set in the root node
|
||||
std::function<void(std::shared_ptr<EditorTreeNode<T>>)> nodeDataChanged;
|
||||
std::function<void(std::shared_ptr<EditorTreeNode<T>>)> treeChanged;
|
||||
T val;
|
||||
EditorTreeNode<T>* parentNode = nullptr;
|
||||
QList<EditorTreeNode<T>*> childrenNodes;
|
||||
EditorTreeNode<T>* nextOnSameLevel();
|
||||
std::shared_ptr<EditorTreeNode<T>> parentNode = nullptr;
|
||||
QList<std::shared_ptr<EditorTreeNode<T>>> childrenNodes;
|
||||
std::shared_ptr<EditorTreeNode<T>> nextOnSameLevel();
|
||||
};
|
||||
|
||||
#endif // EDITORTREENODE_H
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "guidededitorelementview.h"
|
||||
|
||||
#include "flowlayout.h"
|
||||
#include <QLabel>
|
||||
#include <QRegExp>
|
||||
#include <QRegExpValidator>
|
||||
|
@ -8,23 +9,116 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
GuidedEditorElementView::GuidedEditorElementView(const std::shared_ptr<EditorNode> &ref, QWidget* parent)
|
||||
: GuidedEditorElementView(parent)
|
||||
{
|
||||
this->setReference(ref);
|
||||
}
|
||||
GuidedEditorElementView::GuidedEditorElementView(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
this->validator = new QRegExpValidator();
|
||||
this->layout = new QVBoxLayout(this);
|
||||
|
||||
this->setLayout(layout);
|
||||
this->edit = nullptr;
|
||||
this->line = nullptr;
|
||||
this->title = new QLabel(this);
|
||||
}
|
||||
|
||||
void GuidedEditorElementView::render()
|
||||
{
|
||||
for (QWidget* w : this->propertyViews.values())
|
||||
this->layout->removeWidget(w);
|
||||
for (QString prop : this->propertyOrder) {
|
||||
this->layout->addWidget(this->propertyViews[prop]);
|
||||
// TODO: add possibility to collapse a subtree
|
||||
for (QObject* w : this->children())
|
||||
if (w->isWidgetType()) {
|
||||
this->layout->removeWidget((QWidget*)w);
|
||||
}
|
||||
|
||||
//title
|
||||
title->setText(reference->getData()->getTitle());
|
||||
layout->addWidget(title);
|
||||
|
||||
if (reference->getChildren().size() == 0) {
|
||||
// need global information about showing syntax, and other hints
|
||||
|
||||
if (reference->getData()->isMultiLine()) {
|
||||
layout->addWidget(edit);
|
||||
} else {
|
||||
layout->addWidget(line);
|
||||
}
|
||||
} else {
|
||||
//create container for subelements
|
||||
if (subContainer == nullptr) {
|
||||
subContainer = new QWidget(this);
|
||||
FlowLayout* flayout = new FlowLayout(subContainer);
|
||||
//create and render subelementviews! this is DEPTH_FIRST!!! TODO: make this breadth first!
|
||||
for (std::shared_ptr<EditorNode> node : reference->getChildren()) {
|
||||
GuidedEditorElementView* view = new GuidedEditorElementView(subContainer);
|
||||
view->setReference(node);
|
||||
flayout->addWidget(view);
|
||||
view->render();
|
||||
}
|
||||
}
|
||||
layout->addWidget(subContainer);
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedEditorElementView::inputChanged()
|
||||
{
|
||||
// run regex validation
|
||||
if (!validator->regExp().isEmpty()) {
|
||||
int pos;
|
||||
QValidator::State state;
|
||||
if (edit != nullptr) {
|
||||
QString data = edit->document()->toPlainText();
|
||||
state = validator->validate(data, pos);
|
||||
if (state == QValidator::State::Invalid) {
|
||||
edit->setTextColor(QColor(0xFF, 0, 0));
|
||||
} else if (state == QValidator::State::Intermediate) {
|
||||
edit->setTextColor(QColor(0xFF, 0xFF, 0));
|
||||
} else {
|
||||
line->setStyleSheet("color: #000000");
|
||||
}
|
||||
}
|
||||
if (line != nullptr) {
|
||||
QString data = line->text();
|
||||
state = validator->validate(data, pos);
|
||||
if (state == QValidator::State::Invalid) {
|
||||
line->setStyleSheet("color: #FF0000");
|
||||
} else if (state == QValidator::State::Intermediate) {
|
||||
line->setStyleSheet("color: #FFFF00");
|
||||
} else {
|
||||
line->setStyleSheet("color: #000000");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuidedEditorElementView::dataChanged()
|
||||
{
|
||||
//should be reload!
|
||||
this->render();
|
||||
}
|
||||
|
||||
void GuidedEditorElementView::nodeChanged()
|
||||
{
|
||||
this->render();
|
||||
}
|
||||
|
||||
std::shared_ptr<EditorNode> GuidedEditorElementView::getReference() const
|
||||
{
|
||||
return reference;
|
||||
}
|
||||
|
||||
void GuidedEditorElementView::setReference(const std::shared_ptr<EditorNode>& value)
|
||||
{
|
||||
reference = value;
|
||||
QRegExp r(reference->getData()->getRegex());
|
||||
validator->setRegExp(r);
|
||||
if (reference->getData()->isMultiLine()) {
|
||||
edit = new QTextEdit(this);
|
||||
this->connect(edit, &QTextEdit::textChanged, this, &GuidedEditorElementView::dataChanged);
|
||||
} else {
|
||||
line = new QLineEdit(this);
|
||||
this->connect(line, &QLineEdit::textChanged, this, &GuidedEditorElementView::dataChanged);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,33 +7,40 @@
|
|||
#include <QList>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QTextEdit>
|
||||
#include <memory>
|
||||
|
||||
#include "editorelementdata.h"
|
||||
#include "editorgroup.h"
|
||||
#include "guidededitorgroupview.h"
|
||||
#include "EditorNode.h"
|
||||
|
||||
class GuidedEditorElementView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GuidedEditorElementView(const std::shared_ptr<EditorNode> &ref,QWidget* parent = 0);
|
||||
explicit GuidedEditorElementView(QWidget* parent = 0);
|
||||
|
||||
void render();
|
||||
|
||||
std::shared_ptr<EditorNode> getReference() const;
|
||||
void setReference(const std::shared_ptr<EditorNode> &value);
|
||||
|
||||
signals:
|
||||
void inputChanged();
|
||||
|
||||
public slots:
|
||||
void inputChanged();
|
||||
void dataChanged();
|
||||
void nodeChanged();
|
||||
void render();
|
||||
|
||||
private:
|
||||
// layout
|
||||
QVBoxLayout* layout;
|
||||
QList<QString> propertyOrder;
|
||||
QMap<QString, QWidget*> propertyViews;
|
||||
GuidedEditorGroupView* groupView;
|
||||
EditorGroup* group;
|
||||
EditorElementData* elementData;
|
||||
QLabel* title;
|
||||
QTextEdit* edit;
|
||||
QLineEdit* line;
|
||||
QMap<QString, QWidget*> otherWidgets;
|
||||
QRegExpValidator* validator;
|
||||
QWidget* subContainer;
|
||||
std::shared_ptr<EditorNode> reference;
|
||||
};
|
||||
|
||||
#endif // GUIDEDEDITORELEMENTVIEW_H
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#include "guidededitorgroupview.h"
|
||||
|
||||
GuidedEditorGroupView::GuidedEditorGroupView(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
this->color = new QColor(rand() % 20 * 10 + 50, rand() % 20 * 10 + 50, rand() % 20 * 10 + 50);
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef GUIDEDEDITORGROUPVIEW_H
|
||||
#define GUIDEDEDITORGROUPVIEW_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QObject>
|
||||
class GuidedEditorGroupView : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GuidedEditorGroupView(QObject* parent = 0);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
QColor* color;
|
||||
QString stylesheet;
|
||||
};
|
||||
|
||||
#endif // GUIDEDEDITORGROUPVIEW_H
|
|
@ -24,11 +24,11 @@ GuidedEditorView::GuidedEditorView(QWidget* parent)
|
|||
}
|
||||
|
||||
GuidedEditorView::GuidedEditorView(
|
||||
EditorModel* model,
|
||||
EditorTree* tree,
|
||||
QWidget* parent)
|
||||
: GuidedEditorView(parent)
|
||||
{
|
||||
this->model = model;
|
||||
this->tree = tree;
|
||||
}
|
||||
|
||||
GuidedEditorView::~GuidedEditorView() {}
|
||||
|
@ -40,3 +40,37 @@ void GuidedEditorView::addElement(EditorElementData* element)
|
|||
this->layout()->addWidget(elementView);
|
||||
//this->update();
|
||||
}
|
||||
|
||||
void GuidedEditorView::setTree(EditorTree* tree)
|
||||
{
|
||||
this->tree = tree;
|
||||
|
||||
}
|
||||
|
||||
void GuidedEditorView::treeDataChangedSlot(std::shared_ptr<EditorNode> node)
|
||||
{
|
||||
// get node view
|
||||
auto view = nodeToViewMap[node];
|
||||
if(view != nullptr)
|
||||
view->dataChanged();
|
||||
}
|
||||
|
||||
void GuidedEditorView::treeChangedSlot(std::shared_ptr<EditorNode> node)
|
||||
{
|
||||
// get node view
|
||||
auto view = nodeToViewMap[node];
|
||||
if(view != nullptr)
|
||||
view->nodeChanged();
|
||||
}
|
||||
|
||||
void GuidedEditorView::render()
|
||||
{
|
||||
if(tree != nullptr) {
|
||||
// root node is invisible!
|
||||
for(std::shared_ptr<EditorNode> node : tree->getRoot()->getChildren()){
|
||||
auto view = new GuidedEditorElementView(node, this);
|
||||
this->editorLayout->addWidget(view);
|
||||
this->nodeToViewMap.insert(node, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef GUIDEDEDITOR_H
|
||||
#define GUIDEDEDITOR_H
|
||||
|
||||
#include "editormodel.h"
|
||||
#include "EditorTree.h"
|
||||
#include "editorelementdata.h"
|
||||
#include "guidededitorelementview.h"
|
||||
#include "flowlayout.h"
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
@ -11,18 +11,25 @@ class GuidedEditorView : public QWidget {
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit GuidedEditorView(QWidget* parent = 0);
|
||||
GuidedEditorView(EditorModel* model,
|
||||
QWidget* parent = 0);
|
||||
explicit GuidedEditorView(EditorTree* tree, QWidget* parent = 0);
|
||||
~GuidedEditorView();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void addElement(EditorElementData* element);
|
||||
void setTree(EditorTree* tree);
|
||||
|
||||
void treeDataChangedSlot(std::shared_ptr<EditorNode> node);
|
||||
void treeChangedSlot(std::shared_ptr<EditorNode> node);
|
||||
|
||||
void render();
|
||||
|
||||
private:
|
||||
FlowLayout* editorLayout;
|
||||
EditorModel* model;
|
||||
EditorTree* tree;
|
||||
QMap<std::shared_ptr<EditorNode>, GuidedEditorElementView*> nodeToViewMap;
|
||||
|
||||
};
|
||||
|
||||
#endif // GUIDEDEDITOR_H
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
set(HEADERS DataLoader.h
|
||||
)
|
||||
|
||||
set(SOURCES DataLoader.cpp
|
||||
|
||||
)
|
||||
|
||||
add_library(protocol ${HEADERS} ${SOURCES})
|
||||
|
||||
qt5_use_modules(protocol Core)
|
|
@ -0,0 +1,107 @@
|
|||
#include "DataLoader.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
DataLoader::DataLoader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DataLoader::DataLoader(QString directory)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DataLoader::loadProtocolDirectory(QString directory)
|
||||
{
|
||||
// awaiting
|
||||
QDir dir(directory);
|
||||
QStringList filters;
|
||||
filters << "*osi*";
|
||||
for(QString entry : dir.entryList(filters)){
|
||||
if(QDir(entry).exists()) {
|
||||
QDir cdir(entry);
|
||||
QStringList ymlfilter;
|
||||
ymlfilter << "*.yml";
|
||||
for(QString file : cdir.entryList(ymlfilter)){
|
||||
YAML::Node node = YAML::LoadFile(
|
||||
QString(directory + QDir::separator() + entry + QDir::separator() + file).toStdString());
|
||||
protocolList.append(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<EditorNode> DataLoader::generateTree(YAML::Node &node)
|
||||
{
|
||||
if(!node["protocol"])
|
||||
return nullptr;
|
||||
|
||||
// root node containing the protocol.
|
||||
std::shared_ptr<EditorNode> editorNode = std::shared_ptr<EditorNode>(new EditorNode());
|
||||
std::shared_ptr<EditorElementData> data = std::shared_ptr<EditorElementData>(new EditorElementData());
|
||||
|
||||
data->setTitle(QString::fromStdString(node["protocol"]["name"].as<std::string>()));
|
||||
if(node["protocol"]["longname"])
|
||||
data->setHint("subtitle", QString::fromStdString(node["protocol"]["longname"].as<std::string>()));
|
||||
editorNode->setData(data);
|
||||
// add children containing the fields
|
||||
auto fields = node["protocol"]["fields"];
|
||||
for(YAML::Node n : fields) {
|
||||
this->genNode(n, editorNode);
|
||||
}
|
||||
return editorNode;
|
||||
}
|
||||
|
||||
bool DataLoader::getProtocolByName(QString name, YAML::Node &node)
|
||||
{
|
||||
for(YAML::Node n : protocolList) {
|
||||
if(n["protocol"]) {
|
||||
if(name.toStdString().compare(n["protocol"]["name"].as<std::string>())) {
|
||||
node = n;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList DataLoader::getProtocolNames()
|
||||
{
|
||||
QStringList names;
|
||||
for(YAML::Node node : protocolList) {
|
||||
if(node["protocol"]) {
|
||||
names << QString::fromStdString(node["protocol"]["name"].as<std::string>());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
std::shared_ptr<EditorNode> DataLoader::genNode(YAML::Node &n, std::shared_ptr<EditorNode> parent)
|
||||
{
|
||||
|
||||
std::shared_ptr<EditorNode> newNode = std::shared_ptr<EditorNode>(new EditorNode());
|
||||
newNode->setParent(parent);
|
||||
std::shared_ptr<EditorElementData> newData = std::shared_ptr<EditorElementData>(new EditorElementData());
|
||||
newData->setTitle(QString::fromStdString(n["field"].as<std::string>()));
|
||||
if(n["desc"])
|
||||
newData->setHint("desc", QString::fromStdString(n["desc"].as<std::string>()));
|
||||
if(n["bytes"])
|
||||
newData->setBytes(n["bytes"].as<ulong>());
|
||||
else if (n["bits"])
|
||||
newData->setBits(n["bits"].as<ulong>());
|
||||
if(n["data"])
|
||||
newData->setData(n["data"].as<bool>());
|
||||
if(n["optional"])
|
||||
newData->setOptional(n["data"].as<bool>());
|
||||
if(n["nextlayer"])
|
||||
newData->setNextLayer(n["nextlayer"].as<bool>());
|
||||
newNode->setData(newData);
|
||||
if(n["subfields"])
|
||||
{
|
||||
auto fields = n["subfields"];
|
||||
for(YAML::Node subn : fields)
|
||||
newNode->addChild(this->genNode(subn, newNode));
|
||||
}
|
||||
return newNode;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef DATALOADER_H
|
||||
#define DATALOADER_H
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include "../editor/EditorNode.h"
|
||||
|
||||
|
||||
class DataLoader {
|
||||
public:
|
||||
explicit DataLoader();
|
||||
explicit DataLoader(QString directory);
|
||||
|
||||
void loadProtocolDirectory(QString directory);
|
||||
|
||||
std::shared_ptr<EditorNode> generateTree(YAML::Node &node);
|
||||
|
||||
bool getProtocolByName(QString name, YAML::Node &node);
|
||||
QStringList getProtocolNames();
|
||||
|
||||
private:
|
||||
std::shared_ptr<EditorNode> genNode(YAML::Node& n, std::shared_ptr<EditorNode> parent = nullptr);
|
||||
QString directory;
|
||||
QList<YAML::Node> protocolList;
|
||||
};
|
||||
|
||||
|
||||
#endif // DATALOADER_H
|
Reference in New Issue