From 5ee1daf945a2af1e6b415644da28939548dc545f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 18:48:21 +0200 Subject: [PATCH] First draft of solution. Needs polishing. --- src/db/db/dbInstElement.cc | 76 ++++++++++- src/db/db/dbInstElement.h | 15 +++ src/db/db/dbLayoutQuery.cc | 28 ++++ src/edt/edt/edtService.cc | 180 ++++++++++++++++++++++++- src/edt/edt/edtService.h | 74 +++++++++++ src/edt/edt/gsiDeclEdt.cc | 183 ++------------------------ src/lay/lay/laySearchReplaceDialog.cc | 134 ++++++++++++++++++- src/lay/lay/laySearchReplaceDialog.h | 30 ++++- src/tl/tl/tlOptional.h | 21 ++- 9 files changed, 558 insertions(+), 183 deletions(-) diff --git a/src/db/db/dbInstElement.cc b/src/db/db/dbInstElement.cc index 8d3b93817..e253505a9 100644 --- a/src/db/db/dbInstElement.cc +++ b/src/db/db/dbInstElement.cc @@ -23,4 +23,78 @@ #include "dbInstElement.h" -// nothing yet .. +namespace db +{ + +// ------------------------------------------------------------ +// "to_string" implementation + +std::string +InstElement::to_string (bool resolve_cell_name) const +{ + if (inst_ptr.is_null ()) { + return std::string (); + } + + db::cell_index_type ci = inst_ptr.cell_index (); + + std::string r; + if (resolve_cell_name && inst_ptr.instances () && inst_ptr.instances ()->cell () && inst_ptr.instances ()->cell ()->layout ()) { + r = inst_ptr.instances ()->cell ()->layout ()->cell_name (ci); + } else { + r = "cell_index=" + tl::to_string (ci); + } + + r += " " + complex_trans ().to_string (); + + return r; +} + +// ------------------------------------------------------------ +// Implementation of "find_path" + +static bool +find_path (const db::Layout &layout, db::cell_index_type from, db::cell_index_type to, std::set &visited, std::vector &path) +{ + const db::Cell &cell = layout.cell (from); + for (db::Cell::parent_inst_iterator p = cell.begin_parent_insts (); ! p.at_end (); ++p) { + + if (p->parent_cell_index () == to) { + + path.push_back (db::InstElement (p->child_inst ())); + return true; + + } else if (visited.find (p->parent_cell_index ()) == visited.end ()) { + + visited.insert (p->parent_cell_index ()); + path.push_back (db::InstElement (p->child_inst ())); + if (find_path (layout, p->parent_cell_index (), to, visited, path)) { + return true; + } + path.pop_back (); + + } + + } + + return false; +} + +bool +find_path (const db::Layout &layout, db::cell_index_type from, db::cell_index_type to, std::vector &path) +{ + path.clear (); + if (from == to) { + return true; + } else { + std::set v; + if (find_path (layout, from, to, v, path)) { + std::reverse (path.begin (), path.end ()); + return true; + } else { + return false; + } + } +} + +} diff --git a/src/db/db/dbInstElement.h b/src/db/db/dbInstElement.h index ed1b74750..b709b13a4 100644 --- a/src/db/db/dbInstElement.h +++ b/src/db/db/dbInstElement.h @@ -150,8 +150,23 @@ struct DB_PUBLIC InstElement { return inst_ptr.cell_inst ().complex_trans (*array_inst); } + + /** + * @brief Returns a string representation of the element + */ + std::string to_string (bool resolve_cell_name = false) const; }; +/** + * @brief Finds a path from the "from" to the "to" cell + * + * This function will determine one representative instantiation path leading + * from the "from" cell to the "to" cell. If such a path exists, it is stored + * in "path" and true is returned. If no such path exists, false is returned. + */ +DB_PUBLIC bool +find_path (const db::Layout &layout, db::cell_index_type from, db::cell_index_type to, std::vector &path); + } // namespace db #endif diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index b6db4e0bc..ce77f784f 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -24,6 +24,7 @@ #include "dbLayoutQuery.h" #include "dbCellGraphUtils.h" #include "dbStreamLayers.h" +#include "dbInstElement.h" #include "tlAssert.h" #include "tlString.h" #include "tlGlobPattern.h" @@ -476,6 +477,7 @@ struct ChildCellFilterPropertyIDs { path = q->register_property ("path", LQ_variant); path_names = q->register_property ("path_names", LQ_variant); + inst_elements = q->register_property ("inst_elements", LQ_variant); initial_cell = q->register_property ("initial_cell", LQ_cell); initial_cell_index = q->register_property ("initial_cell_index", LQ_variant); initial_cell_name = q->register_property ("initial_cell_name", LQ_variant); @@ -573,6 +575,7 @@ struct ChildCellFilterPropertyIDs unsigned int inst_bbox; // inst_bbox -> The instance bounding box in the top cell unsigned int inst_dbbox; // inst_dbbox -> The instance bounding box in the top cell in micrometer units unsigned int inst; // inst -> The instance object + unsigned int inst_elements; // inst_elements -> Variant array with the db::InstElement objects for the path unsigned int array_a; // array_a -> The a vector for an array instance unsigned int array_da; // array_da -> The a vector for an array instance in micrometer units unsigned int array_na; // array_na -> The a axis array dimension @@ -990,6 +993,31 @@ public: v.push (tl::Variant (cell_index ())); return true; + } else if (id == m_pids.inst_elements) { + + if (! v.is_list ()) { + std::vector vd; + v = tl::Variant (vd.begin (), vd.end ()); + } + + if (mp_parent) { + FilterStateBase::get_property (id, v); + } + + db::Instance inst; + if (m_reading) { + inst = mp_parent->sorted_inst_ptr (std::distance (mp_parent->begin_sorted_insts (), m_inst)); + } else { + inst = m_i; + } + + if (m_instance_mode == ArrayInstances) { + v.push (tl::Variant (db::InstElement (inst))); + } else { + v.push (tl::Variant (db::InstElement (inst, m_array_iter))); + } + return true; + } else if (id == m_pids.path_names) { if (! v.is_list ()) { diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 26ebfccd3..84a7fb536 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -1913,9 +1913,185 @@ Service::handle_guiding_shape_changes () } } +// ------------------------------------------------------------- +// Implementation of EditableSelectionIterator + +EditableSelectionIterator::EditableSelectionIterator (const std::vector &services, bool transient) + : m_services (services), m_service (0), m_transient_selection (transient) +{ + if (! m_services.empty ()) { + if (m_transient_selection) { + m_iter = m_services [m_service]->transient_selection ().begin (); + m_end = m_services [m_service]->transient_selection ().end (); + } else { + m_iter = m_services [m_service]->selection ().begin (); + m_end = m_services [m_service]->selection ().end (); + } + next (); + } +} + +bool +EditableSelectionIterator::at_end () const +{ + return (m_service >= m_services.size ()); +} + +EditableSelectionIterator & +EditableSelectionIterator::operator++ () +{ + ++m_iter; + next (); + return *this; +} + +const EditableSelectionIterator::value_type & +EditableSelectionIterator::operator* () const +{ + return *m_iter; +} + +void +EditableSelectionIterator::next () +{ + while (m_iter == m_end) { + ++m_service; + if (m_service < m_services.size ()) { + if (m_transient_selection) { + m_iter = m_services [m_service]->transient_selection ().begin (); + m_end = m_services [m_service]->transient_selection ().end (); + } else { + m_iter = m_services [m_service]->selection ().begin (); + m_end = m_services [m_service]->selection ().end (); + } + } else { + break; + } + } +} // ------------------------------------------------------------- +// Selection utilities implementation + +/** + * @brief Gets the combined selections over all editor services in the layout view + */ +std::vector object_selection (const lay::LayoutViewBase *view) +{ + std::vector result; + std::vector edt_services = view->get_plugins (); + for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { + std::vector sel; + (*s)->get_selection (sel); + result.insert (result.end (), sel.begin (), sel.end ()); + } + return result; +} + +/** + * @brief Distributes the combined selection over all editor services in the layout view + */ +void set_object_selection (const lay::LayoutViewBase *view, const std::vector &all_selected) +{ + std::vector sel; + + std::vector edt_services = view->get_plugins (); + for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { + + sel.clear (); + + for (std::vector::const_iterator o = all_selected.begin (); o != all_selected.end (); ++o) { + if ((*s)->selection_applies (*o)) { + sel.push_back (*o); + } + } + + (*s)->set_selection (sel.begin (), sel.end ()); + + } +} + +/** + * @brief Gets a value indicating whether any editor service in the view has a selection + */ +bool has_object_selection (const lay::LayoutViewBase *view) +{ + std::vector edt_services = view->get_plugins (); + for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { + if ((*s)->has_selection ()) { + return true; + } + } + return false; +} + +/** + * @brief Clears the selection of all editor services in the view + */ +void clear_object_selection (const lay::LayoutViewBase *view) +{ + std::vector edt_services = view->get_plugins (); + for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { + (*s)->clear_selection (); + } +} + +/** + * @brief Selects a specific object in the appropriate editor service of the view + */ +void select_object (const lay::LayoutViewBase *view, const edt::Service::objects::value_type &object) +{ + std::vector edt_services = view->get_plugins (); + for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { + if ((*s)->selection_applies (object)) { + (*s)->add_selection (object); + break; + } + } +} + +/** + * @brief Unselects a specific object in the appropriate editor service of the view + */ +void unselect_object (const lay::LayoutViewBase *view, const edt::Service::objects::value_type &object) +{ + std::vector edt_services = view->get_plugins (); + for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { + if ((*s)->selection_applies (object)) { + (*s)->remove_selection (object); + break; + } + } +} + +/** + * @brief Gets a value indicating whether any editor service in the view has a transient selection + */ +bool has_transient_object_selection (const lay::LayoutViewBase *view) +{ + std::vector edt_services = view->get_plugins (); + for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { + if ((*s)->has_transient_selection ()) { + return true; + } + } + return false; +} + +/** + * @brief Iterates over all selected object of all editor services + */ +EditableSelectionIterator begin_objects_selected (const lay::LayoutViewBase *view) +{ + return EditableSelectionIterator (view->get_plugins (), false); +} + +/** + * @brief Iterates over all transiently selected object of all editor services + */ +EditableSelectionIterator begin_objects_selected_transient (const lay::LayoutViewBase *view) +{ + return EditableSelectionIterator (view->get_plugins (), true); +} } // namespace edt - - diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 0890d9bc7..733be7298 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -688,6 +688,80 @@ private: void update_vector_snapped_marker (const lay::InstanceMarker *sm, const db::DTrans &trans, db::DVector &vr, bool &result_set, size_t &count) const; }; +/** + * @brief A utility class to implement a selection iterator across all editor services + */ +class EditableSelectionIterator +{ +public: + typedef edt::Service::objects::value_type value_type; + typedef edt::Service::objects::const_iterator iterator_type; + typedef void pointer; + typedef const value_type &reference; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + + EditableSelectionIterator (const std::vector &services, bool transient); + + bool at_end () const; + + EditableSelectionIterator &operator++ (); + const value_type &operator* () const; + +private: + std::vector m_services; + unsigned int m_service; + bool m_transient_selection; + iterator_type m_iter, m_end; + + void next (); +}; + +/** + * @brief Gets the combined selections over all editor services in the layout view + */ +EDT_PUBLIC std::vector object_selection (const lay::LayoutViewBase *view); + +/** + * @brief Distributes the combined selection over all editor services in the layout view + */ +EDT_PUBLIC void set_object_selection (const lay::LayoutViewBase *view, const std::vector &all_selected); + +/** + * @brief Gets a value indicating whether any editor service in the view has a selection + */ +EDT_PUBLIC bool has_object_selection (const lay::LayoutViewBase *view); + +/** + * @brief Clears the selection of all editor services in the view + */ +EDT_PUBLIC void clear_object_selection (const lay::LayoutViewBase *view); + +/** + * @brief Selects a specific object in the appropriate editor service of the view + */ +EDT_PUBLIC void select_object (const lay::LayoutViewBase *view, const edt::Service::objects::value_type &object); + +/** + * @brief Unselects a specific object in the appropriate editor service of the view + */ +EDT_PUBLIC void unselect_object (const lay::LayoutViewBase *view, const edt::Service::objects::value_type &object); + +/** + * @brief Gets a value indicating whether any editor service in the view has a transient selection + */ +EDT_PUBLIC bool has_transient_object_selection (const lay::LayoutViewBase *view); + +/** + * @brief Iterates over all selected object of all editor services + */ +EDT_PUBLIC EditableSelectionIterator begin_objects_selected (const lay::LayoutViewBase *view); + +/** + * @brief Iterates over all transiently selected object of all editor services + */ +EDT_PUBLIC EditableSelectionIterator begin_objects_selected_transient (const lay::LayoutViewBase *view); + } #endif diff --git a/src/edt/edt/gsiDeclEdt.cc b/src/edt/edt/gsiDeclEdt.cc index a7558e917..fcbd05629 100644 --- a/src/edt/edt/gsiDeclEdt.cc +++ b/src/edt/edt/gsiDeclEdt.cc @@ -185,7 +185,7 @@ gsi::Class decl_ObjectInstPath ("lay", "ObjectInstPath", "\n" "The top cell is identical to the current cell provided by the cell view.\n" "It is the cell from which is instantiation path originates and the container cell " - "if not instantiation path is set.\n" + "if no instantiation path is set.\n" "\n" "This method has been introduced in version 0.24." ) + @@ -425,175 +425,12 @@ gsi::Class decl_ObjectInstPath ("lay", "ObjectInstPath", "in the way shown above will help avoiding this issue.\n" ); -class EditableSelectionIterator -{ -public: - typedef edt::Service::objects::value_type value_type; - typedef edt::Service::objects::const_iterator iterator_type; - typedef void pointer; - typedef const value_type &reference; - typedef std::forward_iterator_tag iterator_category; - typedef void difference_type; - - EditableSelectionIterator (const std::vector &services, bool transient) - : m_services (services), m_service (0), m_transient_selection (transient) - { - if (! m_services.empty ()) { - if (m_transient_selection) { - m_iter = m_services [m_service]->transient_selection ().begin (); - m_end = m_services [m_service]->transient_selection ().end (); - } else { - m_iter = m_services [m_service]->selection ().begin (); - m_end = m_services [m_service]->selection ().end (); - } - next (); - } - } - - bool at_end () const - { - return (m_service >= m_services.size ()); - } - - EditableSelectionIterator &operator++ () - { - ++m_iter; - next (); - return *this; - } - - const value_type &operator* () const - { - return *m_iter; - } - -private: - std::vector m_services; - unsigned int m_service; - bool m_transient_selection; - iterator_type m_iter, m_end; - - void next () - { - while (m_iter == m_end) { - ++m_service; - if (m_service < m_services.size ()) { - if (m_transient_selection) { - m_iter = m_services [m_service]->transient_selection ().begin (); - m_end = m_services [m_service]->transient_selection ().end (); - } else { - m_iter = m_services [m_service]->selection ().begin (); - m_end = m_services [m_service]->selection ().end (); - } - } else { - break; - } - } - } -}; - -// extend the layout view by "edtService" specific methods - -static std::vector object_selection (const lay::LayoutViewBase *view) -{ - std::vector result; - std::vector edt_services = view->get_plugins (); - for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { - std::vector sel; - (*s)->get_selection (sel); - result.insert (result.end (), sel.begin (), sel.end ()); - } - return result; -} - -static void set_object_selection (const lay::LayoutViewBase *view, const std::vector &all_selected) -{ - std::vector sel; - - std::vector edt_services = view->get_plugins (); - for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { - - sel.clear (); - - for (std::vector::const_iterator o = all_selected.begin (); o != all_selected.end (); ++o) { - if ((*s)->selection_applies (*o)) { - sel.push_back (*o); - } - } - - (*s)->set_selection (sel.begin (), sel.end ()); - - } -} - -static bool has_object_selection (const lay::LayoutViewBase *view) -{ - std::vector edt_services = view->get_plugins (); - for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { - if ((*s)->has_selection ()) { - return true; - } - } - return false; -} - -static void clear_object_selection (const lay::LayoutViewBase *view) -{ - std::vector edt_services = view->get_plugins (); - for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { - (*s)->clear_selection (); - } -} - -static void select_object (const lay::LayoutViewBase *view, const edt::Service::objects::value_type &object) -{ - std::vector edt_services = view->get_plugins (); - for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { - if ((*s)->selection_applies (object)) { - (*s)->add_selection (object); - break; - } - } -} - -static void unselect_object (const lay::LayoutViewBase *view, const edt::Service::objects::value_type &object) -{ - std::vector edt_services = view->get_plugins (); - for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { - if ((*s)->selection_applies (object)) { - (*s)->remove_selection (object); - break; - } - } -} - -static bool has_transient_object_selection (const lay::LayoutViewBase *view) -{ - std::vector edt_services = view->get_plugins (); - for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { - if ((*s)->has_transient_selection ()) { - return true; - } - } - return false; -} - -static EditableSelectionIterator begin_objects_selected (const lay::LayoutViewBase *view) -{ - return EditableSelectionIterator (view->get_plugins (), false); -} - -static EditableSelectionIterator begin_objects_selected_transient (const lay::LayoutViewBase *view) -{ - return EditableSelectionIterator (view->get_plugins (), true); -} - static gsi::ClassExt layout_view_decl ( - gsi::method_ext ("has_object_selection?", &has_object_selection, + gsi::method_ext ("has_object_selection?", &edt::has_object_selection, "@brief Returns true, if geometrical objects (shapes or cell instances) are selected in this view" ) + - gsi::method_ext ("object_selection", &object_selection, + gsi::method_ext ("object_selection", &edt::object_selection, "@brief Returns a list of selected objects\n" "This method will deliver an array of \\ObjectInstPath objects listing the selected geometrical " "objects. Other selected objects such as annotations and images will not be contained in that " @@ -607,7 +444,7 @@ gsi::ClassExt layout_view_decl ( "\n" "This method has been introduced in version 0.24.\n" ) + - gsi::method_ext ("object_selection=", &set_object_selection, gsi::arg ("sel"), + gsi::method_ext ("object_selection=", &edt::set_object_selection, gsi::arg ("sel"), "@brief Sets the list of selected objects\n" "\n" "This method will set the selection of geometrical objects such as shapes and instances. " @@ -617,13 +454,13 @@ gsi::ClassExt layout_view_decl ( "\n" "This method has been introduced in version 0.24.\n" ) + - gsi::method_ext ("clear_object_selection", &clear_object_selection, + gsi::method_ext ("clear_object_selection", &edt::clear_object_selection, "@brief Clears the selection of geometrical objects (shapes or cell instances)\n" "The selection of other objects (such as annotations and images) will not be affected.\n" "\n" "This method has been introduced in version 0.24\n" ) + - gsi::method_ext ("select_object", &select_object, gsi::arg ("obj"), + gsi::method_ext ("select_object", &edt::select_object, gsi::arg ("obj"), "@brief Adds the given selection to the list of selected objects\n" "\n" "The selection provided by the \\ObjectInstPath descriptor is added to the list of selected objects.\n" @@ -635,7 +472,7 @@ gsi::ClassExt layout_view_decl ( "\n" "This method has been introduced in version 0.24\n" ) + - gsi::method_ext ("unselect_object", &unselect_object, gsi::arg ("obj"), + gsi::method_ext ("unselect_object", &edt::unselect_object, gsi::arg ("obj"), "@brief Removes the given selection from the list of selected objects\n" "\n" "The selection provided by the \\ObjectInstPath descriptor is removed from the list of selected objects.\n" @@ -644,7 +481,7 @@ gsi::ClassExt layout_view_decl ( "\n" "This method has been introduced in version 0.24\n" ) + - gsi::iterator_ext ("each_object_selected", &begin_objects_selected, + gsi::iterator_ext ("each_object_selected", &edt::begin_objects_selected, "@brief Iterates over each selected geometrical object, yielding a \\ObjectInstPath object for each of them\n" "\n" "This iterator will deliver const objects - they cannot be modified. In order to modify the selection, " @@ -653,7 +490,7 @@ gsi::ClassExt layout_view_decl ( "\n" "Another way of obtaining the selection is \\object_selection, which returns an array of \\ObjectInstPath objects.\n" ) + - gsi::method_ext ("has_transient_object_selection?", &has_transient_object_selection, + gsi::method_ext ("has_transient_object_selection?", &edt::has_transient_object_selection, "@brief Returns true, if geometrical objects (shapes or cell instances) are selected in this view in the transient selection\n" "\n" "The transient selection represents the objects selected when the mouse hovers over the " @@ -662,7 +499,7 @@ gsi::ClassExt layout_view_decl ( "\n" "This method was introduced in version 0.18." ) + - gsi::iterator_ext ("each_object_selected_transient", &begin_objects_selected_transient, + gsi::iterator_ext ("each_object_selected_transient", &edt::begin_objects_selected_transient, "@brief Iterates over each geometrical objects in the transient selection, yielding a \\ObjectInstPath object for each of them\n" "\n" "This method was introduced in version 0.18." diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index ec79fc1a3..c8c28477c 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -39,6 +39,7 @@ #include "tlProgress.h" #include "tlTimer.h" #include "rdbUtils.h" +#include "edtService.h" #include #include @@ -457,6 +458,73 @@ escape_csv (const std::string &s) } } +void +SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index) +{ + const lay::CellView &cv = view->cellview (cv_index); + const db::Layout &layout = cv->layout (); + + std::vector sel; + + int n_rows = int (size ()); + for (int r = 0; r < n_rows; ++r) { + + if (r < int (shapes ().size ())) { + + const SearchReplaceResults::QueryShapeResult &sr = shapes () [r]; + if (! sr.shape.is_null () && layout.is_valid_cell_index (sr.initial_cell_index) && layout.is_valid_cell_index (sr.cell_index)) { + + sel.push_back (edt::Service::objects::value_type ()); + sel.back ().set_cv_index (cv_index); + sel.back ().set_layer (sr.layer_index); + sel.back ().set_shape (sr.shape); + sel.back ().set_topcell (sr.initial_cell_index); + + if (sr.inst_elements.has_value ()) { + sel.back ().assign_path (sr.inst_elements->begin (), sr.inst_elements->end ()); + } else { + std::vector path; + if (db::find_path (layout, sr.cell_index, sr.initial_cell_index, path)) { + sel.back ().assign_path (path.begin (), path.end ()); + } else { + sel.pop_back (); + } + } + + } + + } else if (r < int (instances ().size ())) { + + const SearchReplaceResults::QueryInstResult &ir = instances () [r]; + if (! ir.inst.is_null () && layout.is_valid_cell_index (ir.initial_cell_index) && layout.is_valid_cell_index (ir.cell_index)) { + + sel.push_back (edt::Service::objects::value_type ()); + sel.back ().set_cv_index (cv_index); + sel.back ().set_topcell (ir.initial_cell_index); + + if (ir.inst_elements.has_value ()) { + sel.back ().assign_path (ir.inst_elements->begin (), ir.inst_elements->end ()); + sel.back ().add_path (db::InstElement (ir.inst)); + } else { + std::vector path; + if (db::find_path (layout, ir.cell_index, ir.initial_cell_index, path)) { + sel.back ().assign_path (path.begin (), path.end ()); + // TODO: specific array instance? + sel.back ().add_path (db::InstElement (ir.inst)); + } else { + sel.pop_back (); + } + } + + } + + } + + } + + edt::set_object_selection (view, sel); +} + void SearchReplaceResults::export_csv (const std::string &file) { @@ -751,6 +819,7 @@ SearchReplaceDialog::SearchReplaceDialog (lay::Dispatcher *root, LayoutViewBase 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 ())); + menu->addAction (QObject::tr ("Select items"), this, SLOT (select_items ())); export_b->setMenu (menu); bool editable = view->is_editable (); @@ -933,6 +1002,38 @@ SearchReplaceDialog::save_state () } } +void +SearchReplaceDialog::select_items () +{ +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::max (), true, true /*with paths*/); + model.end_changes (); + model.select_items (view (), cv_index); + +END_PROTECTED +} + void SearchReplaceDialog::export_csv () { @@ -1434,7 +1535,7 @@ SearchReplaceDialog::update_results (const std::string &q) } try { - fill_model (lq, iq, &cv->layout (), true); + fill_model (lq, iq, &cv->layout (), true, false); attach_layout (&cv->layout ()); } catch (...) { attach_layout (&cv->layout ()); @@ -1445,7 +1546,7 @@ SearchReplaceDialog::update_results (const std::string &q) } bool -SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, size_t max_item_count, bool all) +SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, size_t max_item_count, bool all, bool with_path) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (QObject::tr ("Query run"))); @@ -1456,6 +1557,7 @@ SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::Layo 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 inst_elements_prop_id = with_path && lq.has_property ("inst_elements") ? int (lq.property_by_name ("inst_elements")) : -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; @@ -1503,6 +1605,18 @@ SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::Layo model.push_back (SearchReplaceResults::QueryShapeResult (shape, layer_index, trans, cell_index, initial_cell_index)); + if (inst_elements_prop_id >= 0 && iq.get (inst_elements_prop_id, v) && v.is_list ()) { + try { + std::vector inst_elements; + for (auto i = v.begin (); i != v.end (); ++i) { + inst_elements.push_back (i->to_user ()); + } + model.shapes ().back ().inst_elements = std::move (inst_elements); + } catch (...) { + // ignore conversion errors + } + } + } else if (instance_prop_id >= 0) { db::Instance instance; @@ -1529,6 +1643,18 @@ SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::Layo model.push_back (SearchReplaceResults::QueryInstResult (instance, trans, cell_index, initial_cell_index)); + if (inst_elements_prop_id >= 0 && iq.get (inst_elements_prop_id, v) && v.is_list ()) { + try { + std::vector inst_elements; + for (auto i = v.begin (); i != v.end (); ++i) { + inst_elements.push_back (i->to_user ()); + } + model.instances ().back ().inst_elements = std::move (inst_elements); + } catch (...) { + // ignore conversion errors + } + } + } else if (cell_index_prop_id >= 0) { db::cell_index_type cell_index = std::numeric_limits::max (); @@ -1559,7 +1685,7 @@ SearchReplaceDialog::query_to_model (SearchReplaceResults &model, const db::Layo } bool -SearchReplaceDialog::fill_model (const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, const db::Layout *layout, bool all) +SearchReplaceDialog::fill_model (const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, const db::Layout *layout, bool all, bool with_paths) { bool res = false; @@ -1568,7 +1694,7 @@ SearchReplaceDialog::fill_model (const db::LayoutQuery &lq, db::LayoutQueryItera m_model.begin_changes (layout); m_model.clear (); - res = query_to_model (m_model, lq, iq, m_max_item_count, all); + res = query_to_model (m_model, lq, iq, m_max_item_count, all, with_paths); m_model.end_changes (); results_stack->setCurrentIndex (0); diff --git a/src/lay/lay/laySearchReplaceDialog.h b/src/lay/lay/laySearchReplaceDialog.h index 19823be47..5ca3a224c 100644 --- a/src/lay/lay/laySearchReplaceDialog.h +++ b/src/lay/lay/laySearchReplaceDialog.h @@ -32,6 +32,8 @@ #include "dbLayout.h" #include "dbShape.h" #include "dbInstances.h" +#include "dbInstElement.h" +#include "tlOptional.h" #include @@ -67,6 +69,7 @@ public: db::ICplxTrans trans; db::cell_index_type cell_index; db::cell_index_type initial_cell_index; + tl::optional > inst_elements; }; struct QueryInstResult @@ -79,6 +82,7 @@ public: db::ICplxTrans trans; db::cell_index_type cell_index; db::cell_index_type initial_cell_index; + tl::optional > inst_elements; }; struct QueryCellResult @@ -106,21 +110,41 @@ public: return m_data_result; } + std::vector &data () + { + return m_data_result; + } + const std::vector &shapes () const { return m_shape_result; } + std::vector &shapes () + { + return m_shape_result; + } + const std::vector &instances () const { return m_inst_result; } + std::vector &instances () + { + return m_inst_result; + } + const std::vector &cells () const { return m_cell_result; } + std::vector &cells () + { + return m_cell_result; + } + int columnCount (const QModelIndex &parent) const; QVariant data (const QModelIndex &index, int role) const; Qt::ItemFlags flags (const QModelIndex &index) const; @@ -135,6 +159,7 @@ public: void export_csv (const std::string &file); void export_layout (db::Layout &layout); void export_rdb (rdb::Database &rdb, double dbu); + void select_items (LayoutViewBase *view, int cv_index); private: std::vector m_data_result; @@ -212,6 +237,7 @@ private slots: void header_columns_changed (int from, int to); void cancel (); void cancel_exec (); + void select_items (); void export_csv (); void export_rdb (); void export_layout (); @@ -227,8 +253,8 @@ private: void issue_query (const std::string &q, const std::set *selected_items, bool with_results); void update_results (const std::string &q); void remove_markers (); - bool fill_model (const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, const db::Layout *layout, bool all); - bool query_to_model (SearchReplaceResults &model, const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, size_t max_item_count, bool all); + bool fill_model (const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, const db::Layout *layout, bool all, bool with_paths); + bool query_to_model (SearchReplaceResults &model, const db::LayoutQuery &lq, db::LayoutQueryIterator &iq, size_t max_item_count, bool all, bool with_path = false); void attach_layout (db::Layout *layout); void layout_changed (); diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h index 3893d6ac0..9fb96e0ea 100644 --- a/src/tl/tl/tlOptional.h +++ b/src/tl/tl/tlOptional.h @@ -53,11 +53,30 @@ public: m_is_valid (false) {} - optional (const T &value) : + explicit optional (const T &value) : m_value (value), m_is_valid (true) {} + explicit optional (T &&value) : + m_value (value), + m_is_valid (true) + {} + + optional &operator= (const T &value) + { + m_value = value; + m_is_valid = true; + return *this; + } + + optional &operator= (T &&value) + { + m_value = value; + m_is_valid = true; + return *this; + } + void reset () { m_is_valid = false;