Introduction
If you implement multi-level undo/redo system in your application you will probably want to give users some way of undoing and redoing several actions at once. There are various controls that achieve this goal, but in my opinion, undo history control used in Microsoft Office is probably the simplest. Also, since many people are used to Word, they would have no trouble adjusting to your application�s undo/redo management if it had the same look and feel. That�s why I wrote CUndoHistoryPopup
control that behaves and looks very similar to the one used in MS Office 2000/XP.
Using the code
To use the control follow these steps:
- Add member variable to
CMainFrame
class which is a pointer to CUndoHistoryPopup
: CUndoHistoryPopup* m_pUndoHistory;
- Add Undo and Redo buttons to your toolbar.
- Add drop down arrow to your toolbar and handler in
CMainFrame
to respond to dropdown messages. See this CodeProject article or my Demo app on how to do it.
- In the toolbar dropdown handler add the following code:
void CMainFrame::OnToolbarDropDown(NMTOOLBAR* pnmtb, LRESULT* )
{
switch(pnmtb->iItem)
{
case ID_EDIT_UNDO:
{
CUndoDemoDoc* pDoc = GetCurrentDoc ();
if (!pDoc) return;
CStringArray arUndoNames;
pDoc->m_Undo.GetListForPopup (arUndoNames, true);
CRect rc;
int ind = m_wndToolBar.CommandToIndex (
ID_EDIT_UNDO);
m_wndToolBar.GetItemRect (ind, &rc);
m_wndToolBar.ClientToScreen (&rc);
m_pUndoHistory = new CUndoHistoryPopup();
m_pUndoHistory->SetActionNames (arUndoNames);
m_pUndoHistory->SetPtrToItself (&m_pUndoHistory);
m_pUndoHistory->Create (CPoint(rc.left,rc.bottom),
CPoint(rc.right, rc.bottom), this,
ID_EDIT_UNDO, true);
break;
}
case ID_EDIT_REDO:
{
CUndoDemoDoc* pDoc = GetCurrentDoc ();
if (!pDoc) return;
CStringArray arUndoNames;
pDoc->m_Undo.GetListForPopup (arUndoNames,
false);
CRect rc;
int ind = m_wndToolBar.CommandToIndex (
ID_EDIT_REDO);
m_wndToolBar.GetItemRect (ind, &rc);
m_wndToolBar.ClientToScreen (&rc);
m_pUndoHistory = new CUndoHistoryPopup();
m_pUndoHistory->SetActionNames (arUndoNames);
m_pUndoHistory->SetPtrToItself (&m_pUndoHistory);
m_pUndoHistory->Create (CPoint(rc.left,rc.bottom),
CPoint(rc.right, rc.bottom), this,
ID_EDIT_REDO, false);
break;
}
default: { ASSERT(false); return; }
}
}
Notice that you must pass your list of undo or redo actions as a CStringArray
- Add
CWnd
virtual function OnNotify
:
virtual BOOL OnNotify (WPARAM wParam, LPARAM lParam,
LRESULT* pResult);
BOOL CMainFrame::OnNotify (WPARAM wParam, LPARAM lParam,
LRESULT* pResult)
{
LPNMHDR pnmh = (LPNMHDR) lParam;
if (m_pUndoHistory != NULL
&& ::IsWindow(m_pUndoHistory->m_hWnd)
&& pnmh->hwndFrom == m_pUndoHistory->m_hWnd)
{
SetFocus();
CUndoDemoDoc* pDoc = GetCurrentDoc ();
if (pDoc){
pDoc->PerformUndoRedo(
(ID_EDIT_UNDO==pnmh->idFrom), (pnmh->code + 1));
}
return TRUE;
}
return CMDIFrameWnd::OnNotify (wParam, lParam, pResult);
}
That�s all you need. If you want to change drawing style from Office 2000 (which is the default) to Office XP look, you can do it like this:
CUndoHistoryPopup::m_bDrawXPStyle = true;
m_bDrawXPStyle
is a static member variable, so you don�t need an instance of a class to change the style. If you look in the Demo app, I set this variable to true in the CUndoDemoApp
constructor.
Points of Interest
One of the most difficult things that I could not get to work was the Flat scroll bar. It would be cool to get it working for a complete flat XP look. However, it worked on some systems and didn�t on the others. Different combinations of Windows and Internet Explorer gave me different results. Recently, when I went back to this issue again and searched MSDN, I found that the Flat scroll bar is only supported by certain versions of common control library. If someone can come up with good solution for this problem, please e-mail it to me.
I made a small demo which is a simple program that allows you to draw lines ellipses and rectangles in different colors. All actions ar stored in memory and can be undone or redone. This is very simple implementation of multi-level undo just to demonstrate how control looks and functions.
History
- 2003-12-24 � Released for Code Project.
Acknowledgements
This control uses CMemDC
written by Keith Rule for flicker-free drawing. The demo project also uses BCMenu
by Brent Corkum for cool XP style menus.