klayout/src/lay/laySearchReplaceDialog.cc

2057 lines
60 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 "laySearchReplaceDialog.h"
#include "laySearchReplacePropertiesWidgets.h"
#include "laySearchReplaceConfigPage.h"
#include "layConfigurationDialog.h"
#include "layMainWindow.h"
#include "tlExceptions.h"
#include "layMarker.h"
#include "layFileDialog.h"
#include "layQtTools.h"
#include "dbLayoutQuery.h"
#include "dbLayoutUtils.h"
#include "tlLog.h"
#include "tlProgress.h"
#include "tlTimer.h"
#include <QInputDialog>
#include <QHeaderView>
#include <fstream>
namespace lay
{
// --------------------------------------------------------------------------------
// SearchReplaceResults implementation
SearchReplaceResults::SearchReplaceResults ()
: m_data_columns (1), m_last_column_count (0), m_has_more (false)
{
// .. nothing yet ..
}
void
SearchReplaceResults::has_more (bool hm)
{
m_has_more = hm;
}
void
SearchReplaceResults::clear ()
{
m_data_result.clear ();
m_shape_result.clear ();
m_inst_result.clear ();
m_cell_result.clear ();
m_data_columns = 1;
m_has_more = false;
}
void
SearchReplaceResults::push_back (const tl::Variant &v)
{
m_data_result.push_back (v);
if (v.is_list ()) {
m_data_columns = std::max (v.get_list ().size (), m_data_columns);
}
}
void
SearchReplaceResults::push_back (const QueryShapeResult &v)
{
m_shape_result.push_back (v);
}
void
SearchReplaceResults::push_back (const QueryInstResult &v)
{
m_inst_result.push_back (v);
}
void
SearchReplaceResults::push_back (const QueryCellResult &v)
{
m_cell_result.push_back (v);
}
void
SearchReplaceResults::begin_changes (const db::Layout *layout)
{
#if QT_VERSION >= 0x040600
beginResetModel ();
#endif
// In order to be independent from the layout object we save the mapping tables here
m_lp_map.clear ();
m_cellname_map.clear ();
if (layout) {
for (db::cell_index_type ci = 0; ci < layout->cells (); ++ci) {
if (layout->is_valid_cell_index (ci)) {
m_cellname_map.insert (std::make_pair (ci, std::string (layout->cell_name (ci))));
}
}
for (db::Layout::layer_iterator l = layout->begin_layers (); l != layout->end_layers (); ++l) {
m_lp_map.insert (std::make_pair ((*l).first, *(*l).second));
}
}
}
void
SearchReplaceResults::end_changes ()
{
#if QT_VERSION >= 0x040600
endResetModel ();
#else
reset ();
#endif
}
size_t
SearchReplaceResults::size () const
{
return std::max (std::max (m_cell_result.size (), m_data_result.size ()), std::max (m_shape_result.size (), m_inst_result.size ())) + (m_has_more ? 1 : 0);
}
int
SearchReplaceResults::columnCount (const QModelIndex & /*parent*/) const
{
// Note: keep last column count for empty model to avoid resize events for the header
if (! m_data_result.empty ()) {
m_last_column_count = int (m_data_columns);
} else if (! m_shape_result.empty ()) {
m_last_column_count = 5;
} else if (! m_inst_result.empty ()) {
m_last_column_count = 4;
} else if (! m_cell_result.empty ()) {
m_last_column_count = 2;
}
return m_last_column_count;
}
QVariant
SearchReplaceResults::headerData (int section, Qt::Orientation /*orientation*/, int role) const
{
if (role == Qt::DisplayRole) {
if (! m_data_result.empty ()) {
if (section == 0) {
return QVariant (QObject::tr ("Value"));
} else {
return QVariant (QString ());
}
} else if (! m_shape_result.empty ()) {
if (section == 0) {
return QVariant (QObject::tr ("Shape"));
} else if (section == 1) {
return QVariant (QObject::tr ("Layer"));
} else if (section == 2) {
return QVariant (QObject::tr ("Cell"));
} else if (section == 3) {
return QVariant (QObject::tr ("As Seen in Top"));
} else if (section == 4) {
return QVariant (QObject::tr ("Top Cell"));
}
} else if (! m_inst_result.empty ()) {
if (section == 0) {
return QVariant (QObject::tr ("Instance"));
} else if (section == 1) {
return QVariant (QObject::tr ("Parent Cell"));
} else if (section == 2) {
return QVariant (QObject::tr ("As Seen in Top"));
} else if (section == 3) {
return QVariant (QObject::tr ("Top Cell"));
}
} else if (! m_cell_result.empty ()) {
if (section == 0) {
return QVariant (QObject::tr ("Cell"));
} else if (section == 1) {
return QVariant (QObject::tr ("Parent Cell"));
}
}
}
return QVariant ();
}
static std::string instance_to_string (const db::Instance &inst, const db::ICplxTrans &t = db::ICplxTrans ())
{
if (inst.is_null ()) {
return std::string ();
}
db::CellInstArray ci = inst.cell_inst ();
std::string r;
double dbu = 1.0;
if (inst.instances () && inst.instances ()->cell () && inst.instances ()->cell ()->layout ()) {
r = inst.instances ()->cell ()->layout ()->cell (ci.object ().cell_index ()).get_qualified_name ();
dbu = inst.instances ()->cell ()->layout ()->dbu ();
}
r += " " + (db::CplxTrans (dbu) * t * ci.complex_trans () * db::CplxTrans (1.0 / dbu)).to_string ();
db::Vector a, b;
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
r += " array=(" + (db::CplxTrans (dbu) * t * a).to_string () + "," + (db::CplxTrans (dbu) * t * b).to_string () + " " + tl::to_string (amax) + "x" + tl::to_string (bmax) + ")";
}
return r;
}
static std::string shape_to_string (const db::Shape &shape, const db::ICplxTrans &t = db::ICplxTrans ())
{
double dbu = 1.0;
if (shape.shapes () && shape.shapes ()->cell () && shape.shapes ()->cell ()->layout ()) {
dbu = shape.shapes ()->cell ()->layout ()->dbu ();
}
if (shape.is_text ()) {
db::Text text;
shape.text (text);
return "text " + text.transformed (db::CplxTrans (dbu) * t).to_string ();
} else if (shape.is_polygon ()) {
db::Polygon polygon;
shape.polygon (polygon);
return "polygon " + polygon.transformed (db::CplxTrans (dbu) * t).to_string ();
} else if (shape.is_path ()) {
db::Path path;
shape.path (path);
return "path " + path.transformed (db::CplxTrans (dbu) * t).to_string ();
} else if (shape.is_box ()) {
db::Box box;
shape.box (box);
if (t.is_ortho ()) {
return "box " + box.transformed (db::CplxTrans (dbu) * t).to_string ();
} else {
return "polygon " + db::Polygon (box).transformed (db::CplxTrans (dbu) * t).to_string ();
}
} else {
return std::string ();
}
}
QVariant
SearchReplaceResults::data (const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole) {
if (index.row () < int (m_data_result.size ())) {
const tl::Variant &v = m_data_result [index.row ()];
if (index.column () == 0 && ! v.is_list ()) {
return QVariant (tl::to_qstring (v.to_string ()));
} else if (index.column () < int (v.get_list ().size ())) {
return QVariant (tl::to_qstring (v.get_list () [index.column ()].to_string ()));
}
} else if (index.row () < int (m_shape_result.size ())) {
const QueryShapeResult &result = m_shape_result [index.row ()];
if (index.column () == 0) {
return QVariant (tl::to_qstring (shape_to_string (result.shape)));
} else if (index.column () == 1) {
unsigned int layer = result.layer_index;
std::map<unsigned int, db::LayerProperties>::const_iterator lm = m_lp_map.find (layer);
if (lm != m_lp_map.end ()) {
return QVariant (tl::to_qstring (lm->second.to_string ()));
}
} else if (index.column () == 2) {
db::cell_index_type cell_index = result.cell_index;
std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.find (cell_index);
if (cn != m_cellname_map.end ()) {
return QVariant (tl::to_qstring (cn->second));
}
} else if (index.column () == 3) {
if (result.trans != db::ICplxTrans ()) {
return QVariant (tl::to_qstring (shape_to_string (result.shape, result.trans)));
}
} else if (index.column () == 4) {
if (result.initial_cell_index != result.cell_index) {
db::cell_index_type cell_index = result.initial_cell_index;
std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.find (cell_index);
if (cn != m_cellname_map.end ()) {
return QVariant (tl::to_qstring (cn->second));
}
}
}
} else if (index.row () < int (m_inst_result.size ())) {
const QueryInstResult &result = m_inst_result [index.row ()];
if (index.column () == 0) {
return QVariant (tl::to_qstring (instance_to_string (result.inst)));
} else if (index.column () == 1) {
db::cell_index_type cell_index = result.cell_index;
std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.find (cell_index);
if (cn != m_cellname_map.end ()) {
return QVariant (tl::to_qstring (cn->second));
}
} else if (index.column () == 2) {
if (result.trans != db::ICplxTrans ()) {
return QVariant (tl::to_qstring (instance_to_string (result.inst, result.trans)));
}
} else if (index.column () == 3) {
if (result.initial_cell_index != result.cell_index) {
db::cell_index_type cell_index = result.initial_cell_index;
std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.find (cell_index);
if (cn != m_cellname_map.end ()) {
return QVariant (tl::to_qstring (cn->second));
}
}
}
} else if (index.row () < int (m_cell_result.size ())) {
if (index.column () == 0 || index.column () == 1) {
db::cell_index_type cell_index;
if (index.column () == 0) {
cell_index = m_cell_result [index.row ()].cell_index;
} else {
cell_index = m_cell_result [index.row ()].parent_cell_index;
}
std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.find (cell_index);
if (cn != m_cellname_map.end ()) {
return QVariant (tl::to_qstring (cn->second));
}
}
} else if (m_has_more) {
if (index.column () == 0) {
return QVariant (tl::to_qstring ("..."));
}
}
}
return QVariant ();
}
Qt::ItemFlags
SearchReplaceResults::flags (const QModelIndex & /*index*/) const
{
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
bool
SearchReplaceResults::hasChildren (const QModelIndex &parent) const
{
return ! parent.isValid ();
}
bool
SearchReplaceResults::hasIndex (int row, int /*column*/, const QModelIndex &parent) const
{
return ! parent.isValid () && row < int (size ());
}
QModelIndex
SearchReplaceResults::index (int row, int column, const QModelIndex &parent) const
{
if (! parent.isValid ()) {
return createIndex (row, column);
} else {
return QModelIndex ();
}
}
QModelIndex
SearchReplaceResults::parent (const QModelIndex & /*index*/) const
{
return QModelIndex ();
}
int
SearchReplaceResults::rowCount (const QModelIndex &parent) const
{
return parent.isValid () ? 0 : int (size ());
}
static std::string
escape_csv (const std::string &s)
{
if (s.find (",") != std::string::npos) {
std::string r = "\"";
for (const char *c = s.c_str (); *c; ++c) {
if (*c == '\"') {
r += "\"\"";
} else {
r += *c;
}
}
r += "\"";
return r;
} else {
return s;
}
}
void
SearchReplaceResults::export_csv (const std::string &file)
{
std::ofstream output (file.c_str ());
QModelIndex parent;
size_t n_columns = columnCount (parent);
size_t n_rows = rowCount (parent);
for (size_t c = 0; c < n_columns; ++c) {
if (c) {
output << ",";
}
output << escape_csv (tl::to_string (headerData (c, Qt::Horizontal, Qt::DisplayRole).toString ()));
}
output << std::endl;
for (size_t r = 0; r < n_rows; ++r) {
for (size_t c = 0; c < n_columns; ++c) {
if (c) {
output << ",";
}
// TODO: optimize
output << escape_csv (tl::to_string (data (index (r, c, parent), Qt::DisplayRole).toString ()));
}
output << std::endl;
}
}
void
SearchReplaceResults::export_layout (db::Layout &layout)
{
if (! m_data_result.empty () || ! m_cell_result.empty () || ! m_inst_result.empty ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Query produces something other than shapes - such results cannot be converted to layout currently.")));
}
db::Cell &top_cell = layout.cell (layout.add_cell ("RESULTS"));
db::LayerMap insert_lm;
for (std::vector<QueryShapeResult>::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s) {
unsigned int layer = s->layer_index;
std::map<unsigned int, db::LayerProperties>::const_iterator lm = m_lp_map.find (layer);
if (lm != m_lp_map.end ()) {
std::pair<bool, unsigned int> ll = insert_lm.logical (lm->second);
if (! ll.first) {
layer = layout.insert_layer (lm->second);
insert_lm.map (lm->second, layer, lm->second);
} else {
layer = ll.second;
}
tl::ident_map<db::Layout::properties_id_type> pm;
top_cell.shapes (layer).insert (s->shape, db::ICplxTrans (s->trans), pm);
}
}
}
void
SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu)
{
if (! m_cell_result.empty ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Query produces cells - such results cannot be exported to a report database.")));
} else if (! m_data_result.empty ()) {
rdb::Category *cat = rdb.create_category ("data");
rdb::Cell *cell = rdb.create_cell (rdb.top_cell_name ());
for (std::vector<tl::Variant>::const_iterator v = m_data_result.begin (); v != m_data_result.end (); ++v) {
rdb::Item *item = rdb.create_item (cell->id (), cat->id ());
if (! v->is_list ()) {
item->add_value (std::string (v->to_string ()));
} else {
for (std::vector<tl::Variant>::const_iterator i = v->get_list ().begin (); i != v->get_list ().end (); ++i) {
item->add_value (std::string (i->to_string ()));
}
}
}
} else if (! m_inst_result.empty ()) {
rdb::Category *cat = rdb.create_category ("instances");
rdb::Cell *rdb_top_cell = rdb.create_cell (rdb.top_cell_name ());
std::map<std::pair<db::cell_index_type, db::CplxTrans>, rdb::Cell *> cells_by_variant;
for (std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.begin (); cn != m_cellname_map.end (); ++cn) {
if (cn->second == rdb.top_cell_name ()) {
cells_by_variant.insert (std::make_pair (std::make_pair (cn->first, db::CplxTrans ()), rdb_top_cell));
}
}
for (std::vector<QueryInstResult>::const_iterator i = m_inst_result.begin (); i != m_inst_result.end (); ++i) {
std::map<std::pair<db::cell_index_type, db::CplxTrans>, rdb::Cell *>::const_iterator v = cells_by_variant.find (std::make_pair (i->cell_index, db::CplxTrans (i->trans)));
if (v == cells_by_variant.end ()) {
std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.find (i->cell_index);
if (cn != m_cellname_map.end ()) {
v = cells_by_variant.insert (std::make_pair (std::make_pair (i->cell_index, db::CplxTrans (i->trans)), rdb.create_cell (cn->second))).first;
v->second->references ().insert (rdb::Reference (db::CplxTrans (dbu) * i->trans * db::VCplxTrans (1.0 / dbu), rdb_top_cell->id ()));
}
}
if (v != cells_by_variant.end ()) {
db::Box inst_bbox = i->inst.bbox ();
rdb::Item *item = rdb.create_item (v->second->id (), cat->id ());
item->add_value (inst_bbox.transformed (db::CplxTrans (dbu)));
item->add_value (i->inst.to_string (true));
}
}
} else if (! m_shape_result.empty ()) {
rdb::Cell *rdb_top_cell = rdb.create_cell (rdb.top_cell_name ());
// create categories
std::map<unsigned int, rdb::Category *> categories;
for (std::vector<QueryShapeResult>::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s) {
unsigned int layer = s->layer_index;
std::map<unsigned int, db::LayerProperties>::const_iterator lm = m_lp_map.find (layer);
if (lm != m_lp_map.end ()) {
std::map<unsigned int, rdb::Category *>::const_iterator cm = categories.find (layer);
if (cm == categories.end ()) {
rdb::Category *cat = rdb.create_category (lm->second.to_string ());
categories.insert (std::make_pair (layer, cat));
}
}
}
std::map<std::pair<db::cell_index_type, db::CplxTrans>, rdb::Cell *> cells_by_variant;
for (std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.begin (); cn != m_cellname_map.end (); ++cn) {
if (cn->second == rdb.top_cell_name ()) {
cells_by_variant.insert (std::make_pair (std::make_pair (cn->first, db::CplxTrans ()), rdb_top_cell));
}
}
for (std::vector<QueryShapeResult>::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s) {
unsigned int layer = s->layer_index;
std::map<unsigned int, rdb::Category *>::const_iterator cm = categories.find (layer);
if (cm != categories.end ()) {
std::map<std::pair<db::cell_index_type, db::CplxTrans>, rdb::Cell *>::const_iterator v = cells_by_variant.find (std::make_pair (s->cell_index, db::CplxTrans (s->trans)));
if (v == cells_by_variant.end ()) {
std::map<db::cell_index_type, std::string>::const_iterator cn = m_cellname_map.find (s->cell_index);
if (cn != m_cellname_map.end ()) {
v = cells_by_variant.insert (std::make_pair (std::make_pair (s->cell_index, db::CplxTrans (s->trans)), rdb.create_cell (cn->second))).first;
v->second->references ().insert (rdb::Reference (db::CplxTrans (dbu) * s->trans * db::VCplxTrans (1.0 / dbu), rdb_top_cell->id ()));
}
}
if (v != cells_by_variant.end ()) {
if (s->shape.is_polygon ()) {
db::Polygon poly;
s->shape.polygon (poly);
rdb::Item *item = rdb.create_item (v->second->id (), cm->second->id ());
item->values ().add (new rdb::Value <db::DPolygon> (poly.transformed (db::CplxTrans (dbu))));
} else if (s->shape.is_path ()) {
db::Path path;
s->shape.path (path);
rdb::Item *item = rdb.create_item (v->second->id (), cm->second->id ());
item->values ().add (new rdb::Value <db::DPath> (path.transformed (db::CplxTrans (dbu))));
} else if (s->shape.is_box ()) {
db::Box box;
s->shape.box (box);
rdb::Item *item = rdb.create_item (v->second->id (), cm->second->id ());
item->values ().add (new rdb::Value <db::DBox> (box.transformed (db::CplxTrans (dbu))));
} else if (s->shape.is_text ()) {
db::Text text;
s->shape.text (text);
rdb::Item *item = rdb.create_item (v->second->id (), cm->second->id ());
item->values ().add (new rdb::Value <db::DText> (text.transformed (db::CplxTrans (dbu))));
} else if (s->shape.is_edge ()) {
db::Edge edge;
s->shape.edge (edge);
rdb::Item *item = rdb.create_item (v->second->id (), cm->second->id ());
item->values ().add (new rdb::Value <db::DEdge> (edge.transformed (db::CplxTrans (dbu))));
}
}
}
}
}
}
// --------------------------------------------------------------------------------
// SearchReplaceDialog implementation
static const char *cfg_sr_mru = "sr-mru";
static const char *cfg_sr_saved = "sr-saved";
static const char *cfg_sr_mode = "sr-mode";
static const char *cfg_sr_object = "sr-object";
static const char *cfg_sr_ctx = "sr-ctx";
static const char *mode_values[] = { "find", "delete", "replace", "custom" };
static const int find_mode_index = 0;
static const int delete_mode_index = 1;
static const int replace_mode_index = 2;
static const int custom_mode_index = 3;
static const char *ctx_values[] = { "current-cell", "current-cell-hierarchy", "all-cells" };
static void
fill_ctx_cbx (QComboBox *cbx)
{
// Note: see also SearchReplaceDialog::cell_expr()
cbx->clear ();
cbx->addItem (QObject::tr ("Current cell"));
cbx->addItem (QObject::tr ("Current cell and below"));
cbx->addItem (QObject::tr ("All cells"));
}
SearchReplaceDialog::SearchReplaceDialog (lay::PluginRoot *root, lay::LayoutView *view)
: lay::Browser (root, view),
Ui::SearchReplaceDialog (),
mp_view (view),
m_current_mode (0),
m_window (FitMarker),
m_window_dim (0.0),
m_max_item_count (0)
{
setObjectName (QString::fromUtf8 ("search_replace_dialog"));
Ui::SearchReplaceDialog::setupUi (this);
connect (find_all_button, SIGNAL (clicked ()), this, SLOT (find_all_button_clicked ()));
connect (delete_button, SIGNAL (clicked ()), this, SLOT (delete_button_clicked ()));
connect (delete_all_button, SIGNAL (clicked ()), this, SLOT (delete_all_button_clicked ()));
connect (replace_button, SIGNAL (clicked ()), this, SLOT (replace_button_clicked ()));
connect (replace_all_button, SIGNAL (clicked ()), this, SLOT (replace_all_button_clicked ()));
connect (execute_all_button, SIGNAL (clicked ()), this, SLOT (execute_all_button_clicked ()));
connect (add_saved_button, SIGNAL (clicked ()), this, SLOT (add_saved_button_clicked ()));
connect (replace_saved_button, SIGNAL (clicked ()), this, SLOT (replace_saved_button_clicked ()));
connect (delete_saved_button, SIGNAL (clicked ()), this, SLOT (delete_saved_button_clicked ()));
connect (rename_saved_button, SIGNAL (clicked ()), this, SLOT (rename_saved_button_clicked ()));
connect (configure_button, SIGNAL (clicked ()), this, SLOT (configure_button_clicked ()));
connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (tab_index_changed (int)));
connect (saved_queries, SIGNAL (itemDoubleClicked (QListWidgetItem *)), this, SLOT (saved_query_double_clicked ()));
connect (recent_queries, SIGNAL (activated (int)), this, SLOT (recent_query_index_changed (int)));
connect (cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_exec ()));
connect (delete_selected_button, SIGNAL (clicked ()), this, SLOT (execute_selected_button_clicked ()));
connect (replace_selected_button, SIGNAL (clicked ()), this, SLOT (execute_selected_button_clicked ()));
connect (hint_label1, SIGNAL (linkActivated (const QString &)), lay::MainWindow::instance (), SLOT (show_help (const QString &)));
connect (hint_label2, SIGNAL (linkActivated (const QString &)), lay::MainWindow::instance (), SLOT (show_help (const QString &)));
connect (hint_label3, SIGNAL (linkActivated (const QString &)), lay::MainWindow::instance (), SLOT (show_help (const QString &)));
connect (hint_label4, SIGNAL (linkActivated (const QString &)), lay::MainWindow::instance (), SLOT (show_help (const QString &)));
fill_ctx_cbx (find_context);
fill_ctx_cbx (delete_context);
fill_ctx_cbx (replace_context);
results->setModel (&m_model);
results->header ()->show ();
results->header ()->setStretchLastSection (false);
connect (results->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (result_selection_changed ()));
connect (results->header (), SIGNAL (sectionCountChanged (int, int)), this, SLOT (header_columns_changed (int, int)));
QMenu *menu = new QMenu (this);
menu->addAction (QObject::tr ("To CSV file"), this, SLOT (export_csv ()));
menu->addAction (QObject::tr ("To report database"), this, SLOT (export_rdb ()));
menu->addAction (QObject::tr ("To layout"), this, SLOT (export_layout ()));
export_b->setMenu (menu);
bool editable = view->is_editable ();
mode_tab->setTabEnabled (replace_mode_index, editable);
mode_tab->setTabEnabled (delete_mode_index, editable);
if (editable) {
setWindowTitle (tr ("Search And Replace"));
} else {
setWindowTitle (tr ("Search"));
}
}
SearchReplaceDialog::~SearchReplaceDialog ()
{
remove_markers ();
}
static void
save_states (QStackedWidget *sw, const std::string &pfx, lay::PluginRoot *config_root)
{
for (int i = 0; i < sw->count (); ++i) {
SearchReplacePropertiesWidget *pw = dynamic_cast<SearchReplacePropertiesWidget *> (sw->widget (i));
if (pw) {
pw->save_state (pfx, config_root);
}
}
}
static void
restore_states (QStackedWidget *sw, const std::string &pfx, lay::PluginRoot *config_root)
{
for (int i = 0; i < sw->count (); ++i) {
SearchReplacePropertiesWidget *pw = dynamic_cast<SearchReplacePropertiesWidget *> (sw->widget (i));
if (pw) {
pw->restore_state (pfx, config_root);
}
}
}
static int
ctx_to_index (const std::string &ctx)
{
for (int i = 0; i < int (sizeof (ctx_values) / sizeof (ctx_values [0])); ++i) {
if (ctx_values [i] == ctx) {
return i;
}
}
return -1;
}
static std::string
ctx_from_index (int index)
{
if (index >= 0 && index < int (sizeof (ctx_values) / sizeof (ctx_values [0]))) {
return ctx_values [index];
} else {
return std::string ();
}
}
void
SearchReplaceDialog::restore_state ()
{
lay::PluginRoot *config_root = root ();
restore_states (find_properties, "sr-find", config_root);
restore_states (delete_properties, "sr-find", config_root);
restore_states (find_replace_properties, "sr-find", config_root);
restore_states (replace_properties, "sr-replace", config_root);
std::string v;
if (config_root->config_get (cfg_sr_mru, v)) {
m_mru.clear ();
tl::Extractor ex (v.c_str ());
while (! ex.at_end ()) {
std::string vv;
ex.read_quoted (vv);
m_mru.push_back (vv);
ex.test (";");
}
}
if (config_root->config_get (cfg_sr_saved, v)) {
m_saved.clear ();
tl::Extractor ex (v.c_str ());
while (! ex.at_end ()) {
SavedQuery sq;
ex.read_quoted (sq.description);
ex.test (":");
ex.read_quoted (sq.text);
m_saved.push_back (sq);
ex.test (";");
}
}
m_current_mode = 0;
mode_tab->blockSignals (true);
mode_tab->setCurrentIndex (m_current_mode);
if (config_root->config_get (cfg_sr_mode, v)) {
for (int i = 0; i < int (sizeof (mode_values) / sizeof (mode_values [0])); ++i) {
if (v == mode_values [i]) {
if (mode_tab->isTabEnabled (i)) {
m_current_mode = i;
mode_tab->setCurrentIndex (m_current_mode);
}
break;
}
}
}
mode_tab->blockSignals (false);
if (config_root->config_get (cfg_sr_object, v)) {
find_objects->setCurrentIndex (index_from_find_object_id (v));
delete_objects->setCurrentIndex (index_from_find_object_id (v));
replace_objects->setCurrentIndex (index_from_find_object_id (v));
}
if (config_root->config_get (cfg_sr_ctx, v)) {
find_context->setCurrentIndex (ctx_to_index (v));
delete_context->setCurrentIndex (ctx_to_index (v));
replace_context->setCurrentIndex (ctx_to_index (v));
}
update_mru_list ();
update_saved_list ();
}
void
SearchReplaceDialog::save_state ()
{
lay::PluginRoot *config_root = root ();
config_root->config_set (cfg_sr_window_state, lay::save_dialog_state (this));
int m = mode_tab->currentIndex ();
if (m == find_mode_index) {
save_states (find_properties, "sr-find", config_root);
config_root->config_set (cfg_sr_object, index_to_find_object_id (find_objects->currentIndex ()));
config_root->config_set (cfg_sr_ctx, ctx_from_index (find_context->currentIndex ()));
} else if (m == delete_mode_index) {
save_states (delete_properties, "sr-find", config_root);
config_root->config_set (cfg_sr_object, index_to_find_object_id (delete_objects->currentIndex ()));
config_root->config_set (cfg_sr_ctx, ctx_from_index (delete_context->currentIndex ()));
} else if (m == replace_mode_index) {
save_states (find_replace_properties, "sr-find", config_root);
save_states (replace_properties, "sr-replace", config_root);
config_root->config_set (cfg_sr_object, index_to_find_object_id (replace_objects->currentIndex ()));
config_root->config_set (cfg_sr_ctx, ctx_from_index (replace_context->currentIndex ()));
}
{
std::string v;
for (std::vector<std::string>::const_iterator i = m_mru.begin (); i != m_mru.end (); ++i) {
if (! v.empty ()) {
v += ";";
}
v += tl::to_quoted_string (*i);
}
config_root->config_set (cfg_sr_mru, v);
}
{
std::string v;
for (std::vector<SavedQuery>::const_iterator i = m_saved.begin (); i != m_saved.end (); ++i) {
if (! v.empty ()) {
v += ";";
}
v += tl::to_quoted_string (i->description);
v += ":";
v += tl::to_quoted_string (i->text);
}
config_root->config_set (cfg_sr_saved, v);
}
if (m >= 0 && m < int (sizeof (mode_values) / sizeof (mode_values [0]))) {
config_root->config_set (cfg_sr_mode, mode_values [m]);
}
}
void
SearchReplaceDialog::export_csv ()
{
BEGIN_PROTECTED
int cv_index = m_last_query_cv_index;
const lay::CellView &cv = mp_view->cellview (cv_index);
if (! cv.is_valid ()) {
return;
}
std::string fn;
lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Export CSV")), tl::to_string (QObject::tr ("CSV Files (*.csv);;All Files (*)")), "csv");
if (! file_dialog.get_save (fn)) {
return;
}
db::LayoutQuery lq (m_last_query);
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Running query")));
progress.set_unit (100000);
progress.set_format ("Processing ..");
db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query;
}
SearchReplaceResults model;
model.begin_changes (& cv->layout ());
query_to_model (model, lq, iq, std::numeric_limits<size_t>::max (), true);
model.end_changes ();
model.export_csv (fn);
END_PROTECTED
}
void
SearchReplaceDialog::export_rdb ()
{
BEGIN_PROTECTED
int cv_index = m_last_query_cv_index;
const lay::CellView &cv = mp_view->cellview (cv_index);
if (! cv.is_valid ()) {
return;
}
std::auto_ptr<rdb::Database> rdb (new rdb::Database ());
rdb->set_description (tl::to_string (QObject::tr ("Query results: ")) + m_last_query);
rdb->set_name ("query_results");
rdb->set_generator ("query: " + m_last_query);
rdb->set_top_cell_name (cv->layout ().cell_name (cv.cell_index ()));
db::LayoutQuery lq (m_last_query);
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Running query")));
progress.set_unit (100000);
progress.set_format ("Processing ..");
db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query;
}
SearchReplaceResults model;
model.begin_changes (& cv->layout ());
query_to_model (model, lq, iq, std::numeric_limits<size_t>::max (), true);
model.end_changes ();
model.export_rdb (*rdb, cv->layout ().dbu ());
int rdb_index = mp_view->add_rdb (rdb.release ());
mp_view->open_rdb_browser (rdb_index, cv_index);
END_PROTECTED
}
void
SearchReplaceDialog::export_layout ()
{
BEGIN_PROTECTED
int cv_index = m_last_query_cv_index;
const lay::CellView &cv = mp_view->cellview (cv_index);
if (! cv.is_valid ()) {
return;
}
db::LayoutQuery lq (m_last_query);
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Running query")));
progress.set_unit (100000);
progress.set_format ("Processing ..");
db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query;
}
SearchReplaceResults model;
model.begin_changes (& cv->layout ());
query_to_model (model, lq, iq, std::numeric_limits<size_t>::max (), true);
model.end_changes ();
std::auto_ptr <lay::LayoutHandle> handle (new lay::LayoutHandle (new db::Layout (), std::string ()));
handle->rename ("query_results");
model.export_layout (handle->layout ());
mp_view->add_layout (handle.release (), true);
END_PROTECTED
}
static void
sync_cbx (QComboBox *cbx, QStackedWidget *sw)
{
cbx->clear ();
for (int i = 0; i < sw->count (); ++i) {
SearchPropertiesWidget *pw = dynamic_cast<SearchPropertiesWidget *> (sw->widget (i));
tl_assert (pw != 0);
cbx->addItem (tl::to_qstring (pw->description ()));
}
}
void
SearchReplaceDialog::activated ()
{
cancel ();
m_find_query.clear ();
m_model.begin_changes (0);
m_model.clear ();
m_model.end_changes ();
int cv_index = mp_view->active_cellview_index ();
lay::CellView cv = mp_view->cellview (cv_index);
if (cv.is_valid ()) {
fill_find_pages (find_properties, mp_view, cv_index);
sync_cbx (find_objects, find_properties);
find_objects->setCurrentIndex (0);
fill_find_pages (delete_properties, mp_view, cv_index);
sync_cbx (delete_objects, delete_properties);
delete_objects->setCurrentIndex (0);
fill_replace_pages (replace_properties, mp_view, cv_index);
fill_find_pages (find_replace_properties, mp_view, cv_index);
sync_cbx (replace_objects, find_replace_properties);
replace_objects->setCurrentIndex (0);
restore_state ();
}
}
void
SearchReplaceDialog::deactivated ()
{
cancel ();
save_state ();
remove_markers ();
m_model.begin_changes (0);
m_model.clear ();
m_model.end_changes ();
}
bool
SearchReplaceDialog::configure (const std::string &name, const std::string &value)
{
bool need_update = false;
bool taken = true;
if (name == cfg_sr_window_state) {
lay::restore_dialog_state (this, value);
} else if (name == cfg_sr_window_mode) {
window_type window = m_window;
SearchReplaceWindowModeConverter ().from_string (value, window);
need_update = lay::test_and_set (m_window, window);
} else if (name == cfg_sr_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_sr_max_item_count) {
unsigned int mic = m_max_item_count;
tl::from_string (value, mic);
need_update = lay::test_and_set (m_max_item_count, mic);
} else {
taken = false;
}
if (isVisible () && need_update && ! m_find_query.empty ()) {
try {
update_results (m_find_query);
} catch (...) { }
}
return taken;
}
static std::string
cell_expr (int ctx, const lay::CellView &cv)
{
std::string ce;
if (ctx == 0) {
ce = "cell ";
ce += tl::to_word_or_quoted_string (cv->layout ().cell_name (cv.cell_index ()));
} else if (ctx == 1) {
ce = "instances of ";
ce += tl::to_word_or_quoted_string (cv->layout ().cell_name (cv.cell_index ()));
ce += "..";
} else {
ce = "cells *";
}
return ce;
}
std::string
SearchReplaceDialog::build_find_expression (QStackedWidget *prop_page, QComboBox *context)
{
const lay::CellView &cv = mp_view->cellview (mp_view->active_cellview_index ());
if (! cv.is_valid ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No layout loaded")));
}
std::string expr;
lay::SearchPropertiesWidget *p = dynamic_cast<lay::SearchPropertiesWidget *> (prop_page->currentWidget ());
if (p) {
expr += p->search_expression (cell_expr (context->currentIndex (), cv));
}
return expr;
}
std::string
SearchReplaceDialog::build_delete_expression ()
{
const lay::CellView &cv = mp_view->cellview (mp_view->active_cellview_index ());
if (! cv.is_valid ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No layout loaded")));
}
std::string expr;
lay::SearchPropertiesWidget *p = dynamic_cast<lay::SearchPropertiesWidget *> (delete_properties->currentWidget ());
if (p) {
expr = "delete ";
expr += p->search_expression (cell_expr (delete_context->currentIndex (), cv));
}
return expr;
}
std::string
SearchReplaceDialog::build_replace_expression ()
{
const lay::CellView &cv = mp_view->cellview (mp_view->active_cellview_index ());
if (! cv.is_valid ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No layout loaded")));
}
std::string expr;
lay::SearchPropertiesWidget *pf = dynamic_cast<lay::SearchPropertiesWidget *> (find_replace_properties->currentWidget ());
lay::ReplacePropertiesWidget *pr = dynamic_cast<lay::ReplacePropertiesWidget *> (replace_properties->currentWidget ());
if (pf && pr) {
expr = "with ";
expr += pf->search_expression (cell_expr (replace_context->currentIndex (), cv));
expr += " do ";
std::string re = pr->replace_expression ();
if (re.empty ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No replacement action specified - replace operation wouldn't do anything")));
}
expr += re;
}
return expr;
}
void
SearchReplaceDialog::update_saved_list ()
{
saved_queries->clear ();
for (std::vector<SavedQuery>::const_iterator s = m_saved.begin (); s != m_saved.end (); ++s) {
saved_queries->addItem (tl::to_qstring (s->description));
}
}
void
SearchReplaceDialog::update_mru_list ()
{
recent_queries->blockSignals (true);
recent_queries->clear ();
for (std::vector<std::string>::const_iterator mru = m_mru.begin (); mru != m_mru.end (); ++mru) {
QString text = tl::to_qstring (*mru);
QString display_text = text;
display_text.replace (QRegExp (QString::fromUtf8 ("\\s+")), QString::fromUtf8 (" "));
int nmax = 50;
if (display_text.size () > nmax) {
display_text = display_text.left (nmax) + QString::fromUtf8 ("...");
}
recent_queries->addItem (display_text, QVariant (text));
}
recent_queries->setCurrentIndex (0);
recent_queries->blockSignals (false);
}
void
SearchReplaceDialog::recent_query_index_changed (int index)
{
if (index >= 0 && index < int (recent_queries->count ())) {
custom_query->setText (recent_queries->itemData (index).toString ());
}
}
void
SearchReplaceDialog::issue_query (const std::string &q, const std::set<size_t> *selected_items, bool with_results)
{
detach_from_all_events (); // don't listen to layout events any longer
remove_markers ();
results->clearSelection ();
int cv_index = mp_view->active_cellview_index ();
const lay::CellView &cv = mp_view->cellview (cv_index);
if (! cv.is_valid ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No layout loaded")));
}
m_last_query.clear ();
m_last_query_cv_index = -1;
// Test-parse the query
db::LayoutQuery lq (q);
m_last_query = q;
m_last_query_cv_index = cv_index;
const size_t max_mru_length = 20;
// put the query into the MRU list
for (int i = 0; i < int (m_mru.size ()); ++i) {
if (m_mru[i] == q) {
m_mru.erase (m_mru.begin () + i);
--i;
}
}
m_mru.insert (m_mru.begin (), q);
while (m_mru.size () > max_mru_length) {
m_mru.pop_back ();
}
update_mru_list ();
if (with_results) {
update_results (q);
} else if (! selected_items) {
db::LayoutQuery lq (q);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (QObject::tr ("Running full query (without results): ")) << q;
}
m_model.begin_changes (0);
m_model.clear ();
m_model.end_changes ();
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Running query")));
progress.set_unit (100000);
progress.set_format ("Processing ..");
db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress);
while (! iq.at_end ()) {
++iq;
}
} else {
db::LayoutQuery lq (q + " pass");
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (QObject::tr ("Running query on selection: ")) << q;
}
m_model.begin_changes (0);
m_model.clear ();
m_model.end_changes ();
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Running query")));
progress.set_unit (100000);
progress.set_format ("Processing ..");
size_t n = 0;
for (db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); ! iq.at_end (); ++n) {
iq.next (selected_items->find (n) == selected_items->end ());
}
}
}
void
SearchReplaceDialog::cancel_exec ()
{
execute_panel->hide ();
remove_markers ();
results->clearSelection ();
m_execute_query.clear ();
m_find_query.clear ();
}
void
SearchReplaceDialog::cancel ()
{
detach_from_all_events (); // don't listen to layout events any longer
execute_panel->hide ();
remove_markers ();
results->clearSelection ();
m_model.begin_changes (0);
m_model.clear ();
m_model.end_changes ();
results_stack->setCurrentIndex (mode_tab->currentIndex () + 1); // show hint
export_b->setEnabled (false);
m_execute_query.clear ();
m_find_query.clear ();
}
void
SearchReplaceDialog::layout_changed ()
{
// cannot call detach_all inside signal handler currently
cancel ();
}
void
SearchReplaceDialog::attach_layout (db::Layout *layout)
{
layout->hier_changed_event.add (this, &SearchReplaceDialog::layout_changed);
layout->bboxes_changed_any_event.add (this, &SearchReplaceDialog::layout_changed);
layout->cell_name_changed_event.add (this, &SearchReplaceDialog::layout_changed);
layout->layer_properties_changed_event.add (this, &SearchReplaceDialog::layout_changed);
}
void
SearchReplaceDialog::update_results (const std::string &q)
{
detach_from_all_events (); // don't listen to layout events any longer
remove_markers ();
results->clearSelection ();
const lay::CellView &cv = mp_view->cellview (mp_view->active_cellview_index ());
if (! cv.is_valid ()) {
m_model.begin_changes (0);
m_model.clear ();
m_model.end_changes ();
} else {
db::LayoutQuery lq (q);
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Running query")));
progress.set_unit (100000);
progress.set_format ("Processing ..");
db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (QObject::tr ("Running query: ")) << q;
}
try {
fill_model (lq, iq, &cv->layout (), true);
attach_layout (&cv->layout ());
} catch (...) {
attach_layout (&cv->layout ());
throw;
}
}
}
bool
SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, size_t max_item_count, bool all)
{
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (QObject::tr ("Query run")));
size_t n = 0;
bool res = false;
int data_prop_id = lq.has_property ("data") ? int (lq.property_by_name ("data")) : -1;
int shape_prop_id = lq.has_property ("shape") ? int (lq.property_by_name ("shape")) : -1;
int layer_index_prop_id = lq.has_property ("layer_index") ? int (lq.property_by_name ("layer_index")) : -1;
int instance_prop_id = lq.has_property ("inst") ? int (lq.property_by_name ("inst")) : -1;
int path_trans_prop_id = lq.has_property ("path_trans") ? int (lq.property_by_name ("path_trans")) : -1;
int trans_prop_id = lq.has_property ("trans") ? int (lq.property_by_name ("trans")) : -1;
int cell_index_prop_id = lq.has_property ("cell_index") ? int (lq.property_by_name ("cell_index")) : -1;
int parent_cell_index_prop_id = lq.has_property ("parent_cell_index") ? int (lq.property_by_name ("parent_cell_index")) : -1;
int initial_cell_index_prop_id = lq.has_property ("initial_cell_index") ? int (lq.property_by_name ("initial_cell_index")) : -1;
while (! iq.at_end ()) {
if (++n > max_item_count) {
model.has_more (true);
break;
}
res = true;
tl::Variant v;
if (data_prop_id >= 0 && iq.get (data_prop_id, v)) {
model.push_back (v);
} else if (shape_prop_id >= 0) {
db::Shape shape;
int layer_index = 0;
db::ICplxTrans trans;
db::cell_index_type cell_index = std::numeric_limits<db::cell_index_type>::max ();
db::cell_index_type initial_cell_index = std::numeric_limits<db::cell_index_type>::max ();
if (iq.get (shape_prop_id, v)) {
shape = v.to_user<db::Shape> ();
}
if (layer_index_prop_id >= 0 && iq.get (layer_index_prop_id, v)) {
layer_index = v.to_int ();
}
if (cell_index_prop_id >= 0 && iq.get (cell_index_prop_id, v)) {
cell_index = db::cell_index_type (v.to_int ());
}
if (path_trans_prop_id >= 0 && iq.get (path_trans_prop_id, v)) {
trans = v.to_user<db::ICplxTrans> ();
if (initial_cell_index_prop_id >= 0 && iq.get (initial_cell_index_prop_id, v)) {
initial_cell_index = db::cell_index_type (v.to_int ());
}
}
model.push_back (SearchReplaceResults::QueryShapeResult (shape, layer_index, trans, cell_index, initial_cell_index));
} else if (instance_prop_id >= 0) {
db::Instance instance;
db::ICplxTrans trans;
db::cell_index_type cell_index = std::numeric_limits<db::cell_index_type>::max ();
db::cell_index_type initial_cell_index = std::numeric_limits<db::cell_index_type>::max ();
if (iq.get (instance_prop_id, v)) {
instance = v.to_user<db::Instance> ();
}
if (parent_cell_index_prop_id >= 0 && iq.get (parent_cell_index_prop_id, v)) {
cell_index = db::cell_index_type (v.to_int ());
}
if (path_trans_prop_id >= 0 && iq.get (path_trans_prop_id, v)) {
trans = v.to_user<db::ICplxTrans> ();
if (trans_prop_id >= 0 && iq.get (trans_prop_id, v)) {
// strip the first transformation (the one from the instance itself)
trans = trans * v.to_user<db::ICplxTrans> ().inverted ();
}
if (initial_cell_index_prop_id >= 0 && iq.get (initial_cell_index_prop_id, v)) {
initial_cell_index = db::cell_index_type (v.to_int ());
}
}
model.push_back (SearchReplaceResults::QueryInstResult (instance, trans, cell_index, initial_cell_index));
} else if (cell_index_prop_id >= 0) {
db::cell_index_type cell_index = std::numeric_limits<db::cell_index_type>::max ();
db::cell_index_type parent_cell_index = std::numeric_limits<db::cell_index_type>::max ();
if (cell_index_prop_id >= 0 && iq.get (cell_index_prop_id, v)) {
cell_index = db::cell_index_type (v.to_int ());
}
if (parent_cell_index_prop_id >= 0 && iq.get (parent_cell_index_prop_id, v)) {
parent_cell_index = db::cell_index_type (v.to_int ());
}
model.push_back (SearchReplaceResults::QueryCellResult (cell_index, parent_cell_index));
} else {
break;
}
if (! all) {
break;
}
++iq;
}
return res;
}
bool
SearchReplaceDialog::fill_model (const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, const db::Layout *layout, bool all)
{
bool res = false;
try {
m_model.begin_changes (layout);
m_model.clear ();
res = query_to_model (m_model, lq, iq, m_max_item_count, all);
m_model.end_changes ();
results_stack->setCurrentIndex (0);
export_b->setEnabled (true);
} catch (...) {
m_model.end_changes ();
results_stack->setCurrentIndex (0);
export_b->setEnabled (true);
throw;
}
return res;
}
void
SearchReplaceDialog::header_columns_changed (int /*from*/, int /*to*/)
{
results->header ()->resizeSections (QHeaderView::ResizeToContents);
}
void
SearchReplaceDialog::remove_markers ()
{
for (std::vector<lay::MarkerBase *>::const_iterator m = mp_markers.begin (); m != mp_markers.end (); ++m) {
delete *m;
}
mp_markers.clear ();
}
void
SearchReplaceDialog::result_selection_changed ()
{
try {
remove_markers ();
int cv_index = m_last_query_cv_index;
const lay::CellView &cv = mp_view->cellview (cv_index);
if (! cv.is_valid ()) {
return;
}
const db::Layout &layout = cv->layout ();
// collect the transformation variants for this cellview - this way we can paint
// the cell boxes for each global transformation
std::vector<db::DCplxTrans> global_trans = view ()->cv_transform_variants (cv_index);
std::map<unsigned int, std::vector<db::DCplxTrans> > tv_map = view ()->cv_transform_variants_by_layer (cv_index);
db::DBox dbox;
QModelIndexList sel = results->selectionModel ()->selectedRows (0);
delete_selected_button->setEnabled (! sel.isEmpty ());
replace_selected_button->setEnabled (! sel.isEmpty ());
for (QModelIndexList::const_iterator s = sel.begin (); s != sel.end (); ++s) {
int index = s->row ();
if (index < 0) {
// .. ignore ..
} else if (index < int (m_model.shapes ().size ())) {
const SearchReplaceResults::QueryShapeResult &sr = m_model.shapes () [index];
if (! sr.shape.is_null ()) {
db::ICplxTrans tr_context;
if (layout.is_valid_cell_index (sr.initial_cell_index)) {
tr_context = db::find_layout_context (layout, sr.initial_cell_index, cv.cell_index ()).second;
}
// transform the box into the cell view shown in micron space
lay::ShapeMarker *marker = new lay::ShapeMarker (view (), cv_index);
mp_markers.push_back (marker);
std::map<unsigned int, std::vector<db::DCplxTrans> >::const_iterator tv = tv_map.find (sr.layer_index);
if (tv != tv_map.end ()) {
marker->set (sr.shape, tr_context * sr.trans, tv->second);
} else {
marker->set (sr.shape, tr_context * sr.trans);
}
dbox += marker->bbox ();
}
} else if (index < int (m_model.instances ().size ())) {
const SearchReplaceResults::QueryInstResult &ir = m_model.instances () [index];
if (! ir.inst.is_null ()) {
db::ICplxTrans tr_context;
if (layout.is_valid_cell_index (ir.initial_cell_index)) {
tr_context = db::find_layout_context (layout, ir.initial_cell_index, cv.cell_index ()).second;
}
lay::InstanceMarker *marker = new lay::InstanceMarker (view (), cv_index);
marker->set (ir.inst, tr_context * ir.trans, global_trans);
mp_markers.push_back (marker);
dbox += marker->bbox ();
}
} else if (index < int (m_model.cells ().size ())) {
const SearchReplaceResults::QueryCellResult &ir = m_model.cells () [index];
std::pair<bool, db::ICplxTrans> si = db::find_layout_context (layout, ir.cell_index, cv.cell_index ());
if (si.first) {
db::Box box = layout.cell (ir.cell_index).bbox ();
lay::Marker *marker = new lay::Marker (view (), cv_index);
marker->set (box, si.second, global_trans);
mp_markers.push_back (marker);
dbox += marker->bbox ();
}
}
}
if (! dbox.empty ()) {
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));
}
}
} catch (...) {
// .. ignore exceptions ..
}
}
void
SearchReplaceDialog::find_all_button_clicked ()
{
BEGIN_PROTECTED
cancel_exec ();
m_find_query = build_find_expression (find_properties, find_context);
issue_query (m_find_query, 0, true);
END_PROTECTED
}
void
SearchReplaceDialog::delete_button_clicked ()
{
BEGIN_PROTECTED
cancel_exec ();
m_execute_query = build_delete_expression ();
m_find_query = build_find_expression (delete_properties, delete_context);
issue_query (m_find_query, 0, true);
delete_selected_button->show ();
delete_selected_button->setEnabled (false);
replace_selected_button->hide ();
execute_panel->show ();
END_PROTECTED
}
void
SearchReplaceDialog::delete_all_button_clicked ()
{
BEGIN_PROTECTED
cancel_exec ();
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Delete all")));
mp_view->cancel ();
issue_query (build_delete_expression (), 0, false);
mp_view->manager ()->commit ();
END_PROTECTED
}
void
SearchReplaceDialog::replace_button_clicked ()
{
BEGIN_PROTECTED
cancel_exec ();
m_execute_query = build_replace_expression ();
m_find_query = build_find_expression (find_replace_properties, replace_context);
issue_query (m_find_query, 0, true);
delete_selected_button->hide ();
replace_selected_button->show ();
replace_selected_button->setEnabled (false);
execute_panel->show ();
END_PROTECTED
}
void
SearchReplaceDialog::execute_selected_button_clicked ()
{
BEGIN_PROTECTED
if (m_execute_query.empty ()) {
return;
}
std::set<size_t> selected_items;
QModelIndexList sel = results->selectionModel ()->selectedRows (0);
for (QModelIndexList::const_iterator s = sel.begin (); s != sel.end (); ++s) {
int index = s->row ();
if (index >= 0) {
selected_items.insert (size_t (index));
}
}
if (! sel.empty ()) {
if (sender () == delete_selected_button) {
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Delete selected")));
} else {
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Replace selected")));
}
mp_view->cancel ();
issue_query (m_execute_query, &selected_items, false);
mp_view->manager ()->commit ();
issue_query (m_find_query, 0, true);
}
END_PROTECTED
}
void
SearchReplaceDialog::replace_all_button_clicked ()
{
BEGIN_PROTECTED
cancel_exec ();
m_execute_query.clear ();
m_find_query.clear ();
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Replace all")));
mp_view->cancel ();
issue_query (build_replace_expression (), 0, false);
mp_view->manager ()->commit ();
END_PROTECTED
}
void
SearchReplaceDialog::execute_all_button_clicked ()
{
BEGIN_PROTECTED
cancel_exec ();
m_execute_query.clear ();
m_find_query.clear ();
mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Execute custom query")));
mp_view->cancel ();
issue_query (tl::to_string (custom_query->toPlainText ()), 0, true);
mp_view->manager ()->commit ();
END_PROTECTED
}
void
SearchReplaceDialog::configure_button_clicked ()
{
lay::ConfigurationDialog config_dialog (this, root (), "SearchReplacePlugin");
config_dialog.exec ();
}
void
SearchReplaceDialog::tab_index_changed (int index)
{
cancel ();
lay::PluginRoot *config_root = root ();
std::string v;
// share find settings between pages: first save
if (m_current_mode == find_mode_index) {
save_states (find_properties, "sr-find", config_root);
config_root->config_set (cfg_sr_object, index_to_find_object_id (find_objects->currentIndex ()));
config_root->config_set (cfg_sr_ctx, ctx_from_index (find_context->currentIndex ()));
} else if (m_current_mode == delete_mode_index) {
save_states (delete_properties, "sr-find", config_root);
config_root->config_set (cfg_sr_object, index_to_find_object_id (delete_objects->currentIndex ()));
config_root->config_set (cfg_sr_ctx, ctx_from_index (delete_context->currentIndex ()));
} else if (m_current_mode == replace_mode_index) {
save_states (find_replace_properties, "sr-find", config_root);
config_root->config_set (cfg_sr_object, index_to_find_object_id (replace_objects->currentIndex ()));
config_root->config_set (cfg_sr_ctx, ctx_from_index (replace_context->currentIndex ()));
}
if (index == find_mode_index) {
restore_states (find_properties, "sr-find", config_root);
if (config_root->config_get (cfg_sr_object, v)) {
find_objects->setCurrentIndex (index_from_find_object_id (v));
}
if (config_root->config_get (cfg_sr_ctx, v)) {
find_context->setCurrentIndex (ctx_to_index (v));
}
} else if (index == delete_mode_index) {
restore_states (delete_properties, "sr-find", config_root);
if (config_root->config_get (cfg_sr_object, v)) {
delete_objects->setCurrentIndex (index_from_find_object_id (v));
}
if (config_root->config_get (cfg_sr_ctx, v)) {
delete_context->setCurrentIndex (ctx_to_index (v));
}
} else if (index == replace_mode_index) {
restore_states (find_replace_properties, "sr-find", config_root);
if (config_root->config_get (cfg_sr_object, v)) {
replace_objects->setCurrentIndex (index_from_find_object_id (v));
}
if (config_root->config_get (cfg_sr_ctx, v)) {
replace_context->setCurrentIndex (ctx_to_index (v));
}
} else if (index == custom_mode_index) {
// update query on the custom query page
if (m_current_mode == find_mode_index) {
try {
custom_query->setText (tl::to_qstring (build_find_expression (find_properties, find_context)));
} catch (...) {
// ignore errors
custom_query->setText (tl::to_qstring (""));
}
} else if (m_current_mode == delete_mode_index) {
try {
custom_query->setText (tl::to_qstring (build_delete_expression ()));
} catch (...) {
// ignore errors
custom_query->setText (tl::to_qstring (""));
}
} else if (m_current_mode == replace_mode_index) {
try {
custom_query->setText (tl::to_qstring (build_replace_expression ()));
} catch (...) {
// ignore errors
custom_query->setText (tl::to_qstring (""));
}
}
}
m_current_mode = index;
}
void
SearchReplaceDialog::replace_saved_button_clicked ()
{
int index = saved_queries->currentRow ();
if (index >= 0 && index < int (m_saved.size ())) {
m_saved [index].text = tl::to_string (custom_query->toPlainText ());
}
}
void
SearchReplaceDialog::add_saved_button_clicked ()
{
BEGIN_PROTECTED
bool ok = false;
QString desc = QInputDialog::getText (this, QObject::tr ("Enter Description"),
QObject::tr ("Enter a description text for the current query.\nThat text will be shown in the selection box."),
QLineEdit::Normal, QString (), &ok);
if (ok) {
m_saved.push_back (SavedQuery ());
m_saved.back ().description = tl::to_string (desc);
m_saved.back ().text = tl::to_string (custom_query->toPlainText ());
update_saved_list ();
saved_queries->setCurrentRow (saved_queries->count () - 1);
}
END_PROTECTED
}
void
SearchReplaceDialog::delete_saved_button_clicked ()
{
int index = saved_queries->currentRow ();
if (index >= 0 && index < int (m_saved.size ())) {
m_saved.erase (m_saved.begin () + index);
update_saved_list ();
saved_queries->setCurrentRow (std::min (saved_queries->count () - 1, index));
}
}
void
SearchReplaceDialog::rename_saved_button_clicked ()
{
int index = saved_queries->currentRow ();
if (index >= 0 && index < int (m_saved.size ())) {
bool ok = false;
QString desc = QInputDialog::getText (this, QObject::tr ("Enter Description"),
QObject::tr ("Enter a description text for the current query.\nThat text will be shown in the selection box."),
QLineEdit::Normal,
tl::to_qstring (m_saved [index].description),
&ok);
if (ok) {
m_saved [index].description = tl::to_string (desc);
update_saved_list ();
}
}
}
void
SearchReplaceDialog::saved_query_double_clicked ()
{
int index = saved_queries->currentRow ();
if (index >= 0 && index < int (m_saved.size ())) {
custom_query->setText (tl::to_qstring (m_saved [index].text));
}
}
void
SearchReplaceDialog::menu_activated (const std::string &symbol)
{
if (symbol == "search_replace::show") {
view ()->deactivate_all_browsers ();
activate ();
} else {
lay::Plugin::menu_activated (symbol);
}
}
}