
No arches ever actually used this to implement a Cell->Bel pin mapping, and in practice if any did try they would inevitably hit bitrot. This field had limited use in practice as it is necessary to also support cases where one cell pin maps to more than one bel pin. Removing this old field is the first step towards developing a new API for this. Signed-off-by: gatecat <gatecat@ds0.me>
1057 lines
42 KiB
C++
1057 lines
42 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
|
* Copyright (C) 2018 Serge Bazanski <q3k@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.
|
|
*
|
|
*/
|
|
|
|
#include "designwidget.h"
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
#include <QGridLayout>
|
|
#include <QLineEdit>
|
|
#include <QMenu>
|
|
#include <QSplitter>
|
|
#include <QToolBar>
|
|
#include <QTreeWidgetItem>
|
|
#include "fpgaviewwidget.h"
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
TreeView::TreeView(QWidget *parent) : QTreeView(parent) {}
|
|
|
|
TreeView::~TreeView() {}
|
|
|
|
void TreeView::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
QModelIndex index = indexAt(event->pos());
|
|
if (index != current) {
|
|
current = index;
|
|
Q_EMIT hoverIndexChanged(index);
|
|
}
|
|
QTreeView::mouseMoveEvent(event);
|
|
}
|
|
|
|
void TreeView::leaveEvent(QEvent *event) { Q_EMIT hoverIndexChanged(QModelIndex()); }
|
|
|
|
DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr)
|
|
{
|
|
tabWidget = new QTabWidget();
|
|
|
|
// Add tree view
|
|
for (int i = 0; i < 6; i++) {
|
|
treeView[i] = new TreeView();
|
|
treeModel[i] = new TreeModel::Model();
|
|
treeView[i]->setModel(treeModel[i]);
|
|
treeView[i]->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
treeView[i]->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
treeView[i]->viewport()->setMouseTracking(true);
|
|
selectionModel[i] = nullptr;
|
|
}
|
|
|
|
tabWidget->addTab(treeView[0], "Bels");
|
|
tabWidget->addTab(treeView[1], "Wires");
|
|
tabWidget->addTab(treeView[2], "Pips");
|
|
tabWidget->addTab(treeView[3], "Cells");
|
|
tabWidget->addTab(treeView[4], "Nets");
|
|
tabWidget->addTab(treeView[5], "Groups");
|
|
|
|
// Add property view
|
|
variantManager = new QtVariantPropertyManager(this);
|
|
readOnlyManager = new QtVariantPropertyManager(this);
|
|
groupManager = new QtGroupPropertyManager(this);
|
|
variantFactory = new QtVariantEditorFactory(this);
|
|
propertyEditor = new QtTreePropertyBrowser(this);
|
|
propertyEditor->setFactoryForManager(variantManager, variantFactory);
|
|
propertyEditor->setPropertiesWithoutValueMarked(true);
|
|
propertyEditor->show();
|
|
propertyEditor->treeWidget()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
propertyEditor->treeWidget()->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
propertyEditor->treeWidget()->viewport()->setMouseTracking(true);
|
|
|
|
searchEdit = new QLineEdit();
|
|
searchEdit->setClearButtonEnabled(true);
|
|
searchEdit->addAction(QIcon(":/icons/resources/zoom.png"), QLineEdit::LeadingPosition);
|
|
searchEdit->setPlaceholderText("Search...");
|
|
connect(searchEdit, &QLineEdit::returnPressed, this, &DesignWidget::onSearchInserted);
|
|
|
|
actionFirst = new QAction("", this);
|
|
actionFirst->setIcon(QIcon(":/icons/resources/resultset_first.png"));
|
|
actionFirst->setEnabled(false);
|
|
connect(actionFirst, &QAction::triggered, this, [this] {
|
|
history_ignore = true;
|
|
history_index = 0;
|
|
auto h = history.at(history_index);
|
|
if (tabWidget->currentIndex() != h.first) {
|
|
selectionModel[tabWidget->currentIndex()]->clearSelection();
|
|
tabWidget->setCurrentIndex(h.first);
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select);
|
|
} else {
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
updateButtons();
|
|
});
|
|
|
|
actionPrev = new QAction("", this);
|
|
actionPrev->setIcon(QIcon(":/icons/resources/resultset_previous.png"));
|
|
actionPrev->setEnabled(false);
|
|
connect(actionPrev, &QAction::triggered, this, [this] {
|
|
history_ignore = true;
|
|
history_index--;
|
|
auto h = history.at(history_index);
|
|
if (tabWidget->currentIndex() != h.first) {
|
|
selectionModel[tabWidget->currentIndex()]->clearSelection();
|
|
tabWidget->setCurrentIndex(h.first);
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select);
|
|
} else {
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
updateButtons();
|
|
});
|
|
|
|
actionNext = new QAction("", this);
|
|
actionNext->setIcon(QIcon(":/icons/resources/resultset_next.png"));
|
|
actionNext->setEnabled(false);
|
|
connect(actionNext, &QAction::triggered, this, [this] {
|
|
history_ignore = true;
|
|
history_index++;
|
|
auto h = history.at(history_index);
|
|
if (tabWidget->currentIndex() != h.first) {
|
|
selectionModel[tabWidget->currentIndex()]->clearSelection();
|
|
tabWidget->setCurrentIndex(h.first);
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select);
|
|
} else {
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
updateButtons();
|
|
});
|
|
|
|
actionLast = new QAction("", this);
|
|
actionLast->setIcon(QIcon(":/icons/resources/resultset_last.png"));
|
|
actionLast->setEnabled(false);
|
|
connect(actionLast, &QAction::triggered, this, [this] {
|
|
history_ignore = true;
|
|
history_index = int(history.size() - 1);
|
|
auto h = history.at(history_index);
|
|
if (tabWidget->currentIndex() != h.first) {
|
|
selectionModel[tabWidget->currentIndex()]->clearSelection();
|
|
tabWidget->setCurrentIndex(h.first);
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::Select);
|
|
} else {
|
|
selectionModel[h.first]->setCurrentIndex(h.second, QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
updateButtons();
|
|
});
|
|
|
|
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();
|
|
int num = tabWidget->currentIndex();
|
|
if (selectionModel[num]->selectedIndexes().size() > 0) {
|
|
QModelIndex index = selectionModel[num]->selectedIndexes().at(0);
|
|
if (index.isValid()) {
|
|
ElementType type = treeModel[num]->nodeFromIndex(index)->type();
|
|
if (type != ElementType::NONE)
|
|
addToHistory(num, index);
|
|
}
|
|
}
|
|
updateButtons();
|
|
});
|
|
|
|
QToolBar *toolbar = new QToolBar();
|
|
toolbar->addAction(actionFirst);
|
|
toolbar->addAction(actionPrev);
|
|
toolbar->addAction(actionNext);
|
|
toolbar->addAction(actionLast);
|
|
toolbar->addAction(actionClear);
|
|
|
|
QWidget *topWidget = new QWidget();
|
|
QVBoxLayout *vbox1 = new QVBoxLayout();
|
|
topWidget->setLayout(vbox1);
|
|
vbox1->setSpacing(5);
|
|
vbox1->setContentsMargins(0, 0, 0, 0);
|
|
vbox1->addWidget(searchEdit);
|
|
vbox1->addWidget(tabWidget);
|
|
|
|
QWidget *toolbarWidget = new QWidget();
|
|
QHBoxLayout *hbox = new QHBoxLayout;
|
|
hbox->setAlignment(Qt::AlignCenter);
|
|
toolbarWidget->setLayout(hbox);
|
|
hbox->setSpacing(0);
|
|
hbox->setContentsMargins(0, 0, 0, 0);
|
|
hbox->addWidget(toolbar);
|
|
|
|
QWidget *btmWidget = new QWidget();
|
|
|
|
QVBoxLayout *vbox2 = new QVBoxLayout();
|
|
btmWidget->setLayout(vbox2);
|
|
vbox2->setSpacing(0);
|
|
vbox2->setContentsMargins(0, 0, 0, 0);
|
|
vbox2->addWidget(toolbarWidget);
|
|
vbox2->addWidget(propertyEditor);
|
|
|
|
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
|
splitter->addWidget(topWidget);
|
|
splitter->addWidget(btmWidget);
|
|
|
|
QGridLayout *mainLayout = new QGridLayout();
|
|
mainLayout->setSpacing(0);
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
|
mainLayout->addWidget(splitter);
|
|
setLayout(mainLayout);
|
|
|
|
// Connection
|
|
connect(propertyEditor->treeWidget(), &QTreeWidget::customContextMenuRequested, this,
|
|
&DesignWidget::prepareMenuProperty);
|
|
connect(propertyEditor->treeWidget(), &QTreeWidget::itemDoubleClicked, this, &DesignWidget::onItemDoubleClicked);
|
|
connect(propertyEditor, &QtTreePropertyBrowser::hoverPropertyChanged, this, &DesignWidget::onHoverPropertyChanged);
|
|
|
|
for (int num = 0; num < 6; num++) {
|
|
connect(treeView[num], &TreeView::customContextMenuRequested,
|
|
[this, num](const QPoint &pos) { prepareMenuTree(num, pos); });
|
|
connect(treeView[num], &TreeView::doubleClicked, [this](const QModelIndex &index) { onDoubleClicked(index); });
|
|
connect(treeView[num], &TreeView::hoverIndexChanged,
|
|
[this, num](QModelIndex index) { onHoverIndexChanged(num, index); });
|
|
selectionModel[num] = treeView[num]->selectionModel();
|
|
connect(selectionModel[num], &QItemSelectionModel::selectionChanged,
|
|
[this, num](const QItemSelection &selected, const QItemSelection &deselected) {
|
|
onSelectionChanged(num, selected, deselected);
|
|
});
|
|
}
|
|
|
|
history_index = -1;
|
|
history_ignore = false;
|
|
|
|
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");
|
|
}
|
|
|
|
DesignWidget::~DesignWidget() {}
|
|
|
|
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(int tab, QModelIndex item)
|
|
{
|
|
if (!history_ignore) {
|
|
int count = int(history.size());
|
|
for (int i = count - 1; i > history_index; i--)
|
|
history.pop_back();
|
|
history.push_back(std::make_pair(tab, item));
|
|
history_index++;
|
|
}
|
|
history_ignore = false;
|
|
updateButtons();
|
|
}
|
|
|
|
void DesignWidget::newContext(Context *ctx)
|
|
{
|
|
if (!ctx)
|
|
return;
|
|
|
|
highlightSelected.clear();
|
|
history_ignore = false;
|
|
history_index = -1;
|
|
history.clear();
|
|
updateButtons();
|
|
|
|
highlightSelected.clear();
|
|
this->ctx = ctx;
|
|
{
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
{
|
|
TreeModel::ElementXYRoot<BelId>::ElementMap belMap;
|
|
for (const auto &bel : ctx->getBels()) {
|
|
auto loc = ctx->getBelLocation(bel);
|
|
belMap[std::pair<int, int>(loc.x, loc.y)].push_back(bel);
|
|
}
|
|
auto belGetter = [](Context *ctx, BelId id) { return ctx->getBelName(id); };
|
|
|
|
getTreeByElementType(ElementType::BEL)
|
|
->loadData(ctx,
|
|
std::unique_ptr<TreeModel::ElementXYRoot<BelId>>(
|
|
new TreeModel::ElementXYRoot<BelId>(ctx, belMap, belGetter, ElementType::BEL)));
|
|
}
|
|
|
|
{
|
|
TreeModel::ElementXYRoot<WireId>::ElementMap wireMap;
|
|
#ifdef ARCH_ICE40
|
|
for (int i = 0; i < int(ctx->chip_info->wire_data.size()); i++) {
|
|
const auto wire = &ctx->chip_info->wire_data[i];
|
|
WireId wireid;
|
|
wireid.index = i;
|
|
wireMap[std::pair<int, int>(wire->x, wire->y)].push_back(wireid);
|
|
}
|
|
#endif
|
|
#ifdef ARCH_ECP5
|
|
for (const auto &wire : ctx->getWires()) {
|
|
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
|
|
}
|
|
#endif
|
|
auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); };
|
|
getTreeByElementType(ElementType::WIRE)
|
|
->loadData(ctx,
|
|
std::unique_ptr<TreeModel::ElementXYRoot<WireId>>(new TreeModel::ElementXYRoot<WireId>(
|
|
ctx, wireMap, wireGetter, ElementType::WIRE)));
|
|
}
|
|
|
|
{
|
|
TreeModel::ElementXYRoot<PipId>::ElementMap pipMap;
|
|
for (const auto &pip : ctx->getPips()) {
|
|
auto loc = ctx->getPipLocation(pip);
|
|
pipMap[std::pair<int, int>(loc.x, loc.y)].push_back(pip);
|
|
}
|
|
auto pipGetter = [](Context *ctx, PipId id) { return ctx->getPipName(id); };
|
|
|
|
getTreeByElementType(ElementType::PIP)
|
|
->loadData(ctx,
|
|
std::unique_ptr<TreeModel::ElementXYRoot<PipId>>(
|
|
new TreeModel::ElementXYRoot<PipId>(ctx, pipMap, pipGetter, ElementType::PIP)));
|
|
}
|
|
|
|
getTreeByElementType(ElementType::CELL)
|
|
->loadData(ctx, std::unique_ptr<TreeModel::IdList>(new TreeModel::IdList(ElementType::CELL)));
|
|
getTreeByElementType(ElementType::NET)
|
|
->loadData(ctx, std::unique_ptr<TreeModel::IdList>(new TreeModel::IdList(ElementType::NET)));
|
|
}
|
|
updateTree();
|
|
}
|
|
|
|
void DesignWidget::updateTree()
|
|
{
|
|
clearProperties();
|
|
|
|
QMap<TreeModel::Item *, int>::iterator i = highlightSelected.begin();
|
|
while (i != highlightSelected.end()) {
|
|
QMap<TreeModel::Item *, int>::iterator prev = i;
|
|
++i;
|
|
if (prev.key()->type() == ElementType::NET && ctx->nets.find(prev.key()->id()[0]) == ctx->nets.end()) {
|
|
highlightSelected.erase(prev);
|
|
}
|
|
if (prev.key()->type() == ElementType::CELL && ctx->cells.find(prev.key()->id()[0]) == ctx->cells.end()) {
|
|
highlightSelected.erase(prev);
|
|
}
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
std::vector<IdStringList> cells;
|
|
for (auto &pair : ctx->cells) {
|
|
cells.push_back(IdStringList(pair.first));
|
|
}
|
|
std::vector<IdStringList> nets;
|
|
for (auto &pair : ctx->nets) {
|
|
nets.push_back(IdStringList(pair.first));
|
|
}
|
|
|
|
getTreeByElementType(ElementType::CELL)->updateElements(cells);
|
|
getTreeByElementType(ElementType::NET)->updateElements(nets);
|
|
}
|
|
}
|
|
QtProperty *DesignWidget::addTopLevelProperty(const QString &id)
|
|
{
|
|
QtProperty *topItem = groupManager->addProperty(id);
|
|
propertyToId[topItem] = id;
|
|
idToProperty[id] = topItem;
|
|
topItem->setSelectable(false);
|
|
propertyEditor->addProperty(topItem);
|
|
return topItem;
|
|
}
|
|
|
|
void DesignWidget::clearProperties()
|
|
{
|
|
QMap<QtProperty *, QString>::ConstIterator itProp = propertyToId.constBegin();
|
|
while (itProp != propertyToId.constEnd()) {
|
|
delete itProp.key();
|
|
itProp++;
|
|
}
|
|
propertyToId.clear();
|
|
idToProperty.clear();
|
|
}
|
|
|
|
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 "";
|
|
}
|
|
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;
|
|
}
|
|
|
|
TreeModel::Model *DesignWidget::getTreeByElementType(ElementType type)
|
|
{
|
|
if (type == ElementType::NONE)
|
|
return nullptr;
|
|
if (type == ElementType::BEL)
|
|
return treeModel[0];
|
|
if (type == ElementType::WIRE)
|
|
return treeModel[1];
|
|
if (type == ElementType::PIP)
|
|
return treeModel[2];
|
|
if (type == ElementType::CELL)
|
|
return treeModel[3];
|
|
if (type == ElementType::NET)
|
|
return treeModel[4];
|
|
return nullptr;
|
|
}
|
|
int DesignWidget::getIndexByElementType(ElementType type)
|
|
{
|
|
if (type == ElementType::NONE)
|
|
return -1;
|
|
if (type == ElementType::BEL)
|
|
return 0;
|
|
if (type == ElementType::WIRE)
|
|
return 1;
|
|
if (type == ElementType::PIP)
|
|
return 2;
|
|
if (type == ElementType::CELL)
|
|
return 3;
|
|
if (type == ElementType::NET)
|
|
return 4;
|
|
if (type == ElementType::GROUP)
|
|
return 5;
|
|
return -1;
|
|
}
|
|
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));
|
|
item->setSelectable(type != ElementType::NONE);
|
|
topItem->addSubProperty(item);
|
|
}
|
|
|
|
QtProperty *DesignWidget::addSubGroup(QtProperty *topItem, const QString &name)
|
|
{
|
|
QtProperty *item = groupManager->addProperty(name);
|
|
item->setSelectable(false);
|
|
topItem->addSubProperty(item);
|
|
return item;
|
|
}
|
|
|
|
void DesignWidget::clearAllSelectionModels()
|
|
{
|
|
for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++)
|
|
selectionModel[i]->clearSelection();
|
|
}
|
|
|
|
void DesignWidget::onClickedBel(BelId bel, bool keep)
|
|
{
|
|
boost::optional<TreeModel::Item *> item;
|
|
{
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
item = getTreeByElementType(ElementType::BEL)->nodeForId(ctx->getBelName(bel));
|
|
if (!item)
|
|
return;
|
|
|
|
Q_EMIT selected(getDecals(ElementType::BEL, ctx->getBelName(bel)), keep);
|
|
}
|
|
int index = getIndexByElementType(ElementType::BEL);
|
|
if (!keep)
|
|
clearAllSelectionModels();
|
|
if (tabWidget->currentIndex() != index) {
|
|
tabWidget->setCurrentIndex(index);
|
|
}
|
|
selectionModel[index]->setCurrentIndex(getTreeByElementType(ElementType::BEL)->indexFromNode(*item),
|
|
keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
|
|
void DesignWidget::onClickedWire(WireId wire, bool keep)
|
|
{
|
|
boost::optional<TreeModel::Item *> item;
|
|
{
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
item = getTreeByElementType(ElementType::WIRE)->nodeForId(ctx->getWireName(wire));
|
|
if (!item)
|
|
return;
|
|
|
|
Q_EMIT selected(getDecals(ElementType::WIRE, ctx->getWireName(wire)), keep);
|
|
}
|
|
int index = getIndexByElementType(ElementType::WIRE);
|
|
if (!keep)
|
|
clearAllSelectionModels();
|
|
if (tabWidget->currentIndex() != index)
|
|
tabWidget->setCurrentIndex(index);
|
|
selectionModel[index]->setCurrentIndex(getTreeByElementType(ElementType::WIRE)->indexFromNode(*item),
|
|
keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
|
|
void DesignWidget::onClickedPip(PipId pip, bool keep)
|
|
{
|
|
boost::optional<TreeModel::Item *> item;
|
|
{
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
item = getTreeByElementType(ElementType::PIP)->nodeForId(ctx->getPipName(pip));
|
|
if (!item)
|
|
return;
|
|
|
|
Q_EMIT selected(getDecals(ElementType::PIP, ctx->getPipName(pip)), keep);
|
|
}
|
|
|
|
int index = getIndexByElementType(ElementType::PIP);
|
|
if (!keep)
|
|
clearAllSelectionModels();
|
|
if (tabWidget->currentIndex() != index)
|
|
tabWidget->setCurrentIndex(index);
|
|
selectionModel[index]->setCurrentIndex(getTreeByElementType(ElementType::PIP)->indexFromNode(*item),
|
|
keep ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
|
|
void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QItemSelection &)
|
|
{
|
|
int num_selected = 0;
|
|
std::vector<DecalXY> decals;
|
|
for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++) {
|
|
num_selected += selectionModel[i]->selectedIndexes().size();
|
|
for (auto index : selectionModel[i]->selectedIndexes()) {
|
|
TreeModel::Item *item = treeModel[i]->nodeFromIndex(index);
|
|
std::vector<DecalXY> d = getDecals(item->type(), item->id());
|
|
std::move(d.begin(), d.end(), std::back_inserter(decals));
|
|
}
|
|
}
|
|
|
|
// Keep other tree seleciton only if Control is pressed
|
|
if (num_selected > 1 && QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) == true) {
|
|
Q_EMIT selected(decals, false);
|
|
return;
|
|
}
|
|
|
|
// For deselect and multiple select just send all
|
|
if (selectionModel[num]->selectedIndexes().size() != 1) {
|
|
Q_EMIT selected(decals, false);
|
|
return;
|
|
}
|
|
|
|
QModelIndex index = selectionModel[num]->selectedIndexes().at(0);
|
|
if (!index.isValid())
|
|
return;
|
|
TreeModel::Item *clickItem = treeModel[num]->nodeFromIndex(index);
|
|
|
|
ElementType type = clickItem->type();
|
|
if (type == ElementType::NONE)
|
|
return;
|
|
|
|
// Clear other tab selections
|
|
for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++)
|
|
if (i != num)
|
|
selectionModel[i]->clearSelection();
|
|
|
|
addToHistory(num, index);
|
|
|
|
clearProperties();
|
|
|
|
IdStringList c = clickItem->id();
|
|
Q_EMIT selected(getDecals(type, c), false);
|
|
|
|
if (type == ElementType::BEL) {
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
BelId bel = ctx->getBelByName(c);
|
|
QtProperty *topItem = addTopLevelProperty("Bel");
|
|
|
|
addProperty(topItem, QVariant::String, "Name", ctx->nameOfBel(bel));
|
|
addProperty(topItem, QVariant::String, "Type", ctx->getBelType(bel).c_str(ctx));
|
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel));
|
|
addProperty(topItem, QVariant::String, "Bound Cell", ctx->nameOf(ctx->getBoundBelCell(bel)), ElementType::CELL);
|
|
addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->nameOf(ctx->getConflictingBelCell(bel)),
|
|
ElementType::CELL);
|
|
|
|
QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
|
|
for (auto &item : ctx->getBelAttrs(bel)) {
|
|
addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
|
}
|
|
|
|
QtProperty *belpinsItem = addSubGroup(topItem, "Ports");
|
|
for (const auto &item : ctx->getBelPins(bel)) {
|
|
QtProperty *portInfoItem = addSubGroup(belpinsItem, item.c_str(ctx));
|
|
addProperty(portInfoItem, QVariant::String, "Name", 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->nameOfWire(wire), ElementType::WIRE);
|
|
}
|
|
} else if (type == ElementType::WIRE) {
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
WireId wire = ctx->getWireByName(c);
|
|
QtProperty *topItem = addTopLevelProperty("Wire");
|
|
|
|
addProperty(topItem, QVariant::String, "Name", ctx->nameOfWire(wire));
|
|
addProperty(topItem, QVariant::String, "Type", ctx->getWireType(wire).c_str(ctx));
|
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
|
|
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundWireNet(wire)), ElementType::NET);
|
|
addProperty(topItem, QVariant::String, "Conflicting Wire", ctx->nameOfWire(ctx->getConflictingWireWire(wire)),
|
|
ElementType::WIRE);
|
|
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingWireNet(wire)),
|
|
ElementType::NET);
|
|
|
|
QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
|
|
for (auto &item : ctx->getWireAttrs(wire)) {
|
|
addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
|
}
|
|
|
|
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());
|
|
|
|
QtProperty *belpinsItem = addSubGroup(topItem, "BelPins");
|
|
for (const auto &item : ctx->getWireBelPins(wire)) {
|
|
QString belname = "";
|
|
if (item.bel != BelId())
|
|
belname = ctx->nameOfBel(item.bel);
|
|
QString pinname = item.pin.c_str(ctx);
|
|
|
|
QtProperty *dhItem = addSubGroup(belpinsItem, belname + "-" + pinname);
|
|
addProperty(dhItem, QVariant::String, "Bel", belname, ElementType::BEL);
|
|
addProperty(dhItem, QVariant::String, "PortPin", pinname);
|
|
}
|
|
|
|
int counter = 0;
|
|
QtProperty *pipsDownItem = addSubGroup(topItem, "Pips Downhill");
|
|
for (const auto &item : ctx->getPipsDownhill(wire)) {
|
|
addProperty(pipsDownItem, QVariant::String, "", ctx->nameOfPip(item), ElementType::PIP);
|
|
counter++;
|
|
if (counter == 50) {
|
|
addProperty(pipsDownItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
counter = 0;
|
|
QtProperty *pipsUpItem = addSubGroup(topItem, "Pips Uphill");
|
|
for (const auto &item : ctx->getPipsUphill(wire)) {
|
|
addProperty(pipsUpItem, QVariant::String, "", ctx->nameOfPip(item), ElementType::PIP);
|
|
counter++;
|
|
if (counter == 50) {
|
|
addProperty(pipsUpItem, QVariant::String, "Warning", "Too many items...", ElementType::NONE);
|
|
break;
|
|
}
|
|
}
|
|
} else if (type == ElementType::PIP) {
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
PipId pip = ctx->getPipByName(c);
|
|
QtProperty *topItem = addTopLevelProperty("Pip");
|
|
|
|
addProperty(topItem, QVariant::String, "Name", ctx->nameOfPip(pip));
|
|
addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
|
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
|
|
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
|
|
if (ctx->getConflictingPipWire(pip) != WireId()) {
|
|
addProperty(topItem, QVariant::String, "Conflicting Wire", ctx->nameOfWire(ctx->getConflictingPipWire(pip)),
|
|
ElementType::WIRE);
|
|
} else {
|
|
addProperty(topItem, QVariant::String, "Conflicting Wire", "", ElementType::NONE);
|
|
}
|
|
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
|
|
ElementType::NET);
|
|
addProperty(topItem, QVariant::String, "Src Wire", ctx->nameOfWire(ctx->getPipSrcWire(pip)), ElementType::WIRE);
|
|
addProperty(topItem, QVariant::String, "Dest Wire", ctx->nameOfWire(ctx->getPipDstWire(pip)),
|
|
ElementType::WIRE);
|
|
|
|
QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
|
|
for (auto &item : ctx->getPipAttrs(pip)) {
|
|
addProperty(attrsItem, QVariant::String, item.first.c_str(ctx), item.second.c_str());
|
|
}
|
|
|
|
DelayInfo delay = ctx->getPipDelay(pip);
|
|
|
|
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());
|
|
} else if (type == ElementType::NET) {
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
NetInfo *net = ctx->nets.at(c[0]).get();
|
|
|
|
QtProperty *topItem = addTopLevelProperty("Net");
|
|
|
|
addProperty(topItem, QVariant::String, "Name", net->name.c_str(ctx));
|
|
|
|
QtProperty *driverItem = addSubGroup(topItem, "Driver");
|
|
addProperty(driverItem, QVariant::String, "Port", net->driver.port.c_str(ctx));
|
|
addProperty(driverItem, QVariant::Double, "Budget", net->driver.budget);
|
|
if (net->driver.cell)
|
|
addProperty(driverItem, QVariant::String, "Cell", net->driver.cell->name.c_str(ctx), ElementType::CELL);
|
|
else
|
|
addProperty(driverItem, QVariant::String, "Cell", "", ElementType::CELL);
|
|
|
|
QtProperty *usersItem = addSubGroup(topItem, "Users");
|
|
for (auto &item : net->users) {
|
|
QtProperty *portItem = addSubGroup(usersItem, item.port.c_str(ctx));
|
|
|
|
addProperty(portItem, QVariant::String, "Port", item.port.c_str(ctx));
|
|
addProperty(portItem, QVariant::Double, "Budget", item.budget);
|
|
if (item.cell)
|
|
addProperty(portItem, QVariant::String, "Cell", item.cell->name.c_str(ctx), ElementType::CELL);
|
|
else
|
|
addProperty(portItem, QVariant::String, "Cell", "", ElementType::CELL);
|
|
}
|
|
|
|
QtProperty *attrsItem = addSubGroup(topItem, "Attributes");
|
|
for (auto &item : net->attrs) {
|
|
addProperty(attrsItem, QVariant::String, item.first.c_str(ctx),
|
|
item.second.is_string ? item.second.as_string().c_str() : item.second.to_string().c_str());
|
|
}
|
|
|
|
QtProperty *wiresItem = addSubGroup(topItem, "Wires");
|
|
for (auto &item : net->wires) {
|
|
auto name = ctx->nameOfWire(item.first);
|
|
|
|
QtProperty *wireItem = addSubGroup(wiresItem, name);
|
|
addProperty(wireItem, QVariant::String, "Wire", name, ElementType::WIRE);
|
|
|
|
if (item.second.pip != PipId())
|
|
addProperty(wireItem, QVariant::String, "Pip", ctx->nameOfPip(item.second.pip), ElementType::PIP);
|
|
else
|
|
addProperty(wireItem, QVariant::String, "Pip", "", ElementType::PIP);
|
|
|
|
addProperty(wireItem, QVariant::Int, "Strength", (int)item.second.strength);
|
|
}
|
|
|
|
} else if (type == ElementType::CELL) {
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
CellInfo *cell = ctx->cells.at(c[0]).get();
|
|
|
|
QtProperty *topItem = addTopLevelProperty("Cell");
|
|
|
|
addProperty(topItem, QVariant::String, "Name", cell->name.c_str(ctx));
|
|
addProperty(topItem, QVariant::String, "Type", cell->type.c_str(ctx));
|
|
if (cell->bel != BelId())
|
|
addProperty(topItem, QVariant::String, "Bel", ctx->nameOfBel(cell->bel), ElementType::BEL);
|
|
else
|
|
addProperty(topItem, QVariant::String, "Bel", "", ElementType::BEL);
|
|
addProperty(topItem, QVariant::Int, "Bel strength", int(cell->belStrength));
|
|
|
|
QtProperty *cellPortsItem = addSubGroup(topItem, "Ports");
|
|
for (auto &item : cell->ports) {
|
|
PortInfo p = item.second;
|
|
|
|
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));
|
|
if (p.net)
|
|
addProperty(portInfoItem, QVariant::String, "Net", p.net->name.c_str(ctx), ElementType::NET);
|
|
else
|
|
addProperty(portInfoItem, QVariant::String, "Net", "", ElementType::NET);
|
|
}
|
|
|
|
QtProperty *cellAttrItem = addSubGroup(topItem, "Attributes");
|
|
for (auto &item : cell->attrs) {
|
|
addProperty(cellAttrItem, QVariant::String, item.first.c_str(ctx),
|
|
item.second.is_string ? item.second.as_string().c_str() : item.second.to_string().c_str());
|
|
}
|
|
|
|
QtProperty *cellParamsItem = addSubGroup(topItem, "Parameters");
|
|
for (auto &item : cell->params) {
|
|
addProperty(cellParamsItem, QVariant::String, item.first.c_str(ctx),
|
|
item.second.is_string ? item.second.as_string().c_str() : item.second.to_string().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdStringList 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: {
|
|
NetInfo *net = ctx->nets.at(value[0]).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));
|
|
}
|
|
}
|
|
} break;
|
|
case ElementType::CELL: {
|
|
CellInfo *cell = ctx->cells.at(value[0]).get();
|
|
if (cell->bel != BelId()) {
|
|
decals.push_back(ctx->getBelDecal(cell->bel));
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
return decals;
|
|
}
|
|
|
|
void DesignWidget::updateHighlightGroup(QList<TreeModel::Item *> items, int group)
|
|
{
|
|
const bool shouldClear = items.size() == 1;
|
|
for (auto item : items) {
|
|
if (highlightSelected.contains(item)) {
|
|
if (shouldClear && highlightSelected[item] == group) {
|
|
highlightSelected.remove(item);
|
|
} else
|
|
highlightSelected[item] = group;
|
|
} else
|
|
highlightSelected.insert(item, group);
|
|
}
|
|
std::vector<DecalXY> decals[8];
|
|
|
|
for (auto it : highlightSelected.toStdMap()) {
|
|
std::vector<DecalXY> d = getDecals(it.first->type(), it.first->id());
|
|
std::move(d.begin(), d.end(), std::back_inserter(decals[it.second]));
|
|
}
|
|
for (int i = 0; i < 8; i++)
|
|
Q_EMIT highlight(decals[i], i);
|
|
}
|
|
|
|
void DesignWidget::prepareMenuProperty(const QPoint &pos)
|
|
{
|
|
QTreeWidget *tree = propertyEditor->treeWidget();
|
|
QList<TreeModel::Item *> items;
|
|
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;
|
|
IdStringList value = IdStringList::parse(ctx, selectedProperty->valueText().toStdString());
|
|
auto node = getTreeByElementType(type)->nodeForId(value);
|
|
if (!node)
|
|
continue;
|
|
items.append(*node);
|
|
}
|
|
int selectedIndex = -1;
|
|
if (items.size() == 1) {
|
|
TreeModel::Item *item = items.at(0);
|
|
if (highlightSelected.contains(item))
|
|
selectedIndex = highlightSelected[item];
|
|
}
|
|
|
|
QMenu menu(this);
|
|
QAction *selectAction = new QAction("&Select", this);
|
|
connect(selectAction, &QAction::triggered, this, [this, items] {
|
|
std::vector<DecalXY> decals;
|
|
for (auto clickItem : items) {
|
|
std::vector<DecalXY> d = getDecals(clickItem->type(), clickItem->id());
|
|
std::move(d.begin(), d.end(), std::back_inserter(decals));
|
|
}
|
|
Q_EMIT selected(decals, false);
|
|
});
|
|
menu.addAction(selectAction);
|
|
|
|
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);
|
|
if (selectedIndex == i)
|
|
action->setChecked(true);
|
|
connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); });
|
|
}
|
|
menu.exec(tree->mapToGlobal(pos));
|
|
}
|
|
|
|
void DesignWidget::prepareMenuTree(int num, const QPoint &pos)
|
|
{
|
|
int selectedIndex = -1;
|
|
|
|
if (selectionModel[num]->selectedIndexes().size() == 0)
|
|
return;
|
|
|
|
QList<TreeModel::Item *> items;
|
|
for (int i = 0; i <= getIndexByElementType(ElementType::GROUP); i++) {
|
|
for (auto index : selectionModel[i]->selectedIndexes()) {
|
|
TreeModel::Item *item = treeModel[i]->nodeFromIndex(index);
|
|
items.append(item);
|
|
}
|
|
}
|
|
if (items.size() == 1) {
|
|
TreeModel::Item *item = items.at(0);
|
|
if (highlightSelected.contains(item))
|
|
selectedIndex = highlightSelected[item];
|
|
}
|
|
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);
|
|
if (selectedIndex == i)
|
|
action->setChecked(true);
|
|
connect(action, &QAction::triggered, this, [this, i, items] { updateHighlightGroup(items, i); });
|
|
}
|
|
menu.exec(treeView[num]->mapToGlobal(pos));
|
|
}
|
|
|
|
void DesignWidget::onItemDoubleClicked(QTreeWidgetItem *item, int column)
|
|
{
|
|
QtProperty *selectedProperty = propertyEditor->itemToBrowserItem(item)->property();
|
|
ElementType type = getElementTypeByName(selectedProperty->propertyId());
|
|
if (type == ElementType::NONE)
|
|
return;
|
|
|
|
IdStringList value = IdStringList::parse(ctx, selectedProperty->valueText().toStdString());
|
|
auto it = getTreeByElementType(type)->nodeForId(value);
|
|
if (it) {
|
|
int num = getIndexByElementType(type);
|
|
clearAllSelectionModels();
|
|
if (tabWidget->currentIndex() != num)
|
|
tabWidget->setCurrentIndex(num);
|
|
selectionModel[num]->setCurrentIndex(getTreeByElementType(type)->indexFromNode(*it),
|
|
QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
}
|
|
|
|
void DesignWidget::onDoubleClicked(const QModelIndex &index) { Q_EMIT zoomSelected(); }
|
|
|
|
void DesignWidget::onSearchInserted()
|
|
{
|
|
if (currentSearch == searchEdit->text() && currentIndexTab == tabWidget->currentIndex()) {
|
|
currentIndex++;
|
|
if (currentIndex >= currentSearchIndexes.size())
|
|
currentIndex = 0;
|
|
} else {
|
|
std::lock_guard<std::mutex> lock_ui(ctx->ui_mutex);
|
|
std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
currentSearch = searchEdit->text();
|
|
currentSearchIndexes = treeModel[tabWidget->currentIndex()]->search(searchEdit->text());
|
|
currentIndex = 0;
|
|
currentIndexTab = tabWidget->currentIndex();
|
|
}
|
|
if (currentSearchIndexes.size() > 0 && currentIndex < currentSearchIndexes.size())
|
|
selectionModel[tabWidget->currentIndex()]->setCurrentIndex(currentSearchIndexes.at(currentIndex),
|
|
QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
|
|
void DesignWidget::onHoverIndexChanged(int num, QModelIndex index)
|
|
{
|
|
if (index.isValid()) {
|
|
TreeModel::Item *item = treeModel[num]->nodeFromIndex(index);
|
|
if (item->type() != ElementType::NONE) {
|
|
std::vector<DecalXY> decals = getDecals(item->type(), item->id());
|
|
if (decals.size() > 0)
|
|
Q_EMIT hover(decals.at(0));
|
|
return;
|
|
}
|
|
}
|
|
Q_EMIT hover(DecalXY());
|
|
}
|
|
|
|
void DesignWidget::onHoverPropertyChanged(QtBrowserItem *item)
|
|
{
|
|
if (item != nullptr) {
|
|
QtProperty *selectedProperty = item->property();
|
|
ElementType type = getElementTypeByName(selectedProperty->propertyId());
|
|
if (type != ElementType::NONE) {
|
|
IdStringList value = IdStringList::parse(ctx, selectedProperty->valueText().toStdString());
|
|
if (value != IdStringList()) {
|
|
auto node = getTreeByElementType(type)->nodeForId(value);
|
|
if (node) {
|
|
std::vector<DecalXY> decals = getDecals((*node)->type(), (*node)->id());
|
|
if (decals.size() > 0)
|
|
Q_EMIT hover(decals.at(0));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Q_EMIT hover(DecalXY());
|
|
}
|
|
NEXTPNR_NAMESPACE_END
|