Files
klayout/src/edt/edtInstPropertiesPage.cc
T
Matthias Koefferlein 1091ea6d5b Technology specific libraries
Libraries now carry a technology association and
only libraries associated with the current technology
can be selected.

To enforce proper update, cancel() will be used upon
change of technology. This avoids side effects when
changing the technology without notifying the library
selection widgets.

The MainWindow::cancel method has been generalized to
cover the functionality of cm_cancel without the
exception handling.
2017-04-23 19:30:55 +02:00

715 lines
23 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 "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 "tlExceptions.h"
#include "tlString.h"
#include <string>
#include <QMessageBox>
namespace edt
{
// -------------------------------------------------------------------------
// InstPropertiesPage implementation
InstPropertiesPage::InstPropertiesPage (edt::Service *service, QWidget *parent)
: lay::PropertiesPage (parent, 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 &)));
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 ()) {
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 ())));
}
update_pcell_parameters ();
}
END_PROTECTED
}
void
InstPropertiesPage::show_props ()
{
lay::UserPropertiesForm props_form (this);
props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id);
}
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 = 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);
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;
}
std::pair<bool, db::pcell_id_type> pci = def_layout->is_pcell_instance (def_cell_index);
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)));
}
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)
{
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 ();
}
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 name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ());
}
db::cell_index_type inst_cell_index = ci.second;
// 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 ());
}
// 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));
}
double x = 0.0, y = 0.0;
tl::from_string (tl::to_string (pos_x_le->text ()), x);
tl::from_string (tl::to_string (pos_y_le->text ()), y);
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;
tl::from_string (tl::to_string (angle_le->text ()), angle);
double mag = 0.0;
tl::from_string (tl::to_string (mag_le->text ()), mag);
angle -= (floor (angle / 360.0) + 1.0) * 360.0;
while (angle < -1e-6) {
angle += 360.0;
}
db::CellInstArray::complex_trans_type tr = pos->back ().inst_ptr.complex_trans ();
if (fabs (angle - tr.angle ()) > 1e-6 || mirror != tr.is_mirror () || fabs (mag - tr.mag ()) > 1e-6 || ! disp.equal (tr.disp () * dbu)) {
appl->add (new ChangeInstanceTransApplicator (angle, tr.angle (), mirror, tr.is_mirror (), mag, tr.mag (), disp, tr.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;
tl::from_string (tl::to_string (column_x_le->text ()), cx);
tl::from_string (tl::to_string (column_y_le->text ()), cy);
tl::from_string (tl::to_string (row_x_le->text ()), rx);
tl::from_string (tl::to_string (row_y_le->text ()), ry);
tl::from_string (tl::to_string (rows_le->text ()), rows);
tl::from_string (tl::to_string (columns_le->text ()), cols);
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 ());
}
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)
{
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 ()) {
static bool s_relative_mode = true;
QMessageBox mb (QMessageBox::Question,
tr ("Apply Changes To All"),
tr ("For this operation absolute or relative mode is available which affects the way parameters of the selected objects are changed:\n\n"
"In absolute mode, they will be set to the given value. In relative mode, they will be adjusted by the same amount.\n"),
QMessageBox::NoButton, this);
mb.addButton (tr ("Cancel"), QMessageBox::RejectRole);
QPushButton *absolute = mb.addButton (tr ("Absolute"), QMessageBox::NoRole);
QPushButton *relative = mb.addButton (tr ("Relative"), QMessageBox::YesRole);
mb.setDefaultButton (s_relative_mode ? relative : absolute);
mb.exec ();
if (mb.clickedButton () == absolute) {
s_relative_mode = relative_mode = false;
} else if (mb.clickedButton () == relative) {
s_relative_mode = relative_mode = true;
} else {
// Cancel pressed
return;
}
}
// 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);
}
bool
InstPropertiesPage::can_apply_to_all () const
{
return m_selection_ptrs.size () > 1;
}
void
InstPropertiesPage::apply_to_all ()
{
do_apply (false);
}
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 ());
// by the way, update the foreground color of the cell edit box as well (red, if not valid)
QPalette pl = cell_name_le->palette ();
if (! pc.first && ! cc.first) {
pl.setColor (QPalette::Text, Qt::red);
pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180));
} else {
pl.setColor (QPalette::Text, palette ().color (QPalette::Text));
pl.setColor (QPalette::Base, palette ().color (QPalette::Base));
}
cell_name_le->setPalette (pl);
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, &cv->layout (), 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);
param_tab_widget->setCurrentIndex (1);
} 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;
param_tab_widget->setCurrentIndex (0);
param_tab_widget->setTabEnabled (1, false);
}
}
}