klayout/src/laybasic/laybasic/layBrowseShapesForm.cc

1185 lines
33 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2022 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
*/
#if defined(HAVE_QT)
#include <vector>
#include <string>
#include <QTreeWidgetItem>
#include <QKeyEvent>
#include "layBrowseShapesForm.h"
#include "layConfigurationDialog.h"
#include "dbCellGraphUtils.h"
#include "tlException.h"
#include "tlString.h"
#include "tlAlgorithm.h"
#include "layQtTools.h"
#include "layMarker.h"
namespace lay
{
// ------------------------------------------------------------
// Declaration of the configuration options
const std::string cfg_shb_context_cell ("shb-context-cell");
const std::string cfg_shb_context_mode ("shb-context-mode");
const std::string cfg_shb_window_state ("shb-window-state");
const std::string cfg_shb_window_mode ("shb-window-mode");
const std::string cfg_shb_window_dim ("shb-window-dim");
const std::string cfg_shb_max_inst_count ("shb-max-inst-count");
const std::string cfg_shb_max_shape_count ("shb-max-shape-count");
class BrowseShapesPluginDeclaration
: public lay::PluginDeclaration
{
public:
virtual void get_options (std::vector < std::pair<std::string, std::string> > &options) const
{
options.push_back (std::pair<std::string, std::string> (cfg_shb_context_cell, ""));
options.push_back (std::pair<std::string, std::string> (cfg_shb_context_mode, "any-top"));
options.push_back (std::pair<std::string, std::string> (cfg_shb_window_mode, "fit-marker"));
options.push_back (std::pair<std::string, std::string> (cfg_shb_window_state, ""));
options.push_back (std::pair<std::string, std::string> (cfg_shb_window_dim, "1.0"));
options.push_back (std::pair<std::string, std::string> (cfg_shb_max_inst_count, "1000"));
options.push_back (std::pair<std::string, std::string> (cfg_shb_max_shape_count, "1000"));
}
virtual lay::ConfigPage *config_page (QWidget *parent, std::string &title) const
{
title = tl::to_string (QObject::tr ("Browsers|Shape Browser"));
return new BrowseShapesConfigPage (parent);
}
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
{
lay::PluginDeclaration::get_menu_entries (menu_entries);
menu_entries.push_back (lay::separator ("browser_group", "tools_menu.end"));
menu_entries.push_back (lay::menu_item ("browse_shapes::show", "browse_shapes", "tools_menu.end", tl::to_string (QObject::tr ("Browse Shapes"))));
}
virtual lay::Plugin *create_plugin (db::Manager *, lay::Dispatcher *root, lay::LayoutView *view) const
{
return new BrowseShapesForm (root, view);
}
};
static tl::RegisteredClass<lay::PluginDeclaration> config_decl (new BrowseShapesPluginDeclaration (), 10000, "BrowseShapesPlugin");
// ------------------------------------------------------------
static struct {
BrowseShapesForm::mode_type mode;
const char *string;
} context_modes [] = {
{ BrowseShapesForm::AnyTop, "any-top" },
{ BrowseShapesForm::Local, "local" },
{ BrowseShapesForm::ToCellView, "given-cell" }
};
class BrowseShapesContextModeConverter
{
public:
void
from_string (const std::string &value, BrowseShapesForm::mode_type &mode)
{
for (unsigned int i = 0; i < sizeof (context_modes) / sizeof (context_modes [0]); ++i) {
if (value == context_modes [i].string) {
mode = context_modes [i].mode;
return;
}
}
throw tl::Exception (tl::to_string (QObject::tr ("Invalid cell browser context mode: ")) + value);
}
std::string
to_string (BrowseShapesForm::mode_type mode)
{
for (unsigned int i = 0; i < sizeof (context_modes) / sizeof (context_modes [0]); ++i) {
if (mode == context_modes [i].mode) {
return context_modes [i].string;
}
}
return "";
}
};
static struct {
BrowseShapesForm::window_type mode;
const char *string;
} window_modes [] = {
{ BrowseShapesForm::DontChange, "dont-change" },
{ BrowseShapesForm::FitCell, "fit-cell" },
{ BrowseShapesForm::FitMarker, "fit-marker" },
{ BrowseShapesForm::Center, "center" },
{ BrowseShapesForm::CenterSize, "center-size" }
};
class BrowseShapesWindowModeConverter
{
public:
void
from_string (const std::string &value, BrowseShapesForm::window_type &mode)
{
for (unsigned int i = 0; i < sizeof (window_modes) / sizeof (window_modes [0]); ++i) {
if (value == window_modes [i].string) {
mode = window_modes [i].mode;
return;
}
}
throw tl::Exception (tl::to_string (QObject::tr ("Invalid cell browser window mode: ")) + value);
}
std::string
to_string (BrowseShapesForm::window_type mode)
{
for (unsigned int i = 0; i < sizeof (window_modes) / sizeof (window_modes [0]); ++i) {
if (mode == window_modes [i].mode) {
return window_modes [i].string;
}
}
return "";
}
};
// ------------------------------------------------------------
BrowseShapesConfigPage::BrowseShapesConfigPage (QWidget *parent)
: lay::ConfigPage (parent)
{
Ui::BrowseShapesConfigPage::setupUi (this);
connect (cbx_context, SIGNAL (currentIndexChanged (int)), this, SLOT (context_changed (int)));
connect (cbx_window, SIGNAL (currentIndexChanged (int)), this, SLOT (window_changed (int)));
}
void
BrowseShapesConfigPage::setup (lay::Dispatcher *root)
{
std::string value;
// context cell
root->config_get (cfg_shb_context_cell, value);
le_cell_name->setText (tl::to_qstring (value));
// context mode
BrowseShapesForm::mode_type cmode = BrowseShapesForm::AnyTop;
root->config_get (cfg_shb_context_mode, cmode, BrowseShapesContextModeConverter ());
cbx_context->setCurrentIndex (int (cmode));
// window mode
BrowseShapesForm::window_type wmode = BrowseShapesForm::FitMarker;
root->config_get (cfg_shb_window_mode, wmode, BrowseShapesWindowModeConverter ());
cbx_window->setCurrentIndex (int (wmode));
// window dimension
double wdim = 1.0;
root->config_get (cfg_shb_window_dim, wdim);
le_window->setText (tl::to_qstring (tl::to_string (wdim)));
// max. instance count
unsigned int max_inst_count = 1000;
root->config_get (cfg_shb_max_inst_count, max_inst_count);
le_max_inst->setText (tl::to_qstring (tl::to_string (max_inst_count)));
// max. instance count
unsigned int max_shape_count = 1000;
root->config_get (cfg_shb_max_shape_count, max_shape_count);
le_max_shapes->setText (tl::to_qstring (tl::to_string (max_shape_count)));
// enable controls
context_changed (int (cmode));
window_changed (int (wmode));
}
void
BrowseShapesConfigPage::context_changed (int m)
{
le_cell_name->setEnabled (m == int (BrowseShapesForm::ToCellView));
}
void
BrowseShapesConfigPage::window_changed (int m)
{
le_window->setEnabled (m == int (BrowseShapesForm::FitMarker) || m == int (BrowseShapesForm::CenterSize));
}
void
BrowseShapesConfigPage::commit (lay::Dispatcher *root)
{
double dim = 1.0;
tl::from_string_ext (tl::to_string (le_window->text ()), dim);
unsigned int max_inst_count = 1000;
tl::from_string_ext (tl::to_string (le_max_inst->text ()), max_inst_count);
unsigned int max_shape_count = 1000;
tl::from_string_ext (tl::to_string (le_max_shapes->text ()), max_shape_count);
root->config_set (cfg_shb_context_cell, tl::to_string (le_cell_name->text ()));
root->config_set (cfg_shb_context_mode, BrowseShapesForm::mode_type (cbx_context->currentIndex ()), BrowseShapesContextModeConverter ());
root->config_set (cfg_shb_window_mode, BrowseShapesForm::window_type (cbx_window->currentIndex ()), BrowseShapesWindowModeConverter ());
root->config_set (cfg_shb_window_dim, dim);
root->config_set (cfg_shb_max_inst_count, max_inst_count);
root->config_set (cfg_shb_max_shape_count, max_shape_count);
}
// ------------------------------------------------------------
class BrowseShapesFormLVI : public QTreeWidgetItem
{
public:
BrowseShapesFormLVI (const std::string &text)
: QTreeWidgetItem (),
m_value (0.0), m_value_flat (0.0)
{
setText (0, tl::to_qstring (text));
}
virtual bool operator< (const QTreeWidgetItem &i) const
{
const BrowseShapesFormLVI *other = dynamic_cast <const BrowseShapesFormLVI *> (&i);
if (other) {
return m_value < other->m_value;
}
return QTreeWidgetItem::operator< (i);
}
void set_value (double v)
{
m_value = v;
}
void set_value_flat (double v)
{
m_value_flat = v;
}
private:
double m_value, m_value_flat;
};
// ------------------------------------------------------------
class BrowseShapesFormLayerLVI : public BrowseShapesFormLVI
{
public:
BrowseShapesFormLayerLVI (const std::string &text)
: BrowseShapesFormLVI (text)
{
QFont f (font (0));
f.setBold (true);
QColor tc (0, 0, 255);
for (int col = 0; col < 3; ++col) {
setFont (col, f);
#if QT_VERSION >= 0x60000
setForeground (col, tc);
#else
setTextColor (col, tc);
#endif
}
}
};
// ------------------------------------------------------------
class BrowseShapesFormCellLVI : public BrowseShapesFormLVI
{
public:
BrowseShapesFormCellLVI (const std::string &cn, lay::CellView::cell_index_type index, unsigned int lindex)
: BrowseShapesFormLVI (cn),
m_index (index), m_lindex (lindex)
{
// .. nothing yet ..
}
lay::CellView::cell_index_type index () const
{
return m_index;
}
unsigned int lindex () const
{
return m_lindex;
}
private:
lay::CellView::cell_index_type m_index;
unsigned int m_lindex;
};
// ------------------------------------------------------------
class BrowseShapesFormCellInstanceLVI : public QTreeWidgetItem
{
public:
BrowseShapesFormCellInstanceLVI (const std::string &text, const std::string &path,
const db::ICplxTrans &trans, lay::CellView::cell_index_type index)
: QTreeWidgetItem (),
m_trans (trans), m_index (index)
{
setText (0, tl::to_qstring (text));
setText (1, tl::to_qstring (path));
}
const db::ICplxTrans &trans () const
{
return m_trans;
}
lay::CellView::cell_index_type index () const
{
return m_index;
}
private:
db::ICplxTrans m_trans;
lay::CellView::cell_index_type m_index;
};
// ------------------------------------------------------------
class BrowseShapesFormShapeInstanceLVI : public QTreeWidgetItem
{
public:
BrowseShapesFormShapeInstanceLVI (const std::string &text,
const db::ShapeIterator &iter,
const db::ICplxTrans &trans)
: QTreeWidgetItem (), m_iter (iter), m_trans (trans)
{
setText (0, tl::to_qstring (text));
}
const db::Shape &shape () const
{
return *(m_iter.operator-> ());
}
const db::ICplxTrans &trans () const
{
return m_trans;
}
private:
db::ShapeIterator m_iter;
db::ICplxTrans m_trans;
};
// ------------------------------------------------------------
BrowseShapesForm::BrowseShapesForm (lay::Dispatcher *root, LayoutView *vw)
: lay::Browser (root, vw),
Ui::BrowseShapesForm (),
m_cv_index (-1),
m_cell_changed_enabled (true),
m_view_changed (false),
m_cell_inst_changed_enabled (true),
m_shape_inst_changed_enabled (true),
m_ef_enabled (true),
m_mode (AnyTop),
m_window (FitMarker),
m_window_dim (0.0),
m_max_inst_count (0),
m_max_shape_count (0)
{
Ui::BrowseShapesForm::setupUi (this);
lv_cell->setSortingEnabled (false); // don't enable sorting: this messes up the list completely
lv_cell->setSelectionMode (QTreeWidget::SingleSelection);
lv_cell->setUniformRowHeights (true);
lv_cell_instance->installEventFilter (this);
lv_cell_instance->setSortingEnabled (false);
lv_cell_instance->setSelectionMode (QTreeWidget::SingleSelection);
lv_cell_instance->setUniformRowHeights (true);
lv_shape_instance->installEventFilter (this);
lv_shape_instance->setSelectionMode (QTreeWidget::ExtendedSelection);
lv_shape_instance->setSortingEnabled (false);
lv_shape_instance->setUniformRowHeights (true);
update ();
update_cell_list ();
// signals and slots connections
connect (lv_cell, SIGNAL (currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT (cell_changed(QTreeWidgetItem*, QTreeWidgetItem*)));
connect (lv_cell_instance, SIGNAL (currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT (cell_inst_changed(QTreeWidgetItem*, QTreeWidgetItem*)));
connect (lv_shape_instance, SIGNAL (itemSelectionChanged()), this, SLOT (shape_inst_changed()));
connect (pb_next_cell, SIGNAL (clicked()), this, SLOT (next_cell()));
connect (pb_prev_cell, SIGNAL (clicked()), this, SLOT (prev_cell()));
connect (pb_next_shape, SIGNAL (clicked()), this, SLOT (next_shape()));
connect (pb_prev_shape, SIGNAL (clicked()), this, SLOT (prev_shape()));
connect (pb_next_inst, SIGNAL (clicked()), this, SLOT (next_inst()));
connect (pb_prev_inst, SIGNAL (clicked()), this, SLOT (prev_inst()));
connect (configureButton, SIGNAL (clicked ()), this, SLOT (configure ()));
}
void
BrowseShapesForm::menu_activated (const std::string &symbol)
{
if (symbol == "browse_shapes::show") {
view ()->deactivate_all_browsers ();
activate ();
} else {
lay::Browser::menu_activated (symbol);
}
}
BrowseShapesForm::~BrowseShapesForm ()
{
remove_marker ();
}
void
BrowseShapesForm::configure ()
{
lay::ConfigurationDialog config_dialog (this, root (), "BrowseShapesPlugin");
config_dialog.exec ();
}
bool
BrowseShapesForm::configure (const std::string &name, const std::string &value)
{
bool need_update = false;
bool taken = true;
if (name == cfg_shb_context_cell) {
need_update = lay::test_and_set (m_context_cell, value);
} else if (name == cfg_shb_window_state) {
lay::restore_dialog_state (this, value);
} else if (name == cfg_shb_context_mode) {
mode_type mode = m_mode;
BrowseShapesContextModeConverter ().from_string (value, mode);
need_update = lay::test_and_set (m_mode, mode);
} else if (name == cfg_shb_window_mode) {
window_type window = m_window;
BrowseShapesWindowModeConverter ().from_string (value, window);
need_update = lay::test_and_set (m_window, window);
} else if (name == cfg_shb_window_dim) {
double wdim = m_window_dim;
tl::from_string (value, wdim);
if (fabs (wdim - m_window_dim) > 1e-6) {
m_window_dim = wdim;
need_update = true;
}
} else if (name == cfg_shb_max_inst_count) {
unsigned int mic = m_max_inst_count;
tl::from_string (value, mic);
need_update = lay::test_and_set (m_max_inst_count, mic);
} else if (name == cfg_shb_max_shape_count) {
unsigned int mic = 0;
tl::from_string (value, mic);
need_update = lay::test_and_set (m_max_shape_count, mic);
} else {
taken = false;
}
if (active () && need_update) {
update ();
}
return taken;
}
void
BrowseShapesForm::remove_marker ()
{
for (std::vector<lay::ShapeMarker *>::iterator m = mp_markers.begin (); m != mp_markers.end (); ++m) {
delete *m;
}
mp_markers.clear ();
}
void
BrowseShapesForm::cell_changed (QTreeWidgetItem *item, QTreeWidgetItem *)
{
lv_cell_instance->clear ();
lv_shape_instance->clear ();
if (m_cv_index < 0 || ! m_cell_changed_enabled) {
return;
}
BrowseShapesFormCellLVI *it = dynamic_cast <BrowseShapesFormCellLVI *> (item);
if (! it) {
remove_marker ();
return;
}
const db::Layout &layout = m_cellview->layout ();
double dbu = layout.dbu ();
unsigned int layer = m_lprops [it->lindex ()]->layer_index ();
db::ICplxTrans trans = db::VCplxTrans(1.0 / dbu) * m_lprops [it->lindex ()]->trans () [0] * db::CplxTrans(dbu);
const std::set<db::properties_id_type> *prop_sel = &m_lprops [it->lindex ()]->prop_sel ();
bool inv_prop_sel = m_lprops [it->lindex ()]->inverse_prop_sel ();
const db::Cell &cell = layout.cell (it->index ());
m_cell_inst_changed_enabled = false;
QList<QTreeWidgetItem *> items;
// fill cell instances
bool shortened = false;
unsigned int count = 0;
if (m_mode == AnyTop) {
shortened = fill_cell_instances (db::ICplxTrans (), layout, &cell, 0, false, std::string (), items, count);
} else if (m_mode == ToCellView) {
if (m_cellview.is_valid ()) {
shortened = fill_cell_instances (db::ICplxTrans (), layout, &cell, m_cellview.cell (), false, std::string (), items, count);
}
}
// add an entry to indicate that there are more ..
if (shortened) {
items.append (new QTreeWidgetItem ());
items.back ()->setText (0, QString::fromUtf8 ("..."));
}
lv_cell_instance->addTopLevelItems (items);
if (lv_cell_instance->topLevelItemCount () > 0) {
lv_cell_instance->setCurrentItem (lv_cell_instance->topLevelItem (0));
}
m_cell_inst_changed_enabled = true;
// fill shape instances
items.clear ();
m_shape_inst_changed_enabled = false;
// fill the list of shape instances
count = 0;
db::ShapeIterator shape;
for (shape = cell.shapes (layer).begin (db::ShapeIterator::All, prop_sel, inv_prop_sel); !shape.at_end () && count++ < m_max_shape_count; ++shape) {
db::Box box (shape->bbox ());
std::string name;
if (shape->is_polygon ()) {
name = tl::to_string (QObject::tr ("polygon"));
} else if (shape->is_edge ()) {
name = tl::to_string (QObject::tr ("edge"));
} else if (shape->is_text ()) {
name = tl::to_string (QObject::tr ("text"));
} else if (shape->is_box ()) {
name = tl::to_string (QObject::tr ("box"));
} else if (shape->is_path ()) {
name = tl::to_string (QObject::tr ("path"));
} else {
name = tl::to_string (QObject::tr ("non-geometric"));
}
items.append (new BrowseShapesFormShapeInstanceLVI ((name + std::string (" at (") + tl::micron_to_string (0.5 * dbu * (box.left () + box.right ())) +
"," + tl::micron_to_string (0.5 * dbu * (box.bottom () + box.top ())) + ")"),
shape,
trans));
}
shortened = !shape.at_end ();
// add an entry to indicate that there are more ..
if (shortened) {
items.append (new QTreeWidgetItem ());
items.back ()->setText (0, QString::fromUtf8 ("..."));
}
lv_shape_instance->addTopLevelItems (items);
if (lv_shape_instance->topLevelItemCount () > 0) {
lv_shape_instance->topLevelItem (0)->setSelected (true);
lv_shape_instance->setCurrentItem (lv_shape_instance->topLevelItem (0));
}
m_shape_inst_changed_enabled = true;
highlight_current ();
}
void
BrowseShapesForm::shape_inst_changed ()
{
if (m_cv_index >= 0 && m_shape_inst_changed_enabled) {
highlight_current ();
}
}
void
BrowseShapesForm::cell_inst_changed (QTreeWidgetItem *, QTreeWidgetItem *)
{
if (m_cv_index >= 0 && m_cell_inst_changed_enabled) {
highlight_current ();
}
}
void
BrowseShapesForm::activated ()
{
view ()->save_view (m_display_state);
std::vector <lay::LayerPropertiesConstIterator> sel_layers = view ()->selected_layers ();
if (sel_layers.empty ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No layer selected")));
}
m_lprops.clear ();
m_cellview = lay::CellView ();
m_cv_index = -1;
for (std::vector <lay::LayerPropertiesConstIterator>::const_iterator l = sel_layers.begin (); l != sel_layers.end (); ++l) {
if ((*l)->layer_index () >= 0 && (*l)->cellview_index () >= 0) {
m_lprops.push_back (*l);
int cv_index = (*l)->cellview_index ();
// check if the cellviews have the same index
if (m_cv_index >= 0) {
if (cv_index != m_cv_index) {
throw tl::Exception (tl::to_string (QObject::tr ("Layers selected for shape browsing must originate from the same cellview")));
}
} else {
m_cv_index = cv_index;
m_cellview = view ()->cellview (m_cv_index);
}
}
}
update ();
update_cell_list ();
m_view_changed = false;
}
void
BrowseShapesForm::update ()
{
if (m_mode == ToCellView) {
m_cellview.set_cell (m_context_cell);
}
lv_cell_instance->setEnabled (m_mode != Local);
if (m_mode == Local) {
lv_cell_instance->clear ();
} else if (lv_cell->currentItem ()) {
cell_changed (lv_cell->currentItem (), 0);
}
}
// A helper structure to hold all information relevant to the cells to
// show
struct BrowseShapesCellInfo
{
BrowseShapesCellInfo (const std::string &n, size_t s, size_t sf, lay::CellView::cell_index_type i)
: name (n), shapes (s), shapes_flat (sf), cell_index (i)
{
// ..
}
std::string name;
size_t shapes, shapes_flat;
lay::CellView::cell_index_type cell_index;
bool operator< (const BrowseShapesCellInfo &d) const
{
return name < d.name;
}
};
template <class T>
size_t
num_shape_instances (unsigned int layer, const db::Cell &cell)
{
typename T::tag tag;
return std::distance (cell.shapes (layer).begin (tag), cell.shapes (layer).end (tag));
}
void
BrowseShapesForm::update_cell_list ()
{
BrowseShapesFormCellLVI *sel_item = 0;
lv_cell->clear ();
for (unsigned int lindex = 0; lindex < m_lprops.size (); ++lindex) {
if (m_cv_index >= 0) {
size_t all_shapes = 0;
size_t all_shapes_flat = 0;
BrowseShapesFormLayerLVI *layer_root = new BrowseShapesFormLayerLVI (m_lprops [lindex]->display_string (view (), true /*real*/));
lv_cell->addTopLevelItem (layer_root);
const db::Layout &layout = m_cellview->layout ();
m_cell_changed_enabled = false;
// obtain all cell names, sort by shape count and fill into the lv_cell
std::vector <BrowseShapesCellInfo> cell_info;
cell_info.reserve (layout.cells ());
const std::set<db::properties_id_type> *prop_sel = &m_lprops [lindex]->prop_sel ();
bool inv_prop_sel = m_lprops [lindex]->inverse_prop_sel ();
db::CellCounter counter (& layout);
for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) {
size_t shapes = 0;
for (db::ShapeIterator iter = (*c).shapes (m_lprops [lindex]->layer_index ()).begin (db::ShapeIterator::All, prop_sel, inv_prop_sel); !iter.at_end (); ++iter) {
++shapes;
}
if (shapes > 0) {
size_t cm = counter.weight (c->cell_index ());
cell_info.push_back (BrowseShapesCellInfo (layout.cell_name (c->cell_index ()), shapes, shapes * cm, c->cell_index ()));
all_shapes += shapes;
all_shapes_flat += shapes * cm;
}
}
tl::sort (cell_info.begin (), cell_info.end ());
// create the entries.
QList<QTreeWidgetItem *> items;
BrowseShapesFormCellLVI *item = 0;
for (std::vector<BrowseShapesCellInfo>::const_iterator cn = cell_info.end (); cn != cell_info.begin (); ) {
--cn;
item = new BrowseShapesFormCellLVI (cn->name, cn->cell_index, lindex);
item->setText (1, tl::to_qstring (tl::to_string (cn->shapes)));
item->setText (2, tl::to_qstring (tl::to_string (cn->shapes_flat)));
sel_item = item;
items.prepend (item);
}
layer_root->addChildren (items);
m_cell_changed_enabled = true;
layer_root->setText (1, tl::to_qstring (tl::to_string (all_shapes)));
layer_root->setText (2, tl::to_qstring (tl::to_string (all_shapes_flat)));
layer_root->set_value (all_shapes);
layer_root->set_value_flat (all_shapes);
}
}
if (sel_item) {
lv_cell->setCurrentItem (sel_item);
sel_item->setSelected (true);
lv_cell->scrollToItem (sel_item);
}
}
void
BrowseShapesForm::deactivated ()
{
root ()->config_set (cfg_shb_window_state, lay::save_dialog_state (this));
// remove the cellview reference and clean up everything that could reference
// database objects
lv_cell->clear ();
lv_cell_instance->clear ();
lv_shape_instance->clear ();
m_cellview = lay::CellView ();
if (m_view_changed) {
view ()->store_state ();
}
view ()->goto_view (m_display_state);
remove_marker ();
}
bool
BrowseShapesForm::fill_cell_instances (const db::ICplxTrans &t, const db::Layout &layout, const db::Cell *from, const db::Cell *to, bool to_parent, const std::string &path, QList<QTreeWidgetItem *> &items, unsigned int &count)
{
if (from == to || (! to_parent && to == 0 && from->is_top ())) {
if (count == m_max_inst_count) {
return true; // shorten list
}
++count;
std::string text;
if (! path.empty ()) {
text += t.to_string (true /*lazy*/, layout.dbu ());
}
items.append (new BrowseShapesFormCellInstanceLVI (text, path, t, from->cell_index ()));
} else {
// traverse the parents
for (db::Cell::parent_inst_iterator p = from->begin_parent_insts (); ! p.at_end (); ++p) {
db::CellInstArray parent_inst = p->inst ();
db::Vector a, b;
unsigned long r = 1, c = 1;
parent_inst.is_regular_array (a, b, r, c);
std::string aref;
if (r > 1 || c > 1) {
aref = "[";
aref += tl::to_string (c);
aref += ",";
aref += tl::to_string (r);
aref += "]";
} else if (parent_inst.size () > 1) {
aref = "(+";
aref += tl::to_string (parent_inst.size () - 1);
aref += "x)";
}
std::string new_path;
if (! path.empty ()) {
new_path = layout.cell_name (p->parent_cell_index ()) + aref + "/" + path;
} else {
new_path = layout.cell_name (p->parent_cell_index ()) + aref;
}
db::ICplxTrans tt (parent_inst.complex_trans ());
const db::Cell *cell = & layout.cell (p->parent_cell_index ());
if (fill_cell_instances (tt.inverted () * t, layout, cell, to_parent ? cell : to, false, new_path, items, count)) {
return true; // list too long - no more entries possible
}
}
}
return false;
}
void
BrowseShapesForm::highlight_current ()
{
remove_marker ();
if (m_cv_index < 0) {
return;
}
db::ICplxTrans t;
lay::CellView::cell_index_type cell_index = 0;
BrowseShapesFormCellLVI *item = dynamic_cast <BrowseShapesFormCellLVI *> (lv_cell->currentItem ());
if (! item) {
return;
}
if (m_mode != Local) {
BrowseShapesFormCellInstanceLVI *ci_item = dynamic_cast <BrowseShapesFormCellInstanceLVI *> (lv_cell_instance->currentItem ());
if (! ci_item) {
return;
}
t = ci_item->trans ();
cell_index = ci_item->index ();
} else {
cell_index = item->index ();
}
db::DBox dbox;
const db::Layout &layout = m_cellview->layout ();
// TODO: the selectedItems () method is somewhat slow for large selections
QList<QTreeWidgetItem *> selected_items = lv_shape_instance->selectedItems ();
for (QList<QTreeWidgetItem *>::const_iterator s = selected_items.begin (); s != selected_items.end (); ++s) {
BrowseShapesFormShapeInstanceLVI *shape_item = dynamic_cast <BrowseShapesFormShapeInstanceLVI *> (*s);
if (shape_item) {
// transform the box into the cell view shown in micron space
mp_markers.push_back (new lay::ShapeMarker (view (), m_cv_index));
mp_markers.back ()->set (shape_item->shape (), shape_item->trans () * t);
dbox += (db::CplxTrans(layout.dbu ()) * shape_item->trans () * t) * shape_item->shape ().bbox ();
}
}
if (! dbox.empty ()) {
view ()->select_cell (cell_index, m_cv_index);
if (m_window == FitCell) {
view ()->zoom_fit ();
} else if (m_window == FitMarker) {
view ()->zoom_box (dbox.enlarged (db::DVector (m_window_dim, m_window_dim)));
} else if (m_window == Center) {
view ()->pan_center (dbox.p1 () + (dbox.p2 () - dbox.p1 ()) * 0.5);
} else if (m_window == CenterSize) {
double w = std::max (dbox.width (), m_window_dim);
double h = std::max (dbox.height (), m_window_dim);
db::DPoint center (dbox.p1 () + (dbox.p2 () - dbox.p1 ()) * 0.5);
db::DVector d (w * 0.5, h * 0.5);
view ()->zoom_box (db::DBox (center - d, center + d));
}
m_view_changed = true;
}
}
bool
BrowseShapesForm::adv_cell (bool up)
{
QTreeWidgetItem *current = lv_cell->currentItem ();
m_ef_enabled = false; // prevent recursion
QKeyEvent ke (QEvent::KeyPress, up ? Qt::Key_Up : Qt::Key_Down, Qt::NoModifier);
((QObject *)lv_cell)->event (&ke);
m_ef_enabled = true;
if (lv_cell->currentItem () != current) {
BrowseShapesFormLayerLVI *litem = dynamic_cast <BrowseShapesFormLayerLVI *> (lv_cell->currentItem ());
if (litem) {
if (! up) {
while (litem && litem->childCount () == 0) {
litem = dynamic_cast <BrowseShapesFormLayerLVI *> (lv_cell->topLevelItem (lv_cell->indexOfTopLevelItem (litem) + 1)); // TODO: slow!
}
if (litem) {
QTreeWidgetItem *ni = litem->child (0);
lv_cell->setCurrentItem (ni);
ni->setSelected (true);
lv_cell->scrollToItem (ni);
}
} else if (current->parent () == litem) {
// determine the layer item that is before the current one
do {
int i = lv_cell->indexOfTopLevelItem (litem);
if (i > 0) {
litem = dynamic_cast <BrowseShapesFormLayerLVI *> (lv_cell->topLevelItem (i - 1)); // TODO: slow!
} else {
litem = 0;
}
} while (litem && litem->childCount () == 0);
if (litem) {
QTreeWidgetItem *ni = litem->child (litem->childCount () - 1);
lv_cell->setCurrentItem (ni);
ni->setSelected (true);
lv_cell->scrollToItem (ni);
} else {
// revert to the original
lv_cell->setCurrentItem (current);
current->setSelected (true);
lv_cell->scrollToItem (current);
}
}
}
return true;
} else {
return false;
}
}
bool
BrowseShapesForm::adv_shape (bool up)
{
QTreeWidgetItem *current = lv_shape_instance->currentItem ();
m_ef_enabled = false; // prevent recursion
QKeyEvent ke (QEvent::KeyPress, up ? Qt::Key_Up : Qt::Key_Down, Qt::NoModifier);
((QObject *)lv_shape_instance)->event (&ke);
m_ef_enabled = true;
if (lv_shape_instance->currentItem () == current) {
// if we are at the end of the list, pass the event
// forward to the cell list
if (adv_cell (up)) {
// position at the last shape if required
if (up) {
QTreeWidgetItem *ni = lv_shape_instance->topLevelItem (lv_shape_instance->topLevelItemCount () - 1);
if (ni) {
lv_shape_instance->setCurrentItem (ni);
ni->setSelected (true);
lv_shape_instance->scrollToItem (ni);
}
}
return true;
} else {
return false;
}
} else {
return true;
}
}
bool
BrowseShapesForm::adv_cell_inst (bool up)
{
QTreeWidgetItem *current = lv_cell_instance->currentItem ();
m_ef_enabled = false; // prevent recursion
QKeyEvent ke (QEvent::KeyPress, up ? Qt::Key_Up : Qt::Key_Down, Qt::NoModifier);
((QObject *)lv_cell_instance)->event (&ke);
m_ef_enabled = true;
if (lv_cell_instance->currentItem () == current) {
// if we are at the end of the list, pass the event
// forward to the shape instance list
if (adv_shape (up)) {
// select first or last item
QTreeWidgetItem *ni;
if (up) {
ni = lv_cell_instance->topLevelItem (lv_cell_instance->topLevelItemCount () - 1);
} else {
ni = lv_cell_instance->topLevelItem (0);
}
if (ni) {
lv_cell_instance->setCurrentItem (ni);
ni->setSelected (true);
lv_cell_instance->scrollToItem (ni);
}
return true;
} else {
return false;
}
} else {
return false;
}
}
bool
BrowseShapesForm::eventFilter (QObject *watched, QEvent *event)
{
if (m_ef_enabled && event->type () == QEvent::KeyPress) {
QKeyEvent *ke = dynamic_cast <QKeyEvent *> (event);
if (ke && (ke->key () == Qt::Key_Up || ke->key () == Qt::Key_Down)) {
bool up = ke->key () == Qt::Key_Up;
if (watched == lv_cell) {
adv_cell (up);
} else if (watched == lv_shape_instance) {
adv_shape (up);
} else if (watched == lv_cell_instance) {
adv_cell_inst (up);
}
return true;
}
}
return QDialog::eventFilter (watched, event);
}
void
BrowseShapesForm::next_cell ()
{
lv_cell->setFocus ();
adv_cell (false);
}
void
BrowseShapesForm::prev_cell ()
{
lv_cell->setFocus ();
adv_cell (true);
}
void
BrowseShapesForm::next_shape ()
{
lv_shape_instance->setFocus ();
adv_shape (false);
}
void
BrowseShapesForm::prev_shape ()
{
lv_shape_instance->setFocus ();
adv_shape (true);
}
void
BrowseShapesForm::next_inst ()
{
lv_cell_instance->setFocus ();
adv_cell_inst (false);
}
void
BrowseShapesForm::prev_inst ()
{
lv_cell_instance->setFocus ();
adv_cell_inst (true);
}
}
#endif