Objective Grid : PART II Programmer’s Guide : Chapter 15 Advanced Concepts : Extending the Undo/Redo Mechanism
Extending the Undo/Redo Mechanism
The Objective Grid Undo/Redo mechanism is fully extensible. You can add custom support for Undo/Redo using self-written commands.
The CGXCommand class is an abstract base class for storing Undo information for commands. If you want to support Undo/Redo for your commands, you should derive a class from CGXCommand and store all information necessary for undoing the operation into the command.
Objective Grid maintains CGXCommand objects in an Undo and Redo list. When the user wants to undo the last operation, Objective Grid removes the first CGXCommand object in the list and calls the object's Execute() method. In your CGXCommand-derived class, you should override the Execute() method and embed the necessary steps to undo the command.
Basic steps for adding Undo/Redo support for a specific command are:
1. Derive a class from CGXCommand.
2. Insert the GRID_DECLARE_DYNAMIC / GRID_IMPLEMENT_DYNAMIC and the GRID_DECLARE_COMMAND / GRID_IMPLEMENT_COMMAND macros (see the example).
3. Provide a constructor.
4. Override the Execute() method.
5. In your command method, create the CGXCommand-derived object and add it to the Undo/Redo list. This is done by calling AddCommand().
Here is a complete example:
// header file:
 
class CGXSetFrozenRowsCmd: public CGXCommand
{
GRID_DECLARE_DYNAMIC(CGXSetFrozenRowsCmd);
GRID_DECLARE_COMMAND(CGXSetFrozenRowsCmd);
 
public:
// Construction
CGXSetFrozenRowsCmd(ROWCOL nFrozenRows, ROWCOL nHeaderRows);
 
// Operation
virtual BOOL Execute(CGXGridCore* pGrid, GXCmdType ctType);
 
// Data
ROWCOL m_nFrozenRows;
ROWCOL m_nHeaderRows;
};
// implementation file (.cpp)
 
GRID_IMPLEMENT_DYNAMIC(CGXSetFrozenColsCmd, CGXCommand);
GRID_IMPLEMENT_COMMAND(CGXSetFrozenColsCmd, GX_IDM_SETFROZENCOLS);
// GX_IDM_SETFROZENCOLS is a string resource id with
// the description of the command.
 
CGXSetFrozenRowsCmd::CGXSetFrozenRowsCmd(ROWCOL nFrozenRows,
ROWCOL nHeaderRows)
{
m_nFrozenRows = nFrozenRows;
m_nHeaderRows = nHeaderRows;
}
 
BOOL CGXSetFrozenRowsCmd::Execute(CGXGridCore* pGrid,
GXCmdType ctCmd)
{
return pGrid->SetFrozenRows(m_nFrozenRows, m_nHeaderRows,
GX_UPDATENOW, ctCmd);
}
 
BOOL CGXGridCore::SetFrozenRows(ROWCOL nFrozenRows,
ROWCOL nHeaderRows, UINT flags, GXCmdType ctCmd)
{
ASSERT(nHeaderRows <= nFrozenRows);
// ASSERTION-> Rows with headers must be frozen ->END
 
ROWCOL nOldFrozenRows = GetFrozenRows( );
ROWCOL nOldHeaderRows = GetHeaderRows( );
if (StoreFrozenRows(nFrozenRows, nHeaderRows))
{
UpdateFrozenRows(nOldFrozenRows, nOldHeaderRows,
flags, TRUE);
 
if (ctCmd != gxRollback && m_pParam->m_bUndoEnabled)
AddCommand(new CGXSetFrozenRowsCmd(nOldFrozenRows,
nOldHeaderRows), ctCmd);
 
return TRUE;
}
 
return FALSE;
}