klayout/src/edt/edtDialogs.cc

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;
}
}