2018-06-22 22:21:20 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-06-15 02:03:59 +08:00
|
|
|
#include "designwidget.h"
|
|
|
|
#include <QAction>
|
|
|
|
#include <QGridLayout>
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QSplitter>
|
|
|
|
#include <QTreeWidgetItem>
|
|
|
|
#include "fpgaviewwidget.h"
|
|
|
|
|
2018-06-22 19:10:27 +08:00
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
2018-06-15 02:03:59 +08:00
|
|
|
enum class ElementType
|
|
|
|
{
|
2018-07-06 01:31:01 +08:00
|
|
|
NONE,
|
2018-06-15 02:03:59 +08:00
|
|
|
BEL,
|
|
|
|
WIRE,
|
|
|
|
PIP
|
|
|
|
};
|
|
|
|
|
|
|
|
class ElementTreeItem : public QTreeWidgetItem
|
|
|
|
{
|
|
|
|
public:
|
2018-07-06 01:31:01 +08:00
|
|
|
ElementTreeItem(ElementType t, QString str, QTreeWidgetItem *parent) : QTreeWidgetItem(parent, QStringList(str)), type(t) {}
|
2018-06-15 02:03:59 +08:00
|
|
|
virtual ~ElementTreeItem(){};
|
|
|
|
|
|
|
|
ElementType getType() { return type; };
|
|
|
|
|
|
|
|
private:
|
|
|
|
ElementType type;
|
|
|
|
};
|
|
|
|
|
|
|
|
class BelTreeItem : public ElementTreeItem
|
|
|
|
{
|
|
|
|
public:
|
2018-07-06 01:31:01 +08:00
|
|
|
BelTreeItem(IdString d, QString str, QTreeWidgetItem *parent) : ElementTreeItem(ElementType::BEL, str, parent) { this->data = d; }
|
2018-06-15 02:03:59 +08:00
|
|
|
virtual ~BelTreeItem(){};
|
|
|
|
|
|
|
|
IdString getData() { return this->data; };
|
|
|
|
|
|
|
|
private:
|
|
|
|
IdString data;
|
|
|
|
};
|
|
|
|
|
|
|
|
class WireTreeItem : public ElementTreeItem
|
|
|
|
{
|
|
|
|
public:
|
2018-07-06 01:31:01 +08:00
|
|
|
WireTreeItem(IdString d, QString str, QTreeWidgetItem *parent) : ElementTreeItem(ElementType::WIRE, str, parent) { this->data = d; }
|
2018-06-15 02:03:59 +08:00
|
|
|
virtual ~WireTreeItem(){};
|
|
|
|
|
|
|
|
IdString getData() { return this->data; };
|
|
|
|
|
|
|
|
private:
|
|
|
|
IdString data;
|
|
|
|
};
|
|
|
|
|
|
|
|
class PipTreeItem : public ElementTreeItem
|
|
|
|
{
|
|
|
|
public:
|
2018-07-06 01:31:01 +08:00
|
|
|
PipTreeItem(IdString d, QString str, QTreeWidgetItem *parent) : ElementTreeItem(ElementType::PIP, str, parent) { this->data = d; }
|
2018-06-15 02:03:59 +08:00
|
|
|
virtual ~PipTreeItem(){};
|
|
|
|
|
|
|
|
IdString getData() { return this->data; };
|
|
|
|
|
|
|
|
private:
|
|
|
|
IdString data;
|
|
|
|
};
|
|
|
|
|
2018-06-26 21:47:22 +08:00
|
|
|
DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr)
|
2018-06-15 02:03:59 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
treeWidget = new QTreeWidget();
|
|
|
|
|
|
|
|
// Add tree view
|
|
|
|
treeWidget->setColumnCount(1);
|
|
|
|
treeWidget->setHeaderLabel(QString("Items"));
|
|
|
|
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
|
|
|
// Add property view
|
|
|
|
variantManager = new QtVariantPropertyManager();
|
|
|
|
variantFactory = new QtVariantEditorFactory();
|
|
|
|
propertyEditor = new QtTreePropertyBrowser();
|
|
|
|
propertyEditor->setFactoryForManager(variantManager, variantFactory);
|
|
|
|
propertyEditor->setPropertiesWithoutValueMarked(true);
|
|
|
|
propertyEditor->setRootIsDecorated(false);
|
|
|
|
|
|
|
|
propertyEditor->show();
|
|
|
|
|
|
|
|
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
|
|
|
splitter->addWidget(treeWidget);
|
|
|
|
splitter->addWidget(propertyEditor);
|
|
|
|
|
|
|
|
QGridLayout *mainLayout = new QGridLayout();
|
2018-06-15 02:24:05 +08:00
|
|
|
mainLayout->setSpacing(0);
|
|
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
2018-06-15 02:03:59 +08:00
|
|
|
mainLayout->addWidget(splitter);
|
|
|
|
setLayout(mainLayout);
|
|
|
|
|
|
|
|
// Connection
|
2018-06-23 22:06:49 +08:00
|
|
|
connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenu);
|
2018-06-15 02:03:59 +08:00
|
|
|
|
2018-06-23 22:06:49 +08:00
|
|
|
connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), SLOT(onItemClicked(QTreeWidgetItem *, int)));
|
2018-06-15 02:03:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
DesignWidget::~DesignWidget()
|
|
|
|
{
|
|
|
|
delete variantManager;
|
|
|
|
delete variantFactory;
|
|
|
|
delete propertyEditor;
|
|
|
|
}
|
|
|
|
|
2018-06-26 21:47:22 +08:00
|
|
|
void DesignWidget::newContext(Context *ctx)
|
|
|
|
{
|
|
|
|
treeWidget->clear();
|
|
|
|
this->ctx = ctx;
|
|
|
|
|
|
|
|
// Add bels to tree
|
|
|
|
QTreeWidgetItem *bel_root = new QTreeWidgetItem(treeWidget);
|
2018-07-06 01:31:01 +08:00
|
|
|
QMap<QString, QTreeWidgetItem *> bel_items;
|
2018-06-26 21:47:22 +08:00
|
|
|
bel_root->setText(0, QString("Bels"));
|
2018-07-06 01:31:01 +08:00
|
|
|
treeWidget->insertTopLevelItem(0, bel_root);
|
2018-06-29 00:06:31 +08:00
|
|
|
if (ctx) {
|
2018-06-26 21:47:22 +08:00
|
|
|
for (auto bel : ctx->getBels()) {
|
2018-07-06 01:31:01 +08:00
|
|
|
auto id = ctx->getBelName(bel);
|
|
|
|
QStringList items = QString(id.c_str(ctx)).split("/");
|
|
|
|
QString name;
|
|
|
|
QTreeWidgetItem *parent = nullptr;
|
|
|
|
for(int i=0;i<items.size();i++)
|
|
|
|
{
|
|
|
|
if (!name.isEmpty()) name += "/";
|
|
|
|
name += items.at(i);
|
|
|
|
if (!bel_items.contains(name)) {
|
|
|
|
if (i==items.size()-1)
|
|
|
|
bel_items.insert(name,new BelTreeItem(id, items.at(i),parent));
|
|
|
|
else
|
|
|
|
bel_items.insert(name,new ElementTreeItem(ElementType::NONE, items.at(i),parent));
|
|
|
|
}
|
|
|
|
parent = bel_items[name];
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-06 01:31:01 +08:00
|
|
|
for (auto bel : bel_items.toStdMap()) {
|
|
|
|
bel_root->addChild(bel.second);
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
|
|
|
|
// Add wires to tree
|
|
|
|
QTreeWidgetItem *wire_root = new QTreeWidgetItem(treeWidget);
|
2018-07-06 01:31:01 +08:00
|
|
|
QMap<QString, QTreeWidgetItem *> wire_items;
|
2018-06-26 21:47:22 +08:00
|
|
|
wire_root->setText(0, QString("Wires"));
|
2018-07-06 01:31:01 +08:00
|
|
|
treeWidget->insertTopLevelItem(0, wire_root);
|
2018-06-29 00:06:31 +08:00
|
|
|
if (ctx) {
|
2018-06-26 21:47:22 +08:00
|
|
|
for (auto wire : ctx->getWires()) {
|
2018-07-06 01:31:01 +08:00
|
|
|
auto id = ctx->getWireName(wire);
|
|
|
|
QStringList items = QString(id.c_str(ctx)).split("/");
|
|
|
|
QString name;
|
|
|
|
QTreeWidgetItem *parent = nullptr;
|
|
|
|
for(int i=0;i<items.size();i++)
|
|
|
|
{
|
|
|
|
if (!name.isEmpty()) name += "/";
|
|
|
|
name += items.at(i);
|
|
|
|
if (!wire_items.contains(name)) {
|
|
|
|
if (i==items.size()-1)
|
|
|
|
wire_items.insert(name,new WireTreeItem(id, items.at(i),parent));
|
|
|
|
else
|
|
|
|
wire_items.insert(name,new ElementTreeItem(ElementType::NONE, items.at(i),parent));
|
|
|
|
}
|
|
|
|
parent = wire_items[name];
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-06 01:31:01 +08:00
|
|
|
for (auto wire : wire_items.toStdMap()) {
|
|
|
|
wire_root->addChild(wire.second);
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
|
|
|
|
// Add pips to tree
|
|
|
|
QTreeWidgetItem *pip_root = new QTreeWidgetItem(treeWidget);
|
2018-07-06 01:31:01 +08:00
|
|
|
QMap<QString, QTreeWidgetItem *> pip_items;
|
2018-06-26 21:47:22 +08:00
|
|
|
pip_root->setText(0, QString("Pips"));
|
|
|
|
treeWidget->insertTopLevelItem(0, pip_root);
|
2018-06-29 00:06:31 +08:00
|
|
|
if (ctx) {
|
2018-06-26 21:47:22 +08:00
|
|
|
for (auto pip : ctx->getPips()) {
|
2018-07-06 01:31:01 +08:00
|
|
|
auto id = ctx->getPipName(pip);
|
|
|
|
QStringList items = QString(id.c_str(ctx)).split("/");
|
|
|
|
QString name;
|
|
|
|
QTreeWidgetItem *parent = nullptr;
|
|
|
|
for(int i=0;i<items.size();i++)
|
|
|
|
{
|
|
|
|
if (!name.isEmpty()) name += "/";
|
|
|
|
name += items.at(i);
|
|
|
|
if (!pip_items.contains(name)) {
|
|
|
|
if (i==items.size()-1)
|
|
|
|
pip_items.insert(name,new PipTreeItem(id, items.at(i),parent));
|
|
|
|
else
|
|
|
|
pip_items.insert(name,new ElementTreeItem(ElementType::NONE, items.at(i),parent));
|
|
|
|
}
|
|
|
|
parent = pip_items[name];
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-06 01:31:01 +08:00
|
|
|
for (auto pip : pip_items.toStdMap()) {
|
|
|
|
pip_root->addChild(pip.second);
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
|
|
|
|
2018-06-15 02:03:59 +08:00
|
|
|
void DesignWidget::addProperty(QtVariantProperty *property, const QString &id)
|
|
|
|
{
|
|
|
|
propertyToId[property] = id;
|
|
|
|
idToProperty[id] = property;
|
2018-06-21 03:01:09 +08:00
|
|
|
propertyEditor->addProperty(property);
|
2018-06-15 02:03:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void DesignWidget::clearProperties()
|
|
|
|
{
|
2018-06-23 22:06:49 +08:00
|
|
|
QMap<QtProperty *, QString>::ConstIterator itProp = propertyToId.constBegin();
|
2018-06-15 02:03:59 +08:00
|
|
|
while (itProp != propertyToId.constEnd()) {
|
|
|
|
delete itProp.key();
|
|
|
|
itProp++;
|
|
|
|
}
|
|
|
|
propertyToId.clear();
|
|
|
|
idToProperty.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesignWidget::onItemClicked(QTreeWidgetItem *item, int pos)
|
|
|
|
{
|
|
|
|
if (!item->parent())
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
ElementType type = static_cast<ElementTreeItem *>(item)->getType();
|
2018-07-06 01:31:01 +08:00
|
|
|
if (type == ElementType::NONE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clearProperties();
|
2018-06-15 02:03:59 +08:00
|
|
|
if (type == ElementType::BEL) {
|
|
|
|
IdString c = static_cast<BelTreeItem *>(item)->getData();
|
|
|
|
|
2018-06-24 02:25:10 +08:00
|
|
|
BelType type = ctx->getBelType(ctx->getBelByName(c));
|
2018-06-23 22:06:49 +08:00
|
|
|
QtVariantProperty *topItem = variantManager->addProperty(QVariant::String, QString("Name"));
|
2018-06-21 03:01:09 +08:00
|
|
|
topItem->setValue(QString(c.c_str(ctx)));
|
2018-06-15 02:03:59 +08:00
|
|
|
addProperty(topItem, QString("Name"));
|
2018-06-24 02:25:10 +08:00
|
|
|
|
|
|
|
QtVariantProperty *typeItem = variantManager->addProperty(QVariant::String, QString("Type"));
|
|
|
|
typeItem->setValue(QString(ctx->belTypeToId(type).c_str(ctx)));
|
|
|
|
addProperty(typeItem, QString("Type"));
|
|
|
|
|
2018-06-15 02:03:59 +08:00
|
|
|
} else if (type == ElementType::WIRE) {
|
|
|
|
IdString c = static_cast<WireTreeItem *>(item)->getData();
|
|
|
|
|
2018-06-23 22:06:49 +08:00
|
|
|
QtVariantProperty *topItem = variantManager->addProperty(QVariant::String, QString("Name"));
|
2018-06-21 03:01:09 +08:00
|
|
|
topItem->setValue(QString(c.c_str(ctx)));
|
2018-06-15 02:03:59 +08:00
|
|
|
addProperty(topItem, QString("Name"));
|
|
|
|
|
|
|
|
} else if (type == ElementType::PIP) {
|
|
|
|
IdString c = static_cast<PipTreeItem *>(item)->getData();
|
|
|
|
|
2018-06-23 22:06:49 +08:00
|
|
|
QtVariantProperty *topItem = variantManager->addProperty(QVariant::String, QString("Name"));
|
2018-06-21 03:01:09 +08:00
|
|
|
topItem->setValue(QString(c.c_str(ctx)));
|
2018-06-15 02:03:59 +08:00
|
|
|
addProperty(topItem, QString("Name"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesignWidget::prepareMenu(const QPoint &pos)
|
|
|
|
{
|
|
|
|
QTreeWidget *tree = treeWidget;
|
|
|
|
|
|
|
|
itemContextMenu = tree->itemAt(pos);
|
|
|
|
|
|
|
|
QAction *selectAction = new QAction("&Select", this);
|
|
|
|
selectAction->setStatusTip("Select item on view");
|
2018-06-15 17:10:11 +08:00
|
|
|
|
2018-06-15 02:03:59 +08:00
|
|
|
connect(selectAction, SIGNAL(triggered()), this, SLOT(selectObject()));
|
|
|
|
|
|
|
|
QMenu menu(this);
|
|
|
|
menu.addAction(selectAction);
|
|
|
|
|
|
|
|
menu.exec(tree->mapToGlobal(pos));
|
|
|
|
}
|
|
|
|
|
2018-06-23 22:06:49 +08:00
|
|
|
void DesignWidget::selectObject() { Q_EMIT info("selected " + itemContextMenu->text(0).toStdString() + "\n"); }
|
2018-06-22 19:10:27 +08:00
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_END
|