From d14892382c3be7fd2be36efab4c2666e2461e57f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 Jul 2021 00:44:28 +0200 Subject: [PATCH] Fixed #864 (Shapes#copy_shapes does not support undo/redo) While doing so changed the following things too: - Instance and Shapes methods raise an exception if not in editable mode and with undo/redo - Faster and leaner undo/redo on Shapes#clear --- src/db/db/dbInstances.cc | 19 +++ src/db/db/dbShapes.cc | 67 +++++++- src/db/db/dbShapes.h | 71 +++++++- src/db/db/dbShapes2.cc | 16 +- src/db/db/dbShapes2.h | 3 +- src/db/db/dbShapes3.cc | 2 + src/db/db/gsiDeclDbShapes.cc | 7 +- src/db/unit_tests/dbShapesTests.cc | 254 +++++++++++++++++++++++++++++ 8 files changed, 409 insertions(+), 30 deletions(-) diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index f1f73eef4..7b3784e8d 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -837,6 +837,14 @@ Instance::bbox () const // ------------------------------------------------------------------------------------- // Instances implementation +static void +check_is_editable_for_undo_redo (const Instances *instances) +{ + if (! instances->is_editable ()) { + throw tl::Exception (tl::to_string (tr ("No undo/redo support on non-editable instance lists"))); + } +} + Instances::Instances (cell_type *cell) : mp_cell (cell) { @@ -957,6 +965,10 @@ Instances::erase_positions (Tag tag, ET editable_tag, I first, I last) if (mp_cell) { mp_cell->invalidate_insts (); // HINT: must come before the change is done! if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); + if (! is_editable ()) { + throw tl::Exception (tl::to_string (tr ("No undo/redo support for non-editable instance lists in 'erase_positions'"))); + } mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, first, last, true /*dummy*/)); } } @@ -973,6 +985,7 @@ Instances::insert (const InstArray &inst) if (mp_cell) { if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); if (editable) { mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, inst)); } else { @@ -999,6 +1012,7 @@ Instances::insert (I from, I to) if (mp_cell) { if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, from, to)); } mp_cell->invalidate_insts (); @@ -1049,6 +1063,7 @@ Instances::replace (const InstArray *replace, const InstArray &with) { if (mp_cell) { if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); if (is_editable ()) { mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, *replace)); mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, with)); @@ -1138,6 +1153,7 @@ Instances::erase_inst_by_iter (Tag tag, ET editable_tag, I iter) if (mp_cell) { mp_cell->invalidate_insts (); if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, *iter)); } } @@ -1152,6 +1168,7 @@ Instances::erase_inst_by_tag (Tag tag, ET editable_tag, const typename Tag::obje if (mp_cell) { mp_cell->invalidate_insts (); if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, obj)); } } @@ -1183,6 +1200,7 @@ Instances::clear_insts (ET editable_tag) if (mp_cell) { mp_cell->invalidate_insts (); if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); const Instances *const_this = this; if (! const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).empty ()) { mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); @@ -1588,6 +1606,7 @@ void Instances::apply_op (const Op &op, ET editable_tag) if (mp_cell) { mp_cell->invalidate_insts (); if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + check_is_editable_for_undo_redo (this); transacting = true; if (has_insts) { mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index 7b4ddf96c..f905f66ed 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -131,6 +131,36 @@ layer_op::erase (Shapes *shapes) } } +// --------------------------------------------------------------------------------------- +// FullLayerOp implementation + +void +FullLayerOp::insert (Shapes *shapes) +{ + for (tl::vector::iterator l = shapes->get_layers ().end (); l != shapes->get_layers ().begin (); ) { + --l; + if (*l == mp_layer) { + return; + } + } + shapes->get_layers ().push_back (mp_layer); + shapes->invalidate_state (); + m_owns_layer = false; +} + +void +FullLayerOp::erase (Shapes *shapes) +{ + for (tl::vector::iterator l = shapes->get_layers ().begin (); l != shapes->get_layers ().end (); ++l) { + if (*l == mp_layer) { + shapes->get_layers ().erase (l); + shapes->invalidate_state (); + m_owns_layer = true; + break; + } + } +} + // --------------------------------------------------------------------------------------- // Shapes implementation @@ -155,11 +185,17 @@ Shapes::layout () const return c ? c->layout () : 0; } +void +Shapes::check_is_editable_for_undo_redo () const +{ + if (! is_editable ()) { + throw tl::Exception (tl::to_string (tr ("No undo/redo support on non-editable shape lists"))); + } +} + void Shapes::insert (const Shapes &d) { - // no undo support for this currently - tl_assert (! manager () || ! manager ()->transacting ()); do_insert (d); } @@ -175,10 +211,18 @@ Shapes::do_insert (const Shapes &d) // both shape containers reside in the same repository space - simply copy if (m_layers.empty ()) { + m_layers.reserve (d.m_layers.size ()); for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { - m_layers.push_back ((*l)->clone (this, manager ())); + m_layers.push_back ((*l)->clone ()); + if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); + manager ()->queue (this, new FullLayerOp (true, m_layers.back ())); + } } + + invalidate_state (); + } else { for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { (*l)->insert_into (this); @@ -939,12 +983,19 @@ void Shapes::clear () { if (!m_layers.empty ()) { + for (tl::vector::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { - (*l)->clear (this, manager ()); - delete *l; + if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); + manager ()->queue (this, new FullLayerOp (false, (*l))); + } else { + delete *l; + } } + invalidate_state (); // HINT: must come before the change is done! m_layers.clear (); + } } @@ -1104,6 +1155,7 @@ Shapes::replace_prop_id (const Sh *pos, db::properties_id_type prop_id) throw tl::Exception (tl::to_string (tr ("Function 'replace' is permitted only in editable mode"))); } if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, *pos); } invalidate_state (); // HINT: must come before the change is done! @@ -1123,6 +1175,7 @@ Shapes::replace_prop_id_iter (typename db::object_tag, const Iter &iter, db: } if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, *iter); } db::object_with_properties wp (*iter, prop_id); @@ -1190,6 +1243,7 @@ Shapes::replace_member_with_props (typename db::object_tag tag, const shape_ // simple replace case if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, *ref.basic_ptr (tag)); } @@ -1214,6 +1268,7 @@ Shapes::replace_member_with_props (typename db::object_tag tag, const shape_ if (! ref.with_props ()) { if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, *ref.basic_ptr (tag)); } @@ -1234,6 +1289,7 @@ Shapes::replace_member_with_props (typename db::object_tag tag, const shape_ get_layer ().replace (ref.basic_iter (tag), sh); if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, true /*insert*/, sh); } @@ -1242,6 +1298,7 @@ Shapes::replace_member_with_props (typename db::object_tag tag, const shape_ } else { if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op, db::stable_layer_tag>::queue_or_append (manager (), this, false /*not insert*/, *ref.basic_ptr (typename db::object_with_properties::tag ())); } diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index f89ac98b9..234d42e1d 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -486,8 +486,7 @@ public: virtual size_t size () const = 0; virtual bool empty () const = 0; virtual void sort () = 0; - virtual void clear (Shapes *target, db::Manager *manager) = 0; - virtual LayerBase *clone (Shapes *target, db::Manager *manager) const = 0; + virtual LayerBase *clone () const = 0; virtual void translate_into (Shapes *target, GenericRepository &rep, ArrayRepository &array_rep) const = 0; virtual void translate_into (Shapes *target, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const = 0; virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep) const = 0; @@ -682,6 +681,8 @@ public: if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); + tl::ident_map pm; // for undo support iterate over the elements @@ -721,6 +722,8 @@ public: if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); + // for undo support iterate over the elements for (ShapeIterator s = d.begin (ShapeIterator::All); ! s.at_end (); ++s) { insert (*s, trans, pm); @@ -760,6 +763,8 @@ public: if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); + // for undo support iterate over the elements for (ShapeIterator s = d.begin (ShapeIterator::All); ! s.at_end (); ++s) { insert (*s, pm); @@ -806,6 +811,7 @@ public: shape_type insert (const Sh &sh) { if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); if (is_editable ()) { db::layer_op::queue_or_append (manager (), this, true /*insert*/, sh); } else { @@ -849,6 +855,7 @@ public: // insert the array as a whole in non-editable mode if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op, db::unstable_layer_tag>::queue_or_append (manager (), this, true /*insert*/, arr); } invalidate_state (); // HINT: must come before the change is done! @@ -886,6 +893,7 @@ public: // insert the array as a whole in non-editable mode if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op< db::object_with_properties< db::array >, db::unstable_layer_tag>::queue_or_append (manager (), this, true /*insert*/, arr); } invalidate_state (); // HINT: must come before the change is done! @@ -906,6 +914,7 @@ public: { typedef typename std::iterator_traits ::value_type value_type; if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); if (is_editable ()) { db::layer_op::queue_or_append (manager (), this, true /*insert*/, from, to); } else { @@ -1001,6 +1010,7 @@ public: throw tl::Exception (tl::to_string (tr ("Function 'erase' is permitted only in editable mode"))); } if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, *pos); } invalidate_state (); // HINT: must come before the change is done! @@ -1064,6 +1074,7 @@ public: throw tl::Exception (tl::to_string (tr ("Function 'erase' is permitted only in editable mode"))); } if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, from, to); } invalidate_state (); // HINT: must come before the change is done! @@ -1090,6 +1101,7 @@ public: throw tl::Exception (tl::to_string (tr ("Function 'erase' is permitted only in editable mode"))); } if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, first, last, true /*dummy*/); } invalidate_state (); // HINT: must come before the change is done! @@ -1480,12 +1492,20 @@ public: private: friend class ShapeIterator; + friend class FullLayerOp; tl::vector m_layers; db::Cell *mp_cell; // HINT: contains "dirty" in bit 0 and "editable" in bit 1 void invalidate_state (); void do_insert (const Shapes &d); + void check_is_editable_for_undo_redo () const; + + // gets the layers array + tl::vector &get_layers () + { + return m_layers; + } // extract dirty flag from mp_cell bool is_dirty () const @@ -1604,6 +1624,7 @@ private: for (typename Array::iterator a = arr.begin (); ! a.at_end (); ++a) { res_wp_type obj_wp (*a * arr.object (), arr.properties_id ()); if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, true /*insert*/, obj_wp); } l.insert (obj_wp); @@ -1618,6 +1639,7 @@ private: db::layer &l = get_layer (); for (typename Array::iterator a = arr.begin (); ! a.at_end (); ++a) { if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, true /*insert*/, *a * arr.object ()); } l.insert (*a * arr.object ()); @@ -1743,6 +1765,51 @@ private: void erase (Shapes *shapes); }; +class FullLayerOp + : public LayerOpBase +{ +public: + FullLayerOp (bool insert, LayerBase *layer) + : m_insert (insert), mp_layer (layer), m_owns_layer (! insert) + { + // .. nothing yet .. + } + + ~FullLayerOp () + { + if (m_owns_layer) { + delete mp_layer; + mp_layer = 0; + } + } + + virtual void undo (Shapes *shapes) + { + if (m_insert) { + erase (shapes); + } else { + insert (shapes); + } + } + + virtual void redo (Shapes *shapes) + { + if (m_insert) { + insert (shapes); + } else { + erase (shapes); + } + } + +private: + bool m_insert; + LayerBase *mp_layer; + bool m_owns_layer; + + void insert (Shapes *shapes); + void erase (Shapes *shapes); +}; + } // namespace db #endif diff --git a/src/db/db/dbShapes2.cc b/src/db/db/dbShapes2.cc index 9214cfa9e..435088e56 100644 --- a/src/db/db/dbShapes2.cc +++ b/src/db/db/dbShapes2.cc @@ -752,24 +752,11 @@ inline unsigned int iterator_type_mask (db::object_tag< db::object_with_properti return iterator_type_mask (typename Sh::tag ()); } -template -void -layer_class::clear (Shapes *target, db::Manager *manager) -{ - if (manager && manager->transacting ()) { - manager->queue (target, new db::layer_op (false /*not insert*/, m_layer.begin (), m_layer.end ())); - } - m_layer.clear (); -} - template LayerBase * -layer_class::clone (Shapes *target, db::Manager *manager) const +layer_class::clone () const { layer_class *r = new layer_class (); - if (manager && manager->transacting ()) { - manager->queue (target, new db::layer_op (true /*insert*/, m_layer.begin (), m_layer.end ())); - } r->m_layer = m_layer; return r; } @@ -877,7 +864,6 @@ layer_class::deref_and_transform_into (Shapes *target, const Tran { deref_and_transform_into_shapes deref_op (target); for (typename layer_type::iterator s = m_layer.begin (); s != m_layer.end (); ++s) { - deref_op (*s, trans, pm); } } diff --git a/src/db/db/dbShapes2.h b/src/db/db/dbShapes2.h index 9acdf6e15..1e86aa4e9 100644 --- a/src/db/db/dbShapes2.h +++ b/src/db/db/dbShapes2.h @@ -141,8 +141,7 @@ public: m_layer.sort (); } - virtual void clear (Shapes *target, db::Manager *manager); - virtual LayerBase *clone (Shapes *target, db::Manager *manager) const; + virtual LayerBase *clone () const; virtual void translate_into (Shapes *target, GenericRepository &rep, ArrayRepository &array_rep) const; virtual void translate_into (Shapes *target, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const; virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep) const; diff --git a/src/db/db/dbShapes3.cc b/src/db/db/dbShapes3.cc index c21f2070d..cd57844b8 100644 --- a/src/db/db/dbShapes3.cc +++ b/src/db/db/dbShapes3.cc @@ -344,6 +344,7 @@ Shapes::erase_shape_by_tag_ws (Tag /*tag*/, StableTag /*stable_tag*/, const shap db::layer &l = get_layer (); typename db::layer::iterator i = iterator_from_shape (l, shape); if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, *i); } invalidate_state (); // HINT: must come before the change is done! @@ -356,6 +357,7 @@ Shapes::erase_shape_by_tag_ws (Tag /*tag*/, StableTag /*stable_tag*/, const shap db::layer &l = get_layer (); typename db::layer::iterator i = iterator_from_shape (l, shape); if (manager () && manager ()->transacting ()) { + check_is_editable_for_undo_redo (); db::layer_op::queue_or_append (manager (), this, false /*not insert*/, *i); } invalidate_state (); // HINT: must come before the change is done! diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index 4772516be..572506ce4 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -246,12 +246,7 @@ static void insert_iter_with_trans (db::Shapes *sh, const db::RecursiveShapeIter static void insert_shapes (db::Shapes *sh, const db::Shapes &s) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); - for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end(); ++i) { - sh->insert (*i); - } + sh->insert (s); } static void insert_shapes_with_flags (db::Shapes *sh, const db::Shapes &s, unsigned int flags) diff --git a/src/db/unit_tests/dbShapesTests.cc b/src/db/unit_tests/dbShapesTests.cc index 4c1044af4..fadd52ce4 100644 --- a/src/db/unit_tests/dbShapesTests.cc +++ b/src/db/unit_tests/dbShapesTests.cc @@ -3235,6 +3235,193 @@ TEST(23) EXPECT_EQ (shapes_to_string_norm (_this, s2), "edge_pair (0,0;1,1)/(10,10;11,11) #17\n"); } +// Shape insert and clear and undo/redo +TEST(24a) +{ + db::Manager m; + db::Shapes s1 (&m, 0, true), s2; + + s2.insert (db::Edge (db::Point (0, 0), db::Point (100, 200))); + s2.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + + m.undo (); + s1.insert (db::Box (db::Point (1, 1), db::Point (101, 201))); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + s1.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); +} + +// Shape insert and clear and undo/redo - different layers, same layout +TEST(24b) +{ + db::Manager m; + db::Layout l (true, &m); + db::Cell &cell = l.cell (l.add_cell ("top")); + l.insert_layer (1); + l.insert_layer (2); + db::Shapes &s1 = cell.shapes (1); + db::Shapes &s2 = cell.shapes (2); + + s2.insert (db::Edge (db::Point (0, 0), db::Point (100, 200))); + s2.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + + m.undo (); + s1.insert (db::Box (db::Point (1, 1), db::Point (101, 201))); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + s1.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); +} + +// Shape insert and clear and undo/redo - no layout on target +TEST(24c) +{ + db::Manager m; + db::Layout l; + db::Cell &cell = l.cell (l.add_cell ("top")); + l.insert_layer (1); + l.insert_layer (2); + db::Shapes s1 (&m, 0, true); + db::Shapes &s2 = cell.shapes (2); + + s2.insert (db::Edge (db::Point (0, 0), db::Point (100, 200))); + s2.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + + m.undo (); + s1.insert (db::Box (db::Point (1, 1), db::Point (101, 201))); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + s1.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); +} + // Bug #107 TEST(100) { @@ -3262,6 +3449,72 @@ TEST(100) ); } +// Shape insert and clear and undo/redo - different layouts +TEST(24d) +{ + db::Manager m; + db::Layout l1 (true, &m); + db::Cell &cell1 = l1.cell (l1.add_cell ("top")); + l1.insert_layer (1); + db::Layout l2 (true, &m); + db::Cell &cell2 = l2.cell (l2.add_cell ("top")); + l2.insert_layer (2); + db::Shapes &s1 = cell1.shapes (1); + db::Shapes &s2 = cell2.shapes (2); + + s2.insert (db::Edge (db::Point (0, 0), db::Point (100, 200))); + s2.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nedge (0,0;100,200) #0\n"); + + m.undo (); + s1.insert (db::Box (db::Point (1, 1), db::Point (101, 201))); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + s1.insert (s2); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (1,1;101,201) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.transaction ("test"); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + s1.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + m.commit (); + + m.undo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), "box (0,0;100,200) #0\nbox (1,1;101,201) #0\nedge (0,0;100,200) #0\n"); + + m.redo (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); + + m.clear (); + EXPECT_EQ (shapes_to_string_norm (_this, s1), ""); +} + // Bug #835 TEST(101) { @@ -3322,3 +3575,4 @@ TEST(101) const db::Path &qr2_obj = *b.shape_repository ().repository (db::Path::tag ()).begin (); EXPECT_EQ (& qr2.obj () == &qr2_obj, true); } +