Compare commits
No commits in common. "f93a2afa639e1aa196b79fb9048ab55035c98cff" and "0c4afe87d9f96b1be16cf651343ddb3727e513dc" have entirely different histories.
f93a2afa63
...
0c4afe87d9
|
@ -37,5 +37,5 @@ add_executable(SchemeEditor
|
||||||
comdel/parser/comdellexer.cpp
|
comdel/parser/comdellexer.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
mainwindow.ui
|
mainwindow.ui
|
||||||
comdel/domain/comdel_validator.cpp comdel/domain/comdel_validator.h comdel/display/attribute_dialog.cpp comdel/display/attribute_dialog.h comdel/display/name_dialog.cpp comdel/display/name_dialog.h comdel/domain/comdel_generator.cpp comdel/domain/comdel_generator.h comdel/display/library_list.cpp comdel/display/library_list.h application.cpp application.h)
|
comdel/domain/comdel_validator.cpp comdel/domain/comdel_validator.h comdel/display/attribute_dialog.cpp comdel/display/attribute_dialog.h comdel/display/name_dialog.cpp comdel/display/name_dialog.h comdel/domain/comdel_generator.cpp comdel/domain/comdel_generator.h comdel/display/library_list.cpp comdel/display/library_list.h)
|
||||||
target_link_libraries(SchemeEditor Qt5::Core Qt5::Gui Qt5::Widgets)
|
target_link_libraries(SchemeEditor Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||||
|
|
199
application.cpp
199
application.cpp
|
@ -1,199 +0,0 @@
|
||||||
//
|
|
||||||
// Created by bbr on 27.05.22..
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "application.h"
|
|
||||||
#include "comdel/parser/parse_context.h"
|
|
||||||
#include "comdel/parser/parser_util.h"
|
|
||||||
#include "comdel/domain/schema_creator.h"
|
|
||||||
#include "comdel/domain/comdel_generator.h"
|
|
||||||
#include "comdel/domain/comdel_validator.h"
|
|
||||||
|
|
||||||
std::optional<domain::Library> Application::getLibrary() {
|
|
||||||
return library;
|
|
||||||
}
|
|
||||||
|
|
||||||
domain::Schema *Application::getSchema() {
|
|
||||||
return schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::clear() {
|
|
||||||
if(schema != nullptr) {
|
|
||||||
delete schema;
|
|
||||||
schema = nullptr;
|
|
||||||
}
|
|
||||||
library = std::nullopt;
|
|
||||||
libraryPath = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::loadLibrary(std::string& filename, std::ostream &errorOutput) {
|
|
||||||
clear();
|
|
||||||
|
|
||||||
ParseContext parseContext;
|
|
||||||
auto libraryNode = load_library_from_file(&parseContext, filename.c_str(), errorOutput);
|
|
||||||
if(libraryNode) {
|
|
||||||
domain::SchemaCreator generator(validators);
|
|
||||||
library = generator.loadLibrary(*libraryNode);
|
|
||||||
|
|
||||||
for (auto& error : generator.getErrors()) {
|
|
||||||
parseContext.formatError(error, errorOutput, "ERROR: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(library.has_value()) {
|
|
||||||
errorOutput<<"Failed creating library model"<<std::endl;
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
libraryPath = filename;
|
|
||||||
// on library load we create a new schema
|
|
||||||
schema = new domain::Schema();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
errorOutput<<"Failed parsing library"<<std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::loadSchema(std::string &filename, std::ostream &errorOutput) {
|
|
||||||
clear();
|
|
||||||
|
|
||||||
ParseContext parseContext;
|
|
||||||
auto schemaNode = load_schema_from_file(&parseContext, filename.c_str(), errorOutput);
|
|
||||||
|
|
||||||
if(schemaNode) {
|
|
||||||
domain::SchemaCreator generator(validators);
|
|
||||||
library = generator.loadLibrary(*schemaNode->library);
|
|
||||||
|
|
||||||
libraryPath = schemaNode->source->asString();
|
|
||||||
|
|
||||||
for (auto& error : generator.getErrors()) {
|
|
||||||
parseContext.formatError(error, errorOutput, "ERROR: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(library) {
|
|
||||||
schema = generator.loadSchema(*schemaNode, *library);
|
|
||||||
|
|
||||||
for (auto& error : generator.getErrors()) {
|
|
||||||
parseContext.formatError(error, errorOutput, "ERROR: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(schema == nullptr) {
|
|
||||||
clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
errorOutput<<"Failed parsing library"<<std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::generateSchema(std::ostringstream &output) {
|
|
||||||
if(schema == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
domain::generate_schema(libraryPath, schema, output);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<domain::ValidationError> Application::validateSchema() {
|
|
||||||
if(schema == nullptr) {
|
|
||||||
return std::vector<domain::ValidationError>{domain::ValidationError(domain::Action::ERROR, "No schema loaded")};
|
|
||||||
}
|
|
||||||
|
|
||||||
domain::ComdelValidator validator{validators};
|
|
||||||
|
|
||||||
domain::ValidationContext context;
|
|
||||||
context.instance = nullptr;
|
|
||||||
context.attribute = nullptr;
|
|
||||||
context.addressSpaces = {};
|
|
||||||
|
|
||||||
for(auto &lib: library->getAddressSpaces()) {
|
|
||||||
context.addressSpaces.insert(std::make_pair(lib.getName(), lib));
|
|
||||||
}
|
|
||||||
auto errors = validator.validateSchema(*schema, context);
|
|
||||||
|
|
||||||
auto countValidation = validator.validateInstanceCount(*schema, *library, context);
|
|
||||||
errors.insert(errors.end(), countValidation.begin(), countValidation.end());
|
|
||||||
|
|
||||||
auto nameValidation = validator.validateInstanceNames(*schema, *library, context);
|
|
||||||
errors.insert(errors.end(), nameValidation.begin(), nameValidation.end());
|
|
||||||
|
|
||||||
auto pinValidation = validator.validatePinConnections(*schema, *library, context);
|
|
||||||
errors.insert(errors.end(), pinValidation.begin(), pinValidation.end());
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<domain::ValidationError> Application::generateComdel(std::ostringstream &output) {
|
|
||||||
|
|
||||||
auto errors = validateSchema();
|
|
||||||
if(Application::hasErrors(errors)) {
|
|
||||||
// as long as all validation errors are warning we continue with build
|
|
||||||
domain::generate_comdel(schema, library.value(), output);
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::hasErrors(std::vector<domain::ValidationError> errors) {
|
|
||||||
for(auto& err: errors) {
|
|
||||||
if(err.type == domain::Action::ERROR) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static instance of application
|
|
||||||
static Application *application = nullptr;
|
|
||||||
|
|
||||||
Application *Application::instance() {
|
|
||||||
if(application == nullptr) {
|
|
||||||
application = new Application();
|
|
||||||
}
|
|
||||||
return application;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<domain::ComponentInstance> Application::addComponent(domain::Component component, std::vector<domain::InstanceAttribute> attributes, int x, int y) {
|
|
||||||
std::set<std::string> names;
|
|
||||||
for(const auto& c: schema->componentInstances) {
|
|
||||||
names.insert(c->name);
|
|
||||||
}
|
|
||||||
std::string name = generateName(names, component.getInstanceName());
|
|
||||||
|
|
||||||
schema->componentInstances.push_back(std::make_shared<domain::ComponentInstance>(name, attributes, std::make_pair(x, y), component));
|
|
||||||
return schema->componentInstances.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<domain::BusInstance> Application::addBus(domain::Bus bus, int x, int y) {
|
|
||||||
std::set<std::string> names;
|
|
||||||
for(const auto& b: schema->busInstances) {
|
|
||||||
names.insert(b->name);
|
|
||||||
}
|
|
||||||
std::string name = generateName(names, bus.getInstanceName());
|
|
||||||
|
|
||||||
schema->busInstances.push_back(std::make_shared<domain::BusInstance>(name, std::make_pair(x, y), bus));
|
|
||||||
return schema->busInstances.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string Application::generateName(std::set<std::string>& names, std::string instanceName) {
|
|
||||||
if(names.find(instanceName) == names.end()) {
|
|
||||||
return instanceName;
|
|
||||||
}
|
|
||||||
char buffer[4];
|
|
||||||
for(int i=0; i<1000; i++) {
|
|
||||||
sprintf(buffer, "%03d", i);
|
|
||||||
auto name = instanceName + "_" + buffer;
|
|
||||||
if(names.find(name) == names.end()) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return default value as this should never happen
|
|
||||||
return instanceName;
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
//
|
|
||||||
// Created by bbr on 27.05.22..
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SCHEMEEDITOR_APPLICATION_H
|
|
||||||
#define SCHEMEEDITOR_APPLICATION_H
|
|
||||||
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include "comdel/domain/library.h"
|
|
||||||
#include "comdel/domain/schema.h"
|
|
||||||
#include "comdel/domain/comdel_validator.h"
|
|
||||||
|
|
||||||
|
|
||||||
class Application {
|
|
||||||
private:
|
|
||||||
std::string libraryPath;
|
|
||||||
std::optional<domain::Library> library = std::nullopt;
|
|
||||||
domain::Schema* schema = nullptr;
|
|
||||||
std::vector<domain::FunctionValidator*> validators = domain::getSupportedValidators();
|
|
||||||
|
|
||||||
std::string generateName(std::set<std::string>& names, std::string instanceName);
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::optional<domain::Library> getLibrary();
|
|
||||||
domain::Schema* getSchema();
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
bool loadLibrary(std::string& filename, std::ostream& errorOutput);
|
|
||||||
bool loadSchema(std::string& filename, std::ostream& errorOutput);
|
|
||||||
|
|
||||||
static Application* instance();
|
|
||||||
|
|
||||||
bool generateSchema(std::ostringstream &output);
|
|
||||||
std::vector<domain::ValidationError> validateSchema();
|
|
||||||
std::vector<domain::ValidationError> generateComdel(std::ostringstream &output);
|
|
||||||
|
|
||||||
std::shared_ptr<domain::ComponentInstance> addComponent(domain::Component component, std::vector<domain::InstanceAttribute> attributes, int x, int y);
|
|
||||||
std::shared_ptr<domain::BusInstance> addBus(domain::Bus bus, int x, int y);
|
|
||||||
|
|
||||||
static bool hasErrors(std::vector<domain::ValidationError> empty);
|
|
||||||
|
|
||||||
~Application() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //SCHEMEEDITOR_APPLICATION_H
|
|
|
@ -5,8 +5,6 @@
|
||||||
#include "attribute_dialog.h"
|
#include "attribute_dialog.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
#include "application.h"
|
|
||||||
|
|
||||||
namespace display {
|
namespace display {
|
||||||
|
|
||||||
void AttributeDialog::onUpdate() {
|
void AttributeDialog::onUpdate() {
|
||||||
|
@ -17,7 +15,7 @@ namespace display {
|
||||||
|
|
||||||
domain::ValidationContext context;
|
domain::ValidationContext context;
|
||||||
|
|
||||||
for (auto &addressSpace: Application::instance()->getLibrary()->getAddressSpaces()) {
|
for (auto &addressSpace: MainWindow::getLibrary()->getAddressSpaces()) {
|
||||||
context.addressSpaces.insert(std::make_pair(addressSpace.getName(), addressSpace));
|
context.addressSpaces.insert(std::make_pair(addressSpace.getName(), addressSpace));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,24 +2,17 @@
|
||||||
#include "attribute_dialog.h"
|
#include "attribute_dialog.h"
|
||||||
#include "name_dialog.h"
|
#include "name_dialog.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "application.h"
|
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QLine>
|
#include <QLine>
|
||||||
#include <QGraphicsSceneContextMenuEvent>
|
#include <QGraphicsSceneContextMenuEvent>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace display {
|
namespace display {
|
||||||
|
|
||||||
void Component::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
|
void Component::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
menu.addAction("Izmjeni ime", [this]() {
|
menu.addAction("Izmjeni ime", [this]() {
|
||||||
std::set<std::string> names;
|
auto dialog = new NameDialog(this->instance.get());
|
||||||
for(const auto &component: Application::instance()->getSchema()->componentInstances) {
|
|
||||||
names.insert(component->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dialog = new NameDialog(this->instance.get(), names);
|
|
||||||
dialog->exec();
|
dialog->exec();
|
||||||
});
|
});
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
|
@ -31,7 +24,7 @@ namespace display {
|
||||||
[attr]() {
|
[attr]() {
|
||||||
if (attr->value.getType() == domain::Value::MEMORY_REFERENCE) {
|
if (attr->value.getType() == domain::Value::MEMORY_REFERENCE) {
|
||||||
auto dialog = new MemoryDialog(attr,
|
auto dialog = new MemoryDialog(attr,
|
||||||
Application::instance()->getSchema()->componentInstances);
|
MainWindow::getSchema()->componentInstances);
|
||||||
dialog->exec();
|
dialog->exec();
|
||||||
} else {
|
} else {
|
||||||
auto dialog = new AttributeDialog(attr);
|
auto dialog = new AttributeDialog(attr);
|
||||||
|
@ -99,12 +92,7 @@ namespace display {
|
||||||
void Bus::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
|
void Bus::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
menu.addAction("Izmjeni ime", [this]() {
|
menu.addAction("Izmjeni ime", [this]() {
|
||||||
std::set<std::string> names;
|
auto dialog = new NameDialog(this->busInstance.get());
|
||||||
for(const auto &component: Application::instance()->getSchema()->busInstances) {
|
|
||||||
names.insert(component->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dialog = new NameDialog(this->busInstance.get(), names);
|
|
||||||
dialog->exec();
|
dialog->exec();
|
||||||
});
|
});
|
||||||
menu.exec(event->screenPos());
|
menu.exec(event->screenPos());
|
||||||
|
@ -121,7 +109,7 @@ namespace display {
|
||||||
QVariant BusGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) {
|
QVariant BusGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) {
|
||||||
if (change == ItemPositionChange && scene()) {
|
if (change == ItemPositionChange && scene()) {
|
||||||
// value is the new position.
|
// value is the new position.
|
||||||
QPoint newPos = value.toPointF().toPoint();
|
QPointF newPos = value.toPointF();
|
||||||
busInstance->position.first = newPos.x();
|
busInstance->position.first = newPos.x();
|
||||||
busInstance->position.second = newPos.y();
|
busInstance->position.second = newPos.y();
|
||||||
|
|
||||||
|
@ -147,7 +135,7 @@ namespace display {
|
||||||
QVariant ComponentGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) {
|
QVariant ComponentGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) {
|
||||||
if (change == ItemPositionChange && scene()) {
|
if (change == ItemPositionChange && scene()) {
|
||||||
// value is the new position.
|
// value is the new position.
|
||||||
QPoint newPos = value.toPointF().toPoint();
|
QPointF newPos = value.toPointF();
|
||||||
componentInstance->position.first = newPos.x();
|
componentInstance->position.first = newPos.x();
|
||||||
componentInstance->position.second = newPos.y();
|
componentInstance->position.second = newPos.y();
|
||||||
|
|
||||||
|
|
|
@ -2,54 +2,37 @@
|
||||||
// Created by bbr on 18. 04. 2022..
|
// Created by bbr on 18. 04. 2022..
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include "name_dialog.h"
|
#include "name_dialog.h"
|
||||||
|
|
||||||
display::NameDialog::NameDialog(domain::ComponentInstance *instance, std::set<std::string>& names) : componentInstance(instance), usedNames(names) {
|
display::NameDialog::NameDialog(domain::ComponentInstance *instance) : componentInstance(instance) {
|
||||||
usedNames.erase(instance->name);
|
|
||||||
|
|
||||||
auto *layout = new QVBoxLayout(this);
|
auto *layout = new QVBoxLayout(this);
|
||||||
layout->addWidget(new QLabel("Izmjeni ime", this));
|
layout->addWidget(new QLabel("Izmjeni ime", this));
|
||||||
|
|
||||||
edit = new QLineEdit(this);
|
edit = new QLineEdit(this);
|
||||||
edit->insert(instance->name.c_str());
|
edit->insert(instance->name.c_str());
|
||||||
connect(edit, &QLineEdit::textChanged, this, &NameDialog::onNameUpdate);
|
|
||||||
layout->addWidget(edit);
|
layout->addWidget(edit);
|
||||||
this->setWindowTitle("Izmjeni ime");
|
this->setWindowTitle("Izmjeni ime");
|
||||||
button = new QPushButton("Ažuriraj", this);
|
auto *button = new QPushButton("Ažuriraj", this);
|
||||||
connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange);
|
connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange);
|
||||||
layout->addWidget(button);
|
layout->addWidget(button);
|
||||||
this->setLayout(layout);
|
this->setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
display::NameDialog::NameDialog(domain::BusInstance *instance, std::set<std::string>& names): busInstance(instance), usedNames(names) {
|
display::NameDialog::NameDialog(domain::BusInstance *instance): busInstance(instance) {
|
||||||
usedNames.erase(instance->name);
|
|
||||||
|
|
||||||
auto *layout = new QVBoxLayout(this);
|
auto *layout = new QVBoxLayout(this);
|
||||||
layout->addWidget(new QLabel("Izmjeni ime", this));
|
layout->addWidget(new QLabel("Izmjeni ime", this));
|
||||||
|
|
||||||
edit = new QLineEdit(this);
|
edit = new QLineEdit(this);
|
||||||
edit->insert(instance->name.c_str());
|
edit->insert(instance->name.c_str());
|
||||||
connect(edit, &QLineEdit::textChanged, this, &NameDialog::onNameUpdate);
|
|
||||||
layout->addWidget(edit);
|
layout->addWidget(edit);
|
||||||
this->setWindowTitle("Izmjeni ime");
|
this->setWindowTitle("Izmjeni ime");
|
||||||
button = new QPushButton("Ažuriraj", this);
|
auto *button = new QPushButton("Ažuriraj", this);
|
||||||
connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange);
|
connect(button, &QPushButton::clicked, this, &NameDialog::onNameChange);
|
||||||
layout->addWidget(button);
|
layout->addWidget(button);
|
||||||
this->setLayout(layout);
|
this->setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void display::NameDialog::onNameUpdate(const QString &text) {
|
|
||||||
if(usedNames.find(text.toStdString()) == usedNames.end()) {
|
|
||||||
button->setDisabled(false);
|
|
||||||
} else {
|
|
||||||
button->setDisabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void display::NameDialog::onNameChange() {
|
void display::NameDialog::onNameChange() {
|
||||||
|
|
||||||
|
|
||||||
if (componentInstance != nullptr) {
|
if (componentInstance != nullptr) {
|
||||||
componentInstance->name = this->edit->text().toStdString();
|
componentInstance->name = this->edit->text().toStdString();
|
||||||
} else if (busInstance != nullptr) {
|
} else if (busInstance != nullptr) {
|
||||||
|
|
|
@ -7,28 +7,24 @@
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <comdel/domain/instance.h>
|
#include <comdel/domain/instance.h>
|
||||||
|
|
||||||
namespace display {
|
namespace display {
|
||||||
|
|
||||||
class NameDialog : public QDialog {
|
class NameDialog : public QDialog {
|
||||||
|
|
||||||
std::set<std::string> usedNames;
|
|
||||||
QLineEdit *edit = nullptr;
|
QLineEdit *edit = nullptr;
|
||||||
domain::ComponentInstance *componentInstance = nullptr;
|
domain::ComponentInstance *componentInstance = nullptr;
|
||||||
domain::BusInstance *busInstance = nullptr;
|
domain::BusInstance *busInstance = nullptr;
|
||||||
QPushButton *button;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
NameDialog(domain::ComponentInstance *instance, std::set<std::string>& names);
|
NameDialog(domain::ComponentInstance *instance);
|
||||||
|
|
||||||
NameDialog(domain::BusInstance *instance, std::set<std::string>& names);
|
NameDialog(domain::BusInstance *instance);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onNameUpdate(const QString& text);
|
|
||||||
void onNameChange();
|
void onNameChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "component_display.h"
|
#include "component_display.h"
|
||||||
#include "schema_display.h"
|
#include "schema_display.h"
|
||||||
#include "application.h"
|
|
||||||
#include "attribute_dialog.h"
|
|
||||||
|
|
||||||
#include <QDrag>
|
#include <QDrag>
|
||||||
#include <QDragEnterEvent>
|
#include <QDragEnterEvent>
|
||||||
|
@ -12,14 +10,13 @@ namespace display {
|
||||||
Schema::Schema() {
|
Schema::Schema() {
|
||||||
this->selectedBrush.setColor(QColor::fromRgb(20, 20, 125));
|
this->selectedBrush.setColor(QColor::fromRgb(20, 20, 125));
|
||||||
this->selectedPen.setColor(QColor::fromRgb(20, 20, 125));
|
this->selectedPen.setColor(QColor::fromRgb(20, 20, 125));
|
||||||
schema = nullptr;
|
|
||||||
library = std::nullopt;
|
|
||||||
this->setScene(&scene);
|
this->setScene(&scene);
|
||||||
this->setAcceptDrops(true);
|
this->setAcceptDrops(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Schema::setSchema(domain::Schema *_schema, std::optional<domain::Library> _library) {
|
void Schema::setSchema(domain::Schema *_schema, domain::Library *_library) {
|
||||||
components.clear();
|
components.clear();
|
||||||
buses.clear();
|
buses.clear();
|
||||||
|
|
||||||
|
@ -88,30 +85,15 @@ namespace display {
|
||||||
|
|
||||||
auto attributes = std::vector<domain::InstanceAttribute>();
|
auto attributes = std::vector<domain::InstanceAttribute>();
|
||||||
for (auto attr: component.getAttributes()) {
|
for (auto attr: component.getAttributes()) {
|
||||||
domain::InstanceAttribute attribute(attr.getName(), attr.getDefault(), attr);
|
attributes.emplace_back(attr.getName(), attr.getDefault(), attr);
|
||||||
if(attr.getPopup().has_value() && attr.getPopup()->getType() == domain::Popup::AUTOMATIC) {
|
|
||||||
if(attr.getDefault().isType(domain::Value::MEMORY_REFERENCE)) {
|
|
||||||
auto dialog = new MemoryDialog(&attribute, schema->componentInstances);
|
|
||||||
if(dialog->exec() == QDialog::Rejected) {
|
|
||||||
// if any dialog isn't set, whole creation is rejected
|
|
||||||
event->ignore();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto dialog = new AttributeDialog(&attribute);
|
|
||||||
if(dialog->exec() == QDialog::Rejected) {
|
|
||||||
// if any dialog isn't set, whole creation is rejected
|
|
||||||
event->ignore();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributes.push_back(attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto currentPos = this->mapToScene(event->pos()).toPoint();
|
auto currentPos = this->mapToScene(event->pos());
|
||||||
|
|
||||||
auto instance = Application::instance()->addComponent(component, attributes, currentPos.x(), currentPos.y());
|
auto instance = std::make_shared<domain::ComponentInstance>(component.getInstanceName(), attributes,
|
||||||
|
std::make_pair(currentPos.x(), currentPos.y()),
|
||||||
|
component);
|
||||||
|
schema->componentInstances.push_back(instance);
|
||||||
|
|
||||||
auto group = new display::ComponentGroup(instance);
|
auto group = new display::ComponentGroup(instance);
|
||||||
scene.addItem(group);
|
scene.addItem(group);
|
||||||
|
@ -128,9 +110,11 @@ namespace display {
|
||||||
if (event->mimeData()->hasFormat("comdel/bus")) {
|
if (event->mimeData()->hasFormat("comdel/bus")) {
|
||||||
auto bus = library->getBus(event->mimeData()->data("comdel/bus").toStdString());
|
auto bus = library->getBus(event->mimeData()->data("comdel/bus").toStdString());
|
||||||
|
|
||||||
auto currentPos = this->mapToScene(event->pos()).toPoint();
|
auto currentPos = this->mapToScene(event->pos());
|
||||||
|
|
||||||
auto instance = Application::instance()->addBus(bus, currentPos.x(), currentPos.y());
|
auto instance = std::make_shared<domain::BusInstance>(bus.getName(),
|
||||||
|
std::make_pair(currentPos.x(), currentPos.y()), bus);
|
||||||
|
schema->busInstances.push_back(instance);
|
||||||
|
|
||||||
auto group = new display::BusGroup(instance);
|
auto group = new display::BusGroup(instance);
|
||||||
scene.addItem(group);
|
scene.addItem(group);
|
||||||
|
@ -232,9 +216,6 @@ namespace display {
|
||||||
|
|
||||||
for (auto bus: busInstances) {
|
for (auto bus: busInstances) {
|
||||||
auto &group = buses[bus->name];
|
auto &group = buses[bus->name];
|
||||||
if(group == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto rect = new QGraphicsRectItem(group->boundingRect());
|
auto rect = new QGraphicsRectItem(group->boundingRect());
|
||||||
rect->setPen(selectedPen);
|
rect->setPen(selectedPen);
|
||||||
rect->setPos(group->scenePos());
|
rect->setPos(group->scenePos());
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace display {
|
||||||
std::vector<BusConnection *> busConnections;
|
std::vector<BusConnection *> busConnections;
|
||||||
std::vector<DirectConnection *> directConnections;
|
std::vector<DirectConnection *> directConnections;
|
||||||
|
|
||||||
void setSchema(domain::Schema *schema, std::optional<domain::Library> library);
|
void setSchema(domain::Schema *schema, domain::Library *library);
|
||||||
|
|
||||||
void updateConnections();
|
void updateConnections();
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ namespace display {
|
||||||
private:
|
private:
|
||||||
QGraphicsScene scene;
|
QGraphicsScene scene;
|
||||||
|
|
||||||
domain::Schema *schema{};
|
domain::Schema *schema;
|
||||||
std::optional<domain::Library> library;
|
domain::Library *library;
|
||||||
|
|
||||||
std::vector<domain::BusInstance *>
|
std::vector<domain::BusInstance *>
|
||||||
getAvailableConnectionBusses(domain::ComponentInstance *instance, domain::Pin &pin);
|
getAvailableConnectionBusses(domain::ComponentInstance *instance, domain::Pin &pin);
|
||||||
|
|
|
@ -10,11 +10,6 @@ namespace domain {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Bus::getInstanceName() {
|
|
||||||
return instanceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Wire::getWidth() {
|
int Wire::getWidth() {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
@ -35,9 +30,9 @@ namespace domain {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bus::Bus(std::string name, std::string instanceName, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires,
|
Bus::Bus(std::string name, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires,
|
||||||
std::optional<ui::Bus> displayBus)
|
std::optional<ui::Bus> displayBus)
|
||||||
: name(name), instanceName(instanceName), tooltip(tooltip), type(type), count(count), wires(wires), displayBus(displayBus) {}
|
: name(name), tooltip(tooltip), type(type), count(count), wires(wires), displayBus(displayBus) {}
|
||||||
|
|
||||||
std::string Bus::getName() {
|
std::string Bus::getName() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -62,4 +57,5 @@ namespace domain {
|
||||||
std::optional<ui::Bus> Bus::getDisplayBus() {
|
std::optional<ui::Bus> Bus::getDisplayBus() {
|
||||||
return displayBus;
|
return displayBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace domain
|
} // namespace domain
|
||||||
|
|
|
@ -54,7 +54,6 @@ namespace domain {
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string instanceName;
|
|
||||||
std::string tooltip;
|
std::string tooltip;
|
||||||
BusType type;
|
BusType type;
|
||||||
|
|
||||||
|
@ -63,13 +62,11 @@ namespace domain {
|
||||||
std::vector<Wire> wires;
|
std::vector<Wire> wires;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Bus(std::string name, std::string instanceName, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires,
|
Bus(std::string name, std::string tooltip, BusType type, std::pair<int, int> count, std::vector<Wire> wires,
|
||||||
std::optional<ui::Bus> display = std::nullopt);
|
std::optional<ui::Bus> display = std::nullopt);
|
||||||
|
|
||||||
std::string getName();
|
std::string getName();
|
||||||
|
|
||||||
std::string getInstanceName();
|
|
||||||
|
|
||||||
std::string getTooltip();
|
std::string getTooltip();
|
||||||
|
|
||||||
BusType getType();
|
BusType getType();
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#include <set>
|
|
||||||
#include "comdel_validator.h"
|
#include "comdel_validator.h"
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
|
|
||||||
|
@ -39,11 +38,11 @@ namespace domain {
|
||||||
auto message = populateMessage(
|
auto message = populateMessage(
|
||||||
"Not enough instances of component '{componentName}' required at least {min}, found {count}",
|
"Not enough instances of component '{componentName}' required at least {min}, found {count}",
|
||||||
context);
|
context);
|
||||||
errors.emplace_back(Action::ERROR, message);
|
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message});
|
||||||
} else if (count > comp.getCount().second) {
|
} else if (count > comp.getCount().second) {
|
||||||
auto message = populateMessage(
|
auto message = populateMessage(
|
||||||
"To many instances of component '{componentName}' allow at most {max}, found {count}", context);
|
"To many instances of component '{componentName}' allow at most {max}, found {count}", context);
|
||||||
errors.emplace_back(Action::ERROR, message);
|
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,11 +64,11 @@ namespace domain {
|
||||||
if (count < bus.getCount().first) {
|
if (count < bus.getCount().first) {
|
||||||
auto message = populateMessage(
|
auto message = populateMessage(
|
||||||
"Not enough instances of bus '{busName}' required at least {min}, found {count}", context);
|
"Not enough instances of bus '{busName}' required at least {min}, found {count}", context);
|
||||||
errors.emplace_back(Action::ERROR, message);
|
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message});
|
||||||
} else if (count > bus.getCount().second) {
|
} else if (count > bus.getCount().second) {
|
||||||
auto message = populateMessage(
|
auto message = populateMessage(
|
||||||
"To many instances of bus '{busName}' allow at most {max}, found {count}", context);
|
"To many instances of bus '{busName}' allow at most {max}, found {count}", context);
|
||||||
errors.emplace_back(Action::ERROR, message);
|
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +86,7 @@ namespace domain {
|
||||||
context.instance = inst.get();
|
context.instance = inst.get();
|
||||||
context.attributes["instanceName"] = Value::fromString(inst->name);
|
context.attributes["instanceName"] = Value::fromString(inst->name);
|
||||||
auto message = populateMessage(pin.getConnection().getMessage(), context);
|
auto message = populateMessage(pin.getConnection().getMessage(), context);
|
||||||
errors.emplace_back(Action::ERROR, message);
|
errors.push_back(ValidationError{nullptr, nullptr, Action::ERROR, message});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,24 +152,6 @@ namespace domain {
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ValidationError>
|
|
||||||
ComdelValidator::validateInstanceNames(Schema &schema, Library &library, ValidationContext context) {
|
|
||||||
std::set<std::string> names;
|
|
||||||
std::vector<ValidationError> errors;
|
|
||||||
|
|
||||||
for(auto& component: schema.componentInstances) {
|
|
||||||
if(names.find(component->name) != names.end()) {
|
|
||||||
context.attributes["componentName"] = Value::fromString(component->name);
|
|
||||||
auto message = populateMessage(
|
|
||||||
"There are multiple component instances named '{componentName}'", context);
|
|
||||||
errors.emplace_back(Action::ERROR, message);
|
|
||||||
}
|
|
||||||
names.insert(component->name);
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string ComdelValidator::populateMessage(string source, ValidationContext context) {
|
std::string ComdelValidator::populateMessage(string source, ValidationContext context) {
|
||||||
for (auto &[key, value]: context.attributes) {
|
for (auto &[key, value]: context.attributes) {
|
||||||
source = replacePlaceholder(source, key, value);
|
source = replacePlaceholder(source, key, value);
|
||||||
|
@ -221,4 +202,5 @@ namespace domain {
|
||||||
this->validators.insert(std::make_pair(validator->getName(), validator));
|
this->validators.insert(std::make_pair(validator->getName(), validator));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -12,10 +12,6 @@ namespace domain {
|
||||||
InstanceAttribute *attribute;
|
InstanceAttribute *attribute;
|
||||||
Action::ActionType type;
|
Action::ActionType type;
|
||||||
std::string message;
|
std::string message;
|
||||||
|
|
||||||
ValidationError(Action::ActionType type, std::string message): instance(nullptr), attribute(nullptr), type(type), message(message) {}
|
|
||||||
|
|
||||||
ValidationError(ComponentInstance* instance, InstanceAttribute* attribute, Action::ActionType type, std::string message): instance(instance), attribute(attribute), type(type), message(message) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ValidationContext {
|
struct ValidationContext {
|
||||||
|
@ -35,8 +31,6 @@ namespace domain {
|
||||||
|
|
||||||
std::optional<ValidationError> validateRule(Rule rule, ValidationContext context);
|
std::optional<ValidationError> validateRule(Rule rule, ValidationContext context);
|
||||||
|
|
||||||
std::vector<ValidationError> validateInstanceNames(Schema &schema, Library &library, ValidationContext context);
|
|
||||||
|
|
||||||
std::vector<ValidationError> validateInstanceCount(Schema &schema, Library &library, ValidationContext context);
|
std::vector<ValidationError> validateInstanceCount(Schema &schema, Library &library, ValidationContext context);
|
||||||
|
|
||||||
std::vector<ValidationError>
|
std::vector<ValidationError>
|
||||||
|
|
|
@ -167,7 +167,8 @@ namespace domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &comp: node.components) {
|
for (auto &comp: node.components) {
|
||||||
auto component = loadComponent(comp);
|
std::optional<Component> component;
|
||||||
|
component = loadComponent(comp);
|
||||||
if (component) {
|
if (component) {
|
||||||
components.push_back(*component);
|
components.push_back(*component);
|
||||||
}
|
}
|
||||||
|
@ -195,24 +196,13 @@ namespace domain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errors.empty()) {
|
return Library(name, libraryInfo, header, componentDirectory, componentHeader, addressSpaces, components, buses,
|
||||||
return Library(name, libraryInfo, header, componentDirectory, componentHeader, addressSpaces, components, buses,
|
connections, messages);
|
||||||
connections, messages);
|
|
||||||
} else {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Bus> SchemaCreator::loadBus(BusNode node) {
|
std::optional<Bus> SchemaCreator::loadBus(BusNode node) {
|
||||||
std::string busName = node.name.value;
|
std::string busName = node.name.value;
|
||||||
|
|
||||||
if (!node.instanceName) {
|
|
||||||
errors.emplace_back(node.span, "missing @instanceName");
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
std::string instanceName = node.instanceName->value;
|
|
||||||
|
|
||||||
auto count = std::make_pair<int, int>(1, 1);
|
auto count = std::make_pair<int, int>(1, 1);
|
||||||
if (node.count) {
|
if (node.count) {
|
||||||
count = std::make_pair<int, int>(node.count->first.value, node.count->second.value);
|
count = std::make_pair<int, int>(node.count->first.value, node.count->second.value);
|
||||||
|
@ -269,7 +259,7 @@ namespace domain {
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Bus(busName, instanceName, tooltip, type, count, wires, displayBus);
|
return Bus(busName, tooltip, type, count, wires, displayBus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -507,7 +497,7 @@ namespace domain {
|
||||||
pop();
|
pop();
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
std::string instanceName = node.instanceName->value;
|
std::string instanceName = node.instanceName->asString();
|
||||||
|
|
||||||
auto count = std::make_pair<int, int>(1, 1);
|
auto count = std::make_pair<int, int>(1, 1);
|
||||||
if (node.count) {
|
if (node.count) {
|
||||||
|
@ -960,13 +950,7 @@ namespace domain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errors.empty()) {
|
return schema;
|
||||||
return schema;
|
|
||||||
} else {
|
|
||||||
delete schema;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<ComponentInstance> SchemaCreator::loadComponentInstance(InstanceNode instance, Library &library) {
|
shared_ptr<ComponentInstance> SchemaCreator::loadComponentInstance(InstanceNode instance, Library &library) {
|
||||||
|
|
|
@ -309,7 +309,7 @@ struct ComponentNode : public AstNode {
|
||||||
std::optional<StringNode> source;
|
std::optional<StringNode> source;
|
||||||
EnumNode<ComponentType> type;
|
EnumNode<ComponentType> type;
|
||||||
std::vector<RuleNode> rules;
|
std::vector<RuleNode> rules;
|
||||||
std::optional<IdentifierNode> instanceName;
|
std::optional<StringNode> instanceName;
|
||||||
std::optional<CountNode> count;
|
std::optional<CountNode> count;
|
||||||
std::optional<DisplayNode> display;
|
std::optional<DisplayNode> display;
|
||||||
std::vector<PinNode> pins;
|
std::vector<PinNode> pins;
|
||||||
|
@ -325,7 +325,6 @@ struct BusNode : public AstNode {
|
||||||
|
|
||||||
EnumNode<BusType> type;
|
EnumNode<BusType> type;
|
||||||
IdentifierNode name;
|
IdentifierNode name;
|
||||||
std::optional<IdentifierNode> instanceName;
|
|
||||||
std::optional<StringNode> tooltip;
|
std::optional<StringNode> tooltip;
|
||||||
std::optional<CountNode> count;
|
std::optional<CountNode> count;
|
||||||
std::optional<DisplayNode> display;
|
std::optional<DisplayNode> display;
|
||||||
|
|
|
@ -438,7 +438,7 @@ PResult<ComponentNode> ComdelParser::parseComponent() {
|
||||||
PResult<poly<AstNode>> err;
|
PResult<poly<AstNode>> err;
|
||||||
if (check(TokenType::KW_INSTANCE_NAME)) {
|
if (check(TokenType::KW_INSTANCE_NAME)) {
|
||||||
bump();
|
bump();
|
||||||
ASSIGN_OR_SET_ERR(component.instanceName, parseIdentifier());
|
ASSIGN_OR_SET_ERR(component.instanceName, parseString());
|
||||||
} else if (check(TokenType::KW_TOOLTIP)) {
|
} else if (check(TokenType::KW_TOOLTIP)) {
|
||||||
bump();
|
bump();
|
||||||
ASSIGN_OR_SET_ERR(component.tooltip, parseString());
|
ASSIGN_OR_SET_ERR(component.tooltip, parseString());
|
||||||
|
@ -554,30 +554,22 @@ PResult<BusNode> ComdelParser::parseBus() {
|
||||||
PResult<poly<AstNode>> err;
|
PResult<poly<AstNode>> err;
|
||||||
if (check(TokenType::KW_TOOLTIP)) {
|
if (check(TokenType::KW_TOOLTIP)) {
|
||||||
bump();
|
bump();
|
||||||
ASSIGN_OR_SET_ERR(bus.tooltip, parseString());
|
ASSIGN_OR_RETURN_IF_ERR(bus.tooltip, parseString());
|
||||||
} else if (check(TokenType::KW_INSTANCE_NAME)) {
|
|
||||||
bump();
|
|
||||||
ASSIGN_OR_SET_ERR(bus.instanceName, parseIdentifier());
|
|
||||||
} else if (check(TokenType::KW_COUNT)) {
|
} else if (check(TokenType::KW_COUNT)) {
|
||||||
ASSIGN_OR_SET_ERR(bus.count, parseCount());
|
ASSIGN_OR_RETURN_IF_ERR(bus.count, parseCount());
|
||||||
} else if (check(TokenType::KW_DISPLAY)) {
|
} else if (check(TokenType::KW_DISPLAY)) {
|
||||||
ASSIGN_OR_SET_ERR(bus.display, parseDisplay());
|
ASSIGN_OR_RETURN_IF_ERR(bus.display, parseDisplay());
|
||||||
} else if (check(TokenType::KW_WIRES)) {
|
} else if (check(TokenType::KW_WIRES)) {
|
||||||
bump();
|
bump();
|
||||||
if(consume(TokenType::LBRACE)) {
|
RETURN_IF_NOT_TOKEN(TokenType::LBRACE);
|
||||||
while (check(TokenType::IDENTIFIER)) {
|
while (check(TokenType::IDENTIFIER)) {
|
||||||
APPEND_OR_RETURN_IF_ERR(bus.wires, parseWire());
|
APPEND_OR_RETURN_IF_ERR(bus.wires, parseWire());
|
||||||
|
|
||||||
if (check(TokenType::COMMA)) {
|
if (check(TokenType::COMMA)) {
|
||||||
RETURN_IF_NOT_TOKEN(TokenType::COMMA);
|
RETURN_IF_NOT_TOKEN(TokenType::COMMA);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = unexpected();
|
|
||||||
}
|
|
||||||
if(!consume(TokenType::RBRACE)) {
|
|
||||||
err = unexpected();
|
|
||||||
}
|
}
|
||||||
|
RETURN_IF_NOT_TOKEN(TokenType::RBRACE);
|
||||||
} else {
|
} else {
|
||||||
err = unexpected();
|
err = unexpected();
|
||||||
bump();
|
bump();
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
component System
|
component System
|
||||||
{
|
{
|
||||||
clock 100MHz;
|
clock 100MHz;
|
||||||
//bus
|
//glavnaSabirnica
|
||||||
wire<32> ADR;
|
wire<32> ADR;
|
||||||
wire<32> DATA;
|
wire<32> DATA;
|
||||||
wire READ;
|
wire READ;
|
||||||
|
@ -23,32 +23,31 @@ component System
|
||||||
wire --BACK;
|
wire --BACK;
|
||||||
|
|
||||||
|
|
||||||
|
//directRam
|
||||||
|
wire INT;
|
||||||
|
|
||||||
|
|
||||||
// components --------------------------------------------
|
// components --------------------------------------------
|
||||||
subcomponent Memorija mem<false, 1, 1024, 8, 1024>(ADR, DATA, READ, WRITE, SIZE, WAIT, 94534054858378, 0, null, null);
|
subcomponent Memorija memorija<false, 1, 65536, 8, 0>(ADR, DATA, READ, WRITE, SIZE, WAIT, INT);
|
||||||
subcomponent FRISC procesor_002(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
|
subcomponent FRISC procesor(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, INT) uses memorija;
|
||||||
subcomponent FRISC procesor_001(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
|
|
||||||
subcomponent FRISC procesor_000(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
|
|
||||||
subcomponent FRISC procesor(ADR, DATA, READ, WRITE, SIZE, WAIT, INT0, INT1, INT2, INT3, --IACK, 1, *, 94534054858378, 0, null, null);
|
|
||||||
|
|
||||||
display {
|
display {
|
||||||
component { x: 0; y: 0; ref: "procesor_002"; }
|
component { x: -582; y: -296; ref: "procesor"; }
|
||||||
component { x: 0; y: 250; ref: "mem"; }
|
component { x: -446; y: -12; ref: "memorija"; }
|
||||||
component { x: -89; y: 74; ref: "procesor_001"; }
|
|
||||||
component { x: -175; y: 195; ref: "procesor_000"; }
|
|
||||||
component { x: -195; y: 63; ref: "procesor"; }
|
|
||||||
|
|
||||||
// bus bus
|
// glavnaSabirnica bus
|
||||||
|
|
||||||
rectangle {
|
rectangle {
|
||||||
x: 0; y: 200;
|
x: -581; y: -37;
|
||||||
w: 100; h: 20;
|
w: 100; h: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
line {x1:50; y1:116; x2:50; y2:210;}
|
// directRam bus
|
||||||
line {x1:50; y1:234; x2:50; y2:210;}
|
|
||||||
line {x1:-39; y1:190; x2:50; y2:210;}
|
|
||||||
line {x1:-145; y1:179; x2:50; y2:210;}
|
line {x1:-532; y1:-180; x2:-530; y2:-26;}
|
||||||
line {x1:-125; y1:311; x2:50; y2:210;}
|
line {x1:-462; y1:16; x2:-466; y2:-246;}
|
||||||
|
line {x1:-396; y1:-28; x2:-530; y2:-26;}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,7 +18,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@component FRISC processor {
|
@component FRISC processor {
|
||||||
@instanceName procesor
|
@instanceName "procesor"
|
||||||
@tooltip "Procesor FRISC, mora postojati jedan"
|
@tooltip "Procesor FRISC, mora postojati jedan"
|
||||||
@count (1, 1)
|
@count (1, 1)
|
||||||
@source "FRISC.cdl"
|
@source "FRISC.cdl"
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@component Memorija memory {
|
@component Memorija memory {
|
||||||
@instanceName memorija
|
@instanceName "memorija"
|
||||||
@tooltip "Memorijska komponenta, mora postojati barem jedna"
|
@tooltip "Memorijska komponenta, mora postojati barem jedna"
|
||||||
@count (1,1000)
|
@count (1,1000)
|
||||||
@source "memory.cdl"
|
@source "memory.cdl"
|
||||||
|
@ -219,7 +219,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@component DMA {
|
@component DMA {
|
||||||
@instanceName dma
|
@instanceName "dma"
|
||||||
@tooltip "DMA-kontroler"
|
@tooltip "DMA-kontroler"
|
||||||
@count (0,1000)
|
@count (0,1000)
|
||||||
@source "dma.cdl"
|
@source "dma.cdl"
|
||||||
|
@ -299,7 +299,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@bus glavnaSabirnica regular {
|
@bus glavnaSabirnica regular {
|
||||||
@instanceName glavnaSabirnica
|
|
||||||
@tooltip "sabirnica za spajanje FRISC a s memorijama i UI/jedinicama"
|
@tooltip "sabirnica za spajanje FRISC a s memorijama i UI/jedinicama"
|
||||||
@count (1,1)
|
@count (1,1)
|
||||||
@display {
|
@display {
|
||||||
|
@ -335,7 +334,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@bus PIOSabirnica automatic {
|
@bus PIOSabirnica automatic {
|
||||||
@instanceName PIOSabirnica
|
|
||||||
@count (0, 20)
|
@count (0, 20)
|
||||||
@wires {
|
@wires {
|
||||||
PIO_DATA<8>,
|
PIO_DATA<8>,
|
||||||
|
@ -345,7 +343,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@bus directRam automatic {
|
@bus directRam automatic {
|
||||||
@instanceName directRam
|
|
||||||
@wires {
|
@wires {
|
||||||
INT
|
INT
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@source "frisc_library.csl"
|
@source "frisc_library.csl"
|
||||||
|
|
||||||
@schema {
|
@schema {
|
||||||
@instance procesor_002 FRISC {
|
@instance proc FRISC {
|
||||||
@position (0, 0)
|
@position (0, 0)
|
||||||
@attribute _memory null
|
@attribute _memory null
|
||||||
}
|
}
|
||||||
|
@ -15,28 +15,11 @@
|
||||||
@attribute pocetnaAdresa 1024
|
@attribute pocetnaAdresa 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
@instance procesor_001 FRISC {
|
|
||||||
@position (-89, 74)
|
|
||||||
@attribute _memory null
|
|
||||||
}
|
|
||||||
|
|
||||||
@instance procesor_000 FRISC {
|
|
||||||
@position (-175, 195)
|
|
||||||
@attribute _memory null
|
|
||||||
}
|
|
||||||
|
|
||||||
@instance procesor FRISC {
|
|
||||||
@position (-195, 63)
|
|
||||||
@attribute _memory null
|
|
||||||
}
|
|
||||||
|
|
||||||
@instance bus glavnaSabirnica {
|
@instance bus glavnaSabirnica {
|
||||||
@position (0, 200)
|
@position (0, 200)
|
||||||
@size 100
|
@size 100
|
||||||
}
|
}
|
||||||
|
|
||||||
@connection (procesor_002.glavniPin, bus) {
|
|
||||||
}
|
|
||||||
@connection (mem.glavniPin, bus) {
|
@connection (mem.glavniPin, bus) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
216
mainwindow.cpp
216
mainwindow.cpp
|
@ -1,6 +1,5 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include "application.h"
|
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
@ -15,13 +14,22 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <comdel/domain/comdel_generator.h>
|
#include <comdel/domain/comdel_generator.h>
|
||||||
|
|
||||||
|
std::optional<domain::Library> MainWindow::library;
|
||||||
|
domain::Schema* MainWindow::schema;
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
: QMainWindow(parent)
|
: QMainWindow(parent)
|
||||||
, ui(new Ui::MainWindow)
|
, ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
|
schema = nullptr;
|
||||||
|
library = std::nullopt;
|
||||||
|
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
setupUi();
|
setupUi();
|
||||||
|
|
||||||
|
// define allowed methods
|
||||||
|
validators = domain::getSupportedValidators();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setupUi()
|
void MainWindow::setupUi()
|
||||||
|
@ -63,123 +71,187 @@ void MainWindow::setupUi()
|
||||||
void MainWindow::onLoadLibrary() {
|
void MainWindow::onLoadLibrary() {
|
||||||
auto filename = QFileDialog::getOpenFileName(this,
|
auto filename = QFileDialog::getOpenFileName(this,
|
||||||
tr("Open library"), "/home", tr("Comdel library (*.csl)"));
|
tr("Open library"), "/home", tr("Comdel library (*.csl)"));
|
||||||
|
|
||||||
|
std::ostringstream buffer;
|
||||||
|
|
||||||
|
log->clear();
|
||||||
|
|
||||||
if(!filename.isEmpty()) {
|
if(!filename.isEmpty()) {
|
||||||
std::ostringstream output;
|
librarySource = filename.toStdString();
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
auto librarySource = filename.toStdString();
|
ParseContext parseContext;
|
||||||
|
auto libraryNode = load_library_from_file(&parseContext, filename.toStdString().c_str(), buffer);
|
||||||
|
if(libraryNode) {
|
||||||
|
domain::SchemaCreator generator(validators);
|
||||||
|
library = generator.loadLibrary(*libraryNode);
|
||||||
|
|
||||||
auto instance = Application::instance();
|
for (auto& error : generator.getErrors()) {
|
||||||
if(!instance->loadLibrary(librarySource, output)) {
|
parseContext.formatError(error, buffer, "ERROR: ");
|
||||||
log->appendPlainText(QString::fromStdString(output.str()));
|
}
|
||||||
|
|
||||||
|
if(generator.getErrors().empty()) {
|
||||||
|
libraryDisplay->setLibrary(library);
|
||||||
|
|
||||||
|
// on library load we create a new schema
|
||||||
|
schema = new domain::Schema();
|
||||||
|
schemaDisplay->setSchema(schema, &(*library));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
buffer<<"Failed loading library"<<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
libraryDisplay->setLibrary(instance->getLibrary());
|
|
||||||
schemaDisplay->setSchema(instance->getSchema(), instance->getLibrary());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log->appendPlainText(QString::fromStdString(buffer.str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onLoadSchema() {
|
void MainWindow::onLoadSchema() {
|
||||||
auto filename = QFileDialog::getOpenFileName(this,
|
auto filename = QFileDialog::getOpenFileName(this,
|
||||||
tr("Open schema"), "/home", tr("Comdel schema (*.csl)"));
|
tr("Open schema"), "/home", tr("Comdel schema (*.csl)"));
|
||||||
|
|
||||||
|
std::ostringstream buffer;
|
||||||
|
|
||||||
|
log->clear();
|
||||||
|
|
||||||
if(!filename.isEmpty()) {
|
if(!filename.isEmpty()) {
|
||||||
std::ostringstream output;
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
auto schemaSource = filename.toStdString();
|
ParseContext parseContext;
|
||||||
|
auto schemaNode = load_schema_from_file(&parseContext, filename.toStdString().c_str(), buffer);
|
||||||
|
|
||||||
auto instance = Application::instance();
|
if(schemaNode) {
|
||||||
if(!instance->loadSchema(schemaSource, output)) {
|
domain::SchemaCreator generator(validators);
|
||||||
log->appendPlainText(QString::fromStdString(output.str()));
|
library = generator.loadLibrary(*schemaNode->library);
|
||||||
|
|
||||||
|
librarySource = schemaNode->source->asString();
|
||||||
|
|
||||||
|
for (auto& error : generator.getErrors()) {
|
||||||
|
parseContext.formatError(error, buffer, "ERROR: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(library) {
|
||||||
|
schema = generator.loadSchema(*schemaNode, *library);
|
||||||
|
|
||||||
|
for (auto& error : generator.getErrors()) {
|
||||||
|
parseContext.formatError(error, buffer, "ERROR: ");
|
||||||
|
delete schema;
|
||||||
|
schema = nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
library = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(generator.getErrors().empty()) {
|
||||||
|
libraryDisplay->setLibrary(library);
|
||||||
|
schemaDisplay->setSchema(schema, &(*library));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
libraryDisplay->setLibrary(instance->getLibrary());
|
|
||||||
schemaDisplay->setSchema(instance->getSchema(), instance->getLibrary());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log->appendPlainText(QString::fromStdString(buffer.str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onStoreScheme() {
|
void MainWindow::onStoreScheme() {
|
||||||
auto filename = QFileDialog::getSaveFileName(this,
|
if(schema == nullptr) {
|
||||||
tr("Save schema"), "/home", tr("Comdel schema (*.csl)"));
|
return;
|
||||||
if(!filename.isEmpty()) {
|
|
||||||
log->clear();
|
|
||||||
|
|
||||||
std::ostringstream output;
|
|
||||||
|
|
||||||
if(Application::instance()->generateSchema(output)) {
|
|
||||||
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary);
|
|
||||||
out<<output.str();
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
log->appendPlainText("Successfully stored schema\n");
|
|
||||||
} else {
|
|
||||||
log->appendPlainText("Failed storing schema\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto filename = QFileDialog::getSaveFileName(this,
|
||||||
|
tr("Save schema"), "/home", tr("Comdel schema (*.csl)"));
|
||||||
|
|
||||||
|
std::ostringstream buffer;
|
||||||
|
|
||||||
|
domain::generate_schema(librarySource, schema, buffer);
|
||||||
|
|
||||||
|
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary);
|
||||||
|
out<<buffer.str();
|
||||||
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onGenerateComdel() {
|
void MainWindow::onGenerateComdel() {
|
||||||
|
if(schema == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto filename = QFileDialog::getSaveFileName(this,
|
auto filename = QFileDialog::getSaveFileName(this,
|
||||||
tr("Save schema"), "/home", tr("Comdel system (*.system)"));
|
tr("Save schema"), "/home", tr("Comdel system (*.system)"));
|
||||||
if(!filename.isEmpty()) {
|
|
||||||
log->clear();
|
|
||||||
|
|
||||||
std::ostringstream output;
|
std::ostringstream buffer;
|
||||||
|
|
||||||
auto validationErrors = Application::instance()->generateComdel(output);
|
domain::generate_comdel(schema, library.value(), buffer);
|
||||||
|
|
||||||
std::ostringstream buff;
|
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary);
|
||||||
formatErrors(validationErrors, buff);
|
out<<buffer.str();
|
||||||
log->appendPlainText(QString::fromStdString(buff.str()));
|
out.close();
|
||||||
|
|
||||||
if(Application::hasErrors(validationErrors)) {
|
|
||||||
std::ofstream out(filename.toStdString(), std::ios::out | std::ios::binary);
|
|
||||||
out<<output.str();
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
log->appendPlainText("Successfully generated comdel\n");
|
|
||||||
} else {
|
|
||||||
log->appendPlainText("Failed generating comdel\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::onValidateSchema(bool /*toggled*/) {
|
void MainWindow::onValidateSchema(bool /*toggled*/) {
|
||||||
|
if(schema == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log->clear();
|
log->clear();
|
||||||
|
|
||||||
auto errors = Application::instance()->validateSchema();
|
this->validationErrors.clear();
|
||||||
|
|
||||||
|
domain::ComdelValidator validator{validators};
|
||||||
|
|
||||||
|
domain::ValidationContext context;
|
||||||
|
context.instance = nullptr;
|
||||||
|
context.attribute = nullptr;
|
||||||
|
context.addressSpaces = {};
|
||||||
|
|
||||||
|
for(auto &lib: library->getAddressSpaces()) {
|
||||||
|
context.addressSpaces.insert(std::make_pair(lib.getName(), lib));
|
||||||
|
}
|
||||||
|
auto errors = validator.validateSchema(*schema, context);
|
||||||
|
|
||||||
|
auto countValidation = validator.validateInstanceCount(*schema, *library, context);
|
||||||
|
errors.insert(errors.end(), countValidation.begin(), countValidation.end());
|
||||||
|
|
||||||
|
auto pinValidation = validator.validatePinConnections(*schema, *library, context);
|
||||||
|
errors.insert(errors.end(), pinValidation.begin(), pinValidation.end());
|
||||||
|
|
||||||
|
errors.erase(std::remove_if(
|
||||||
|
errors.begin(), errors.end(), [](const domain::ValidationError& error) { return error.type == domain::Action::WARNING;}),
|
||||||
|
errors.end());
|
||||||
|
|
||||||
std::ostringstream buff;
|
std::ostringstream buff;
|
||||||
formatErrors(errors, buff);
|
|
||||||
|
for(auto err: errors) {
|
||||||
|
if(err.instance != nullptr) {
|
||||||
|
buff << err.instance->name;
|
||||||
|
}
|
||||||
|
if(err.attribute != nullptr) {
|
||||||
|
buff << "::" << err.attribute->name;
|
||||||
|
}
|
||||||
|
if(err.type == domain::Action::ERROR) {
|
||||||
|
buff << " [ERROR] ";
|
||||||
|
} else {
|
||||||
|
buff << " [WARNING] ";
|
||||||
|
}
|
||||||
|
buff << err.message << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
log->appendPlainText(QString::fromStdString(buff.str()));
|
log->appendPlainText(QString::fromStdString(buff.str()));
|
||||||
|
|
||||||
|
validationErrors = errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::formatErrors(std::vector<domain::ValidationError>& errors, std::ostream& output) {
|
|
||||||
for(auto& err: errors) {
|
|
||||||
if(err.instance != nullptr) {
|
|
||||||
output << err.instance->name;
|
|
||||||
}
|
|
||||||
if(err.attribute != nullptr) {
|
|
||||||
output << "::" << err.attribute->name;
|
|
||||||
}
|
|
||||||
if(err.type == domain::Action::ERROR) {
|
|
||||||
output << " [ERROR] ";
|
|
||||||
} else {
|
|
||||||
output << " [WARNING] ";
|
|
||||||
}
|
|
||||||
output << err.message << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::clear() {
|
void MainWindow::clear() {
|
||||||
log->clear();
|
validationErrors.clear();
|
||||||
this->schemaDisplay->setSchema(nullptr, std::nullopt);
|
if(schema != nullptr) {
|
||||||
this->libraryDisplay->setLibrary(std::nullopt);
|
delete schema;
|
||||||
|
schema = nullptr;
|
||||||
|
}
|
||||||
|
library = std::nullopt;
|
||||||
|
|
||||||
|
libraryDisplay->setLibrary(library);
|
||||||
|
schemaDisplay->setSchema(schema, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
|
|
20
mainwindow.h
20
mainwindow.h
|
@ -24,11 +24,26 @@ public:
|
||||||
MainWindow(QWidget *parent = nullptr);
|
MainWindow(QWidget *parent = nullptr);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
display::Library *libraryDisplay;
|
||||||
|
display::Schema *schemaDisplay;
|
||||||
|
|
||||||
|
static std::optional<domain::Library> library;
|
||||||
|
static domain::Schema* schema;
|
||||||
|
|
||||||
std::vector<domain::FunctionValidator*> validators;
|
std::vector<domain::FunctionValidator*> validators;
|
||||||
|
|
||||||
void setupUi();
|
void setupUi();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
static domain::Schema* getSchema() {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<domain::Library> getLibrary() {
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onLoadLibrary();
|
void onLoadLibrary();
|
||||||
void onLoadSchema();
|
void onLoadSchema();
|
||||||
|
@ -37,11 +52,10 @@ private slots:
|
||||||
void onGenerateComdel();
|
void onGenerateComdel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
display::Library *libraryDisplay;
|
std::string librarySource;
|
||||||
display::Schema *schemaDisplay;
|
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
QPlainTextEdit *log;
|
QPlainTextEdit *log;
|
||||||
|
std::vector<domain::ValidationError> validationErrors;
|
||||||
|
|
||||||
static void formatErrors(std::vector<domain::ValidationError>& errors, std::ostream& output);
|
|
||||||
};
|
};
|
||||||
#endif // MAIN_WINDOW_H
|
#endif // MAIN_WINDOW_H
|
||||||
|
|
Loading…
Reference in New Issue