Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

UNDO REDO functions

Status
Not open for further replies.
Sep 17, 2001
673
US
I want to give users the ability to undo/redo any changes they make in my application. Does anyone have any links, examples etc to share. I am pulling my hair out trying to make it work since I have to keep track of multiple data types and allow them flexibility.

Regards,

Rob
 
Not sure what changes you're refering to, but if you store info in a buffered table you can table revert.

Brian
 
Let me explain a bit. Say I have a grid on a form with 10 textbox's. Each textbox allows the user to edit First Name, Last Name, address 1, city, state, etc. As they make changes to the textbox's but before they save I want them to be able to click undo/redo to their hearts content. Basically taking snapshots of each change in the order made. Allowing them to undo in the reverse order entered. But also allowing them to redo, undo half the changes then enter some new changes, then undo redo as wanted. So with this in mind, any ideas would be appreciated.

Regards,

Rob
 

Rob,

I'm not sure if this what you want, but you can easily implement Undo/Redo for a single textbox or edit box. All you have to do is to generate a menu (either a top-of-screen menu or a right-click menu) that contains those two commands.

Use the "Insert Bar" button in the menu designer, and select the commmands in question. The shortcut keys for Undo and Redo (Ctrl+Z and Ctrl+R) will be assigned automatically.

In your application, you will need to launch the menu, but it doesn't have to be visible in order for the user to press the shortcut keys.

Alternatively, you can give them command buttons to execute the two commands. In that case, call SYS(1500) in the Click events, passing the refernce for those same two menu commands.

I know this works within a control, but I don't know to what extent you can stack up the edits when moving from one control to another. Might be worth experimenting.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

My Visual FoxPro site: www.ml-consult.co.uk
 
I guess each instance of your form would need a kind of 'stack' and as each of your controls either 'got focus' or 'lost focus' append a new record to the stack with the field name, the value as was and as is (if you follow me).

Then you 'undo' and 'redo' functions could just push and pop off the stack - so to speak!

I expect we'll all be doing it next week!

Martin
B-)

Regards

Griff
Keep [Smile]ing
 

Rob,

To follow up my earlier post .... the idea I outlined won't do what you want. It seems that each control keeps its own Undo / Redo stack, so the user can only undo the edits in the control that currently has focus. I feel sure that's not what you want.

Brian's suggestion of using buffering would go part way to what you want. You could use OLDVAL() to find the state of the control before the most recent edit. But it woulnd't give you the stack-like behaviour that you need.

I suspect the only solution would be for you to code the whole thing from scratch. If anyone has a better solution, it would be interesting to hear it.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

My Visual FoxPro site: www.ml-consult.co.uk
 
After reading the tips above (thanks to all) and finding a link on the web:
I have come up with the solution I need.
I have a cursor called Cvendors
I create 2 temp tables as stacks called cvendors_undo
and cvendors_redo
as the user starts making changes I insert into the undo cursor.
If the user clicks undo I merely take the snap shot from the last undo stack and insert into redo stack then use next lower recno and scatter its contents. Each click of undo takes last rec in undo and inserts into redo stack. If user click undo half way through then starts typing new info I clear the redo stack and insert into undo. Hope this makes sense but it is working for me. Once the user gets to the first record of undo stack and clicks undo again I disable undo button. They can reverse the entire process by clicking redo. Using the analogy of 2 stacks of plates did it for me. Using this model you can't take from the middle of the stacks.

Regards,

Rob
 
Copy the following into a prg and run it to see what I did. If you type in the age and name box repeatedly before moving the grid or saving then click undo and redo you can decide on the final values you want and save. I am still trying to figure out why my code in beforemove event of grid does not save.

** DEMO OF UNDO/REDO/UNDO ALL
** BY Rob Sutton
** 03/22/06
loForm = Createobject("myForm")

loForm.Show()
Read Events


Define Class myForm As Form

DoCreate = .T.
AutoCenter = .T.
Caption = "Test form"
Width = 500
Height = 300

Add Object lblName As Label With ;
TOP = 35, ;
Left = 60, ;
Width = 100,;
CAPTION = "Name",;
AutoSize = .T., ;
Caption = "Name"

Add Object txtName As myTextBox With ;
TOP = 48, ;
Left = 60, ;
Width = 121,;
ControlSource = "m.First_Name"

Add Object lblAge As Label With ;
Top = 83, ;
Left = 60, ;
Width = 40,;
Caption = "Age",;
AutoSize = .T., ;
Caption = "Age"

Add Object txtAge As myTextBox With ;
TOP = 96, ;
Left = 60, ;
Width = 40, ;
Alignment = 0,;
ControlSource = "m.Age"

Add Object grid1 As myGrid WITH ;
RecordSource = ""

Add Object cmdSave As myCmdSave With ;
TOP = 48, ;
Left = 216, ;
Width = 70,;
Caption = "Save"

Add Object cmdUndo As myCmdUndo With ;
TOP = 48, ;
Left = 336, ;
Width = 70,;
Caption = "Undo"

Add Object cmdRedo As myCmdRedo With ;
TOP = 84, ;
Left = 336, ;
Width = 70,;
Caption = "Redo"

Add Object cmdUndoAll As myCmdUndoAll With ;
TOP = 120, ;
Left = 336, ;
Width = 70,;
Caption = "Undo All"

Add Object cmdExit As myCmdExit With ;
TOP = 240, ;
Left = 336, ;
Width = 70,;
Caption = "Exit"

Procedure Init

Set Deleted On
m.First_Name = ""
m.Age = 0
Thisform.grid1.RecordSource=""
Thisform.cmdRedo.Enabled = .F.
Thisform.cmdUndo.Enabled = .F.
Thisform.cmdUndoAll.Enabled = .F.
Create Cursor Cnames_Undo(First_Name C(50), Age Int, Pointer Int)
Index On Pointer Tag Pointer
Index On Recno() Tag recno_
Set Order To recno_
Create Cursor Cnames (First_Name C(50), Age Int)
Insert Into Cnames Values("Rob",37)
Insert Into Cnames Values("Michelle",23)
Insert Into Cnames Values("Rick",33)
Go Top
Thisform.grid1.RecordSource="Cnames"
Select Cnames
Scatter Memvar

Thisform.Refresh()
Endproc

Procedure Unload
Clear Events
Endproc

Procedure Save_

Select Cnames
Gather Memvar
Create Cursor Cnames_Undo(First_Name C(50), Age Int, Pointer Int)
Index On Pointer Tag Pointer
Set Order To Pointer
Thisform.cmdUndoAll.Enabled =.F.
Thisform.cmdUndo.Enabled =.F.
Thisform.cmdRedo.Enabled=.F.
Thisform.Refresh()

Endproc

Enddefine

Define Class myTextBox As TextBox


Procedure Init
This.AddProperty("llThisChanged",.F.)
Endproc

PROCEDURE GotFocus
KEYBOARD '{CTRL+A}'
ENDPROC

Procedure Valid
If This.llThisChanged = .T.
Select Cnames_Undo
=Seek(1,"Cnames_undo","pointer")
If Recno() = Reccount()
Update Cnames_Undo Set Pointer = 0
m.Pointer = 1
Insert Into Cnames_Undo From Memvar
Thisform.cmdUndo.Enabled=.T.
Thisform.cmdUndoAll.Enabled =.T.
Else
lnRecNo = Recno() + 1
Update Cnames_Undo Set Pointer = 0
Delete From Cnames_Undo Where Recno() = lnRecNo
m.Pointer = 1
Insert Into Cnames_Undo From Memvar
Thisform.cmdUndo.Enabled=.T.
Thisform.cmdUndoAll.Enabled =.T.
Endif
This.llThisChanged = .F.
Endif
Endproc

Procedure InteractiveChange
This.llThisChanged = .T.
Endproc


Enddefine

Define Class myGrid As Grid

Top = 144
Height = 121
Left = 60
Width = 192
ColumnCount = 2
DeleteMark = .F.
RecordMark=.F.
HighlightStyle=2
TabStop=.F.




Procedure AfterRowColChange
Lparameters nColIndex

Select Cnames
Scatter Memvar
Create Cursor Cnames_Undo(First_Name C(50), Age Int, Pointer Int)
Index On Pointer Tag Pointer
Index On Recno() Tag recno_
Set Order To recno_
Thisform.cmdRedo.Enabled = .F.
Thisform.cmdUndo.Enabled = .F.
Thisform.cmdUndoAll.Enabled = .F.
Thisform.Refresh()
Endproc

Procedure BeforeRowColChange
Lparameters nColIndex
Thisform.Save_()
Endproc

Enddefine

Define Class myCmdSave As CommandButton
Name = "cmdSave"
Procedure Click
Thisform.Save_()
Endproc

Enddefine

Define Class myCmdUndo As CommandButton
Procedure Click
Select Cnames_Undo
=Seek(1,"Cnames_undo","pointer")

Do Case

Case Reccount() = 0
This.Enabled=.F.
Thisform.cmdRedo.Enabled=.F.


Case Recno() = 1
Select Cnames
Scatter Memvar
Select Cnames_Undo
This.Enabled = .F.
Thisform.cmdRedo.Enabled=.T.


Case Recno() > 1
Skip -1
lnRecNo = Recno()
Update Cnames_Undo Set Pointer = 0
Update Cnames_Undo Set Pointer = 1 Where Recno() = lnRecNo
Scatter Memvar
Thisform.cmdRedo.Enabled=.T.


Endcase
Thisform.Refresh()

Endproc

Enddefine

Define Class myCmdUndoAll As CommandButton


Procedure Click
Select Cnames_Undo
=Seek(1,"Cnames_undo","pointer")

Do Case

Case Reccount() = 0
This.Enabled=.F.
Thisform.cmdRedo.Enabled=.F.
? 0

Otherwise
Go Top
Select Cnames
Scatter Memvar
Select Cnames_Undo
This.Enabled = .F.
Thisform.cmdUndo.Enabled =.F.
Thisform.cmdRedo.Enabled=.T.
lnRecNo = Recno()
Update Cnames_Undo Set Pointer = 0
Update Cnames_Undo Set Pointer = 1 Where Recno() = lnRecNo

Endcase
Thisform.Refresh()


Endproc

Enddefine

Define Class myCmdRedo As CommandButton
Procedure Click
Select Cnames_Undo
=Seek(1,"Cnames_undo","pointer")

Do Case

Case Reccount() = 0
This.Enabled = .F.
Thisform.cmdUndo.Enabled=.F.
Thisform.cmdUndoAll.Enabled=.F.


Case Recno() = Reccount()
lnRecNo = Recno()
Update Cnames_Undo Set Pointer = 0
Update Cnames_Undo Set Pointer = 1 Where Recno() = lnRecNo
Scatter Memvar
This.Enabled=.F.
Thisform.cmdUndo.Enabled = .T.
Thisform.cmdUndoAll.Enabled=.T.


Case Recno() > 0
Scatter Memvar
Skip +1
lnRecNo = Recno()
Update Cnames_Undo Set Pointer = 0
Update Cnames_Undo Set Pointer = 1 Where Recno() = lnRecNo
Thisform.cmdUndo.Enabled = .T.
Thisform.cmdUndoAll.Enabled=.T.


Endcase
Thisform.Refresh()
Endproc

Enddefine

Define Class myCmdExit As CommandButton

Procedure Click
Thisform.Release()
Endproc

Enddefine


Regards,

Rob
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top