Identity of Shapes whose creation has been undone and redone

K

Konrad Anton

Hello everyone,

while trying to integrate my .NET-based Visio-Add-In with the Undo
mechanism, I arrived at the following question:

If I create a shape, and the creation is done and redone as in...

Shape s = DropShapeFromSomeStencilOnThatPage();
int id = s.ID;
string guid = s.get_UniqueID(visGetOrMakeGUID);
Page page = s.Page;
app.Undo();
app.Redo();


.... and I later retrieve the logically same shape by ID...

Shape s1 = s.Page.Shapes[id];
Shape s2 = s.Page.Shapes[guid];


will then s1, s2 and s be pointer-equal, i.e. can I assume that Redo of
a Shape creation will result in a pointer-equal object such that my own
UndoItems can assume that the old s reference is still a reference to
the right Shape? My observations in the debugger suggest that it is
indeed the case, but I haven't found any promise about that in the Visio
SDK doc, I'm used to pointer-equality being an incidental implementation
artifact, and my .NET roots aren't deep enough to say "That's how it's
always been done, son".

Can anyone enlighten me?
--Konrad
 
P

Paul Herber

Hello everyone,

while trying to integrate my .NET-based Visio-Add-In with the Undo
mechanism, I arrived at the following question:

If I create a shape, and the creation is done and redone as in...

Shape s = DropShapeFromSomeStencilOnThatPage();
int id = s.ID;
string guid = s.get_UniqueID(visGetOrMakeGUID);
Page page = s.Page;
app.Undo();
app.Redo();


... and I later retrieve the logically same shape by ID...

Shape s1 = s.Page.Shapes[id];
Shape s2 = s.Page.Shapes[guid];


will then s1, s2 and s be pointer-equal, i.e. can I assume that Redo of
a Shape creation will result in a pointer-equal object such that my own
UndoItems can assume that the old s reference is still a reference to
the right Shape? My observations in the debugger suggest that it is
indeed the case, but I haven't found any promise about that in the Visio
SDK doc, I'm used to pointer-equality being an incidental implementation
artifact, and my .NET roots aren't deep enough to say "That's how it's
always been done, son".

I can only comment as a software engineer of 30 years experience - I
would say that whatever .NET might do internally at the moment you are
sitting on a time bomb writing code like that. Even if you have no
code between your Undo and Redo (in which case I cannot see the point)
and the code from the start of the Undo until the end of the Redo act
as an atomic unit, even then I wouldn't like to guarantee anything.
 
K

Konrad Anton

Paul said:
[Konrad Anton wrote:]
will then s1, s2 and s be pointer-equal, i.e. can I assume that Redo of
a Shape creation will result in a pointer-equal object such that my own
UndoItems can assume that the old s reference is still a reference to
the right Shape? My observations in the debugger suggest that it is
indeed the case, but I haven't found any promise about that in the Visio
SDK doc, I'm used to pointer-equality being an incidental implementation
artifact, and my .NET roots aren't deep enough to say "That's how it's
always been done, son".

I can only comment as a software engineer of 30 years experience - I
would say that whatever .NET might do internally at the moment you are
sitting on a time bomb writing code like that. Even if you have no
code between your Undo and Redo (in which case I cannot see the point)
and the code from the start of the Undo until the end of the Redo act
as an atomic unit, even then I wouldn't like to guarantee anything.

Thanks. Let me rephrase my question, since I'm not really trying to pin
the memory location of objects in spite of automatic memory management.

What I'm worried about is the following: let's say I have a data
structure which references shapes. Perhaps a Model where the Things know
the Shape they're represented by. Then I have

Thing t = thingCollection.CreateNewThing(); // 1
Shape s = myThingMaster.Drop(myPage); // 2
t.SetDiagramShape(s); // 3

Now let's say I post Undo Units to the Undo Stack for each Thing
manipulation. (1) would be undone by removing the new thing from the
collection, (2) is Visio's job, and (3) would be undone by unsetting the
DiagramShape attribute of t.

But what do I do at Redo? Redoing (1) could either be done by making the
same Thing instance reappear or by creating a new Thing instance which
compares equal to the old instance (i.e. is indistinguishable from the
last Thing except for ==-comparison). Let's say I make the same Thing
instance reappear. To redo (3), I need a reference to the precise Shape
instance whose creation was redone in (2).

Now what I wonder is if the Redo unit of (3) may reuse the s reference
it got the first time

class MySetCommand {
Shape s;
Thing t;
void Redo() {
t.Shape = s;
}
}

or if I have to fetch the Shape using some redo-invariant identifier of
the shape as in

class MySetCommand {
int id; // or NameU or UniqueID ...
int pageNo;
Document doc;
Thing t;
void Redo() {
Page p = doc.Pages[pageNo];
Shape s = page.Shapes[id];
t.Shape = s;
}
}

?

--Konrad
 
P

Paul Herber

Paul said:
[Konrad Anton wrote:]
will then s1, s2 and s be pointer-equal, i.e. can I assume that Redo of
a Shape creation will result in a pointer-equal object such that my own
UndoItems can assume that the old s reference is still a reference to
the right Shape? My observations in the debugger suggest that it is
indeed the case, but I haven't found any promise about that in the Visio
SDK doc, I'm used to pointer-equality being an incidental implementation
artifact, and my .NET roots aren't deep enough to say "That's how it's
always been done, son".

I can only comment as a software engineer of 30 years experience - I
would say that whatever .NET might do internally at the moment you are
sitting on a time bomb writing code like that. Even if you have no
code between your Undo and Redo (in which case I cannot see the point)
and the code from the start of the Undo until the end of the Redo act
as an atomic unit, even then I wouldn't like to guarantee anything.

Thanks. Let me rephrase my question, since I'm not really trying to pin
the memory location of objects in spite of automatic memory management.

What I'm worried about is the following: let's say I have a data
structure which references shapes. Perhaps a Model where the Things know
the Shape they're represented by. Then I have

Thing t = thingCollection.CreateNewThing(); // 1
Shape s = myThingMaster.Drop(myPage); // 2
t.SetDiagramShape(s); // 3

Now let's say I post Undo Units to the Undo Stack for each Thing
manipulation. (1) would be undone by removing the new thing from the
collection, (2) is Visio's job, and (3) would be undone by unsetting the
DiagramShape attribute of t.

So, from the above example you want to perform steps 1, 2 and 3, where
item 3 depends upon the 2 previous items.
You then want to undo (using your own undo mechanism) these steps in
the same order 1, 2 and 3.
You then want to perform some other unspecified task ....
Then you want to redo (using your own redo mechanism) these steps 1, 2
and 3 but keep all the variables exactly the same.

Maybe the way you are describing what you are doing here isn't exactly
what you want to achieve.
Do you want a stack/list of shapes? Create an invisible page and put
the shapes on that page. You can pull shapes off the top/bottom of the
shapes container.
 
K

Konrad Anton

Paul said:
[Konrad Anton wrote:]
Thing t = thingCollection.CreateNewThing(); // 1 I
Shape s = myThingMaster.Drop(myPage); // 2 II
t.SetDiagramShape(s); // 3 III

Now let's say I post Undo Units to the Undo Stack for each Thing
manipulation. (1) would be undone by removing the new thing from the
collection, (2) is Visio's job, and (3) would be undone by unsetting the
DiagramShape attribute of t.

So, from the above example you want to perform steps 1, 2 and 3, where
item 3 depends upon the 2 previous items.
You then want to undo (using your own undo mechanism) these steps in
the same order 1, 2 and 3.

No, undo happens in the usual order (III), (II), (I); I was just
describing how the undo item of each step would work, sorry for the
confusion. (line names changed for the explanation below).

In my example, I've still got some objects from the application domain,
let's call them Things; each Thing is represented by one Shape; each
Thing holds a reference to its Shape.

The sequence of things happening would be:

0. An undo scope begins
1. (I) is executed, resulting in a new thing instance Thing1 to which
the thingCollection holds a reference
2. (II) is executed, resulting in a new Shape instance Shape1
3. (III) is executed, setting an attribute in Thing1 to point to Shape1
4. The undo scope ends

5. User initiates Undo
6. (III) is undone, unsetting an attribute in Thing1
7. (II) is undone, at least removing Shape1 from the page
8. (I) is undone

9. User initiates Redo
10. (I) is redone, Thing1 is added back to the thingCollection
11. (II) is redone by Visio, and the page now contains a Shape2 which
looks like Shape1
12. (III) is redone

In step 12, the redoer (which restores the reference from Thing1 to its
Shape) needs a reference to Shape2, which is the result of step 11.

However, I know pretty little for certain about Shape2. It could be the
same instance (pointer-equal, ==-equal, reference-equal) as Shape1. It
could be a newly allocated Shape with the same ID. It could be a newly
allocated Shape with different ID but the same GUID. It could be a newly
allocated Shape with different ID and GUID (and little hope of finding
it among the many shapes).

So what I'd like to know is: how much and what is known about Shape2
after step 11? (Dream answer would be: "you can safely assume that when
a Visio-internal undo step undoes the creation of an object, the same
(and not just a similar) object will reappear at redo.")

--Konrad
 
P

Paul Herber

Paul said:
[Konrad Anton wrote:]
Thing t = thingCollection.CreateNewThing(); // 1 I
Shape s = myThingMaster.Drop(myPage); // 2 II
t.SetDiagramShape(s); // 3 III

Now let's say I post Undo Units to the Undo Stack for each Thing
manipulation. (1) would be undone by removing the new thing from the
collection, (2) is Visio's job, and (3) would be undone by unsetting the
DiagramShape attribute of t.

So, from the above example you want to perform steps 1, 2 and 3, where
item 3 depends upon the 2 previous items.
You then want to undo (using your own undo mechanism) these steps in
the same order 1, 2 and 3.

No, undo happens in the usual order (III), (II), (I); I was just
describing how the undo item of each step would work, sorry for the
confusion. (line names changed for the explanation below).

In my example, I've still got some objects from the application domain,
let's call them Things; each Thing is represented by one Shape; each
Thing holds a reference to its Shape.

The sequence of things happening would be:

0. An undo scope begins
1. (I) is executed, resulting in a new thing instance Thing1 to which
the thingCollection holds a reference
2. (II) is executed, resulting in a new Shape instance Shape1
3. (III) is executed, setting an attribute in Thing1 to point to Shape1
4. The undo scope ends

5. User initiates Undo
6. (III) is undone, unsetting an attribute in Thing1
7. (II) is undone, at least removing Shape1 from the page
8. (I) is undone

9. User initiates Redo
10. (I) is redone, Thing1 is added back to the thingCollection
11. (II) is redone by Visio, and the page now contains a Shape2 which
looks like Shape1
12. (III) is redone

In step 12, the redoer (which restores the reference from Thing1 to its
Shape) needs a reference to Shape2, which is the result of step 11.

However, I know pretty little for certain about Shape2. It could be the
same instance (pointer-equal, ==-equal, reference-equal) as Shape1. It
could be a newly allocated Shape with the same ID. It could be a newly
allocated Shape with different ID but the same GUID. It could be a newly
allocated Shape with different ID and GUID (and little hope of finding
it among the many shapes).

So what I'd like to know is: how much and what is known about Shape2
after step 11? (Dream answer would be: "you can safely assume that when
a Visio-internal undo step undoes the creation of an object, the same
(and not just a similar) object will reappear at redo.")

I think a lot will depend upon what operations are performed between
steps 8 and 9. If other shapes are added then who knows. What you are
doing may work 99% of the time. Maybe people who know the internals of
..NET would know but more likely to just say that it's indeterminate
and likely to change anyway.
 
K

Konrad Anton

Paul said:
I think a lot will depend upon what operations are performed between
steps 8 and 9. If other shapes are added then who knows. What you are
doing may work 99% of the time. Maybe people who know the internals of
.NET would know but more likely to just say that it's indeterminate
and likely to change anyway.

I'm not trying to bet on .NET objects reoccurring at certain addresses.
It's a question of how the Visio people designed their UndoItem classes
which Visio uses for its own undo and redo. There are two major ways
(that I know of) of designing the Undo and Redo of an object's creation:

1. Undo discards the reference to the object (deallocating it), Redo
creates a new object

2. Undo keeps a reference to the object, Redo restores that same object

So, if the Visio people used design 1, the shape at Redo will be
reference-inequal to the old shape most of the time; if they used design
2, the shape at Redo will be reference-equal all the time.

I just wonder if I'm the only one with that question. It seems quite
natural to me to have references to shapes in my model objects and to
implement undo functionality on my model. Have you in your career at a
Visio solution company never had the need to add undo to your solutions?

--Konrad
 
Top