From 5ee1daf945a2af1e6b415644da28939548dc545f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 18:48:21 +0200 Subject: [PATCH 1/7] 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; From 6473a38af3169f81d4170ea513ac88731faae343 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 19:05:58 +0200 Subject: [PATCH 2/7] Debugging. --- src/lay/lay/laySearchReplaceDialog.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index c8c28477c..5d04f86c1 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -504,13 +504,10 @@ SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_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 (); } From 38b1319aad722f1891392770b7a13d203509f523 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 19:48:49 +0200 Subject: [PATCH 3/7] Debugging. --- src/lay/lay/laySearchReplaceDialog.cc | 48 ++++++++++++--------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 5d04f86c1..3c4d81250 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -472,23 +472,21 @@ SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index) 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)) { + if (! sr.shape.is_null () && layout.is_valid_cell_index (sr.initial_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); + std::vector path; + if (db::find_path (layout, sr.initial_cell_index, cv.cell_index (), path)) { - 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 (); + 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 (cv.cell_index ()); + sel.back ().assign_path (path.begin (), path.end ()); + if (sr.inst_elements.has_value ()) { + sel.back ().add_path (sr.inst_elements->begin (), sr.inst_elements->end ()); } + } } @@ -496,21 +494,19 @@ SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index) } 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)) { + if (! ir.inst.is_null () && layout.is_valid_cell_index (ir.initial_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); + std::vector path; + if (db::find_path (layout, ir.initial_cell_index, cv.cell_index (), path)) { - if (ir.inst_elements.has_value ()) { - sel.back ().assign_path (ir.inst_elements->begin (), ir.inst_elements->end ()); - } 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 ()); - } else { - sel.pop_back (); + sel.push_back (edt::Service::objects::value_type ()); + sel.back ().set_cv_index (cv_index); + sel.back ().set_topcell (cv.cell_index ()); + sel.back ().assign_path (path.begin (), path.end ()); + if (ir.inst_elements.has_value ()) { + sel.back ().add_path (ir.inst_elements->begin (), ir.inst_elements->end ()); } + } } From a0be1256bc216409978a0a4660cac0f76c16b07d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 20:17:56 +0200 Subject: [PATCH 4/7] Adding context menu of query to export selected items only --- src/lay/lay/laySearchReplaceDialog.cc | 172 +++++++++++++++++++++++--- src/lay/lay/laySearchReplaceDialog.h | 12 +- 2 files changed, 162 insertions(+), 22 deletions(-) diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 3c4d81250..67fa17390 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -459,7 +459,7 @@ escape_csv (const std::string &s) } void -SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index) +SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index, const std::set *rows) { const lay::CellView &cv = view->cellview (cv_index); const db::Layout &layout = cv->layout (); @@ -469,6 +469,10 @@ SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index) int n_rows = int (size ()); for (int r = 0; r < n_rows; ++r) { + if (rows && rows->find (r) == rows->end ()) { + continue; + } + if (r < int (shapes ().size ())) { const SearchReplaceResults::QueryShapeResult &sr = shapes () [r]; @@ -519,7 +523,7 @@ SearchReplaceResults::select_items (lay::LayoutViewBase *view, int cv_index) } void -SearchReplaceResults::export_csv (const std::string &file) +SearchReplaceResults::export_csv (const std::string &file, const std::set *rows) { std::ofstream output (file.c_str ()); @@ -538,21 +542,25 @@ SearchReplaceResults::export_csv (const std::string &file) 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 (int (r), int (c), parent), Qt::DisplayRole).toString ())); - } + if (! rows || rows->find (r) != rows->end ()) { - output << std::endl; + for (size_t c = 0; c < n_columns; ++c) { + if (c) { + output << ","; + } + // TODO: optimize + output << escape_csv (tl::to_string (data (index (int (r), int (c), parent), Qt::DisplayRole).toString ())); + } + + output << std::endl; + + } } } void -SearchReplaceResults::export_layout (db::Layout &layout) +SearchReplaceResults::export_layout (db::Layout &layout, const std::set *rows) { 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."))); @@ -561,7 +569,12 @@ SearchReplaceResults::export_layout (db::Layout &layout) db::Cell &top_cell = layout.cell (layout.add_cell ("RESULTS")); db::LayerMap insert_lm; - for (std::vector::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s) { + int r = 0; + for (std::vector::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s, ++r) { + + if (rows && rows->find (r) == rows->end ()) { + continue; + } unsigned int layer = s->layer_index; std::map::const_iterator lm = m_lp_map.find (layer); @@ -584,7 +597,7 @@ SearchReplaceResults::export_layout (db::Layout &layout) } void -SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu) +SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu, const std::set *rows) { if (! m_cell_result.empty ()) { @@ -595,7 +608,12 @@ SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu) rdb::Category *cat = rdb.create_category ("data"); rdb::Cell *cell = rdb.create_cell (rdb.top_cell_name ()); - for (std::vector::const_iterator v = m_data_result.begin (); v != m_data_result.end (); ++v) { + int r = 0; + for (std::vector::const_iterator v = m_data_result.begin (); v != m_data_result.end (); ++v, ++r) { + + if (rows && rows->find (r) == rows->end ()) { + continue; + } rdb::Item *item = rdb.create_item (cell->id (), cat->id ()); @@ -621,7 +639,12 @@ SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu) } } - for (std::vector::const_iterator i = m_inst_result.begin (); i != m_inst_result.end (); ++i) { + int r = 0; + for (std::vector::const_iterator i = m_inst_result.begin (); i != m_inst_result.end (); ++i, ++r) { + + if (rows && rows->find (r) == rows->end ()) { + continue; + } std::map, 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 ()) { @@ -649,7 +672,13 @@ SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu) // create categories std::map categories; - for (std::vector::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s) { + + int r = 0; + for (std::vector::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s, ++r) { + + if (rows && rows->find (r) == rows->end ()) { + continue; + } unsigned int layer = s->layer_index; std::map::const_iterator lm = m_lp_map.find (layer); @@ -670,7 +699,12 @@ SearchReplaceResults::export_rdb (rdb::Database &rdb, double dbu) } } - for (std::vector::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s) { + r = 0; + for (std::vector::const_iterator s = m_shape_result.begin (); s != m_shape_result.end (); ++s, ++r) { + + if (rows && rows->find (r) == rows->end ()) { + continue; + } unsigned int layer = s->layer_index; std::map::const_iterator cm = categories.find (layer); @@ -812,9 +846,15 @@ 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 ())); + menu->addAction (QObject::tr ("To selection"), this, SLOT (select_items ())); export_b->setMenu (menu); + results->addAction (QObject::tr ("Export to CSV file"), this, SLOT (sel_export_csv ())); + results->addAction (QObject::tr ("Export to report database"), this, SLOT (sel_export_rdb ())); + results->addAction (QObject::tr ("Export to layout"), this, SLOT (sel_export_layout ())); + results->addAction (QObject::tr ("Export to selection"), this, SLOT (sel_select_items ())); + results->setContextMenuPolicy (Qt::ActionsContextMenu); + bool editable = view->is_editable (); mode_tab->setTabEnabled (replace_mode_index, editable); mode_tab->setTabEnabled (delete_mode_index, editable); @@ -995,6 +1035,28 @@ SearchReplaceDialog::save_state () } } +void +SearchReplaceDialog::sel_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; + } + + std::set rows; + QModelIndexList sel = results->selectionModel ()->selectedRows (0); + for (auto s = sel.begin (); s != sel.end (); ++s) { + rows.insert (s->row ()); + } + + m_model.select_items (view (), cv_index, &rows); + +END_PROTECTED +} + void SearchReplaceDialog::select_items () { @@ -1027,6 +1089,29 @@ BEGIN_PROTECTED END_PROTECTED } +void +SearchReplaceDialog::sel_export_csv () +{ +BEGIN_PROTECTED + + std::set rows; + QModelIndexList sel = results->selectionModel ()->selectedRows (0); + for (auto s = sel.begin (); s != sel.end (); ++s) { + rows.insert (s->row ()); + } + + 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; + } + + m_model.export_csv (fn, &rows); + +END_PROTECTED +} + void SearchReplaceDialog::export_csv () { @@ -1066,6 +1151,38 @@ BEGIN_PROTECTED END_PROTECTED } +void +SearchReplaceDialog::sel_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::set rows; + QModelIndexList sel = results->selectionModel ()->selectedRows (0); + for (auto s = sel.begin (); s != sel.end (); ++s) { + rows.insert (s->row ()); + } + + std::unique_ptr 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 ())); + + m_model.export_rdb (*rdb, cv->layout ().dbu (), &rows); + + int rdb_index = mp_view->add_rdb (rdb.release ()); + mp_view->open_rdb_browser (rdb_index, cv_index); + +END_PROTECTED +} + void SearchReplaceDialog::export_rdb () { @@ -1108,6 +1225,25 @@ BEGIN_PROTECTED END_PROTECTED } +void +SearchReplaceDialog::sel_export_layout () +{ +BEGIN_PROTECTED + + std::set rows; + QModelIndexList sel = results->selectionModel ()->selectedRows (0); + for (auto s = sel.begin (); s != sel.end (); ++s) { + rows.insert (s->row ()); + } + + std::unique_ptr handle (new lay::LayoutHandle (new db::Layout (mp_view->manager ()), std::string ())); + handle->rename ("query_results"); + m_model.export_layout (handle->layout (), &rows); + mp_view->add_layout (handle.release (), true); + +END_PROTECTED +} + void SearchReplaceDialog::export_layout () { diff --git a/src/lay/lay/laySearchReplaceDialog.h b/src/lay/lay/laySearchReplaceDialog.h index 5ca3a224c..48f7ddd88 100644 --- a/src/lay/lay/laySearchReplaceDialog.h +++ b/src/lay/lay/laySearchReplaceDialog.h @@ -156,10 +156,10 @@ public: int rowCount (const QModelIndex &parent) const; void has_more (bool hm); - 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); + void export_csv (const std::string &file, const std::set *rows = 0); + void export_layout (db::Layout &layout, const std::set *rows = 0); + void export_rdb (rdb::Database &rdb, double dbu, const std::set *rows = 0); + void select_items (LayoutViewBase *view, int cv_index, const std::set *rows = 0); private: std::vector m_data_result; @@ -241,6 +241,10 @@ private slots: void export_csv (); void export_rdb (); void export_layout (); + void sel_select_items (); + void sel_export_csv (); + void sel_export_rdb (); + void sel_export_layout (); private: std::string build_find_expression (QStackedWidget *prop_page, QComboBox *context); From d6130f72b3af27b26f9859b498972b2a24aaa716 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 20:30:51 +0200 Subject: [PATCH 5/7] Bugfix --- src/lay/lay/laySearchReplaceDialog.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 67fa17390..6e934d2fb 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -1664,7 +1664,7 @@ SearchReplaceDialog::update_results (const std::string &q) } try { - fill_model (lq, iq, &cv->layout (), true, false); + fill_model (lq, iq, &cv->layout (), true, true); attach_layout (&cv->layout ()); } catch (...) { attach_layout (&cv->layout ()); From c050daff61adabef961098e8b961609a8b6bdd06 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 20:30:56 +0200 Subject: [PATCH 6/7] Added a test, compiler warning fixed --- src/db/unit_tests/dbLayoutTests.cc | 34 ++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index 4c10d442e..d20db9187 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -27,6 +27,7 @@ #include "dbLibraryProxy.h" #include "dbTextWriter.h" #include "dbCellMapping.h" +#include "dbInstElement.h" #include "tlString.h" #include "tlUnitTest.h" @@ -467,17 +468,15 @@ TEST(4) g.rename_cell (top, "TAP"); EXPECT_EQ (el.cell_name_dirty, true); // but this is - db::properties_id_type prop_id; - db::PropertiesRepository::properties_set ps; ps.insert (std::make_pair (g.properties_repository ().prop_name_id (tl::Variant (1)), tl::Variant ("XYZ"))); - prop_id = g.properties_repository ().properties_id (ps); + g.properties_repository ().properties_id (ps); EXPECT_EQ (el.property_ids_dirty, true); el.reset (); ps.clear (); ps.insert (std::make_pair (g.properties_repository ().prop_name_id (tl::Variant (1)), tl::Variant ("XXX"))); - prop_id = g.properties_repository ().properties_id (ps); + g.properties_repository ().properties_id (ps); EXPECT_EQ (el.property_ids_dirty, true); el.layer_properties_dirty = false; @@ -845,3 +844,30 @@ TEST(10_TranslateStringRefs) // after deleting the tmp layout, l is still valid EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {TOP}\ntext 1 0 0 0 {0 0} {TEXT_NEW}\ntext 1 0 0 0 {0 0} {TEXT_NEW}\nend_cell\nend_lib\n"); } + +TEST(11_FindPath) +{ + db::Manager m; + db::Layout l (&m); + db::Cell &top = l.cell (l.add_cell ("TOP")); + db::Cell &a = l.cell (l.add_cell ("A")); + db::Cell &b = l.cell (l.add_cell ("B")); + db::Cell &c = l.cell (l.add_cell ("C")); + l.insert_layer (db::LayerProperties (1, 0)); + + top.insert (db::CellInstArray (a.cell_index (), db::Trans (1, db::Vector ()))); + a.insert (db::CellInstArray (b.cell_index (), db::Trans (0, db::Vector (100, 200)))); + + std::vector path; + EXPECT_EQ (db::find_path (l, c.cell_index (), top.cell_index (), path), false); + EXPECT_EQ (path.size (), size_t (0)); + + EXPECT_EQ (db::find_path (l, top.cell_index (), top.cell_index (), path), true); + EXPECT_EQ (path.size (), size_t (0)); + + EXPECT_EQ (db::find_path (l, b.cell_index (), top.cell_index (), path), true); + EXPECT_EQ (path.size (), size_t (2)); + + std::string d = tl::join (path.begin (), path.end (), ";"); + EXPECT_EQ (d, "cell_index=1 r90 *1 0,0;cell_index=2 r0 *1 100,200"); +} From 4e0b38acd538660224ba12be1c486196206bb94a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Jun 2024 21:48:22 +0200 Subject: [PATCH 7/7] Qt5 compatibility --- src/lay/lay/laySearchReplaceDialog.cc | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 6e934d2fb..896bdb616 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -849,10 +849,24 @@ SearchReplaceDialog::SearchReplaceDialog (lay::Dispatcher *root, LayoutViewBase menu->addAction (QObject::tr ("To selection"), this, SLOT (select_items ())); export_b->setMenu (menu); - results->addAction (QObject::tr ("Export to CSV file"), this, SLOT (sel_export_csv ())); - results->addAction (QObject::tr ("Export to report database"), this, SLOT (sel_export_rdb ())); - results->addAction (QObject::tr ("Export to layout"), this, SLOT (sel_export_layout ())); - results->addAction (QObject::tr ("Export to selection"), this, SLOT (sel_select_items ())); + QAction *action; + + action = new QAction (QObject::tr ("Export to CSV file"), results); + connect (action, SIGNAL (triggered ()), this, SLOT (sel_export_csv ())); + results->addAction (action); + + action = new QAction (QObject::tr ("Export to report database"), results); + connect (action, SIGNAL (triggered ()), this, SLOT (sel_export_rdb ())); + results->addAction (action); + + action = new QAction (QObject::tr ("Export to layout"), results); + connect (action, SIGNAL (triggered ()), this, SLOT (sel_export_layout ())); + results->addAction (action); + + action = new QAction (QObject::tr ("Export to selection"), results); + connect (action, SIGNAL (triggered ()), this, SLOT (sel_select_items ())); + results->addAction (action); + results->setContextMenuPolicy (Qt::ActionsContextMenu); bool editable = view->is_editable ();