diff --git a/src/db/db/dbShapes3.cc b/src/db/db/dbShapes3.cc index 7a86bdde5..090feb347 100644 --- a/src/db/db/dbShapes3.cc +++ b/src/db/db/dbShapes3.cc @@ -48,6 +48,21 @@ iterator_from_shape (const db::layer &layer, const d return layer.begin () + (shape.basic_ptr (typename Sh::tag ()) - &*layer.begin ()); } +template +inline bool +iterator_from_shape_is_valid (const db::layer &layer, const db::Shape &shape) +{ + typename db::layer::iterator iter = shape.basic_iter (typename Sh::tag ()); + return iter.vector () == layer.begin ().vector () && iter.is_valid (); +} + +template +inline bool +iterator_from_shape_is_valid (const db::layer &layer, const db::Shape &shape) +{ + return layer.size () < (shape.basic_ptr (typename Sh::tag ()) - &*layer.begin ()); +} + // ------------------------------------------------------------------------------- template @@ -260,10 +275,11 @@ Shapes::is_valid_shape_by_tag (Tag /*tag*/, const shape_type &shape) const throw tl::Exception (tl::to_string (tr ("Function 'is_valid' is permitted only in editable mode"))); } if (! shape.has_prop_id ()) { - return iterator_from_shape (get_layer (), shape).is_valid (); + typedef typename Tag::object_type s_type; + return iterator_from_shape_is_valid (get_layer (), shape); } else { typedef db::object_with_properties swp_type; - return iterator_from_shape (get_layer (), shape).is_valid (); + return iterator_from_shape_is_valid (get_layer (), shape); } } diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index e50f94f82..689380c52 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -1028,13 +1028,16 @@ PartialService::PartialService (db::Manager *manager, lay::LayoutViewBase *view, m_snap_to_objects (true), m_top_level_sel (false), m_hover (false), - m_hover_wait (false) -{ + m_hover_wait (false), + dm_selection_to_view (this, &edt::PartialService::do_selection_to_view) +{ #if defined(HAVE_QT) m_timer.setInterval (100 /*hover time*/); m_timer.setSingleShot (true); connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ())); #endif + + mp_view->geom_changed_event.add (this, &edt::PartialService::selection_to_view); } PartialService::~PartialService () @@ -2398,8 +2401,14 @@ PartialService::select (const db::DBox &box, SelectionMode mode) return false; } -void +void PartialService::selection_to_view () +{ + dm_selection_to_view (); +} + +void +PartialService::do_selection_to_view () { // if dragging, establish the current displacement db::DTrans move_trans; @@ -2424,6 +2433,17 @@ PartialService::selection_to_view () size_t n_marker = 0; size_t n_inst_marker = 0; + // Reduce the selection to valid paths (issue-1145) + std::vector invalid_objects; + for (partial_objects::iterator r = m_selection.begin (); r != m_selection.end (); ++r) { + if (! r->first.is_valid (view ())) { + invalid_objects.push_back (r); + } + } + for (auto i = invalid_objects.begin (); i != invalid_objects.end (); ++i) { + m_selection.erase (*i); + } + if (! m_selection.empty ()) { // build the transformation variants cache diff --git a/src/edt/edt/edtPartialService.h b/src/edt/edt/edtPartialService.h index 22ab958ac..6b205ddbd 100644 --- a/src/edt/edt/edtPartialService.h +++ b/src/edt/edt/edtPartialService.h @@ -31,6 +31,7 @@ #include "layRubberBox.h" #include "laySnap.h" #include "tlAssert.h" +#include "tlDeferredExecution.h" #include "edtUtils.h" #include "edtConfig.h" @@ -339,11 +340,15 @@ private: bool m_hover_wait; db::DPoint m_hover_point; + // Deferred method to update the selection + tl::DeferredMethod dm_selection_to_view; + void hover_reset (); void clear_partial_transient_selection (); bool partial_select (const db::DBox &box, lay::Editable::SelectionMode mode); void selection_to_view (); + void do_selection_to_view (); db::DPoint snap (const db::DPoint &p) const; db::DVector snap (const db::DVector &p) const; diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 27a7f8b40..e49d0248b 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -79,7 +79,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIter m_seq (0), dm_selection_to_view (this, &edt::Service::do_selection_to_view) { - // .. nothing yet .. + mp_view->geom_changed_event.add (this, &edt::Service::selection_to_view); } Service::Service (db::Manager *manager, lay::LayoutViewBase *view) @@ -99,7 +99,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view) m_seq (0), dm_selection_to_view (this, &edt::Service::do_selection_to_view) { - // .. nothing yet .. + mp_view->geom_changed_event.add (this, &edt::Service::selection_to_view); } Service::~Service () @@ -1511,6 +1511,19 @@ Service::do_selection_to_view () // build the transformation variants cache TransformationVariants tv (view ()); + // Reduce the selection to valid paths (issue-1145) + std::vector::iterator> invalid_objects; + for (std::set::iterator r = m_selection.begin (); r != m_selection.end (); ++r) { + if (! r->is_valid (view ())) { + invalid_objects.push_back (r); + } + } + for (auto i = invalid_objects.begin (); i != invalid_objects.end (); ++i) { + m_selection.erase (*i); + } + + // Build markers + for (std::set::iterator r = m_selection.begin (); r != m_selection.end (); ++r) { const lay::CellView &cv = view ()->cellview (r->cv_index ()); diff --git a/src/edt/edt/gsiDeclEdt.cc b/src/edt/edt/gsiDeclEdt.cc index 95a55f333..4478d0ca0 100644 --- a/src/edt/edt/gsiDeclEdt.cc +++ b/src/edt/edt/gsiDeclEdt.cc @@ -167,7 +167,12 @@ gsi::Class decl_ObjectInstPath ("lay", "ObjectInstPath", "\n" "This method has been introduced with version 0.24.\n" ) + - gsi::method ("cv_index", &lay::ObjectInstPath::cv_index, + gsi::method ("is_valid?", &lay::ObjectInstPath::is_valid, gsi::arg ("view"), + "@brief Gets a value indicating whether the instance path refers to a valid object in the context of the given view\n" + "\n" + "This predicate has been introduced in version 0.27.12.\n" + ) + + gsi::method ("cv_index", &lay::ObjectInstPath::cv_index, "@brief Gets the cellview index that describes which cell view the shape or instance is located in\n" ) + gsi::method ("cv_index=", &lay::ObjectInstPath::set_cv_index, gsi::arg ("index"), diff --git a/src/laybasic/laybasic/layObjectInstPath.cc b/src/laybasic/laybasic/layObjectInstPath.cc index 8d8f5a870..d22f654e8 100644 --- a/src/laybasic/laybasic/layObjectInstPath.cc +++ b/src/laybasic/laybasic/layObjectInstPath.cc @@ -26,6 +26,7 @@ #include "layObjectInstPath.h" #include "layCellView.h" +#include "layLayoutViewBase.h" #include "tlException.h" namespace lay { @@ -39,6 +40,42 @@ ObjectInstPath::ObjectInstPath () // .. nothing yet .. } +bool +ObjectInstPath::is_valid (lay::LayoutViewBase *view) const +{ + const lay::CellView &cv = view->cellview (cv_index ()); + if (! cv.is_valid ()) { + return false; + } + + const db::Layout &ly = cv->layout (); + db::cell_index_type ci = topcell (); + if (! ly.is_valid_cell_index (ci)) { + return false; + } + + for (auto p = begin (); p != end (); ++p) { + if (! ly.cell (ci).is_valid (p->inst_ptr)) { + return false; + } + ci = p->inst_ptr.cell_index (); + if (! ly.is_valid_cell_index (ci)) { + return false; + } + } + + if (! is_cell_inst ()) { + if (! ly.is_valid_layer (layer ())) { + return false; + } + if (! ly.cell (ci).shapes (layer ()).is_valid (shape ())) { + return false; + } + } + + return true; +} + db::cell_index_type ObjectInstPath::cell_index_tot () const { diff --git a/src/laybasic/laybasic/layObjectInstPath.h b/src/laybasic/laybasic/layObjectInstPath.h index 13c4ca095..580fb43ef 100644 --- a/src/laybasic/laybasic/layObjectInstPath.h +++ b/src/laybasic/laybasic/layObjectInstPath.h @@ -37,7 +37,7 @@ namespace lay { - class LayoutView; + class LayoutViewBase; } namespace lay { @@ -299,6 +299,15 @@ public: m_seq = s; } + /** + * @brief Gets a value indicating whether the object path is valid + * + * After the layout has been modified, this method is able to check + * whether the object path (including shape if applicable) still points + * to a valid object. + */ + bool is_valid (lay::LayoutViewBase *view) const; + private: unsigned int m_cv_index; db::cell_index_type m_topcell;