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
This commit is contained in:
Matthias Koefferlein 2021-07-15 00:44:28 +02:00
parent bad3232415
commit d14892382c
8 changed files with 409 additions and 30 deletions

View File

@ -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<typename Tag::object_type, ET> (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<InstArray, InstancesEditableTag> (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<typename it_traits::value_type, ET> (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<InstArray, InstancesEditableTag> (false /*not insert*/, *replace));
mp_cell->manager ()->queue (mp_cell, new db::InstOp<InstArray, InstancesEditableTag> (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<typename Tag::object_type, ET> (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<typename Tag::object_type, ET> (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<cell_inst_array_type, ET> (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<cell_inst_array_type, ET> (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 ()));

View File

@ -131,6 +131,36 @@ layer_op<Sh, StableTag>::erase (Shapes *shapes)
}
}
// ---------------------------------------------------------------------------------------
// FullLayerOp implementation
void
FullLayerOp::insert (Shapes *shapes)
{
for (tl::vector<LayerBase *>::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<LayerBase *>::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<LayerBase *>::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<LayerBase *>::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<LayerBase *>::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<Sh, db::stable_layer_tag>::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<Sh>, const Iter &iter, db:
}
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<Sh, db::stable_layer_tag>::queue_or_append (manager (), this, false /*not insert*/, *iter);
}
db::object_with_properties <Sh> wp (*iter, prop_id);
@ -1190,6 +1243,7 @@ Shapes::replace_member_with_props (typename db::object_tag<Sh> tag, const shape_
// simple replace case
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<Sh, db::stable_layer_tag>::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<Sh> tag, const shape_
if (! ref.with_props ()) {
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<Sh, db::stable_layer_tag>::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<Sh> tag, const shape_
get_layer<Sh, db::stable_layer_tag> ().replace (ref.basic_iter (tag), sh);
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<Sh, db::stable_layer_tag>::queue_or_append (manager (), this, true /*insert*/, sh);
}
@ -1242,6 +1298,7 @@ Shapes::replace_member_with_props (typename db::object_tag<Sh> tag, const shape_
} else {
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<db::object_with_properties<Sh>, db::stable_layer_tag>::queue_or_append (manager (), this, false /*not insert*/, *ref.basic_ptr (typename db::object_with_properties<Sh>::tag ()));
}

View File

@ -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<db::properties_id_type> 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<Sh, db::stable_layer_tag>::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::array<Obj, Trans>, 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<Obj, Trans> >, 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 <Iter>::value_type value_type;
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
if (is_editable ()) {
db::layer_op<value_type, db::stable_layer_tag>::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<typename Tag::object_type, StableTag>::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<typename Tag::object_type, StableTag>::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<typename Tag::object_type, StableTag>::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<LayerBase *> 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<LayerBase *> &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<res_wp_type, db::stable_layer_tag>::queue_or_append (manager (), this, true /*insert*/, obj_wp);
}
l.insert (obj_wp);
@ -1618,6 +1639,7 @@ private:
db::layer<ResType, db::stable_layer_tag> &l = get_layer<ResType, db::stable_layer_tag> ();
for (typename Array::iterator a = arr.begin (); ! a.at_end (); ++a) {
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<ResType, db::stable_layer_tag>::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

View File

@ -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 <class Sh, class StableTag>
void
layer_class<Sh, StableTag>::clear (Shapes *target, db::Manager *manager)
{
if (manager && manager->transacting ()) {
manager->queue (target, new db::layer_op<Sh, StableTag> (false /*not insert*/, m_layer.begin (), m_layer.end ()));
}
m_layer.clear ();
}
template <class Sh, class StableTag>
LayerBase *
layer_class<Sh, StableTag>::clone (Shapes *target, db::Manager *manager) const
layer_class<Sh, StableTag>::clone () const
{
layer_class<Sh, StableTag> *r = new layer_class<Sh, StableTag> ();
if (manager && manager->transacting ()) {
manager->queue (target, new db::layer_op<Sh, StableTag> (true /*insert*/, m_layer.begin (), m_layer.end ()));
}
r->m_layer = m_layer;
return r;
}
@ -877,7 +864,6 @@ layer_class<Sh, StableTag>::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);
}
}

View File

@ -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;

View File

@ -344,6 +344,7 @@ Shapes::erase_shape_by_tag_ws (Tag /*tag*/, StableTag /*stable_tag*/, const shap
db::layer<typename Tag::object_type, StableTag> &l = get_layer<typename Tag::object_type, StableTag> ();
typename db::layer<typename Tag::object_type, StableTag>::iterator i = iterator_from_shape (l, shape);
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<typename Tag::object_type, StableTag>::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<swp_type, StableTag> &l = get_layer<swp_type, StableTag> ();
typename db::layer<swp_type, StableTag>::iterator i = iterator_from_shape (l, shape);
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
db::layer_op<swp_type, StableTag>::queue_or_append (manager (), this, false /*not insert*/, *i);
}
invalidate_state (); // HINT: must come before the change is done!

View File

@ -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)

View File

@ -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);
}