mirror of https://github.com/KLayout/klayout.git
518 lines
14 KiB
C++
518 lines
14 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 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 <math.h>
|
|
|
|
#include "dbBox.h"
|
|
#include "dbLayout.h"
|
|
|
|
#include "edtDialogs.h"
|
|
#include "layObjectInstPath.h"
|
|
#include "layCellView.h"
|
|
#include "layLayoutView.h"
|
|
#include "layMarker.h"
|
|
#include "tlException.h"
|
|
#include "tlExceptions.h"
|
|
|
|
namespace edt {
|
|
|
|
// ----------------------------------------------------------------------
|
|
// edt::InstantiationForm implementation
|
|
|
|
InstantiationForm::InstantiationForm (QWidget *parent)
|
|
: QDialog (parent), mp_view (0), mp_path (0), mp_marker (0), m_enable_cb_callbacks (false)
|
|
{
|
|
setObjectName (QString::fromUtf8 ("instantiation_form"));
|
|
|
|
Ui::InstantiationForm::setupUi (this);
|
|
|
|
connect (list, SIGNAL (itemDoubleClicked (QListWidgetItem *)), this, SLOT (double_clicked (QListWidgetItem *)));
|
|
connect (dbu_cb, SIGNAL (toggled (bool)), this, SLOT (display_mode_changed (bool)));
|
|
connect (abs_cb, SIGNAL (toggled (bool)), this, SLOT (display_mode_changed (bool)));
|
|
}
|
|
|
|
InstantiationForm::~InstantiationForm ()
|
|
{
|
|
if (mp_marker) {
|
|
delete mp_marker;
|
|
mp_marker = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
InstantiationForm::display_mode_changed (bool)
|
|
{
|
|
if (! m_enable_cb_callbacks) {
|
|
return;
|
|
}
|
|
|
|
mp_view->dbu_coordinates (dbu_cb->isChecked ());
|
|
mp_view->absolute_coordinates (abs_cb->isChecked ());
|
|
|
|
update ();
|
|
}
|
|
|
|
void
|
|
InstantiationForm::double_clicked (QListWidgetItem *item)
|
|
{
|
|
int row = list->row (item);
|
|
if (row < 0) {
|
|
return;
|
|
}
|
|
|
|
lay::CellView::unspecific_cell_path_type path (mp_view->cellview (mp_path->cv_index ()).combined_unspecific_path ());
|
|
int nrow = 0;
|
|
for (lay::ObjectInstPath::iterator p = mp_path->begin (); p != mp_path->end () && nrow < row; ++p, ++nrow) {
|
|
path.push_back (p->inst_ptr.cell_index ());
|
|
}
|
|
|
|
mp_view->set_current_cell_path (mp_path->cv_index (), path);
|
|
|
|
if (! mp_marker) {
|
|
mp_marker = new lay::Marker (mp_view, mp_path->cv_index ());
|
|
}
|
|
|
|
const db::Layout &layout = mp_view->cellview (mp_path->cv_index ())->layout ();
|
|
db::Box box = layout.cell (row == 0 ? mp_path->topcell () : path.back ()).bbox ();
|
|
|
|
// TODO: this does not consider global transformation and variants of this
|
|
db::ICplxTrans abs_trans;
|
|
nrow = 0;
|
|
for (lay::ObjectInstPath::iterator p = mp_path->begin (); p != mp_path->end () && nrow < row; ++p, ++nrow) {
|
|
abs_trans = abs_trans * (p->inst_ptr.cell_inst ().complex_trans (*(p->array_inst)));
|
|
}
|
|
|
|
mp_marker->set (box, abs_trans, mp_view->cv_transform_variants (mp_path->cv_index ()));
|
|
}
|
|
|
|
void
|
|
InstantiationForm::show (lay::LayoutView *view, const lay::ObjectInstPath &path)
|
|
{
|
|
mp_view = view;
|
|
mp_path = &path;
|
|
|
|
m_enable_cb_callbacks = false;
|
|
dbu_cb->setChecked (mp_view->dbu_coordinates ());
|
|
abs_cb->setChecked (mp_view->absolute_coordinates ());
|
|
m_enable_cb_callbacks = true;
|
|
|
|
update ();
|
|
exec ();
|
|
|
|
mp_view = 0;
|
|
mp_path = 0;
|
|
}
|
|
|
|
void
|
|
InstantiationForm::update ()
|
|
{
|
|
bool dbu_coord = dbu_cb->isChecked ();
|
|
bool abs_coord = abs_cb->isChecked ();
|
|
|
|
const lay::CellView &cv = mp_view->cellview (mp_path->cv_index ());
|
|
double dbu = cv->layout ().dbu ();
|
|
|
|
layout_le->setText (tl::to_qstring (cv->name ()));
|
|
|
|
list->clear ();
|
|
|
|
list->addItem (tl::to_qstring (cv->layout ().cell_name (cv.ctx_cell_index ())));
|
|
db::CplxTrans abs_trans;
|
|
|
|
// first include the context path of the cellview in order to tell the path within the cell shown
|
|
for (lay::CellView::specific_cell_path_type::const_iterator p = cv.specific_path ().begin (); p != cv.specific_path ().end (); ++p) {
|
|
|
|
// build the instance information from the path
|
|
|
|
db::CplxTrans trans (p->inst_ptr.cell_inst ().complex_trans (*(p->array_inst)));
|
|
abs_trans = abs_trans * trans;
|
|
|
|
if (abs_coord) {
|
|
trans = abs_trans;
|
|
}
|
|
|
|
std::string line;
|
|
line += cv->layout ().cell_name (p->inst_ptr.cell_index ());
|
|
line += "\tat ";
|
|
line += trans.to_string (true /*lazy*/, dbu_coord ? 0.0 : dbu);
|
|
|
|
list->addItem (tl::to_qstring (line));
|
|
|
|
}
|
|
|
|
// then, add the actual path to the object within the target cell
|
|
for (lay::ObjectInstPath::iterator p = mp_path->begin (); p != mp_path->end (); ++p) {
|
|
|
|
// build the instance information from the path
|
|
|
|
db::CplxTrans trans (p->inst_ptr.cell_inst ().complex_trans (*(p->array_inst)));
|
|
abs_trans = abs_trans * trans;
|
|
|
|
if (abs_coord) {
|
|
trans = abs_trans;
|
|
}
|
|
|
|
std::string line;
|
|
line += cv->layout ().cell_name (p->inst_ptr.cell_index ());
|
|
line += "\tat ";
|
|
line += trans.to_string (true /*lazy*/, dbu_coord ? 0.0 : dbu);
|
|
|
|
list->addItem (tl::to_qstring (line));
|
|
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// edt::CopyModeDialogForm implementation
|
|
|
|
CopyModeDialog::CopyModeDialog (QWidget *parent)
|
|
: QDialog (parent)
|
|
{
|
|
setObjectName (QString::fromUtf8 ("copy_mode_dialog"));
|
|
|
|
Ui::CopyModeDialog::setupUi (this);
|
|
}
|
|
|
|
CopyModeDialog::~CopyModeDialog ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
bool
|
|
CopyModeDialog::exec_dialog (unsigned int &mode)
|
|
{
|
|
if (mode == 0) {
|
|
shallow_rb->setChecked (true);
|
|
}
|
|
if (QDialog::exec ()) {
|
|
if (shallow_rb->isChecked ()) {
|
|
mode = 0;
|
|
} else {
|
|
mode = 1;
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// ChangeLayerOptionsDialog implementation
|
|
|
|
ChangeLayerOptionsDialog::ChangeLayerOptionsDialog (QWidget *parent)
|
|
: QDialog (parent)
|
|
{
|
|
setObjectName (QString::fromUtf8 ("change_layer_options_dialog"));
|
|
|
|
Ui::ChangeLayerOptionsDialog::setupUi (this);
|
|
}
|
|
|
|
ChangeLayerOptionsDialog::~ChangeLayerOptionsDialog ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
bool
|
|
ChangeLayerOptionsDialog::exec_dialog (lay::LayoutView *view, int cv_index, unsigned int &new_layer)
|
|
{
|
|
std::vector <std::pair <db::LayerProperties, unsigned int> > ll;
|
|
|
|
const db::Layout &layout = view->cellview (cv_index)->layout ();
|
|
for (unsigned int i = 0; i < layout.layers (); ++i) {
|
|
if (layout.is_valid_layer (i)) {
|
|
ll.push_back (std::make_pair (layout.get_properties (i), i));
|
|
}
|
|
}
|
|
|
|
std::sort (ll.begin (), ll.end ());
|
|
|
|
target_cbx->clear ();
|
|
int initial_sel = -1;
|
|
int i = 0;
|
|
for (std::vector <std::pair <db::LayerProperties, unsigned int> >::const_iterator lp = ll.begin (); lp != ll.end (); ++lp, ++i) {
|
|
if (lp->second == new_layer) {
|
|
initial_sel = i;
|
|
}
|
|
target_cbx->addItem (tl::to_qstring (lay::ParsedLayerSource (lp->first, cv_index).to_string ()));
|
|
}
|
|
target_cbx->setCurrentIndex (initial_sel);
|
|
|
|
if (QDialog::exec () && target_cbx->currentIndex () >= 0 && target_cbx->currentIndex () < int (ll.size ())) {
|
|
new_layer = ll [target_cbx->currentIndex ()].second;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// AlignOptionsDialog implementation
|
|
|
|
AlignOptionsDialog::AlignOptionsDialog (QWidget *parent)
|
|
: QDialog (parent)
|
|
{
|
|
setObjectName (QString::fromUtf8 ("change_layer_options_dialog"));
|
|
|
|
Ui::AlignOptionsDialog::setupUi (this);
|
|
}
|
|
|
|
AlignOptionsDialog::~AlignOptionsDialog ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
bool
|
|
AlignOptionsDialog::exec_dialog (lay::LayoutView * /*view*/, int &hmode, int &vmode, bool &visible_layers)
|
|
{
|
|
QRadioButton *hmode_buttons [] = { this->h_none_rb, this->h_left_rb, this->h_center_rb, this->h_right_rb };
|
|
QRadioButton *vmode_buttons [] = { this->v_none_rb, this->v_top_rb, this->v_center_rb, this->v_bottom_rb };
|
|
QRadioButton *layers_buttons [] = { this->all_layers_rb, this->visible_layers_rb };
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
hmode_buttons [i]->setChecked (hmode == i);
|
|
}
|
|
for (int i = 0; i < 4; ++i) {
|
|
vmode_buttons [i]->setChecked (vmode == i);
|
|
}
|
|
for (int i = 0; i < 2; ++i) {
|
|
layers_buttons [i]->setChecked (int (visible_layers) == i);
|
|
}
|
|
|
|
if (QDialog::exec ()) {
|
|
|
|
hmode = -1;
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (hmode_buttons [i]->isChecked ()) {
|
|
hmode = i;
|
|
}
|
|
}
|
|
|
|
vmode = -1;
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (vmode_buttons [i]->isChecked ()) {
|
|
vmode = i;
|
|
}
|
|
}
|
|
|
|
visible_layers = false;
|
|
for (int i = 0; i < 2; ++i) {
|
|
if (layers_buttons [i]->isChecked ()) {
|
|
visible_layers = (i != 0);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// MakeCellOptionsDialog implementation
|
|
|
|
MakeCellOptionsDialog::MakeCellOptionsDialog (QWidget *parent)
|
|
: QDialog (parent)
|
|
{
|
|
setupUi (this);
|
|
}
|
|
|
|
bool
|
|
MakeCellOptionsDialog::exec_dialog (const db::Layout &layout, std::string &name)
|
|
{
|
|
do {
|
|
BEGIN_PROTECTED
|
|
if (QDialog::exec ()) {
|
|
name = tl::to_string (cell_name_le->text ());
|
|
if (name.empty ()) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Cell name must not be empty")));
|
|
} else if (layout.cell_by_name (name.c_str ()).first) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("A cell with that name already exists: ")) + name);
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
END_PROTECTED
|
|
} while (true);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// RoundCornerOptionsDialog implementation
|
|
|
|
RoundCornerOptionsDialog::RoundCornerOptionsDialog (QWidget *parent)
|
|
: QDialog (parent), mp_layout (0)
|
|
{
|
|
setObjectName (QString::fromUtf8 ("round_corners_options_dialog"));
|
|
|
|
Ui::RoundCornerOptionsDialog::setupUi (this);
|
|
}
|
|
|
|
RoundCornerOptionsDialog::~RoundCornerOptionsDialog ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
bool
|
|
RoundCornerOptionsDialog::exec_dialog (const db::Layout &layout, double &router, double &rinner, unsigned int &npoints)
|
|
{
|
|
mp_layout = &layout;
|
|
|
|
router_le->setText (tl::to_qstring (tl::to_string (router)));
|
|
if (fabs (router - rinner) < 1e-6) {
|
|
rinner_le->setText (QString ());
|
|
} else {
|
|
rinner_le->setText (tl::to_qstring (tl::to_string (rinner)));
|
|
}
|
|
points_le->setText (tl::to_qstring (tl::to_string (npoints)));
|
|
|
|
if (QDialog::exec ()) {
|
|
|
|
tl::from_string (tl::to_string (router_le->text ()), router);
|
|
if (rinner_le->text ().isEmpty ()) {
|
|
rinner = router;
|
|
} else {
|
|
tl::from_string (tl::to_string (rinner_le->text ()), rinner);
|
|
}
|
|
tl::from_string (tl::to_string (points_le->text ()), npoints);
|
|
|
|
mp_layout = 0;
|
|
return true;
|
|
|
|
} else {
|
|
mp_layout = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
RoundCornerOptionsDialog::accept ()
|
|
{
|
|
BEGIN_PROTECTED;
|
|
|
|
double rhull = 0.0, rholes = 0.0;
|
|
unsigned int npoints = 0;
|
|
|
|
tl::from_string (tl::to_string (router_le->text ()), rhull);
|
|
if (rinner_le->text ().isEmpty ()) {
|
|
rholes = rhull;
|
|
} else {
|
|
tl::from_string (tl::to_string (rinner_le->text ()), rholes);
|
|
}
|
|
tl::from_string (tl::to_string (points_le->text ()), npoints);
|
|
|
|
const unsigned int min_points = 16;
|
|
const double seg_thr = 10.0; // in DBU
|
|
|
|
if (npoints < min_points) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Number of points is too small (must be %d at least)")), min_points);
|
|
}
|
|
|
|
double dbu = mp_layout->dbu ();
|
|
|
|
if ((rholes > 0.0 && dbu * seg_thr > (rholes * M_PI * 2.0) / double (npoints)) ||
|
|
(rhull > 0.0 && dbu * seg_thr > (rhull * M_PI * 2.0) / double (npoints))) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Number of points is too large (one segment must be larger than %g database units)")), seg_thr);
|
|
}
|
|
|
|
if (fabs (rholes - 2.0 * dbu * floor (rholes * 0.5 / dbu + 0.5)) > 1e-6 ||
|
|
fabs (rhull - 2.0 * dbu * floor (rhull * 0.5 / dbu + 0.5)) > 1e-6) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Radius must be a even multiple of the database unit")));
|
|
}
|
|
|
|
QDialog::accept ();
|
|
|
|
END_PROTECTED;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// MakeArrayOptionsDialog implementation
|
|
|
|
MakeArrayOptionsDialog::MakeArrayOptionsDialog (QWidget *parent)
|
|
: QDialog (parent)
|
|
{
|
|
setupUi (this);
|
|
}
|
|
|
|
bool
|
|
MakeArrayOptionsDialog::exec_dialog (db::DVector &a, unsigned int &na, db::DVector &b, unsigned int &nb)
|
|
{
|
|
rows_le->setText (tl::to_qstring (tl::to_string (na)));
|
|
columns_le->setText (tl::to_qstring (tl::to_string (nb)));
|
|
row_x_le->setText (tl::to_qstring (tl::micron_to_string (a.x ())));
|
|
row_y_le->setText (tl::to_qstring (tl::micron_to_string (a.y ())));
|
|
column_x_le->setText (tl::to_qstring (tl::micron_to_string (b.x ())));
|
|
column_y_le->setText (tl::to_qstring (tl::micron_to_string (b.y ())));
|
|
|
|
if (QDialog::exec ()) {
|
|
|
|
double bx = 0.0, by = 0.0;
|
|
double ax = 0.0, ay = 0.0;
|
|
|
|
tl::from_string (tl::to_string (column_x_le->text ()), bx);
|
|
tl::from_string (tl::to_string (column_y_le->text ()), by);
|
|
tl::from_string (tl::to_string (columns_le->text ()), nb);
|
|
tl::from_string (tl::to_string (row_x_le->text ()), ax);
|
|
tl::from_string (tl::to_string (row_y_le->text ()), ay);
|
|
tl::from_string (tl::to_string (rows_le->text ()), na);
|
|
|
|
a = db::DVector (ax, ay);
|
|
b = db::DVector (bx, by);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
MakeArrayOptionsDialog::accept ()
|
|
{
|
|
BEGIN_PROTECTED;
|
|
|
|
double bx = 0.0, by = 0.0;
|
|
double ax = 0.0, ay = 0.0;
|
|
int na, nb;
|
|
|
|
tl::from_string (tl::to_string (column_x_le->text ()), bx);
|
|
tl::from_string (tl::to_string (column_y_le->text ()), by);
|
|
tl::from_string (tl::to_string (columns_le->text ()), nb);
|
|
tl::from_string (tl::to_string (row_x_le->text ()), ax);
|
|
tl::from_string (tl::to_string (row_y_le->text ()), ay);
|
|
tl::from_string (tl::to_string (rows_le->text ()), na);
|
|
|
|
if (na < 1 || nb < 1) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Invalid row or column count (must be larger or equal one)")));
|
|
}
|
|
|
|
QDialog::accept ();
|
|
|
|
END_PROTECTED;
|
|
}
|
|
|
|
}
|
|
|