klayout/src/edt/edt/edtInstPropertiesPage.cc

841 lines
26 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "dbLibrary.h"
#include "dbPCellHeader.h"
#include "edtInstPropertiesPage.h"
#include "edtPropertiesPageUtils.h"
#include "edtPCellParametersPage.h"
#include "edtDialogs.h"
#include "layDialogs.h"
#include "layObjectInstPath.h"
#include "layLayoutView.h"
#include "layCellSelectionForm.h"
#include "layQtTools.h"
#include "tlExceptions.h"
#include "tlString.h"
#include <string>
#include <QMessageBox>
namespace edt
{
// -------------------------------------------------------------------------
// InstPropertiesPage implementation
InstPropertiesPage::InstPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent)
: lay::PropertiesPage (parent, manager, service), mp_service (service), m_enable_cb_callback (true), mp_pcell_parameters (0)
{
m_selection_ptrs.reserve (service->selection ().size ());
for (edt::Service::obj_iterator s = service->selection ().begin (); s != service->selection ().end (); ++s) {
m_selection_ptrs.push_back (s);
}
m_index = 0;
m_prop_id = 0;
mp_service->clear_highlights ();
setupUi (this);
connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ()));
connect (sel_pb, SIGNAL (clicked ()), this, SLOT (show_cell ()));
connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ()));
connect (dbu_cb, SIGNAL (toggled (bool)), this, SLOT (display_mode_changed (bool)));
connect (abs_cb, SIGNAL (toggled (bool)), this, SLOT (display_mode_changed (bool)));
connect (browse_pb, SIGNAL (clicked ()), this, SLOT (browse_cell ()));
connect (lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int)));
connect (cell_name_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &)));
if (! readonly ()) {
connect (lib_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (cell_name_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (array_grp, SIGNAL (clicked ()), this, SIGNAL (edited ()));
connect (rows_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (columns_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (row_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (row_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (column_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (column_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (pos_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (pos_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (mag_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ()));
} else {
browse_pb->setEnabled (false);
cell_name_le->setReadOnly (true);
rows_le->setReadOnly (true);
columns_le->setReadOnly (true);
row_x_le->setReadOnly (true);
row_y_le->setReadOnly (true);
column_x_le->setReadOnly (true);
column_y_le->setReadOnly (true);
pos_x_le->setReadOnly (true);
pos_y_le->setReadOnly (true);
angle_le->setReadOnly (true);
mag_le->setReadOnly (true);
lib_cbx->setEnabled (false);
array_grp->setEnabled (false);
mirror_cbx->setEnabled (false);
}
QHBoxLayout *layout = new QHBoxLayout (pcell_tab);
layout->setMargin (0);
pcell_tab->setLayout (layout);
}
InstPropertiesPage::~InstPropertiesPage ()
{
mp_service->restore_highlights ();
}
void
InstPropertiesPage::library_changed (int)
{
BEGIN_PROTECTED
update_pcell_parameters ();
END_PROTECTED
}
void
InstPropertiesPage::cell_name_changed (const QString &)
{
BEGIN_PROTECTED
update_pcell_parameters ();
END_PROTECTED
}
void
InstPropertiesPage::browse_cell ()
{
BEGIN_PROTECTED
// find the layout the cell has to be looked up: that is either the layout of the current instance or
// the library selected
db::Layout *layout = 0;
db::Library *lib = 0;
if (lib_cbx->current_library ()) {
lib = lib_cbx->current_library ();
layout = &lib->layout ();
} else {
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
layout = &cv->layout ();
}
lay::LibraryCellSelectionForm form (this, layout, "browse_lib_cell");
if (lib) {
form.setWindowTitle (tl::to_qstring (tl::to_string (QObject::tr ("Select Cell - Library: ")) + lib->get_description ()));
}
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
if (pc.first) {
form.set_selected_pcell_id (pc.second);
} else {
std::pair<bool, db::cell_index_type> c = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
if (c.first) {
form.set_selected_cell_index (c.second);
}
}
if (form.exec ()) {
cell_name_le->blockSignals (true);
if (form.selected_cell_is_pcell ()) {
cell_name_le->setText (tl::to_qstring (layout->pcell_header (form.selected_pcell_id ())->get_name ()));
} else if (layout->is_valid_cell_index (form.selected_cell_index ())) {
cell_name_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ())));
}
cell_name_le->blockSignals (false);
update_pcell_parameters ();
emit edited ();
}
END_PROTECTED
}
void
InstPropertiesPage::show_props ()
{
lay::UserPropertiesForm props_form (this);
if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) {
emit edited ();
}
}
void
InstPropertiesPage::display_mode_changed (bool)
{
if (! m_enable_cb_callback) {
return;
}
mp_service->view ()->dbu_coordinates (dbu_cb->isChecked ());
mp_service->view ()->absolute_coordinates (abs_cb->isChecked ());
update ();
}
void
InstPropertiesPage::back ()
{
m_index = (unsigned int) m_selection_ptrs.size ();
}
void
InstPropertiesPage::front ()
{
m_index = 0;
}
bool
InstPropertiesPage::at_begin () const
{
return (m_index == 0);
}
bool
InstPropertiesPage::at_end () const
{
return (m_index == m_selection_ptrs.size ());
}
void
InstPropertiesPage::operator-- ()
{
--m_index;
}
void
InstPropertiesPage::operator++ ()
{
++m_index;
}
void
InstPropertiesPage::leave ()
{
mp_service->clear_highlights ();
}
void
InstPropertiesPage::update ()
{
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
tl_assert (pos->is_cell_inst ());
mp_service->highlight (m_index);
m_enable_cb_callback = false;
dbu_cb->setChecked (mp_service->view ()->dbu_coordinates ());
abs_cb->setChecked (mp_service->view ()->absolute_coordinates ());
m_enable_cb_callback = true;
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
double dbu = cv->layout ().dbu ();
std::string info (tl::to_string (QObject::tr ("Cell ")));
info += cv->layout ().cell_name (pos->cell_index ());
info_lbl->setText (tl::to_qstring (info));
db::Layout *def_layout = &cv->layout ();
db::cell_index_type def_cell_index = pos->back ().inst_ptr.cell_index ();
const db::Cell &def_cell = def_layout->cell (def_cell_index);
lib_cbx->blockSignals (true);
std::pair<db::Library *, db::cell_index_type> dl = def_layout->defining_library (def_cell_index);
lib_cbx->set_technology_filter (cv->tech_name (), true);
lib_cbx->set_current_library (dl.first);
if (dl.first) {
def_layout = &dl.first->layout ();
def_cell_index = dl.second;
}
lib_cbx->blockSignals (false);
std::pair<bool, db::pcell_id_type> pci = def_layout->is_pcell_instance (def_cell_index);
cell_name_le->blockSignals (true);
if (pci.first && def_layout->pcell_declaration (pci.second)) {
cell_name_le->setText (tl::to_qstring (def_layout->pcell_header (pci.second)->get_name ()));
} else {
cell_name_le->setText (tl::to_qstring (def_layout->cell_name (def_cell_index)));
}
cell_name_le->blockSignals (false);
db::Vector rowv, columnv;
unsigned long rows, columns;
db::ICplxTrans gt;
if (abs_cb->isChecked ()) {
gt = pos->trans ();
}
bool du = dbu_cb->isChecked ();
db::Box cell_bbox = def_cell.bbox ();
cw_le->setText (tl::to_qstring (coord_to_string (cell_bbox.width (), dbu, du)));
ch_le->setText (tl::to_qstring (coord_to_string (cell_bbox.height (), dbu, du)));
db::Trans t (pos->back ().inst_ptr.front ());
if (pos->back ().inst_ptr.is_regular_array (rowv, columnv, rows, columns)) {
array_grp->setChecked (true);
rows_le->setText (tl::to_qstring (tl::to_string (rows)));
columns_le->setText (tl::to_qstring (tl::to_string (columns)));
row_x_le->setText (tl::to_qstring (coord_to_string ((gt * rowv).x (), dbu, du)));
row_y_le->setText (tl::to_qstring (coord_to_string ((gt * rowv).y (), dbu, du)));
column_x_le->setText (tl::to_qstring (coord_to_string ((gt * columnv).x (), dbu, du)));
column_y_le->setText (tl::to_qstring (coord_to_string ((gt * columnv).y (), dbu, du)));
if (! pos->back ().array_inst.at_end ()) {
// show the array indices
long row = pos->back ().array_inst.index_a ();
long column = pos->back ().array_inst.index_b ();
inst_lbl->setText (tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("This is instance [%ld,%ld] of array with")), row, column)));
}
} else {
array_grp->setChecked (false);
rows_le->setText (QString ());
columns_le->setText (QString ());
row_x_le->setText (QString ());
row_y_le->setText (QString ());
column_x_le->setText (QString ());
column_y_le->setText (QString ());
inst_lbl->setText (QString ());
}
pos_x_le->setText (tl::to_qstring (coord_to_string ((gt * db::ICplxTrans (t)).disp ().x (), dbu, du)));
pos_y_le->setText (tl::to_qstring (coord_to_string ((gt * db::ICplxTrans (t)).disp ().y (), dbu, du)));
angle_le->setText (tl::to_qstring (tl::to_string (pos->back ().inst_ptr.complex_trans ().angle ())));
mirror_cbx->setChecked (t.is_mirror ());
mag_le->setText (tl::to_qstring (tl::to_string (pos->back ().inst_ptr.complex_trans ().mag ())));
m_prop_id = pos->back ().inst_ptr.prop_id ();
update_pcell_parameters ();
}
void
InstPropertiesPage::show_cell ()
{
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
lay::CellView::unspecific_cell_path_type path (mp_service->view ()->cellview (pos->cv_index ()).combined_unspecific_path ());
for (lay::ObjectInstPath::iterator p = pos->begin (); p != pos->end (); ++p) {
path.push_back (p->inst_ptr.cell_index ());
}
mp_service->view ()->set_current_cell_path (pos->cv_index (), path);
}
void
InstPropertiesPage::show_inst ()
{
InstantiationForm inst_form (this);
inst_form.show (mp_service->view (), *m_selection_ptrs [m_index]);
}
bool
InstPropertiesPage::readonly ()
{
return ! mp_service->view ()->is_editable ();
}
ChangeApplicator *
InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & /*inst*/, double dbu)
{
bool has_error = false;
bool has_pcell_error = false;
std::auto_ptr<CombinedChangeApplicator> appl (new CombinedChangeApplicator ());
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
bool du = dbu_cb->isChecked ();
db::Layout *layout;
db::Library *lib = lib_cbx->current_library ();
// find the layout the cell has to be looked up: that is either the layout of the current instance or
// the library selected
if (lib) {
layout = &lib->layout ();
} else {
layout = &cv->layout ();
}
try {
std::pair<bool, db::cell_index_type> ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
std::pair<bool, db::pcell_id_type> pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
if (! ci.first && ! pci.first) {
throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ());
}
lay::indicate_error (cell_name_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (cell_name_le, &ex);
has_error = true;
}
try {
std::pair<bool, db::cell_index_type> ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
std::pair<bool, db::pcell_id_type> pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
tl_assert (ci.first || pci.first);
db::cell_index_type inst_cell_index = 0;
// instantiate the PCell
if (pci.first) {
tl_assert (mp_pcell_parameters != 0);
tl_assert (layout->pcell_declaration (pci.second) == mp_pcell_parameters->pcell_decl ());
inst_cell_index = layout->get_pcell_variant (pci.second, mp_pcell_parameters->get_parameters ());
} else {
inst_cell_index = ci.second;
}
// reference the library
if (lib) {
layout = & cv->layout ();
inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index);
}
if (inst_cell_index != pos->back ().inst_ptr.cell_index ()) {
appl->add (new ChangeTargetCellApplicator (inst_cell_index));
}
} catch (tl::Exception &) {
has_pcell_error = true;
}
double x = 0.0, y = 0.0;
try {
tl::from_string (tl::to_string (pos_x_le->text ()), x);
lay::indicate_error (pos_x_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (pos_x_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (pos_y_le->text ()), y);
lay::indicate_error (pos_y_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (pos_y_le, &ex);
has_error = true;
}
db::DCplxTrans t;
if (abs_cb->isChecked ()) {
t = db::DCplxTrans (pos->trans ().inverted ());
}
db::DVector disp = db::DVector (dpoint_from_dpoint (db::DPoint (x, y), dbu, du, t));
bool mirror = mirror_cbx->isChecked ();
double angle = 0.0;
try {
tl::from_string (tl::to_string (angle_le->text ()), angle);
lay::indicate_error (angle_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (angle_le, &ex);
has_error = true;
}
double mag = 0.0;
try {
tl::from_string (tl::to_string (mag_le->text ()), mag);
lay::indicate_error (mag_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (mag_le, &ex);
has_error = true;
}
angle -= (floor (angle / 360.0) + 1.0) * 360.0;
while (angle < -1e-6) {
angle += 360.0;
}
db::CellInstArray::complex_trans_type trans = pos->back ().inst_ptr.complex_trans ();
if (fabs (angle - trans.angle ()) > 1e-6 || mirror != trans.is_mirror () || fabs (mag - trans.mag ()) > 1e-6 || ! disp.equal (trans.disp () * dbu)) {
appl->add (new ChangeInstanceTransApplicator (angle, trans.angle (), mirror, trans.is_mirror (), mag, trans.mag (), disp, trans.disp () * dbu));
}
db::CellInstArray::vector_type a_org, b_org;
unsigned long na_org = 0, nb_org = 0;
bool is_array_org = pos->back ().inst_ptr.is_regular_array (a_org, b_org, na_org, nb_org);
if (array_grp->isChecked ()) {
double cx = 0.0, cy = 0.0;
double rx = 0.0, ry = 0.0;
unsigned long rows = 0, cols = 0;
try {
tl::from_string (tl::to_string (column_x_le->text ()), cx);
lay::indicate_error (column_x_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (column_x_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (column_y_le->text ()), cy);
lay::indicate_error (column_y_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (column_y_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (row_x_le->text ()), rx);
lay::indicate_error (row_x_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (row_x_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (row_y_le->text ()), ry);
lay::indicate_error (row_y_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (row_y_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (rows_le->text ()), rows);
lay::indicate_error (rows_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (rows_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (columns_le->text ()), cols);
lay::indicate_error (columns_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (columns_le, &ex);
has_error = true;
}
db::DVector rv = db::DVector (dpoint_from_dpoint (db::DPoint (rx, ry), dbu, du, t));
db::DVector cv = db::DVector (dpoint_from_dpoint (db::DPoint (cx, cy), dbu, du, t));
bool set_a = (! rv.equal (a_org * dbu) || ! is_array_org);
bool set_na = (rows != na_org || ! is_array_org);
bool set_b = (! cv.equal (b_org * dbu) || ! is_array_org);
bool set_nb = (cols != nb_org || ! is_array_org);
if (set_a || set_b || set_na || set_nb) {
appl->add (new ChangeInstanceArrayApplicator (rv, set_a, cv, set_b, rows, set_na, cols, set_nb));
}
} else if (is_array_org) {
appl->add (new InstanceRemoveArrayApplicator ());
}
if (has_error || has_pcell_error) {
throw tl::Exception (tl::to_string (tr ("At least one value and PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator")));
} else if (has_error) {
throw tl::Exception (tl::to_string (tr ("At least one value is not correct - see hightlighted entry fields")));
} else if (has_pcell_error) {
throw tl::Exception (tl::to_string (tr ("At least one PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator")));
}
return appl.release ();
}
void
InstPropertiesPage::recompute_selection_ptrs (const std::vector<lay::ObjectInstPath> &new_sel)
{
std::map<lay::ObjectInstPath, edt::Service::obj_iterator> ptrs;
for (edt::Service::obj_iterator pos = mp_service->selection ().begin (); pos != mp_service->selection ().end (); ++pos) {
ptrs.insert (std::make_pair (*pos, pos));
}
m_selection_ptrs.clear ();
for (std::vector<lay::ObjectInstPath>::const_iterator s = new_sel.begin (); s != new_sel.end (); ++s) {
std::map<lay::ObjectInstPath, edt::Service::obj_iterator>::const_iterator pm = ptrs.find (*s);
tl_assert (pm != ptrs.end ());
m_selection_ptrs.push_back (pm->second);
}
}
void
InstPropertiesPage::do_apply (bool current_only, bool relative)
{
lay::LayerState layer_state = mp_service->view ()->layer_snapshot ();
unsigned int cv_index = m_selection_ptrs [m_index]->cv_index ();
std::auto_ptr<ChangeApplicator> applicator;
{
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
tl_assert (pos->is_cell_inst ());
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
db::Cell &cell = cv->layout ().cell (pos->cell_index ());
double dbu = cv->layout ().dbu ();
applicator.reset (create_applicator (cell, pos->back ().inst_ptr, dbu));
if (m_prop_id != pos->back ().inst_ptr.prop_id ()) {
applicator.reset (new CombinedChangeApplicator (applicator.release (), new ChangePropertiesApplicator (m_prop_id)));
}
}
if (! applicator.get ()) {
return;
}
bool relative_mode = false;
if (! current_only && applicator->supports_relative_mode ()) {
relative_mode = relative;
}
// Note: using the apply-all scheme for applying a single change may look like overhead.
// But it avoids issues with duplicate selections of the same instance which may happen when
// an instance is selected multiple times through different hierarchy branches.
db::Instance current = m_selection_ptrs [m_index]->back ().inst_ptr;
std::vector<lay::ObjectInstPath> new_sel;
new_sel.reserve (m_selection_ptrs.size ());
for (std::vector<edt::Service::obj_iterator>::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) {
new_sel.push_back (**p);
}
std::map<db::Instance, db::Instance> insts_seen;
bool update_required = false;
try {
for (std::vector<edt::Service::obj_iterator>::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) {
edt::Service::obj_iterator pos = *p;
// only update objects from the same layout - this is not practical limitation but saves a lot of effort for
// managing different property id's etc.
if (pos->cv_index () != cv_index) {
continue;
}
tl_assert (pos->is_cell_inst ());
db::Instance new_inst = pos->back ().inst_ptr;
// Don't apply the same change twice
std::map<db::Instance, db::Instance>::const_iterator i = insts_seen.find (pos->back ().inst_ptr);
if (i == insts_seen.end ()) {
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
db::Cell &cell = cv->layout ().cell (pos->cell_index ());
double dbu = cv->layout ().dbu ();
if (!current_only || pos->back ().inst_ptr == current) {
new_inst = applicator->do_apply_inst (cell, pos->back ().inst_ptr, dbu, relative_mode);
}
insts_seen.insert (std::make_pair (pos->back ().inst_ptr, new_inst));
} else {
new_inst = i->second;
}
if (new_inst != pos->back ().inst_ptr) {
size_t index = p - m_selection_ptrs.begin ();
// change selection to new instance
new_sel[index].back ().inst_ptr = new_inst;
mp_service->select (*pos, lay::Editable::Reset);
mp_service->select (new_sel[index], lay::Editable::Add);
update_required = true;
}
}
if (update_required) {
recompute_selection_ptrs (new_sel);
}
} catch (...) {
recompute_selection_ptrs (new_sel);
throw;
}
mp_service->view ()->add_new_layers (layer_state);
// remove superfluous proxies
for (unsigned int i = 0; i < mp_service->view ()->cellviews (); ++i) {
mp_service->view ()->cellview (i)->layout ().cleanup ();
}
update ();
}
void
InstPropertiesPage::apply ()
{
do_apply (true, false);
}
bool
InstPropertiesPage::can_apply_to_all () const
{
return m_selection_ptrs.size () > 1;
}
void
InstPropertiesPage::apply_to_all (bool relative)
{
do_apply (false, relative);
}
void
InstPropertiesPage::update_pcell_parameters ()
{
db::Layout *layout;
// find the layout the cell has to be looked up: that is either the layout of the current instance or
// the library selected
if (lib_cbx->current_library ()) {
layout = &lib_cbx->current_library ()->layout ();
} else {
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
layout = &cv->layout ();
}
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
std::pair<bool, db::cell_index_type> cc = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
// indicate an invalid cell name
if (! pc.first && ! cc.first) {
tl::Exception ex (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ());
lay::indicate_error (cell_name_le, &ex);
} else {
lay::indicate_error (cell_name_le, 0);
}
if (pc.first && layout->pcell_declaration (pc.second)) {
const db::PCellDeclaration *pcell_decl = layout->pcell_declaration (pc.second);
std::vector<tl::Variant> parameters;
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
db::Cell &cell = cv->layout ().cell (pos->cell_index ());
std::pair<bool, db::pcell_id_type> pci = cell.is_pcell_instance (pos->back ().inst_ptr);
const db::Library *pci_lib = cv->layout ().defining_library (pos->back ().inst_ptr.cell_index ()).first;
// fetch the parameters of the current instance if it matches the selected PCell and in that
// case use the current parameters
if (pci.first) {
if (pci.second == pc.second && pci_lib == lib_cbx->current_library ()) {
// exact match: take the parameters
parameters = cell.get_pcell_parameters (pos->back ().inst_ptr);
} else {
// otherwise: update the parameter whose name matches, use default for others
parameters = pcell_decl->map_parameters (cell.get_named_pcell_parameters (pos->back ().inst_ptr));
}
}
if (mp_pcell_parameters && mp_pcell_parameters->pcell_decl () == pcell_decl) {
// For identical parameter declarations just set the new values
// TODO: formally the declaration could change - compare current declarations vs. new ones?
// Better: provide a way to install custom parameter pages ...
mp_pcell_parameters->set_parameters (parameters);
} else {
// Hint: we shall not delete the page immediately. This gives a segmentation fault in some cases.
if (mp_pcell_parameters) {
mp_pcell_parameters->hide ();
mp_pcell_parameters->deleteLater ();
}
mp_pcell_parameters = new PCellParametersPage (pcell_tab);
connect (mp_pcell_parameters, SIGNAL (edited ()), this, SIGNAL (edited ()));
mp_pcell_parameters->setup (mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters);
pcell_tab->layout ()->addWidget (mp_pcell_parameters);
}
param_tab_widget->setTabEnabled (1, true);
} else {
// Hint: we shall not delete the page immediately. This gives a segmentation fault in some cases.
if (mp_pcell_parameters) {
mp_pcell_parameters->hide ();
mp_pcell_parameters->deleteLater ();
}
mp_pcell_parameters = 0;
if (param_tab_widget->currentIndex () == 1) {
param_tab_widget->setCurrentIndex (0);
}
param_tab_widget->setTabEnabled (1, false);
}
}
}