mirror of https://github.com/KLayout/klayout.git
1366 lines
41 KiB
C++
1366 lines
41 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2019 Matthias Koefferlein
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#include "layNetlistBrowserPage.h"
|
|
#include "layNetlistBrowserModel.h"
|
|
#include "layNetlistBrowserTreeModel.h"
|
|
#include "layItemDelegates.h"
|
|
#include "layCellView.h"
|
|
#include "layLayoutView.h"
|
|
#include "layMarker.h"
|
|
#include "layNetInfoDialog.h"
|
|
#include "layNetExportDialog.h"
|
|
#include "tlProgress.h"
|
|
#include "tlExceptions.h"
|
|
#include "dbLayoutToNetlist.h"
|
|
#include "dbNetlistDeviceClasses.h"
|
|
#include "dbCellMapping.h"
|
|
#include "dbLayerMapping.h"
|
|
#include "dbCell.h"
|
|
|
|
#include <QUrl>
|
|
#include <QPainter>
|
|
#include <QColorDialog>
|
|
#include <QRegExp>
|
|
#include <QKeyEvent>
|
|
#if QT_VERSION >= 0x050000
|
|
# include <QUrlQuery>
|
|
#endif
|
|
|
|
namespace lay
|
|
{
|
|
|
|
extern const std::string cfg_l2ndb_show_all;
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// NetlistBrowserPage implementation
|
|
|
|
template <class Obj>
|
|
inline const db::Circuit *deref_circuit (const Obj *obj)
|
|
{
|
|
return obj->circuit ();
|
|
}
|
|
|
|
template <>
|
|
inline const db::Circuit *deref_circuit (const db::Circuit *obj)
|
|
{
|
|
return obj;
|
|
}
|
|
|
|
template <class Obj>
|
|
static db::ICplxTrans
|
|
trans_for (const Obj *objs, const db::Layout &ly, const db::Cell &cell, db::ContextCache &cc, const db::DCplxTrans &initial = db::DCplxTrans ())
|
|
{
|
|
db::DCplxTrans t = initial;
|
|
|
|
const db::Circuit *circuit = deref_circuit (objs);
|
|
while (circuit) {
|
|
if (circuit->cell_index () == cell.cell_index ()) {
|
|
circuit = 0;
|
|
break;
|
|
} else if (circuit->begin_refs () != circuit->end_refs ()) {
|
|
const db::SubCircuit &ref = *circuit->begin_refs ();
|
|
t = ref.trans () * t;
|
|
circuit = ref.circuit ();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
db::CplxTrans dbu_trans (ly.dbu ());
|
|
db::ICplxTrans it = dbu_trans.inverted () * t * dbu_trans;
|
|
|
|
// The circuit may not be instantiated and still not be the top cell.
|
|
// This happens if the subcell does not have connections. In this case
|
|
// we look up one instantiation path
|
|
|
|
if (circuit && ly.is_valid_cell_index (circuit->cell_index ())) {
|
|
std::pair<bool, db::ICplxTrans> tc = cc.find_layout_context (circuit->cell_index (), cell.cell_index ());
|
|
if (tc.first) {
|
|
it = tc.second * it;
|
|
}
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
|
|
: m_show_all (true),
|
|
m_window (lay::NetlistBrowserConfig::FitNet),
|
|
m_window_dim (0.0),
|
|
m_max_shape_count (1000),
|
|
m_marker_line_width (-1),
|
|
m_marker_vertex_size (-1),
|
|
m_marker_halo (-1),
|
|
m_marker_dither_pattern (-1),
|
|
m_marker_intensity (0),
|
|
m_use_original_colors (false),
|
|
mp_view (0),
|
|
m_cv_index (0),
|
|
mp_plugin_root (0),
|
|
m_history_ptr (0),
|
|
m_signals_enabled (true),
|
|
m_enable_updates (true),
|
|
m_update_needed (true),
|
|
mp_info_dialog (0),
|
|
dm_update_highlights (this, &NetlistBrowserPage::update_highlights),
|
|
m_cell_context_cache (0)
|
|
{
|
|
Ui::NetlistBrowserPage::setupUi (this);
|
|
|
|
m_show_all_action = new QAction (QObject::tr ("Show All"), this);
|
|
m_show_all_action->setCheckable (true);
|
|
m_show_all_action->setChecked (m_show_all);
|
|
|
|
QAction *color_action = new QAction (QObject::tr ("Colorize Nets"), directory_tree);
|
|
QMenu *menu = new QMenu (directory_tree);
|
|
lay::ColorButton::build_color_menu (menu, this, SLOT (browse_color_for_net ()), SLOT (select_color_for_net ()));
|
|
color_action->setMenu (menu);
|
|
|
|
QAction *sep;
|
|
directory_tree->addAction (m_show_all_action);
|
|
directory_tree->addAction (actionCollapseAll);
|
|
directory_tree->addAction (actionExpandAll);
|
|
sep = new QAction (directory_tree);
|
|
sep->setSeparator (true);
|
|
directory_tree->addAction (sep);
|
|
directory_tree->addAction (actionUnselectAll);
|
|
sep = new QAction (directory_tree);
|
|
sep->setSeparator (true);
|
|
directory_tree->addAction (sep);
|
|
directory_tree->addAction (color_action);
|
|
sep = new QAction (directory_tree);
|
|
sep->setSeparator (true);
|
|
directory_tree->addAction (sep);
|
|
directory_tree->addAction (actionExportSelected);
|
|
directory_tree->addAction (actionExportAll);
|
|
|
|
directory_tree->header ()->setDefaultSectionSize (150);
|
|
|
|
lay::HTMLItemDelegate *delegate;
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
delegate = new lay::HTMLItemDelegate (this);
|
|
delegate->set_text_margin (2);
|
|
delegate->set_anchors_clickable (true);
|
|
connect (delegate, SIGNAL (anchor_clicked (const QString &)), this, SLOT (anchor_clicked (const QString &)));
|
|
directory_tree->setItemDelegateForColumn (i, delegate);
|
|
}
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
delegate = new lay::HTMLItemDelegate (this);
|
|
delegate->set_text_margin (2);
|
|
delegate->set_anchors_clickable (true);
|
|
connect (delegate, SIGNAL (anchor_clicked (const QString &)), this, SLOT (anchor_clicked (const QString &)));
|
|
hierarchy_tree->setItemDelegateForColumn (i, delegate);
|
|
}
|
|
|
|
QMenu *find_edit_menu = new QMenu (find_text);
|
|
find_edit_menu->addAction (actionUseRegularExpressions);
|
|
find_edit_menu->addAction (actionCaseSensitive);
|
|
|
|
find_text->set_clear_button_enabled (true);
|
|
find_text->set_options_button_enabled (true);
|
|
find_text->set_options_menu (find_edit_menu);
|
|
#if QT_VERSION >= 0x40700
|
|
find_text->setPlaceholderText (tr ("Find text ..."));
|
|
#endif
|
|
|
|
connect (m_show_all_action, SIGNAL (triggered ()), this, SLOT (show_all_clicked ()));
|
|
connect (info_button, SIGNAL (pressed ()), this, SLOT (info_button_pressed ()));
|
|
connect (find_button, SIGNAL (pressed ()), this, SLOT (find_button_pressed ()));
|
|
connect (forward, SIGNAL (clicked ()), this, SLOT (navigate_forward ()));
|
|
connect (backward, SIGNAL (clicked ()), this, SLOT (navigate_back ()));
|
|
|
|
connect (show_netlist, SIGNAL (clicked ()), this, SLOT (mode_changed ()));
|
|
connect (show_xref, SIGNAL (clicked ()), this, SLOT (mode_changed ()));
|
|
|
|
connect (actionExportAll, SIGNAL (triggered ()), this, SLOT (export_all ()));
|
|
connect (actionExportSelected, SIGNAL (triggered ()), this, SLOT (export_selected ()));
|
|
|
|
forward->setEnabled (false);
|
|
backward->setEnabled (false);
|
|
|
|
directory_tree->installEventFilter (this);
|
|
}
|
|
|
|
NetlistBrowserPage::~NetlistBrowserPage ()
|
|
{
|
|
clear_markers ();
|
|
}
|
|
|
|
bool
|
|
NetlistBrowserPage::is_netlist_mode ()
|
|
{
|
|
return show_netlist->isChecked ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::set_plugin_root (lay::PluginRoot *pr)
|
|
{
|
|
mp_plugin_root = pr;
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::set_highlight_style (QColor color, int line_width, int vertex_size, int halo, int dither_pattern, int marker_intensity, bool use_original_colors, const lay::ColorPalette *auto_colors)
|
|
{
|
|
m_colorizer.configure (color, auto_colors);
|
|
m_marker_line_width = line_width;
|
|
m_marker_vertex_size = vertex_size;
|
|
m_marker_halo = halo;
|
|
m_marker_dither_pattern = dither_pattern;
|
|
m_marker_intensity = marker_intensity;
|
|
m_use_original_colors = use_original_colors;
|
|
update_highlights ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::set_view (lay::LayoutView *view, unsigned int cv_index)
|
|
{
|
|
if (mp_view) {
|
|
mp_view->layer_list_changed_event.remove (this, &NetlistBrowserPage::layer_list_changed);
|
|
}
|
|
|
|
mp_view = view;
|
|
m_cv_index = cv_index;
|
|
|
|
if (mp_view) {
|
|
mp_view->layer_list_changed_event.add (this, &NetlistBrowserPage::layer_list_changed);
|
|
}
|
|
|
|
update_highlights ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::set_window (lay::NetlistBrowserConfig::net_window_type window, double window_dim)
|
|
{
|
|
if (window != m_window || window_dim != m_window_dim) {
|
|
m_window = window;
|
|
m_window_dim = window_dim;
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::set_max_shape_count (size_t max_shape_count)
|
|
{
|
|
if (m_max_shape_count != max_shape_count) {
|
|
m_max_shape_count = max_shape_count;
|
|
update_highlights ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
NetlistBrowserPage::eventFilter (QObject *watched, QEvent *event)
|
|
{
|
|
if (watched != directory_tree) {
|
|
return false;
|
|
}
|
|
|
|
QKeyEvent *ke = dynamic_cast<QKeyEvent *> (event);
|
|
if (! ke || event->type () != QEvent::KeyPress) {
|
|
return false;
|
|
}
|
|
|
|
if (ke->key () == Qt::Key_Escape) {
|
|
directory_tree->clearSelection ();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::layer_list_changed (int)
|
|
{
|
|
dm_update_highlights ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::anchor_clicked (const QString &a)
|
|
{
|
|
QUrl url (a);
|
|
|
|
QString ids;
|
|
#if QT_VERSION >= 0x050000
|
|
ids = QUrlQuery (url.query ()).queryItemValue (QString::fromUtf8 ("id"));
|
|
#else
|
|
ids = url.queryItemValue (QString::fromUtf8 ("id"));
|
|
#endif
|
|
|
|
if (ids.isEmpty ()) {
|
|
return;
|
|
}
|
|
|
|
void *id = reinterpret_cast<void *> (ids.toULongLong ());
|
|
navigate_to (id, true);
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::current_tree_index_changed (const QModelIndex &index)
|
|
{
|
|
if (index.isValid () && m_signals_enabled) {
|
|
|
|
NetlistBrowserTreeModel *tree_model = dynamic_cast<NetlistBrowserTreeModel *> (hierarchy_tree->model ());
|
|
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
if (! tree_model || ! netlist_model) {
|
|
return;
|
|
}
|
|
|
|
std::pair<const db::Circuit *, const db::Circuit *> circuits = tree_model->circuits_from_index (index);
|
|
QModelIndex circuit_index = netlist_model->index_from_circuit (circuits);
|
|
|
|
m_signals_enabled = false;
|
|
directory_tree->setCurrentIndex (circuit_index);
|
|
m_signals_enabled = true;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::current_index_changed (const QModelIndex &index)
|
|
{
|
|
if (index.isValid () && m_signals_enabled) {
|
|
|
|
NetlistBrowserTreeModel *tree_model = dynamic_cast<NetlistBrowserTreeModel *> (hierarchy_tree->model ());
|
|
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
if (! tree_model || ! netlist_model) {
|
|
return;
|
|
}
|
|
|
|
void *id = index.internalPointer ();
|
|
add_to_history (id, true);
|
|
|
|
std::pair<const db::Circuit *, const db::Circuit *> circuits = netlist_model->circuit_from_index (index);
|
|
QModelIndex circuit_index = tree_model->index_from_circuits (circuits);
|
|
|
|
m_signals_enabled = false;
|
|
hierarchy_tree->setCurrentIndex (circuit_index);
|
|
m_signals_enabled = true;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::select_net (const db::Net *net)
|
|
{
|
|
if (! net || ! net->circuit ()) {
|
|
directory_tree->clearSelection ();
|
|
} else {
|
|
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
tl_assert (model != 0);
|
|
directory_tree->setCurrentIndex (model->index_from_net (net));
|
|
}
|
|
}
|
|
|
|
std::vector<const db::Net *>
|
|
NetlistBrowserPage::selected_nets ()
|
|
{
|
|
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
tl_assert (model != 0);
|
|
|
|
std::vector<const db::Net *> nets;
|
|
|
|
QModelIndexList selection = directory_tree->selectionModel ()->selectedIndexes ();
|
|
for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) {
|
|
if (i->column () == 0) {
|
|
const db::Net *net = model->net_from_index (*i).first;
|
|
if (net) {
|
|
nets.push_back (net);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nets;
|
|
}
|
|
|
|
std::vector<const db::Circuit *>
|
|
NetlistBrowserPage::selected_circuits ()
|
|
{
|
|
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
tl_assert (model != 0);
|
|
|
|
std::vector<const db::Circuit *> circuits;
|
|
|
|
QModelIndexList selection = directory_tree->selectionModel ()->selectedIndexes ();
|
|
for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) {
|
|
if (i->column () == 0 && model->is_circuit_index (*i)) {
|
|
const db::Circuit *circuit = model->circuit_from_index (*i).first;
|
|
if (circuit) {
|
|
circuits.push_back (circuit);
|
|
}
|
|
}
|
|
}
|
|
|
|
return circuits;
|
|
}
|
|
|
|
std::vector<const db::SubCircuit *>
|
|
NetlistBrowserPage::selected_subcircuits ()
|
|
{
|
|
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
tl_assert (model != 0);
|
|
|
|
std::vector<const db::SubCircuit *> subcircuits;
|
|
|
|
QModelIndexList selection = directory_tree->selectionModel ()->selectedIndexes ();
|
|
for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) {
|
|
if (i->column () == 0) {
|
|
const db::SubCircuit *subcircuit = model->subcircuit_from_index (*i).first;
|
|
if (subcircuit) {
|
|
subcircuits.push_back (subcircuit);
|
|
}
|
|
}
|
|
}
|
|
|
|
return subcircuits;
|
|
}
|
|
|
|
std::vector<const db::Device *>
|
|
NetlistBrowserPage::selected_devices ()
|
|
{
|
|
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
tl_assert (model != 0);
|
|
|
|
std::vector<const db::Device *> devices;
|
|
|
|
QModelIndexList selection = directory_tree->selectionModel ()->selectedIndexes ();
|
|
for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) {
|
|
if (i->column () == 0) {
|
|
const db::Device *device = model->device_from_index (*i).first;
|
|
if (device) {
|
|
devices.push_back (device);
|
|
}
|
|
}
|
|
}
|
|
|
|
return devices;
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::selection_changed ()
|
|
{
|
|
std::vector<const db::Net *> nets = selected_nets ();
|
|
if (mp_info_dialog) {
|
|
mp_info_dialog->set_nets (mp_database.get (), nets);
|
|
}
|
|
|
|
std::vector<const db::Device *> devices = selected_devices ();
|
|
|
|
std::vector<const db::SubCircuit *> subcircuits = selected_subcircuits ();
|
|
|
|
std::vector<const db::Circuit *> circuits = selected_circuits ();
|
|
|
|
highlight (nets, devices, subcircuits, circuits);
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::set_color_for_selected_nets (const QColor &color)
|
|
{
|
|
std::vector<const db::Net *> nets = selected_nets ();
|
|
|
|
m_colorizer.begin_changes ();
|
|
for (std::vector<const db::Net *>::const_iterator n = nets.begin (); n != nets.end (); ++n) {
|
|
if (color.isValid ()) {
|
|
m_colorizer.set_color_of_net (*n, color);
|
|
} else {
|
|
m_colorizer.reset_color_of_net (*n);
|
|
}
|
|
}
|
|
m_colorizer.end_changes ();
|
|
|
|
update_highlights ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::browse_color_for_net ()
|
|
{
|
|
QColor c = QColorDialog::getColor (QColor (), this);
|
|
if (c.isValid ()) {
|
|
set_color_for_selected_nets (c);
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::select_color_for_net ()
|
|
{
|
|
QAction *action = dynamic_cast<QAction *> (sender ());
|
|
if (action) {
|
|
set_color_for_selected_nets (action->data ().value<QColor> ());
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::navigate_to (void *id, bool fwd)
|
|
{
|
|
NetlistBrowserTreeModel *tree_model = dynamic_cast<NetlistBrowserTreeModel *> (hierarchy_tree->model ());
|
|
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
if (! tree_model || ! netlist_model) {
|
|
return;
|
|
}
|
|
|
|
QModelIndex index = netlist_model->index_from_id (id, 0);
|
|
if (! index.isValid ()) {
|
|
return;
|
|
}
|
|
|
|
m_signals_enabled = false;
|
|
try {
|
|
|
|
directory_tree->setCurrentIndex (index);
|
|
|
|
std::pair<const db::Circuit *, const db::Circuit *> circuits = netlist_model->circuit_from_index (index);
|
|
QModelIndex circuit_index = tree_model->index_from_circuits (circuits);
|
|
hierarchy_tree->setCurrentIndex (circuit_index);
|
|
|
|
} catch (...) {
|
|
}
|
|
m_signals_enabled = true;
|
|
|
|
add_to_history (id, fwd);
|
|
|
|
selection_changed ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::add_to_history (void *id, bool fwd)
|
|
{
|
|
if (! fwd) {
|
|
if (m_history_ptr > 1) {
|
|
--m_history_ptr;
|
|
m_history [m_history_ptr - 1] = id;
|
|
}
|
|
} else if (m_history_ptr >= m_history.size ()) {
|
|
m_history.push_back (id);
|
|
m_history_ptr = m_history.size ();
|
|
} else {
|
|
if (m_history [m_history_ptr] != id) {
|
|
m_history.erase (m_history.begin () + m_history_ptr + 1, m_history.end ());
|
|
}
|
|
m_history [m_history_ptr] = id;
|
|
++m_history_ptr;
|
|
}
|
|
|
|
backward->setEnabled (m_history_ptr > 1);
|
|
forward->setEnabled (m_history_ptr < m_history.size ());
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::navigate_back ()
|
|
{
|
|
if (m_history_ptr > 1) {
|
|
navigate_to (m_history [m_history_ptr - 2], false);
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::navigate_forward ()
|
|
{
|
|
if (m_history_ptr < m_history.size ()) {
|
|
navigate_to (m_history [m_history_ptr]);
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::info_button_pressed ()
|
|
{
|
|
if (! mp_info_dialog) {
|
|
mp_info_dialog = new lay::NetInfoDialog (this);
|
|
}
|
|
|
|
mp_info_dialog->set_nets (mp_database.get (), selected_nets ());
|
|
mp_info_dialog->show ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::mode_changed ()
|
|
{
|
|
setup_trees ();
|
|
}
|
|
|
|
static QModelIndex find_next (QTreeView *view, QAbstractItemModel *model, const QRegExp &to_find, const QModelIndex &from)
|
|
{
|
|
QModelIndex index = from;
|
|
|
|
if (! index.isValid () && model->hasChildren (index)) {
|
|
index = model->index (0, 0, index);
|
|
}
|
|
|
|
if (! index.isValid ()) {
|
|
return index;
|
|
}
|
|
|
|
QModelIndex current = index;
|
|
|
|
std::vector<QModelIndex> parent_stack;
|
|
std::vector<std::pair<int, int> > rows_stack;
|
|
|
|
while (index.isValid ()) {
|
|
|
|
parent_stack.push_back (model->parent (index));
|
|
rows_stack.push_back (std::make_pair (index.row (), model->rowCount (parent_stack.back ())));
|
|
|
|
index = parent_stack.back ();
|
|
|
|
}
|
|
|
|
std::reverse (parent_stack.begin (), parent_stack.end ());
|
|
std::reverse (rows_stack.begin (), rows_stack.end ());
|
|
|
|
tl::AbsoluteProgress progress (tl::to_string (tr ("Searching ...")));
|
|
|
|
do {
|
|
|
|
++progress;
|
|
|
|
bool has_next = false;
|
|
|
|
if (model->hasChildren (current) && rows_stack.size () < 2) {
|
|
|
|
int row_count = model->rowCount (current);
|
|
if (row_count > 0) {
|
|
|
|
parent_stack.push_back (current);
|
|
rows_stack.push_back (std::make_pair (0, row_count));
|
|
|
|
current = model->index (0, 0, current);
|
|
has_next = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (! has_next && ! rows_stack.empty ()) {
|
|
|
|
++rows_stack.back ().first;
|
|
|
|
if (rows_stack.back ().first >= rows_stack.back ().second) {
|
|
|
|
// up
|
|
current = parent_stack.back ();
|
|
rows_stack.pop_back ();
|
|
parent_stack.pop_back ();
|
|
|
|
} else {
|
|
|
|
current = model->index (rows_stack.back ().first, 0, parent_stack.back ());
|
|
has_next = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (has_next) {
|
|
|
|
QString text = model->data (current, Qt::UserRole).toString ();
|
|
if (text.indexOf (to_find) >= 0 && ! view->isRowHidden (rows_stack.back ().first, parent_stack.back ())) {
|
|
return current;
|
|
}
|
|
|
|
}
|
|
|
|
} while (current.internalPointer () != from.internalPointer () || current.row () != from.row ());
|
|
|
|
return QModelIndex ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::find_button_pressed ()
|
|
{
|
|
QRegExp re (find_text->text (),
|
|
actionCaseSensitive->isChecked () ? Qt::CaseSensitive : Qt::CaseInsensitive,
|
|
actionUseRegularExpressions->isChecked () ? QRegExp::RegExp : QRegExp::FixedString);
|
|
|
|
QModelIndex next = find_next (directory_tree, directory_tree->model (), re, directory_tree->currentIndex ());
|
|
if (next.isValid ()) {
|
|
navigate_to (next.internalPointer ());
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::show_all_clicked ()
|
|
{
|
|
if (mp_plugin_root) {
|
|
mp_plugin_root->config_set (cfg_l2ndb_show_all, tl::to_string (m_show_all_action->isChecked ()));
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::show_all (bool f)
|
|
{
|
|
if (f != m_show_all) {
|
|
|
|
m_show_all = f;
|
|
m_show_all_action->setChecked (f);
|
|
|
|
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
|
|
if (model) {
|
|
model->set_item_visibility (directory_tree, m_show_all, false /*show warnings only with 'show all'*/);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
|
|
{
|
|
if (l2ndb == mp_database.get ()) {
|
|
// not change
|
|
return;
|
|
}
|
|
|
|
if (mp_info_dialog) {
|
|
delete mp_info_dialog;
|
|
mp_info_dialog = 0;
|
|
}
|
|
|
|
db::LayoutVsSchematic *lvsdb = dynamic_cast<db::LayoutVsSchematic *> (l2ndb);
|
|
mp_database.reset (l2ndb);
|
|
|
|
show_netlist->setVisible (lvsdb != 0);
|
|
show_xref->setVisible (lvsdb != 0);
|
|
|
|
bool se = m_signals_enabled;
|
|
m_signals_enabled = false;
|
|
show_netlist->setChecked (lvsdb == 0);
|
|
show_xref->setChecked (lvsdb != 0);
|
|
m_signals_enabled = se;
|
|
|
|
clear_markers ();
|
|
highlight (std::vector<const db::Net *> (), std::vector<const db::Device *> (), std::vector<const db::SubCircuit *> (), std::vector<const db::Circuit *> ());
|
|
|
|
m_cell_context_cache = db::ContextCache (mp_database.get () ? mp_database->internal_layout () : 0);
|
|
|
|
setup_trees ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::setup_trees ()
|
|
{
|
|
if (! mp_database.get ()) {
|
|
delete directory_tree->model ();
|
|
directory_tree->setModel (0);
|
|
delete hierarchy_tree->model ();
|
|
hierarchy_tree->setModel (0);
|
|
return;
|
|
}
|
|
|
|
db::LayoutToNetlist *l2ndb = mp_database.get ();
|
|
db::LayoutVsSchematic *lvsdb = show_netlist->isChecked () ? 0 : dynamic_cast<db::LayoutVsSchematic *> (l2ndb);
|
|
|
|
{
|
|
// NOTE: with the tree as the parent, the tree will take over ownership of the model
|
|
NetlistBrowserModel *new_model = 0;
|
|
if (lvsdb) {
|
|
new_model = new NetlistBrowserModel (directory_tree, lvsdb, &m_colorizer);
|
|
} else {
|
|
new_model = new NetlistBrowserModel (directory_tree, l2ndb, &m_colorizer);
|
|
}
|
|
|
|
int columns = directory_tree->model () ? directory_tree->model ()->columnCount (QModelIndex ()) : 0;
|
|
int new_columns = new_model->columnCount (QModelIndex ());
|
|
|
|
delete directory_tree->model ();
|
|
directory_tree->setModel (new_model);
|
|
connect (directory_tree->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &)));
|
|
connect (directory_tree->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (selection_changed ()));
|
|
|
|
directory_tree->header ()->show ();
|
|
directory_tree->header ()->setStretchLastSection (true);
|
|
directory_tree->header ()->setMinimumSectionSize (25);
|
|
|
|
if (columns < new_columns) {
|
|
// makes sure new columns are properly size-adjusted
|
|
for (int i = std::max (0, columns - 1); i < new_columns; ++i) {
|
|
directory_tree->header ()->resizeSection (i, i == 1 ? directory_tree->header ()->minimumSectionSize () : directory_tree->header ()->defaultSectionSize ());
|
|
}
|
|
}
|
|
|
|
// hide the status column if not needed
|
|
directory_tree->header ()->setSectionHidden (1, new_model->status_column () < 0);
|
|
|
|
// establish visibility according to "show all"
|
|
new_model->set_item_visibility (directory_tree, m_show_all, false /*show warnings only with 'show all'*/);
|
|
}
|
|
|
|
{
|
|
// NOTE: with the tree as the parent, the tree will take over ownership of the model
|
|
NetlistBrowserTreeModel *new_hierarchy_model = 0;
|
|
if (lvsdb) {
|
|
new_hierarchy_model = new NetlistBrowserTreeModel (hierarchy_tree, lvsdb);
|
|
} else {
|
|
new_hierarchy_model = new NetlistBrowserTreeModel (hierarchy_tree, l2ndb);
|
|
}
|
|
|
|
int columns = hierarchy_tree->model () ? hierarchy_tree->model ()->columnCount (QModelIndex ()) : 0;
|
|
int new_columns = new_hierarchy_model->columnCount (QModelIndex ());
|
|
|
|
delete hierarchy_tree->model ();
|
|
hierarchy_tree->setModel (new_hierarchy_model);
|
|
connect (hierarchy_tree->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_tree_index_changed (const QModelIndex &)));
|
|
|
|
hierarchy_tree->header ()->show ();
|
|
hierarchy_tree->header ()->setStretchLastSection (true);
|
|
hierarchy_tree->header ()->setMinimumSectionSize (25);
|
|
|
|
if (columns < new_columns) {
|
|
// makes sure new columns are properly size-adjusted
|
|
for (int i = std::max (0, columns - 1); i < new_columns; ++i) {
|
|
hierarchy_tree->header ()->resizeSection (i, i == 1 ? hierarchy_tree->header ()->minimumSectionSize () : hierarchy_tree->header ()->defaultSectionSize ());
|
|
}
|
|
}
|
|
|
|
// hide the status column if not needed
|
|
hierarchy_tree->header ()->setSectionHidden (1, new_hierarchy_model->status_column () < 0);
|
|
}
|
|
|
|
find_text->setText (QString ());
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::highlight (const std::vector<const db::Net *> &nets, const std::vector<const db::Device *> &devices, const std::vector<const db::SubCircuit *> &subcircuits, const std::vector<const db::Circuit *> &circuits)
|
|
{
|
|
if (nets != m_current_nets || devices != m_current_devices || subcircuits != m_current_subcircuits || circuits != m_current_circuits) {
|
|
|
|
m_current_nets = nets;
|
|
m_current_devices = devices;
|
|
m_current_subcircuits = subcircuits;
|
|
m_current_circuits = circuits;
|
|
|
|
clear_markers ();
|
|
|
|
if (! nets.empty () || ! devices.empty () || ! subcircuits.empty () || ! circuits.empty ()) {
|
|
adjust_view ();
|
|
update_highlights ();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::enable_updates (bool f)
|
|
{
|
|
if (f != m_enable_updates) {
|
|
|
|
m_enable_updates = f;
|
|
|
|
if (f && m_update_needed) {
|
|
update_highlights ();
|
|
}
|
|
|
|
m_update_needed = false;
|
|
|
|
}
|
|
}
|
|
|
|
static db::Box
|
|
bbox_for_device_abstract (const db::Layout *layout, const db::DeviceAbstract *device_abstract, const db::DCplxTrans &trans)
|
|
{
|
|
if (! device_abstract || ! layout->is_valid_cell_index (device_abstract->cell_index ())) {
|
|
return db::Box ();
|
|
}
|
|
|
|
return layout->cell (device_abstract->cell_index ()).bbox ().transformed (db::CplxTrans (layout->dbu ()).inverted () * trans * db::CplxTrans (layout->dbu ()));}
|
|
|
|
static db::Box
|
|
bbox_for_circuit (const db::Layout *layout, const db::Circuit *circuit)
|
|
{
|
|
|
|
if (! circuit || ! layout->is_valid_cell_index (circuit->cell_index ())) {
|
|
return db::Box ();
|
|
}
|
|
|
|
if (circuit->boundary ().vertices () > 0) {
|
|
return db::CplxTrans (layout->dbu ()).inverted () * circuit->boundary ().box ();
|
|
}
|
|
|
|
return layout->cell (circuit->cell_index ()).bbox ();
|
|
}
|
|
|
|
static db::Box
|
|
bbox_for_subcircuit (const db::Layout *layout, const db::SubCircuit *subcircuit)
|
|
{
|
|
return bbox_for_circuit (layout, subcircuit->circuit_ref ());
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::adjust_view ()
|
|
{
|
|
if (! mp_database.get () || ! mp_view) {
|
|
return;
|
|
}
|
|
|
|
const lay::CellView &cv = mp_view->cellview (m_cv_index);
|
|
if (! cv.is_valid ()) {
|
|
return;
|
|
}
|
|
|
|
if (m_window != lay::NetlistBrowserConfig::FitNet && m_window != lay::NetlistBrowserConfig::Center && m_window != lay::NetlistBrowserConfig::CenterSize) {
|
|
return;
|
|
}
|
|
|
|
const db::Layout *layout = mp_database->internal_layout ();
|
|
const db::Cell *cell = mp_database->internal_top_cell ();
|
|
if (! layout || ! cell) {
|
|
return;
|
|
}
|
|
|
|
|
|
db::Box bbox;
|
|
|
|
for (std::vector<const db::Net *>::const_iterator net = m_current_nets.begin (); net != m_current_nets.end (); ++net) {
|
|
|
|
db::ICplxTrans net_trans = trans_for (*net, *layout, *cell, m_cell_context_cache);
|
|
|
|
db::cell_index_type cell_index = (*net)->circuit ()->cell_index ();
|
|
size_t cluster_id = (*net)->cluster_id ();
|
|
|
|
const db::Connectivity &conn = mp_database->connectivity ();
|
|
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
|
|
|
|
db::Box layer_bbox;
|
|
db::recursive_cluster_shape_iterator<db::PolygonRef> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
|
|
while (! shapes.at_end ()) {
|
|
layer_bbox += shapes.trans () * shapes->box ();
|
|
++shapes;
|
|
}
|
|
|
|
bbox += net_trans * layer_bbox;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (std::vector<const db::Device *>::const_iterator device = m_current_devices.begin (); device != m_current_devices.end (); ++device) {
|
|
|
|
db::ICplxTrans trans = trans_for (*device, *layout, *cell, m_cell_context_cache, (*device)->trans ());
|
|
|
|
bbox += trans * bbox_for_device_abstract (layout, (*device)->device_abstract (), db::DCplxTrans ());
|
|
|
|
const std::vector<db::DeviceAbstractRef> &oda = (*device)->other_abstracts ();
|
|
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
|
|
bbox += trans * bbox_for_device_abstract (layout, a->device_abstract, a->trans);
|
|
}
|
|
|
|
}
|
|
|
|
for (std::vector<const db::SubCircuit *>::const_iterator subcircuit = m_current_subcircuits.begin (); subcircuit != m_current_subcircuits.end (); ++subcircuit) {
|
|
bbox += trans_for (*subcircuit, *layout, *cell, m_cell_context_cache, (*subcircuit)->trans ()) * bbox_for_subcircuit (layout, *subcircuit);
|
|
}
|
|
|
|
for (std::vector<const db::Circuit *>::const_iterator circuit = m_current_circuits.begin (); circuit != m_current_circuits.end (); ++circuit) {
|
|
bbox += trans_for (*circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ()) * bbox_for_circuit (layout, *circuit);
|
|
}
|
|
|
|
if (! bbox.empty ()) {
|
|
|
|
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
|
|
|
|
db::DBox tv_bbox;
|
|
db::DBox dbu_bbox = db::CplxTrans (layout->dbu ()) * bbox;
|
|
for (std::vector<db::DCplxTrans>::const_iterator t = tv.begin (); t != tv.end (); ++t) {
|
|
tv_bbox += *t * dbu_bbox;
|
|
}
|
|
|
|
if (m_window == lay::NetlistBrowserConfig::FitNet) {
|
|
|
|
mp_view->zoom_box (tv_bbox.enlarged (db::DVector (m_window_dim, m_window_dim)));
|
|
|
|
} else if (m_window == lay::NetlistBrowserConfig::Center) {
|
|
|
|
mp_view->pan_center (tv_bbox.p1 () + (tv_bbox.p2 () - tv_bbox.p1 ()) * 0.5);
|
|
|
|
} else if (m_window == lay::NetlistBrowserConfig::CenterSize) {
|
|
|
|
double w = std::max (tv_bbox.width (), m_window_dim);
|
|
double h = std::max (tv_bbox.height (), m_window_dim);
|
|
db::DPoint center (tv_bbox.p1() + (tv_bbox.p2 () - tv_bbox.p1 ()) * 0.5);
|
|
db::DVector d (w * 0.5, h * 0.5);
|
|
mp_view->zoom_box (db::DBox (center - d, center + d));
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
QColor
|
|
NetlistBrowserPage::make_valid_color (const QColor &color)
|
|
{
|
|
if (! color.isValid () && mp_view) {
|
|
return mp_view->background_color ().green () < 128 ? QColor (Qt::white) : QColor (Qt::black);
|
|
} else {
|
|
return color;
|
|
}
|
|
}
|
|
|
|
bool
|
|
NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
|
|
{
|
|
const db::Layout *layout = mp_database->internal_layout ();
|
|
const db::Cell *cell = mp_database->internal_top_cell ();
|
|
db::ICplxTrans device_trans = trans_for (device, *layout, *cell, m_cell_context_cache, device->trans ());
|
|
|
|
QColor color = make_valid_color (m_colorizer.marker_color ());
|
|
|
|
db::Box device_bbox = bbox_for_device_abstract (layout, device->device_abstract (), db::DCplxTrans ());
|
|
if (! device_bbox.empty ()) {
|
|
|
|
if (n_markers == m_max_shape_count) {
|
|
return true;
|
|
}
|
|
|
|
++n_markers;
|
|
|
|
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
|
|
mp_markers.back ()->set (device_bbox, device_trans, tv);
|
|
mp_markers.back ()->set_color (color);
|
|
mp_markers.back ()->set_frame_color (color);
|
|
configure_marker (mp_markers.back (), false);
|
|
|
|
}
|
|
|
|
const std::vector<db::DeviceAbstractRef> &oda = device->other_abstracts ();
|
|
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
|
|
|
|
db::Box da_box = bbox_for_device_abstract (layout, a->device_abstract, a->trans);
|
|
if (! da_box.empty ()) {
|
|
|
|
if (n_markers == m_max_shape_count) {
|
|
return true;
|
|
}
|
|
|
|
++n_markers;
|
|
|
|
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
|
|
mp_markers.back ()->set (da_box, device_trans, tv);
|
|
mp_markers.back ()->set_color (color);
|
|
mp_markers.back ()->set_frame_color (color);
|
|
configure_marker (mp_markers.back (), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
NetlistBrowserPage::produce_highlights_for_subcircuit (const db::SubCircuit *subcircuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
|
|
{
|
|
const db::Layout *layout = mp_database->internal_layout ();
|
|
const db::Cell *cell = mp_database->internal_top_cell ();
|
|
db::ICplxTrans subcircuit_trans = trans_for (subcircuit, *layout, *cell, m_cell_context_cache, subcircuit->trans ());
|
|
|
|
QColor color = make_valid_color (m_colorizer.marker_color ());
|
|
db::Box circuit_bbox = bbox_for_subcircuit (layout, subcircuit);
|
|
if (circuit_bbox.empty ()) {
|
|
return false;
|
|
}
|
|
|
|
if (n_markers == m_max_shape_count) {
|
|
return true;
|
|
}
|
|
|
|
++n_markers;
|
|
|
|
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
|
|
mp_markers.back ()->set (circuit_bbox, subcircuit_trans, tv);
|
|
mp_markers.back ()->set_color (color);
|
|
mp_markers.back ()->set_frame_color (color);
|
|
configure_marker (mp_markers.back (), false);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
NetlistBrowserPage::produce_highlights_for_circuit (const db::Circuit *circuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
|
|
{
|
|
const db::Layout *layout = mp_database->internal_layout ();
|
|
const db::Cell *cell = mp_database->internal_top_cell ();
|
|
db::ICplxTrans circuit_trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
|
|
|
|
QColor color = make_valid_color (m_colorizer.marker_color ());
|
|
db::Box circuit_bbox = bbox_for_circuit (layout, circuit);
|
|
if (circuit_bbox.empty ()) {
|
|
return false;
|
|
}
|
|
|
|
if (n_markers == m_max_shape_count) {
|
|
return true;
|
|
}
|
|
|
|
++n_markers;
|
|
|
|
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
|
|
mp_markers.back ()->set (circuit_bbox, circuit_trans, tv);
|
|
mp_markers.back ()->set_color (color);
|
|
mp_markers.back ()->set_frame_color (color);
|
|
configure_marker (mp_markers.back (), false);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
NetlistBrowserPage::produce_highlights_for_net (const db::Net *net, size_t &n_markers, const std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> &display_by_lp, const std::vector<db::DCplxTrans> &tv)
|
|
{
|
|
const db::Layout *layout = mp_database->internal_layout ();
|
|
const db::Cell *cell = mp_database->internal_top_cell ();
|
|
db::ICplxTrans net_trans = trans_for (net, *layout, *cell, m_cell_context_cache);
|
|
|
|
db::cell_index_type cell_index = net->circuit ()->cell_index ();
|
|
size_t cluster_id = net->cluster_id ();
|
|
|
|
QColor net_color = m_colorizer.color_of_net (net);
|
|
QColor fallback_color = make_valid_color (m_colorizer.marker_color ());
|
|
|
|
const db::Connectivity &conn = mp_database->connectivity ();
|
|
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
|
|
|
|
db::LayerProperties lp = layout->get_properties (*layer);
|
|
std::map<db::LayerProperties, lay::LayerPropertiesConstIterator>::const_iterator display = display_by_lp.find (lp);
|
|
|
|
db::recursive_cluster_shape_iterator<db::PolygonRef> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
|
|
while (! shapes.at_end ()) {
|
|
|
|
if (n_markers == m_max_shape_count) {
|
|
return true;
|
|
}
|
|
|
|
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
|
|
mp_markers.back ()->set (*shapes, net_trans * shapes.trans (), tv);
|
|
|
|
if (net_color.isValid ()) {
|
|
|
|
mp_markers.back ()->set_color (net_color);
|
|
mp_markers.back ()->set_frame_color (net_color);
|
|
|
|
} else if (! m_use_original_colors || display == display_by_lp.end ()) {
|
|
|
|
mp_markers.back ()->set_color (fallback_color);
|
|
mp_markers.back ()->set_frame_color (fallback_color);
|
|
|
|
} else if (display != display_by_lp.end ()) {
|
|
|
|
mp_markers.back ()->set_line_width (display->second->width (true));
|
|
mp_markers.back ()->set_vertex_size (1);
|
|
mp_markers.back ()->set_dither_pattern (display->second->dither_pattern (true));
|
|
if (mp_view->background_color ().green () < 128) {
|
|
mp_markers.back ()->set_color (display->second->eff_fill_color_brighter (true, (m_marker_intensity * 255) / 100));
|
|
mp_markers.back ()->set_frame_color (display->second->eff_frame_color_brighter (true, (m_marker_intensity * 255) / 100));
|
|
} else {
|
|
mp_markers.back ()->set_color (display->second->eff_fill_color_brighter (true, (-m_marker_intensity * 255) / 100));
|
|
mp_markers.back ()->set_frame_color (display->second->eff_frame_color_brighter (true, (-m_marker_intensity * 255) / 100));
|
|
}
|
|
|
|
}
|
|
|
|
configure_marker (mp_markers.back (), true);
|
|
|
|
++shapes;
|
|
++n_markers;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::configure_marker (Marker *marker, bool with_fill)
|
|
{
|
|
if (m_marker_line_width >= 0) {
|
|
marker->set_line_width (m_marker_line_width);
|
|
}
|
|
|
|
if (m_marker_vertex_size >= 0) {
|
|
marker->set_vertex_size (m_marker_vertex_size);
|
|
}
|
|
|
|
if (m_marker_halo >= 0) {
|
|
marker->set_halo (m_marker_halo);
|
|
}
|
|
|
|
if (m_marker_dither_pattern >= 0 && with_fill) {
|
|
marker->set_dither_pattern (m_marker_dither_pattern);
|
|
}
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::update_highlights ()
|
|
{
|
|
if (! m_enable_updates) {
|
|
m_update_needed = true;
|
|
return;
|
|
}
|
|
|
|
clear_markers ();
|
|
if (! mp_database.get () || ! mp_view) {
|
|
return;
|
|
}
|
|
|
|
const db::Layout &original_layout = mp_view->cellview (m_cv_index)->layout ();
|
|
|
|
const db::Layout *layout = mp_database->internal_layout ();
|
|
if (! layout) {
|
|
return;
|
|
}
|
|
|
|
// a map of display properties vs. layer properties
|
|
|
|
std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> display_by_lp;
|
|
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
|
|
if (! lp->has_children () && lp->cellview_index () == int (m_cv_index) && lp->layer_index () >= 0 && (unsigned int) lp->layer_index () < original_layout.layers ()) {
|
|
display_by_lp.insert (std::make_pair (original_layout.get_properties (lp->layer_index ()), lp));
|
|
}
|
|
}
|
|
|
|
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
|
|
|
|
// correct DBU differences between the storage layout and the original layout
|
|
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
|
|
*t = *t * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
|
|
}
|
|
|
|
size_t n_markers = 0;
|
|
bool not_all_shapes_are_shown = false;
|
|
|
|
for (std::vector<const db::Net *>::const_iterator net = m_current_nets.begin (); net != m_current_nets.end () && ! not_all_shapes_are_shown; ++net) {
|
|
if ((*net)->circuit ()) {
|
|
not_all_shapes_are_shown = produce_highlights_for_net (*net, n_markers, display_by_lp, tv);
|
|
}
|
|
}
|
|
|
|
for (std::vector<const db::Device *>::const_iterator device = m_current_devices.begin (); device != m_current_devices.end () && ! not_all_shapes_are_shown; ++device) {
|
|
if ((*device)->circuit ()) {
|
|
not_all_shapes_are_shown = produce_highlights_for_device (*device, n_markers, tv);
|
|
}
|
|
}
|
|
|
|
for (std::vector<const db::SubCircuit *>::const_iterator subcircuit = m_current_subcircuits.begin (); subcircuit != m_current_subcircuits.end () && ! not_all_shapes_are_shown; ++subcircuit) {
|
|
if ((*subcircuit)->circuit ()) {
|
|
not_all_shapes_are_shown = produce_highlights_for_subcircuit (*subcircuit, n_markers, tv);
|
|
}
|
|
}
|
|
|
|
for (std::vector<const db::Circuit *>::const_iterator circuit = m_current_circuits.begin (); circuit != m_current_circuits.end () && ! not_all_shapes_are_shown; ++circuit) {
|
|
not_all_shapes_are_shown = produce_highlights_for_circuit (*circuit, n_markers, tv);
|
|
}
|
|
|
|
if (not_all_shapes_are_shown) {
|
|
info_label->setText (tl::to_qstring ("<html><p style=\"color:red; font-weight: bold\">" +
|
|
tl::to_string (QObject::tr ("Not all shapes are highlighted")) +
|
|
"</p></html>"));
|
|
info_label->show ();
|
|
} else {
|
|
info_label->hide ();
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::clear_markers ()
|
|
{
|
|
for (std::vector <lay::Marker *>::iterator m = mp_markers.begin (); m != mp_markers.end (); ++m) {
|
|
delete *m;
|
|
}
|
|
|
|
mp_markers.clear ();
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::export_selected ()
|
|
{
|
|
BEGIN_PROTECTED
|
|
|
|
std::vector<const db::Net *> nets = selected_nets ();
|
|
if (nets.empty ()) {
|
|
return;
|
|
}
|
|
|
|
export_nets (&nets);
|
|
|
|
END_PROTECTED
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::export_all ()
|
|
{
|
|
BEGIN_PROTECTED
|
|
export_nets (0);
|
|
END_PROTECTED
|
|
}
|
|
|
|
void
|
|
NetlistBrowserPage::export_nets (const std::vector<const db::Net *> *nets)
|
|
{
|
|
if (! mp_view || ! mp_database.get () || ! mp_database->internal_layout ()) {
|
|
return;
|
|
}
|
|
|
|
const db::Layout &source_layout = *mp_database->internal_layout ();
|
|
if (source_layout.begin_top_down () == source_layout.end_top_cells ()) {
|
|
// nothing to export
|
|
return;
|
|
}
|
|
|
|
const db::Cell &source_top = source_layout.cell (*source_layout.begin_top_down ());
|
|
|
|
std::auto_ptr<lay::NetExportDialog> dialog (new lay::NetExportDialog (this));
|
|
if (dialog->exec_dialog (mp_plugin_root)) {
|
|
|
|
// NOTE: mp_view and database might get reset to 0 in create_layout
|
|
lay::LayoutView *view = mp_view;
|
|
db::LayoutToNetlist *database = mp_database.get ();
|
|
|
|
unsigned int cv_index = view->create_layout (true);
|
|
db::Layout &target_layout = view->cellview (cv_index)->layout ();
|
|
|
|
db::cell_index_type target_top_index = target_layout.add_cell (source_layout.cell_name (source_top.cell_index ()));
|
|
|
|
db::CellMapping cm;
|
|
if (! nets) {
|
|
cm = database->cell_mapping_into (target_layout, target_layout.cell (target_top_index));
|
|
} else {
|
|
cm = database->cell_mapping_into (target_layout, target_layout.cell (target_top_index), *nets);
|
|
}
|
|
|
|
std::map<unsigned int, const db::Region *> lm = database->create_layermap (target_layout, dialog->start_layer_number ());
|
|
|
|
database->build_nets (nets, cm, target_layout, lm,
|
|
dialog->net_prefix ().empty () ? 0 : dialog->net_prefix ().c_str (),
|
|
dialog->net_propname (),
|
|
dialog->produce_circuit_cells () ? db::LayoutToNetlist::BNH_SubcircuitCells : db::LayoutToNetlist::BNH_Flatten,
|
|
dialog->produce_circuit_cells () ? dialog->circuit_cell_prefix ().c_str () : 0,
|
|
dialog->produce_device_cells () ? dialog->device_cell_prefix ().c_str () : 0);
|
|
|
|
view->zoom_fit ();
|
|
view->max_hier ();
|
|
view->add_missing_layers ();
|
|
view->select_cell (target_top_index, cv_index);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|