2018-06-22 22:21:20 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
2018-07-27 09:28:01 +08:00
|
|
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
2018-06-22 22:21:20 +08:00
|
|
|
*
|
|
|
|
* 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>
|
2018-07-11 22:20:33 +08:00
|
|
|
#include <QLineEdit>
|
2018-06-15 02:03:59 +08:00
|
|
|
#include <QMenu>
|
|
|
|
#include <QSplitter>
|
2018-07-11 22:20:33 +08:00
|
|
|
#include <QToolBar>
|
2018-06-15 02:03:59 +08:00
|
|
|
#include <QTreeWidgetItem>
|
|
|
|
#include "fpgaviewwidget.h"
|
|
|
|
|
2018-06-22 19:10:27 +08:00
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
2018-06-15 02:03:59 +08:00
|
|
|
class ElementTreeItem : public QTreeWidgetItem
|
|
|
|
{
|
|
|
|
public:
|
2018-07-07 01:19:18 +08:00
|
|
|
ElementTreeItem(ElementType t, QString str, QTreeWidgetItem *parent)
|
|
|
|
: QTreeWidgetItem(parent, QStringList(str)), type(t)
|
|
|
|
{
|
2018-07-26 01:10:45 +08:00
|
|
|
this->setFlags(this->flags() & ~Qt::ItemIsSelectable);
|
2018-07-07 01:19:18 +08:00
|
|
|
}
|
2018-06-15 02:03:59 +08:00
|
|
|
virtual ~ElementTreeItem(){};
|
|
|
|
|
|
|
|
ElementType getType() { return type; };
|
|
|
|
|
|
|
|
private:
|
|
|
|
ElementType type;
|
|
|
|
};
|
|
|
|
|
2018-07-06 02:17:32 +08:00
|
|
|
class IdStringTreeItem : public ElementTreeItem
|
2018-06-15 02:03:59 +08:00
|
|
|
{
|
|
|
|
public:
|
2018-07-07 01:19:18 +08:00
|
|
|
IdStringTreeItem(IdString d, ElementType t, QString str, QTreeWidgetItem *parent) : ElementTreeItem(t, str, parent)
|
|
|
|
{
|
2018-07-26 01:10:45 +08:00
|
|
|
this->setFlags(this->flags() | Qt::ItemIsSelectable);
|
2018-07-07 01:19:18 +08:00
|
|
|
this->data = d;
|
|
|
|
}
|
2018-07-06 02:17:32 +08:00
|
|
|
virtual ~IdStringTreeItem(){};
|
2018-06-15 02:03:59 +08:00
|
|
|
|
|
|
|
IdString getData() { return this->data; };
|
|
|
|
|
|
|
|
private:
|
|
|
|
IdString data;
|
|
|
|
};
|
|
|
|
|
2018-07-06 02:06:12 +08:00
|
|
|
DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), nets_root(nullptr), cells_root(nullptr)
|
2018-06-15 02:03:59 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
treeWidget = new QTreeWidget();
|
|
|
|
|
|
|
|
// Add tree view
|
|
|
|
treeWidget->setColumnCount(1);
|
2018-07-07 01:05:05 +08:00
|
|
|
treeWidget->setHeaderLabel("Items");
|
2018-06-15 02:03:59 +08:00
|
|
|
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
2018-07-26 01:10:45 +08:00
|
|
|
treeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
2018-06-15 02:03:59 +08:00
|
|
|
|
|
|
|
// Add property view
|
2018-07-06 14:15:39 +08:00
|
|
|
variantManager = new QtVariantPropertyManager(this);
|
2018-07-06 02:35:47 +08:00
|
|
|
readOnlyManager = new QtVariantPropertyManager(this);
|
2018-07-06 03:51:17 +08:00
|
|
|
groupManager = new QtGroupPropertyManager(this);
|
2018-07-06 14:15:39 +08:00
|
|
|
variantFactory = new QtVariantEditorFactory(this);
|
|
|
|
propertyEditor = new QtTreePropertyBrowser(this);
|
2018-06-15 02:03:59 +08:00
|
|
|
propertyEditor->setFactoryForManager(variantManager, variantFactory);
|
|
|
|
propertyEditor->setPropertiesWithoutValueMarked(true);
|
|
|
|
propertyEditor->show();
|
2018-07-15 18:39:19 +08:00
|
|
|
propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu);
|
2018-07-26 01:10:45 +08:00
|
|
|
propertyEditor->treeWidget()->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
2018-07-15 15:49:19 +08:00
|
|
|
|
2018-07-13 04:04:13 +08:00
|
|
|
QLineEdit *lineEdit = new QLineEdit();
|
2018-07-11 22:20:33 +08:00
|
|
|
lineEdit->setClearButtonEnabled(true);
|
2018-07-14 23:58:58 +08:00
|
|
|
lineEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
|
2018-07-11 22:20:33 +08:00
|
|
|
lineEdit->setPlaceholderText("Search...");
|
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
actionFirst = new QAction("", this);
|
2018-07-14 23:58:58 +08:00
|
|
|
actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png"));
|
2018-07-12 00:30:56 +08:00
|
|
|
actionFirst->setEnabled(false);
|
2018-07-15 22:20:35 +08:00
|
|
|
connect(actionFirst, &QAction::triggered, this, [this] {
|
|
|
|
history_ignore = true;
|
|
|
|
history_index = 0;
|
|
|
|
treeWidget->setCurrentItem(history.at(history_index));
|
|
|
|
updateButtons();
|
|
|
|
});
|
2018-07-11 22:20:33 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
actionPrev = new QAction("", this);
|
2018-07-14 23:58:58 +08:00
|
|
|
actionPrev->setIcon(QIcon(":/icons/resources/resultset_previous.png"));
|
2018-07-12 00:30:56 +08:00
|
|
|
actionPrev->setEnabled(false);
|
2018-07-15 22:20:35 +08:00
|
|
|
connect(actionPrev, &QAction::triggered, this, [this] {
|
|
|
|
history_ignore = true;
|
|
|
|
history_index--;
|
|
|
|
treeWidget->setCurrentItem(history.at(history_index));
|
|
|
|
updateButtons();
|
|
|
|
});
|
2018-07-11 22:20:33 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
actionNext = new QAction("", this);
|
2018-07-14 23:58:58 +08:00
|
|
|
actionNext->setIcon(QIcon(":/icons/resources/resultset_next.png"));
|
2018-07-12 00:30:56 +08:00
|
|
|
actionNext->setEnabled(false);
|
2018-07-15 22:20:35 +08:00
|
|
|
connect(actionNext, &QAction::triggered, this, [this] {
|
|
|
|
history_ignore = true;
|
|
|
|
history_index++;
|
|
|
|
treeWidget->setCurrentItem(history.at(history_index));
|
|
|
|
updateButtons();
|
|
|
|
});
|
2018-07-11 22:20:33 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
actionLast = new QAction("", this);
|
2018-07-14 23:58:58 +08:00
|
|
|
actionLast->setIcon(QIcon(":/icons/resources/resultset_last.png"));
|
2018-07-12 00:30:56 +08:00
|
|
|
actionLast->setEnabled(false);
|
2018-07-15 22:20:35 +08:00
|
|
|
connect(actionLast, &QAction::triggered, this, [this] {
|
|
|
|
history_ignore = true;
|
|
|
|
history_index = int(history.size() - 1);
|
|
|
|
treeWidget->setCurrentItem(history.at(history_index));
|
|
|
|
updateButtons();
|
|
|
|
});
|
2018-07-11 22:20:33 +08:00
|
|
|
|
2018-07-18 23:33:04 +08:00
|
|
|
actionClear = new QAction("", this);
|
|
|
|
actionClear->setIcon(QIcon(":/icons/resources/cross.png"));
|
|
|
|
actionClear->setEnabled(true);
|
|
|
|
connect(actionClear, &QAction::triggered, this, [this] {
|
|
|
|
history_index = -1;
|
|
|
|
history.clear();
|
|
|
|
QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
|
|
|
|
if (clickItem->parent()) {
|
|
|
|
ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
|
|
|
|
if (type != ElementType::NONE)
|
|
|
|
addToHistory(treeWidget->selectedItems().at(0));
|
|
|
|
}
|
|
|
|
updateButtons();
|
|
|
|
});
|
|
|
|
|
2018-07-11 22:20:33 +08:00
|
|
|
QToolBar *toolbar = new QToolBar();
|
|
|
|
toolbar->addAction(actionFirst);
|
|
|
|
toolbar->addAction(actionPrev);
|
|
|
|
toolbar->addAction(actionNext);
|
|
|
|
toolbar->addAction(actionLast);
|
2018-07-18 23:33:04 +08:00
|
|
|
toolbar->addAction(actionClear);
|
2018-07-11 22:20:33 +08:00
|
|
|
|
2018-07-12 00:15:25 +08:00
|
|
|
QWidget *topWidget = new QWidget();
|
|
|
|
QVBoxLayout *vbox1 = new QVBoxLayout();
|
|
|
|
topWidget->setLayout(vbox1);
|
|
|
|
vbox1->setSpacing(5);
|
|
|
|
vbox1->setContentsMargins(0, 0, 0, 0);
|
|
|
|
vbox1->addWidget(lineEdit);
|
|
|
|
vbox1->addWidget(treeWidget);
|
|
|
|
|
|
|
|
QWidget *toolbarWidget = new QWidget();
|
|
|
|
QHBoxLayout *hbox = new QHBoxLayout;
|
|
|
|
hbox->setAlignment(Qt::AlignCenter);
|
|
|
|
toolbarWidget->setLayout(hbox);
|
2018-07-12 00:25:48 +08:00
|
|
|
hbox->setSpacing(0);
|
|
|
|
hbox->setContentsMargins(0, 0, 0, 0);
|
2018-07-12 00:15:25 +08:00
|
|
|
hbox->addWidget(toolbar);
|
|
|
|
|
|
|
|
QWidget *btmWidget = new QWidget();
|
2018-07-13 04:04:13 +08:00
|
|
|
|
2018-07-12 00:15:25 +08:00
|
|
|
QVBoxLayout *vbox2 = new QVBoxLayout();
|
2018-07-13 04:04:13 +08:00
|
|
|
btmWidget->setLayout(vbox2);
|
2018-07-12 00:25:48 +08:00
|
|
|
vbox2->setSpacing(0);
|
2018-07-12 00:15:25 +08:00
|
|
|
vbox2->setContentsMargins(0, 0, 0, 0);
|
|
|
|
vbox2->addWidget(toolbarWidget);
|
|
|
|
vbox2->addWidget(propertyEditor);
|
|
|
|
|
2018-06-15 02:03:59 +08:00
|
|
|
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
2018-07-13 04:04:13 +08:00
|
|
|
splitter->addWidget(topWidget);
|
|
|
|
splitter->addWidget(btmWidget);
|
2018-06-15 02:03:59 +08:00
|
|
|
|
|
|
|
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-07-15 18:39:19 +08:00
|
|
|
connect(propertyEditor->treeWidget(), &QTreeWidget::customContextMenuRequested, this,
|
|
|
|
&DesignWidget::prepareMenuProperty);
|
|
|
|
connect(propertyEditor->treeWidget(), &QTreeWidget::itemDoubleClicked, this, &DesignWidget::onItemDoubleClicked);
|
2018-06-15 02:03:59 +08:00
|
|
|
|
2018-07-14 23:50:06 +08:00
|
|
|
connect(treeWidget, SIGNAL(itemSelectionChanged()), SLOT(onItemSelectionChanged()));
|
2018-07-15 23:50:58 +08:00
|
|
|
connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &DesignWidget::prepareMenuTree);
|
2018-07-15 22:20:35 +08:00
|
|
|
|
|
|
|
history_index = -1;
|
|
|
|
history_ignore = false;
|
2018-07-15 23:50:58 +08:00
|
|
|
|
|
|
|
highlightColors[0] = QColor("#6495ed");
|
|
|
|
highlightColors[1] = QColor("#7fffd4");
|
|
|
|
highlightColors[2] = QColor("#98fb98");
|
|
|
|
highlightColors[3] = QColor("#ffd700");
|
|
|
|
highlightColors[4] = QColor("#cd5c5c");
|
|
|
|
highlightColors[5] = QColor("#fa8072");
|
|
|
|
highlightColors[6] = QColor("#ff69b4");
|
|
|
|
highlightColors[7] = QColor("#da70d6");
|
2018-06-15 02:03:59 +08:00
|
|
|
}
|
|
|
|
|
2018-07-07 01:19:18 +08:00
|
|
|
DesignWidget::~DesignWidget() {}
|
2018-06-15 02:03:59 +08:00
|
|
|
|
2018-07-15 22:20:35 +08:00
|
|
|
void DesignWidget::updateButtons()
|
|
|
|
{
|
|
|
|
int count = int(history.size());
|
|
|
|
actionFirst->setEnabled(history_index > 0);
|
|
|
|
actionPrev->setEnabled(history_index > 0);
|
|
|
|
actionNext->setEnabled(history_index < (count - 1));
|
|
|
|
actionLast->setEnabled(history_index < (count - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesignWidget::addToHistory(QTreeWidgetItem *item)
|
|
|
|
{
|
|
|
|
if (!history_ignore) {
|
|
|
|
int count = int(history.size());
|
|
|
|
for (int i = count - 1; i > history_index; i--)
|
|
|
|
history.pop_back();
|
|
|
|
history.push_back(item);
|
|
|
|
history_index++;
|
|
|
|
}
|
|
|
|
history_ignore = false;
|
|
|
|
updateButtons();
|
|
|
|
}
|
|
|
|
|
2018-06-26 21:47:22 +08:00
|
|
|
void DesignWidget::newContext(Context *ctx)
|
|
|
|
{
|
2018-07-23 23:10:06 +08:00
|
|
|
highlightSelected.clear();
|
2018-06-26 21:47:22 +08:00
|
|
|
treeWidget->clear();
|
2018-07-21 01:16:36 +08:00
|
|
|
// reset pointers since they are not valid after clear
|
|
|
|
nets_root = nullptr;
|
|
|
|
cells_root = nullptr;
|
2018-07-15 22:20:35 +08:00
|
|
|
history_ignore = false;
|
|
|
|
history_index = -1;
|
|
|
|
history.clear();
|
|
|
|
updateButtons();
|
|
|
|
|
2018-07-15 21:12:31 +08:00
|
|
|
for (int i = 0; i < 6; i++)
|
|
|
|
nameToItem[i].clear();
|
|
|
|
|
2018-06-26 21:47:22 +08:00
|
|
|
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-07-07 01:05:05 +08:00
|
|
|
bel_root->setText(0, "Bels");
|
2018-07-26 01:10:45 +08:00
|
|
|
bel_root->setFlags(bel_root->flags() & ~Qt::ItemIsSelectable);
|
2018-07-07 01:19:18 +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;
|
2018-07-07 01:19:18 +08:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
|
|
|
if (!name.isEmpty())
|
|
|
|
name += "/";
|
2018-07-06 01:31:01 +08:00
|
|
|
name += items.at(i);
|
|
|
|
if (!bel_items.contains(name)) {
|
2018-07-07 01:19:18 +08:00
|
|
|
if (i == items.size() - 1)
|
2018-07-15 21:12:31 +08:00
|
|
|
nameToItem[0].insert(name, new IdStringTreeItem(id, ElementType::BEL, items.at(i), parent));
|
2018-07-06 01:31:01 +08:00
|
|
|
else
|
2018-07-07 01:19:18 +08:00
|
|
|
bel_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
|
|
|
|
}
|
2018-07-06 01:31:01 +08:00
|
|
|
parent = bel_items[name];
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto bel : bel_items.toStdMap()) {
|
2018-07-06 01:31:01 +08:00
|
|
|
bel_root->addChild(bel.second);
|
|
|
|
}
|
2018-07-15 21:12:31 +08:00
|
|
|
for (auto bel : nameToItem[0].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-07-07 01:05:05 +08:00
|
|
|
wire_root->setText(0, "Wires");
|
2018-07-26 01:10:45 +08:00
|
|
|
wire_root->setFlags(wire_root->flags() & ~Qt::ItemIsSelectable);
|
2018-07-07 01:19:18 +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;
|
2018-07-07 01:19:18 +08:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
|
|
|
if (!name.isEmpty())
|
|
|
|
name += "/";
|
2018-07-06 01:31:01 +08:00
|
|
|
name += items.at(i);
|
|
|
|
if (!wire_items.contains(name)) {
|
2018-07-07 01:19:18 +08:00
|
|
|
if (i == items.size() - 1)
|
2018-07-15 21:12:31 +08:00
|
|
|
nameToItem[1].insert(name, new IdStringTreeItem(id, ElementType::WIRE, items.at(i), parent));
|
2018-07-06 01:31:01 +08:00
|
|
|
else
|
2018-07-07 01:19:18 +08:00
|
|
|
wire_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
|
|
|
|
}
|
2018-07-06 01:31:01 +08:00
|
|
|
parent = wire_items[name];
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto wire : wire_items.toStdMap()) {
|
2018-07-06 01:31:01 +08:00
|
|
|
wire_root->addChild(wire.second);
|
|
|
|
}
|
2018-07-15 21:12:31 +08:00
|
|
|
for (auto wire : nameToItem[1].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-07-07 01:05:05 +08:00
|
|
|
pip_root->setText(0, "Pips");
|
2018-07-26 01:10:45 +08:00
|
|
|
pip_root->setFlags(pip_root->flags() & ~Qt::ItemIsSelectable);
|
2018-06-26 21:47:22 +08:00
|
|
|
treeWidget->insertTopLevelItem(0, pip_root);
|
2018-07-26 08:53:01 +08:00
|
|
|
#ifndef ARCH_ECP5
|
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;
|
2018-07-07 01:19:18 +08:00
|
|
|
for (int i = 0; i < items.size(); i++) {
|
|
|
|
if (!name.isEmpty())
|
|
|
|
name += "/";
|
2018-07-06 01:31:01 +08:00
|
|
|
name += items.at(i);
|
|
|
|
if (!pip_items.contains(name)) {
|
2018-07-07 01:19:18 +08:00
|
|
|
if (i == items.size() - 1)
|
2018-07-15 21:12:31 +08:00
|
|
|
nameToItem[2].insert(name, new IdStringTreeItem(id, ElementType::PIP, items.at(i), parent));
|
2018-07-06 01:31:01 +08:00
|
|
|
else
|
2018-07-07 01:19:18 +08:00
|
|
|
pip_items.insert(name, new ElementTreeItem(ElementType::NONE, items.at(i), parent));
|
|
|
|
}
|
2018-07-06 01:31:01 +08:00
|
|
|
parent = pip_items[name];
|
|
|
|
}
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto pip : pip_items.toStdMap()) {
|
2018-07-06 01:31:01 +08:00
|
|
|
pip_root->addChild(pip.second);
|
|
|
|
}
|
2018-07-15 21:12:31 +08:00
|
|
|
for (auto pip : nameToItem[2].toStdMap()) {
|
|
|
|
pip_root->addChild(pip.second);
|
|
|
|
}
|
2018-07-26 08:53:01 +08:00
|
|
|
#endif
|
2018-07-24 01:53:56 +08:00
|
|
|
|
|
|
|
nets_root = new QTreeWidgetItem(treeWidget);
|
|
|
|
nets_root->setText(0, "Nets");
|
2018-07-26 01:10:45 +08:00
|
|
|
nets_root->setFlags(nets_root->flags() & ~Qt::ItemIsSelectable);
|
2018-07-24 01:53:56 +08:00
|
|
|
treeWidget->insertTopLevelItem(0, nets_root);
|
|
|
|
|
|
|
|
cells_root = new QTreeWidgetItem(treeWidget);
|
|
|
|
cells_root->setText(0, "Cells");
|
2018-07-26 01:10:45 +08:00
|
|
|
cells_root->setFlags(cells_root->flags() & ~Qt::ItemIsSelectable);
|
2018-07-24 01:53:56 +08:00
|
|
|
treeWidget->insertTopLevelItem(0, cells_root);
|
|
|
|
|
2018-07-20 19:27:21 +08:00
|
|
|
updateTree();
|
2018-07-06 02:06:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void DesignWidget::updateTree()
|
|
|
|
{
|
2018-07-24 01:53:56 +08:00
|
|
|
if (!ctx)
|
|
|
|
return;
|
|
|
|
|
2018-07-06 03:51:17 +08:00
|
|
|
clearProperties();
|
2018-07-06 02:06:12 +08:00
|
|
|
|
2018-07-24 01:53:56 +08:00
|
|
|
// treeWidget->setSortingEnabled(false);
|
|
|
|
|
|
|
|
// Remove nets not existing any more
|
|
|
|
QMap<QString, QTreeWidgetItem *>::iterator i = nameToItem[3].begin();
|
|
|
|
while (i != nameToItem[3].end()) {
|
|
|
|
QMap<QString, QTreeWidgetItem *>::iterator prev = i;
|
|
|
|
++i;
|
|
|
|
if (ctx->nets.find(ctx->id(prev.key().toStdString())) == ctx->nets.end()) {
|
|
|
|
if (treeWidget->currentItem() == prev.value())
|
|
|
|
treeWidget->setCurrentItem(nets_root);
|
|
|
|
if (highlightSelected.contains(prev.value()))
|
|
|
|
highlightSelected.remove(prev.value());
|
|
|
|
delete prev.value();
|
|
|
|
nameToItem[3].erase(prev);
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 02:06:12 +08:00
|
|
|
// Add nets to tree
|
2018-07-24 01:53:56 +08:00
|
|
|
for (auto &item : ctx->nets) {
|
|
|
|
auto id = item.first;
|
|
|
|
QString name = QString(id.c_str(ctx));
|
|
|
|
if (!nameToItem[3].contains(name)) {
|
2018-07-15 21:12:31 +08:00
|
|
|
IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::NET, name, nullptr);
|
2018-07-24 01:53:56 +08:00
|
|
|
nets_root->addChild(newItem);
|
2018-07-15 21:12:31 +08:00
|
|
|
nameToItem[3].insert(name, newItem);
|
2018-07-06 02:06:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 01:53:56 +08:00
|
|
|
// Remove cells not existing any more
|
|
|
|
i = nameToItem[4].begin();
|
|
|
|
while (i != nameToItem[4].end()) {
|
|
|
|
QMap<QString, QTreeWidgetItem *>::iterator prev = i;
|
|
|
|
++i;
|
|
|
|
if (ctx->cells.find(ctx->id(prev.key().toStdString())) == ctx->cells.end()) {
|
|
|
|
if (treeWidget->currentItem() == prev.value())
|
|
|
|
treeWidget->setCurrentItem(cells_root);
|
|
|
|
if (highlightSelected.contains(prev.value()))
|
|
|
|
highlightSelected.remove(prev.value());
|
|
|
|
delete prev.value();
|
|
|
|
nameToItem[4].erase(prev);
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 02:06:12 +08:00
|
|
|
// Add cells to tree
|
2018-07-24 01:53:56 +08:00
|
|
|
for (auto &item : ctx->cells) {
|
|
|
|
auto id = item.first;
|
|
|
|
QString name = QString(id.c_str(ctx));
|
|
|
|
if (!nameToItem[4].contains(name)) {
|
2018-07-15 21:12:31 +08:00
|
|
|
IdStringTreeItem *newItem = new IdStringTreeItem(id, ElementType::CELL, name, nullptr);
|
2018-07-24 01:53:56 +08:00
|
|
|
cells_root->addChild(newItem);
|
2018-07-15 21:12:31 +08:00
|
|
|
nameToItem[4].insert(name, newItem);
|
2018-07-06 02:06:12 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-24 01:53:56 +08:00
|
|
|
// treeWidget->sortByColumn(0, Qt::AscendingOrder);
|
|
|
|
// treeWidget->setSortingEnabled(true);
|
2018-06-26 21:47:22 +08:00
|
|
|
}
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
|
2018-06-15 02:03:59 +08:00
|
|
|
{
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *topItem = groupManager->addProperty(id);
|
|
|
|
propertyToId[topItem] = id;
|
|
|
|
idToProperty[id] = topItem;
|
2018-07-26 01:10:45 +08:00
|
|
|
topItem->setSelectable(false);
|
2018-07-15 15:49:19 +08:00
|
|
|
propertyEditor->addProperty(topItem);
|
|
|
|
return topItem;
|
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();
|
|
|
|
}
|
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QString DesignWidget::getElementTypeName(ElementType type)
|
|
|
|
{
|
|
|
|
if (type == ElementType::NONE)
|
|
|
|
return "";
|
|
|
|
if (type == ElementType::BEL)
|
|
|
|
return "BEL";
|
|
|
|
if (type == ElementType::WIRE)
|
|
|
|
return "WIRE";
|
|
|
|
if (type == ElementType::PIP)
|
|
|
|
return "PIP";
|
|
|
|
if (type == ElementType::NET)
|
|
|
|
return "NET";
|
|
|
|
if (type == ElementType::CELL)
|
|
|
|
return "CELL";
|
|
|
|
return "";
|
|
|
|
}
|
2018-07-15 21:12:31 +08:00
|
|
|
int DesignWidget::getElementIndex(ElementType type)
|
|
|
|
{
|
|
|
|
if (type == ElementType::BEL)
|
|
|
|
return 0;
|
|
|
|
if (type == ElementType::WIRE)
|
|
|
|
return 1;
|
|
|
|
if (type == ElementType::PIP)
|
|
|
|
return 2;
|
|
|
|
if (type == ElementType::NET)
|
|
|
|
return 3;
|
|
|
|
if (type == ElementType::CELL)
|
|
|
|
return 4;
|
|
|
|
return -1;
|
|
|
|
}
|
2018-07-15 15:49:19 +08:00
|
|
|
|
|
|
|
ElementType DesignWidget::getElementTypeByName(QString type)
|
|
|
|
{
|
|
|
|
if (type == "BEL")
|
|
|
|
return ElementType::BEL;
|
|
|
|
if (type == "WIRE")
|
|
|
|
return ElementType::WIRE;
|
|
|
|
if (type == "PIP")
|
|
|
|
return ElementType::PIP;
|
|
|
|
if (type == "NET")
|
|
|
|
return ElementType::NET;
|
|
|
|
if (type == "CELL")
|
|
|
|
return ElementType::CELL;
|
|
|
|
return ElementType::NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesignWidget::addProperty(QtProperty *topItem, int propertyType, const QString &name, QVariant value,
|
|
|
|
const ElementType &type)
|
|
|
|
{
|
|
|
|
QtVariantProperty *item = readOnlyManager->addProperty(propertyType, name);
|
|
|
|
item->setValue(value);
|
|
|
|
item->setPropertyId(getElementTypeName(type));
|
2018-07-26 01:10:45 +08:00
|
|
|
item->setSelectable(type != ElementType::NONE);
|
2018-07-15 15:49:19 +08:00
|
|
|
topItem->addSubProperty(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name)
|
|
|
|
{
|
|
|
|
QtProperty *item = groupManager->addProperty(name);
|
2018-07-26 01:10:45 +08:00
|
|
|
item->setSelectable(false);
|
2018-07-15 15:49:19 +08:00
|
|
|
topItem->addSubProperty(item);
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2018-07-27 09:14:40 +08:00
|
|
|
void DesignWidget::onClickedBel(BelId bel, bool keep)
|
2018-07-26 23:20:58 +08:00
|
|
|
{
|
|
|
|
QTreeWidgetItem *item = nameToItem[getElementIndex(ElementType::BEL)].value(ctx->getBelName(bel).c_str(ctx));
|
|
|
|
treeWidget->setCurrentItem(item);
|
2018-07-27 09:14:40 +08:00
|
|
|
Q_EMIT selected(getDecals(ElementType::BEL, ctx->getBelName(bel)), keep);
|
2018-07-26 23:20:58 +08:00
|
|
|
}
|
|
|
|
|
2018-07-27 09:14:40 +08:00
|
|
|
void DesignWidget::onClickedWire(WireId wire, bool keep)
|
2018-07-27 05:40:45 +08:00
|
|
|
{
|
|
|
|
QTreeWidgetItem *item = nameToItem[getElementIndex(ElementType::WIRE)].value(ctx->getWireName(wire).c_str(ctx));
|
|
|
|
treeWidget->setCurrentItem(item);
|
2018-07-27 09:14:40 +08:00
|
|
|
Q_EMIT selected(getDecals(ElementType::WIRE, ctx->getWireName(wire)), keep);
|
2018-07-27 05:40:45 +08:00
|
|
|
}
|
|
|
|
|
2018-07-27 09:28:01 +08:00
|
|
|
void DesignWidget::onClickedPip(PipId pip, bool keep)
|
|
|
|
{
|
|
|
|
QTreeWidgetItem *item = nameToItem[getElementIndex(ElementType::PIP)].value(ctx->getPipName(pip).c_str(ctx));
|
|
|
|
treeWidget->setCurrentItem(item);
|
|
|
|
Q_EMIT selected(getDecals(ElementType::PIP, ctx->getPipName(pip)), keep);
|
|
|
|
}
|
|
|
|
|
2018-07-14 23:50:06 +08:00
|
|
|
void DesignWidget::onItemSelectionChanged()
|
2018-06-15 02:03:59 +08:00
|
|
|
{
|
2018-07-15 15:49:19 +08:00
|
|
|
if (treeWidget->selectedItems().size() == 0)
|
|
|
|
return;
|
|
|
|
|
2018-07-26 08:53:01 +08:00
|
|
|
if (treeWidget->selectedItems().size() > 1) {
|
2018-07-26 01:21:46 +08:00
|
|
|
std::vector<DecalXY> decals;
|
|
|
|
for (auto clickItem : treeWidget->selectedItems()) {
|
|
|
|
IdString value = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
|
|
|
ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
|
|
|
|
std::vector<DecalXY> d = getDecals(type, value);
|
|
|
|
std::move(d.begin(), d.end(), std::back_inserter(decals));
|
|
|
|
}
|
2018-07-27 09:14:40 +08:00
|
|
|
Q_EMIT selected(decals, false);
|
2018-07-26 01:21:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-14 23:50:06 +08:00
|
|
|
QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
|
|
|
|
|
2018-07-09 02:24:30 +08:00
|
|
|
if (!clickItem->parent())
|
2018-06-15 02:03:59 +08:00
|
|
|
return;
|
|
|
|
|
2018-07-09 02:24:30 +08:00
|
|
|
ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
|
2018-07-06 01:31:01 +08:00
|
|
|
if (type == ElementType::NONE) {
|
|
|
|
return;
|
|
|
|
}
|
2018-07-07 01:19:18 +08:00
|
|
|
|
2018-07-15 01:44:37 +08:00
|
|
|
std::vector<DecalXY> decals;
|
|
|
|
|
2018-07-15 22:20:35 +08:00
|
|
|
addToHistory(clickItem);
|
|
|
|
|
2018-07-06 01:31:01 +08:00
|
|
|
clearProperties();
|
2018-07-07 01:05:05 +08:00
|
|
|
|
2018-07-19 00:06:47 +08:00
|
|
|
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
2018-07-27 09:14:40 +08:00
|
|
|
Q_EMIT selected(getDecals(type, c), false);
|
2018-07-07 18:24:13 +08:00
|
|
|
|
2018-07-19 00:06:47 +08:00
|
|
|
if (type == ElementType::BEL) {
|
|
|
|
BelId bel = ctx->getBelByName(c);
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *topItem = addTopLevelProperty("Bel");
|
2018-07-07 18:24:13 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
|
|
|
addProperty(topItem, QVariant::String, "Type", ctx->belTypeToId(ctx->getBelType(bel)).c_str(ctx));
|
|
|
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel));
|
|
|
|
addProperty(topItem, QVariant::String, "Bound Cell", ctx->getBoundBelCell(bel).c_str(ctx), ElementType::CELL);
|
|
|
|
addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->getConflictingBelCell(bel).c_str(ctx),
|
|
|
|
ElementType::CELL);
|
2018-07-07 18:24:13 +08:00
|
|
|
|
2018-07-23 02:37:54 +08:00
|
|
|
QtProperty *belpinsItem = addSubGroup(topItem, "Ports");
|
|
|
|
for (const auto &item : ctx->getBelPins(bel)) {
|
|
|
|
QtProperty *portInfoItem = addSubGroup(belpinsItem, ctx->portPinToId(item).c_str(ctx));
|
|
|
|
addProperty(portInfoItem, QVariant::String, "Name", ctx->portPinToId(item).c_str(ctx));
|
|
|
|
addProperty(portInfoItem, QVariant::Int, "Type", int(ctx->getBelPinType(bel, item)));
|
|
|
|
WireId wire = ctx->getBelPinWire(bel, item);
|
|
|
|
addProperty(portInfoItem, QVariant::String, "Wire", ctx->getWireName(wire).c_str(ctx), ElementType::WIRE);
|
|
|
|
}
|
2018-06-15 02:03:59 +08:00
|
|
|
} else if (type == ElementType::WIRE) {
|
2018-07-15 01:50:23 +08:00
|
|
|
WireId wire = ctx->getWireByName(c);
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *topItem = addTopLevelProperty("Wire");
|
2018-07-07 18:24:13 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
2018-07-26 22:38:11 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Type", ctx->getWireType(wire).c_str(ctx));
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
|
|
|
|
addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundWireNet(wire).c_str(ctx), ElementType::NET);
|
|
|
|
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx),
|
|
|
|
ElementType::NET);
|
2018-07-07 18:24:13 +08:00
|
|
|
|
2018-07-21 19:52:59 +08:00
|
|
|
DelayInfo delay = ctx->getWireDelay(wire);
|
|
|
|
|
|
|
|
QtProperty *delayItem = addSubGroup(topItem, "Delay");
|
|
|
|
addProperty(delayItem, QVariant::Double, "Min Raise", delay.minRaiseDelay());
|
|
|
|
addProperty(delayItem, QVariant::Double, "Max Raise", delay.maxRaiseDelay());
|
|
|
|
addProperty(delayItem, QVariant::Double, "Min Fall", delay.minFallDelay());
|
|
|
|
addProperty(delayItem, QVariant::Double, "Max Fall", delay.maxFallDelay());
|
|
|
|
|
2018-07-23 01:43:56 +08:00
|
|
|
QtProperty *belpinsItem = addSubGroup(topItem, "BelPins");
|
|
|
|
for (const auto &item : ctx->getWireBelPins(wire)) {
|
2018-07-07 18:24:13 +08:00
|
|
|
QString belname = "";
|
|
|
|
if (item.bel != BelId())
|
|
|
|
belname = ctx->getBelName(item.bel).c_str(ctx);
|
|
|
|
QString pinname = ctx->portPinToId(item.pin).c_str(ctx);
|
|
|
|
|
2018-07-23 01:43:56 +08:00
|
|
|
QtProperty *dhItem = addSubGroup(belpinsItem, belname + "-" + pinname);
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(dhItem, QVariant::String, "Bel", belname, ElementType::BEL);
|
|
|
|
addProperty(dhItem, QVariant::String, "PortPin", pinname);
|
2018-07-07 18:24:13 +08:00
|
|
|
}
|
|
|
|
|
2018-07-16 03:06:04 +08:00
|
|
|
int counter = 0;
|
2018-07-21 19:53:29 +08:00
|
|
|
QtProperty *pipsDownItem = addSubGroup(topItem, "Pips Downhill");
|
2018-07-16 03:06:04 +08:00
|
|
|
for (const auto &item : ctx->getPipsDownhill(wire)) {
|
|
|
|
addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
|
|
|
|
counter++;
|
|
|
|
if (counter == 50) {
|
|
|
|
addProperty(pipsDownItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
counter = 0;
|
2018-07-21 19:53:29 +08:00
|
|
|
QtProperty *pipsUpItem = addSubGroup(topItem, "Pips Uphill");
|
2018-07-16 03:06:04 +08:00
|
|
|
for (const auto &item : ctx->getPipsUphill(wire)) {
|
|
|
|
addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
|
|
|
|
counter++;
|
|
|
|
if (counter == 50) {
|
|
|
|
addProperty(pipsUpItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-06-15 02:03:59 +08:00
|
|
|
} else if (type == ElementType::PIP) {
|
2018-07-15 01:50:23 +08:00
|
|
|
PipId pip = ctx->getPipByName(c);
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *topItem = addTopLevelProperty("Pip");
|
2018-07-07 01:05:05 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
2018-07-26 22:38:11 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
|
|
|
|
addProperty(topItem, QVariant::String, "Bound Net", ctx->getBoundPipNet(pip).c_str(ctx), ElementType::NET);
|
|
|
|
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingPipNet(pip).c_str(ctx),
|
|
|
|
ElementType::NET);
|
|
|
|
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
|
|
|
|
ElementType::WIRE);
|
|
|
|
addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx),
|
|
|
|
ElementType::WIRE);
|
2018-07-07 18:24:13 +08:00
|
|
|
|
|
|
|
DelayInfo delay = ctx->getPipDelay(pip);
|
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *delayItem = addSubGroup(topItem, "Delay");
|
2018-07-21 19:52:59 +08:00
|
|
|
addProperty(delayItem, QVariant::Double, "Min Raise", delay.minRaiseDelay());
|
|
|
|
addProperty(delayItem, QVariant::Double, "Max Raise", delay.maxRaiseDelay());
|
|
|
|
addProperty(delayItem, QVariant::Double, "Min Fall", delay.minFallDelay());
|
|
|
|
addProperty(delayItem, QVariant::Double, "Max Fall", delay.maxFallDelay());
|
2018-07-06 02:17:32 +08:00
|
|
|
} else if (type == ElementType::NET) {
|
2018-07-06 03:51:17 +08:00
|
|
|
NetInfo *net = ctx->nets.at(c).get();
|
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *topItem = addTopLevelProperty("Net");
|
2018-07-07 01:05:05 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Name", net->name.c_str(ctx));
|
2018-07-06 02:17:32 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *driverItem = addSubGroup(topItem, "Driver");
|
|
|
|
addProperty(driverItem, QVariant::String, "Port", net->driver.port.c_str(ctx));
|
|
|
|
addProperty(driverItem, QVariant::Double, "Budget", net->driver.budget);
|
2018-07-07 01:19:18 +08:00
|
|
|
if (net->driver.cell)
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(driverItem, QVariant::String, "Cell", net->driver.cell->name.c_str(ctx), ElementType::CELL);
|
2018-07-07 01:19:18 +08:00
|
|
|
else
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(driverItem, QVariant::String, "Cell", "", ElementType::CELL);
|
2018-07-07 01:19:18 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *usersItem = addSubGroup(topItem, "Users");
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto &item : net->users) {
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *portItem = addSubGroup(usersItem, item.port.c_str(ctx));
|
2018-07-07 01:05:05 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(portItem, QVariant::String, "Port", item.port.c_str(ctx));
|
|
|
|
addProperty(portItem, QVariant::Double, "Budget", item.budget);
|
2018-07-07 01:05:05 +08:00
|
|
|
if (item.cell)
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(portItem, QVariant::String, "Cell", item.cell->name.c_str(ctx), ElementType::CELL);
|
2018-07-07 01:05:05 +08:00
|
|
|
else
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(portItem, QVariant::String, "Cell", "", ElementType::CELL);
|
2018-07-07 01:05:05 +08:00
|
|
|
}
|
2018-07-06 03:51:17 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto &item : net->attrs) {
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
2018-07-07 01:05:05 +08:00
|
|
|
}
|
2018-07-06 03:51:17 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *wiresItem = addSubGroup(topItem, "Wires");
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto &item : net->wires) {
|
2018-07-07 01:05:05 +08:00
|
|
|
auto name = ctx->getWireName(item.first).c_str(ctx);
|
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *wireItem = addSubGroup(wiresItem, name);
|
2018-07-19 00:37:54 +08:00
|
|
|
addProperty(wireItem, QVariant::String, "Wire", name, ElementType::WIRE);
|
2018-07-07 01:19:18 +08:00
|
|
|
|
|
|
|
if (item.second.pip != PipId())
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx),
|
|
|
|
ElementType::PIP);
|
2018-07-07 01:05:05 +08:00
|
|
|
else
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(wireItem, QVariant::String, "Pip", "", ElementType::PIP);
|
2018-07-07 01:05:05 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(wireItem, QVariant::Int, "Strength", (int)item.second.strength);
|
2018-07-07 01:05:05 +08:00
|
|
|
}
|
2018-07-06 03:51:17 +08:00
|
|
|
|
2018-07-07 01:05:05 +08:00
|
|
|
} else if (type == ElementType::CELL) {
|
|
|
|
CellInfo *cell = ctx->cells.at(c).get();
|
2018-07-06 03:51:17 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *topItem = addTopLevelProperty("Cell");
|
2018-07-06 03:51:17 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Name", cell->name.c_str(ctx));
|
|
|
|
addProperty(topItem, QVariant::String, "Type", cell->type.c_str(ctx));
|
2018-07-07 01:19:18 +08:00
|
|
|
if (cell->bel != BelId())
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Bel", ctx->getBelName(cell->bel).c_str(ctx), ElementType::BEL);
|
2018-07-07 01:05:05 +08:00
|
|
|
else
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(topItem, QVariant::String, "Bel", "", ElementType::BEL);
|
|
|
|
addProperty(topItem, QVariant::Int, "Bel strength", int(cell->belStrength));
|
2018-07-07 01:05:05 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *cellPortsItem = addSubGroup(topItem, "Ports");
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto &item : cell->ports) {
|
2018-07-07 01:05:05 +08:00
|
|
|
PortInfo p = item.second;
|
2018-07-07 01:19:18 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *portInfoItem = addSubGroup(cellPortsItem, p.name.c_str(ctx));
|
|
|
|
addProperty(portInfoItem, QVariant::String, "Name", p.name.c_str(ctx));
|
|
|
|
addProperty(portInfoItem, QVariant::Int, "Type", int(p.type));
|
2018-07-07 01:05:05 +08:00
|
|
|
if (p.net)
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(portInfoItem, QVariant::String, "Net", p.net->name.c_str(ctx), ElementType::NET);
|
2018-07-07 01:19:18 +08:00
|
|
|
else
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(portInfoItem, QVariant::String, "Net", "", ElementType::NET);
|
2018-07-07 01:05:05 +08:00
|
|
|
}
|
2018-07-06 03:51:17 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *cellAttrItem = addSubGroup(topItem, "Attributes");
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto &item : cell->attrs) {
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(cellAttrItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
2018-07-07 01:05:05 +08:00
|
|
|
}
|
2018-07-06 03:51:17 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *cellParamsItem = addSubGroup(topItem, "Parameters");
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto &item : cell->params) {
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(cellParamsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
2018-07-06 03:51:17 +08:00
|
|
|
}
|
|
|
|
|
2018-07-07 01:05:05 +08:00
|
|
|
QtProperty *cellPinsItem = groupManager->addProperty("Pins");
|
|
|
|
topItem->addSubProperty(cellPinsItem);
|
2018-07-07 01:19:18 +08:00
|
|
|
for (auto &item : cell->pins) {
|
2018-07-07 01:05:05 +08:00
|
|
|
std::string cell_port = item.first.c_str(ctx);
|
|
|
|
std::string bel_pin = item.second.c_str(ctx);
|
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
QtProperty *pinGroupItem = addSubGroup(cellPortsItem, (cell_port + " -> " + bel_pin).c_str());
|
2018-07-07 01:05:05 +08:00
|
|
|
|
2018-07-15 15:49:19 +08:00
|
|
|
addProperty(pinGroupItem, QVariant::String, "Cell", cell_port.c_str(), ElementType::CELL);
|
|
|
|
addProperty(pinGroupItem, QVariant::String, "Bel", bel_pin.c_str(), ElementType::BEL);
|
2018-07-07 01:19:18 +08:00
|
|
|
}
|
2018-06-15 02:03:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-15 23:50:58 +08:00
|
|
|
std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
|
|
|
|
{
|
|
|
|
std::vector<DecalXY> decals;
|
|
|
|
switch (type) {
|
|
|
|
case ElementType::BEL: {
|
|
|
|
BelId bel = ctx->getBelByName(value);
|
|
|
|
if (bel != BelId()) {
|
|
|
|
decals.push_back(ctx->getBelDecal(bel));
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ElementType::WIRE: {
|
|
|
|
WireId wire = ctx->getWireByName(value);
|
|
|
|
if (wire != WireId()) {
|
|
|
|
decals.push_back(ctx->getWireDecal(wire));
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ElementType::PIP: {
|
|
|
|
PipId pip = ctx->getPipByName(value);
|
|
|
|
if (pip != PipId()) {
|
|
|
|
decals.push_back(ctx->getPipDecal(pip));
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ElementType::NET: {
|
2018-07-19 00:06:47 +08:00
|
|
|
NetInfo *net = ctx->nets.at(value).get();
|
|
|
|
for (auto &item : net->wires) {
|
|
|
|
decals.push_back(ctx->getWireDecal(item.first));
|
|
|
|
if (item.second.pip != PipId()) {
|
|
|
|
decals.push_back(ctx->getPipDecal(item.second.pip));
|
|
|
|
}
|
|
|
|
}
|
2018-07-15 23:50:58 +08:00
|
|
|
} break;
|
|
|
|
case ElementType::CELL: {
|
2018-07-19 00:06:47 +08:00
|
|
|
CellInfo *cell = ctx->cells.at(value).get();
|
|
|
|
if (cell->bel != BelId()) {
|
|
|
|
decals.push_back(ctx->getBelDecal(cell->bel));
|
|
|
|
}
|
2018-07-15 23:50:58 +08:00
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return decals;
|
|
|
|
}
|
|
|
|
|
2018-07-26 23:14:56 +08:00
|
|
|
void DesignWidget::updateHighlightGroup(QList<QTreeWidgetItem *> items, int group)
|
2018-07-15 23:50:58 +08:00
|
|
|
{
|
2018-07-26 17:42:05 +08:00
|
|
|
const bool shouldClear = items.size() == 1;
|
|
|
|
for (auto item : items) {
|
|
|
|
if (highlightSelected.contains(item)) {
|
|
|
|
if (shouldClear && highlightSelected[item] == group) {
|
|
|
|
highlightSelected.remove(item);
|
2018-07-26 23:14:56 +08:00
|
|
|
} else
|
2018-07-26 17:42:05 +08:00
|
|
|
highlightSelected[item] = group;
|
2018-07-15 23:50:58 +08:00
|
|
|
} else
|
2018-07-26 17:42:05 +08:00
|
|
|
highlightSelected.insert(item, group);
|
2018-07-15 23:50:58 +08:00
|
|
|
}
|
2018-07-26 17:42:05 +08:00
|
|
|
std::vector<DecalXY> decals[8];
|
2018-07-15 23:50:58 +08:00
|
|
|
|
|
|
|
for (auto it : highlightSelected.toStdMap()) {
|
2018-07-26 17:42:05 +08:00
|
|
|
ElementType type = static_cast<ElementTreeItem *>(it.first)->getType();
|
|
|
|
IdString value = static_cast<IdStringTreeItem *>(it.first)->getData();
|
|
|
|
std::vector<DecalXY> d = getDecals(type, value);
|
|
|
|
std::move(d.begin(), d.end(), std::back_inserter(decals[it.second]));
|
2018-07-15 23:50:58 +08:00
|
|
|
}
|
2018-07-26 23:14:56 +08:00
|
|
|
for (int i = 0; i < 8; i++)
|
2018-07-26 17:42:05 +08:00
|
|
|
Q_EMIT highlight(decals[i], i);
|
2018-07-15 23:50:58 +08:00
|
|
|
}
|
|
|
|
|
2018-07-15 18:39:19 +08:00
|
|
|
void DesignWidget::prepareMenuProperty(const QPoint &pos)
|
2018-06-15 02:03:59 +08:00
|
|
|
{
|
2018-07-15 18:39:19 +08:00
|
|
|
QTreeWidget *tree = propertyEditor->treeWidget();
|
2018-07-26 23:14:56 +08:00
|
|
|
QList<QTreeWidgetItem *> items;
|
2018-07-26 17:42:05 +08:00
|
|
|
for (auto itemContextMenu : tree->selectedItems()) {
|
|
|
|
QtBrowserItem *browserItem = propertyEditor->itemToBrowserItem(itemContextMenu);
|
|
|
|
if (!browserItem)
|
|
|
|
continue;
|
|
|
|
QtProperty *selectedProperty = browserItem->property();
|
|
|
|
ElementType type = getElementTypeByName(selectedProperty->propertyId());
|
|
|
|
if (type == ElementType::NONE)
|
|
|
|
continue;
|
|
|
|
IdString value = ctx->id(selectedProperty->valueText().toStdString());
|
|
|
|
items.append(nameToItem[getElementIndex(type)].value(value.c_str(ctx)));
|
|
|
|
}
|
|
|
|
int selectedIndex = -1;
|
|
|
|
if (items.size() == 1) {
|
|
|
|
QTreeWidgetItem *item = items.at(0);
|
|
|
|
if (highlightSelected.contains(item))
|
|
|
|
selectedIndex = highlightSelected[item];
|
|
|
|
}
|
2018-06-15 02:03:59 +08:00
|
|
|
|
|
|
|
QMenu menu(this);
|
2018-07-27 01:32:21 +08:00
|
|
|
QAction *selectAction = new QAction("&Select", this);
|
|
|
|
connect(selectAction, &QAction::triggered, this, [this, items] {
|
|
|
|
std::vector<DecalXY> decals;
|
|
|
|
for (auto clickItem : items) {
|
|
|
|
IdString value = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
|
|
|
ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
|
|
|
|
std::vector<DecalXY> d = getDecals(type, value);
|
|
|
|
std::move(d.begin(), d.end(), std::back_inserter(decals));
|
|
|
|
}
|
2018-07-27 09:14:40 +08:00
|
|
|
Q_EMIT selected(decals, false);
|
2018-07-27 01:32:21 +08:00
|
|
|
});
|
|
|
|
menu.addAction(selectAction);
|
|
|
|
|
2018-07-15 23:50:58 +08:00
|
|
|
QMenu *subMenu = menu.addMenu("Highlight");
|
|
|
|
QActionGroup *group = new QActionGroup(this);
|
|
|
|
group->setExclusive(true);
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
QPixmap pixmap(32, 32);
|
|
|
|
pixmap.fill(QColor(highlightColors[i]));
|
|
|
|
QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this);
|
|
|
|
action->setCheckable(true);
|
|
|
|
subMenu->addAction(action);
|
|
|
|
group->addAction(action);
|
2018-07-26 17:42:05 +08:00
|
|
|
if (selectedIndex == i)
|
2018-07-15 23:50:58 +08:00
|
|
|
action->setChecked(true);
|
2018-07-26 17:42:05 +08:00
|
|
|
connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); });
|
2018-07-15 23:50:58 +08:00
|
|
|
}
|
|
|
|
menu.exec(tree->mapToGlobal(pos));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DesignWidget::prepareMenuTree(const QPoint &pos)
|
|
|
|
{
|
2018-07-26 17:42:05 +08:00
|
|
|
if (treeWidget->selectedItems().size() == 0)
|
2018-07-15 23:50:58 +08:00
|
|
|
return;
|
2018-07-26 17:42:05 +08:00
|
|
|
int selectedIndex = -1;
|
2018-07-26 23:14:56 +08:00
|
|
|
QList<QTreeWidgetItem *> items = treeWidget->selectedItems();
|
2018-07-26 17:42:05 +08:00
|
|
|
if (treeWidget->selectedItems().size() == 1) {
|
|
|
|
QTreeWidgetItem *item = treeWidget->selectedItems().at(0);
|
|
|
|
if (highlightSelected.contains(item))
|
|
|
|
selectedIndex = highlightSelected[item];
|
|
|
|
}
|
2018-07-15 23:50:58 +08:00
|
|
|
QMenu menu(this);
|
|
|
|
QMenu *subMenu = menu.addMenu("Highlight");
|
|
|
|
QActionGroup *group = new QActionGroup(this);
|
|
|
|
group->setExclusive(true);
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
QPixmap pixmap(32, 32);
|
|
|
|
pixmap.fill(QColor(highlightColors[i]));
|
|
|
|
QAction *action = new QAction(QIcon(pixmap), ("Group " + std::to_string(i)).c_str(), this);
|
|
|
|
action->setCheckable(true);
|
|
|
|
subMenu->addAction(action);
|
|
|
|
group->addAction(action);
|
2018-07-26 17:42:05 +08:00
|
|
|
if (selectedIndex == i)
|
2018-07-15 23:50:58 +08:00
|
|
|
action->setChecked(true);
|
2018-07-26 17:42:05 +08:00
|
|
|
connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); });
|
2018-07-15 23:50:58 +08:00
|
|
|
}
|
2018-07-26 17:42:05 +08:00
|
|
|
menu.exec(treeWidget->mapToGlobal(pos));
|
2018-06-15 02:03:59 +08:00
|
|
|
}
|
|
|
|
|
2018-07-15 18:39:19 +08:00
|
|
|
void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
|
|
|
|
{
|
|
|
|
QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property();
|
|
|
|
ElementType type = getElementTypeByName(selectedProperty->propertyId());
|
2018-07-15 21:12:31 +08:00
|
|
|
QString value = selectedProperty->valueText();
|
|
|
|
int index = getElementIndex(type);
|
2018-07-27 01:32:21 +08:00
|
|
|
if (type != ElementType::NONE && nameToItem[index].contains(value))
|
|
|
|
treeWidget->setCurrentItem(nameToItem[index].value(value));
|
2018-07-15 18:39:19 +08:00
|
|
|
}
|
2018-06-22 19:10:27 +08:00
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_END
|