We need a mechanism to keep track of the digits entered so as to implement an undo mechanism. This should be a ‘Last In First Out’ collection – basically a stack. MFC supplies various collections, unfortunately a stack is not one of them. We require what digit was the previous value and to which button. Where to implement this? Typically a document would be for the data storage but we are not implementing Sudoku in that way. Here the buttons themselves (on the view) store the information – so I will implement this undo feature in the view. An alternative would have been to store the previous values within each button itself – so all we would require was which button was last changed (but I coded it this way before I had that idea, and this way does work - decisions, choices, for a real application plan, plan PLAN! - there is no substitute for that).
First we need to create a new class – CUndoStack – for the storage mechanism. Solution explorer, Add class, C++ class, Add – enter CUndoStack as the name and accept the defaults. Modify the header file to be as follows:
#include "StdAfx.h"#include "UndoStack.h"CUndoStack::CUndoStack(UINT nUndoMessage): m_nUndoMessage(nUndoMessage), m_bInRemove(false){}CUndoStack::~CUndoStack(void){ //Clean up the internal list - prevent memory leaks Reset();}void CUndoStack::AddItem(CWnd* pWndBtn, int digit){ //The removal sends a message to the window - this is to prevent the item being added to the list again if(m_bInRemove) return; strUndo* pUndo = new strUndo; pUndo->m_pWndBtn = pWndBtn; pUndo->m_iDigit = digit; m_lstUndo.AddTail(pUndo);}void CUndoStack::RemoveItem(){ if(HasItems()) //No items - then nothing to do { strUndo* pUndo = m_lstUndo.RemoveTail(); //unlink from the internal list //Set the internal flag to prevent re-addition to the list of undo items m_bInRemove = true; pUndo->m_pWndBtn->SendMessage(m_nUndoMessage, (WPARAM)(pUndo->m_iDigit), (LPARAM)0); m_bInRemove = false; delete pUndo; //release memory }}bool CUndoStack::HasItems(){ return (m_lstUndo.GetHeadPosition() != NULL);}void CUndoStack::Reset(){ POSITION pos = m_lstUndo.GetHeadPosition(); while(pos != NULL) { delete m_lstUndo.GetNext(pos); //release memory } m_lstUndo.RemoveAll(); //Remove from the list - memory no longer valid}
We have supplied functions to add the latest digit into the collection. To perform an undo we just remove the top item – the caller does not need to know what it is. For updating the availability of the undo option on the toolbar we have the HasItems functions and a Reset which is used when a new game is started. Note the constructor with the nUndoMessage. To construct an instance of this class an UINT is required. This is the message that is to be sent to the recipient window for performing the undo action. Here is a trivial usage of such a technique. In a real situation it is providing a bit more separation between the class instances. A generic solution might uses different messages being sent to different types of windows depending upon circumstances. Passing the message via the constructor means at compile time one could trap the instance of forgetting to set this parameter.
A minor point with the RemoveTail (in the RemoveItem function) is that it does NOT release any memory assigned with new, all it does it detach it from the list. Failure to understand things like that are a common source of memory leaks.
Internally to this class is the structure to hold the two pieces of information and a templated list to store the undo items. We also have a boolean flag (m_bInRemove) which is to stop an item being added to the list as part of the undo. Sounds odd? The undo is actually implemented by setting the value in the button to the value it was prior to the change – basically just as if the previous value had been typed in. Obviously if a number is entered then it is to be added to the undo list.
We send a message to the GridButton to perform the undo action, basically use the supplied digit – so we require a change in the GridButton.h file. At the top of the file we add two more definitions:
This usage of the currently existing function SetValue will ensure that things are performed as if the value was typed in. The SUD_UNDO is the custom message being used, but what is with the SUD_PRESETVALUE ? Well to implement the undo functionality we need to ‘know’ what the previous value was. So when a button has the value changed by keyboard entry we want the view to update the undo list. The current implementation however sets the value then informs the view, so the simplest alternative is to instruct the parent prior to updating the internal value. In other word inside the Pretranslate Message function we change
Now you should be able to compile and run and see the undo functionality in operation. Note here we have added the message map entries by hand – simpler to use the wizard isn’t it. However there are times when the wizard can't help and one needs to do it oneself.
Conclusion:
We have implemented a simple undo facility via a class to represent a stack (LIFO = Last In First Out).
Previous article in the series is here: Sudoku in MFC: Part 8
There we found how to locate on disc where the running application file is located, how to generate and use random numbers and try...catch to handle exceptions so you code runs with crashing.
Next article in the series is here: Sudoku in MFC: Part 10
Here we will be working with a modal dialog, a multi-selection list and some more SQL code to maintain the database of games.
Two points to bear in mind.
You may use the code but you are not allowed to distribute the resulting application either for free or for a reward (monetary or otherwise). At least not without my express permission.
I will perform some things to demonstrate a point – it is not to be taken as that meaning it is a ‘best’ practice, in fact an alternative might be simpler and suitable. Some points in the code would even be called poor design and a possible source of errors.
Comments (0)