Undo/Redo JGoIconicNode

I have the following which I cannot get to display the proper value when I attempt to undo the value of the label to the node. The proper value is displayed in the view however when I click on the node and a JDialog is rendered the value that it displays is always the original value i.e. if it starts with A and I then change it to B and then C and undo the view displays B however the Dialog displays A. In the method changeValue() the case ChangedLabel: is never entered it always goes directly to the default value.



public class TestNode extends JGoIconicNode implements Transferable, Comparable

{

private String text;



final public static DataFlavor TESTNODE_FLAVOR =

new DataFlavor(TestNode.class, “WorkflowState Node”);



static DataFlavor flavors[] = {TESTNODE_FLAVOR };



public TestNode(WorkflowState state)

{

super(state. getShortName());

setToolTipText((new Integer(state.getStateID())).toString());

setUserObjec t(state);

text = state.getShortName();



JGoText text = getLabel();

text.setEdit able(false);

text.setFont Size(14);

setResizable (false);

text .setAutoResize(true);

text.setMult iline(true);

text.setWrap ping(true);

text.setWrap pingWidth(100);

JGoPort port = this.getPort();

port.setVali dDuplicateLinks(true);

port.setValidSelfNode(true);

}



public WorkflowStateNode()

{

}



public JGoObject copyObject(JGoCopyEnvironment env)

{

WorkflowStat eNode state = (WorkflowStateNode)super.copyObject(env);

if (state != null) {

state.setText(text );

state.setUse rObject(getUserObj ect());

state.text = text;

}

return state;

}



public void copyNewValueForRedo(JGoDocumentChangedEdit e)

{

switch (e.getFlags())

{

case ChangedLabel:

e.setNewValue(getLabel());

return;

default:

super.copyNewValueForRedo(e);

return;

}

}



public void changeValue(JGoDocumentChangedEdit e, boolean undo)

{

switch (e.getFlags())

{

//this case is never entered

case ChangedLabel:

if (e.getValue(undo) instanceof JGoText)

{



JGoText t = (JGoText)e.getValue(undo);

if(!text.equalsIgnoreCase(t.getText()))

{

text = t.getText();

getLabel().setText(text);



WorkflowState state = (WorkflowState)getUserObject();

state.setShortName(text);



}

}

return;

//it always goes here

default:

super.changeValue(e, undo);

return;

}

}



I think you need to put all your state-changing code within a transaction. I’m talking about the code that is in your dialog, not the code in your JGoObject class above that implements support for undo/redo.
It sounds like all of the changes you made (from A to B, and then from B to C) got lumped together in one big transaction as defined by the following event (whatever that was).

Thank you walter. Once again youve pointed me in the right direction.



Now I have the following in my dialog class when I change any state related info i.e the node



node.getDocument().startTransaction();

node.update(WorkflowStateNode.WorkflowStateNodeChanged, 0, node);



node.getDocument().endTransaction(…



In my Node class I now have the following in the changeValue()



public void changeValue(JGoDocumentChangedEdit e, boolean undo)

{

case WorkflowStateNodeChanged:

if(e.getValue(undo) instanceof WorkflowStateNode)

{

WorkflowStateNode stateNode = (WorkflowStateNode)e.getValue (undo);



String nodeText = stateNode.getText();



//the 2 strings compared are always equal



if(!getText().equalsIgnoreCase(nodeText))



The proper case is called whenever I undo/redo however the object that it obtains is never the correct one i.e. if the label is A and I then change it to B and C I click undo the view changes but the state obtains the wrong object. In my dialog class I pass the node when it is modified and all modifications are passed in the correct order. It seems that e.getValue (undo) always re the last object

Should the saved value be a JGoObject or a String?

The saved value is a JGoObject i.e. WorkflowStateNode extends JGoIconicNode. Undo now works however Redo does not. The view display the correct value if I redo however the object returned via e.getValue (undo) is not the correct one. It seems to be obtaining the wrong index i.e. if I change from A-B-C-D then undo D-C-B-A and redo the first object it obtains is D when it should be C then once I redo again it gets C when it should be B and so on.

Hmmm. Are A, B, C, and D four separate JGoObjects? If so, then that ought to be OK, as long their getText() values are not changing due to other JGoDocumentChangedEdits.
But if they are the same JGoObject, then you’re not saving the right information, since the object that is referenced in the JGoDocumentChangedEdit is being mutated. That’s why I asked whether you really want to be saving a reference to a JGoObject or a String value.

It is the same JGoObject. The label on my Node in addition to the image can represent multiple things i.e. the state can be locked in turn displaying the appropriate image for such a state. This and many other settings can be set in a dialog for the node and the user can set what he wishes I in turn save all these different items in an object that is maintained in the JGoObject. When the comparison is made in the redo i obtain this object not the JGoObject. I have also tried creating a new object for every change so this is the sequence.



-Initial value A

-Change value to B

-a new object with the old value(A) is created and passed to update()

-a new object with the old value(A)is created and passed to e.setNewValue() in copyNewValueForRedo

-Change value to C

-a new object with the old value(B) is created and passed to update()

-a new object with the old value(B)is created and passed to e.setNewValue() in copyNewValueForRedo

-undo to B

-a new object with the old value© is created and passed to update()

-undo to A

-a new object with the old value(B) is created and passed to update()

-redo to B

-e.getValue() in changeValue() returns A

-redo to C

-e.getValue() in changeValue() returns B



And what is the purpose of copyObject() I have both removed it and included with no effect. If Object X includes all information that will be used to display the node properly i.e. which image should be selected should it be included in copyObject()?

So in copyNewValueForRedo and in copyOldValueForUndo you should be cloning that other state object (if it is mutable) so that each JGoDocumentChangedEdit has the correct state information.
If you add any fields to a JGoObject class, and if there is any chance that the object may be copied, then you need to override copyObject to make sure those additional fields are copied correctly. That includes making clones of mutable data if that data is not supposed to be shared between the different JGoObjects.

I included copyOldValueForUndo which before I did not have. However the same problem persists. I have the following in both copyNewValueForRedo & copyOldValueForUndo



case ChangedFlowLabel:

TransitionLink link = this.getLink();

Transition tran = ((Transition)link.getUserObject());



Transition newTran = new Transition();

newTran.setShortName(tran.getShortNa me());

e.setNewValue(newTran);

break;



The sequence is

-initial value A

-change value to B

-a new Transition object with the old value(A) is created and passed to update()

-both copyNewValueForRedo & copyOldValueForUndo are called

-Transition object with the old value(A)is created and passed to e.setNewValue() of both methods

-change value to C

-a new Transition object with the old value(B) is created and passed to update()

-both copyNewValueForRedo & copyOldValueForUndo are called

-Transition object with the old value(B)is created and passed to e.setNewValue() of both methods

-undo to B

#######

-correct object is obtained and a new object with the old value© is created and passed to update() in changeValue()

-undo to A

#######

-correct object is obtained and a new object with the old value(B) is created and passed to update() in changeValue()

-redo to B

-e.getValue() in changeValue() returns A

-redo to C

-e.getValue() in changeValue() returns B



Is something missing in the process? Shouldn’t copyNewValueForRedo & copyOldValueForUndo be called once again in ######? And should both copyNewValueForRedo & copyOldValueForUndo save the same value ?

Well, you shouldn’t have precisely the same code in both copyNewValueForRedo and copyOldValueForUndo – since the former should be setting the NewValue property and the latter should be setting the OldValue property.
See, for example, the implementation of these methods on JGoRoundRect, where it’s implementing support for undo and redo of a field of type Dimension. Since Dimension is mutable, it can’t just remember a reference to the current Dimension, so it makes a copy of it and then, depending on the method, sets the appropriate property of the JGoDocumentChangedEdit.
[Of course, if Java supported defining structures that could be passed by value, a lot of the AWT/Swing/JGo implementations would have been much simpler…]

Im sorry I should have said the code in both is about the same with the exception of e.setNewValue() for copyNewValueForRedo and e.setOldValue() for copyOldValueForUndo. Is that the difference you are referring to or should there be others? And should both methods be called at the same time? I mean in my application both methods are getting called at the same time thus both old and new have A then B saved at the same time. I mean shouldnt one be called when Im moving forward i.e. from A-B-C and the other when Im moving back C-B-A? Neither method is being called when I undo so I dont understand how it can even remember C.

copyNewValueForRedo and copyOldValueForUndo are called when a change happens and the information needs to be recorded in a JGoDocumentChangedEdit.
When a change happens, then yes, both methods should be called.
Neither method should be called during an undo or during a redo. But then the changeValue method is called to actually perform the undo or redo. The second (boolean) argument tells you which direction it’s going.