Click here to Skip to main content
15,868,141 members
Articles / Desktop Programming / MFC
Article

Undo History Popup

Rate me:
Please Sign up or sign in to vote.
4.65/5 (18 votes)
25 Dec 2003CPOL2 min read 61.6K   2.2K   34   12
Pop-up control that displays undo/redo history similar to MS Office

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:

  1. Add member variable to CMainFrame class which is a pointer to CUndoHistoryPopup:
    CUndoHistoryPopup* m_pUndoHistory;
  2. Add Undo and Redo buttons to your toolbar.
  3. 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.
  4. In the toolbar dropdown handler add the following code:
    void CMainFrame::OnToolbarDropDown(NMTOOLBAR* pnmtb, LRESULT* /*plr*/)
    {
         switch(pnmtb->iItem)
         {
                 case ID_EDIT_UNDO: 
                 {
                          CUndoDemoDoc* pDoc = GetCurrentDoc ();
                          if (!pDoc) return;
                          CStringArray arUndoNames;
                          pDoc->m_Undo.GetListForPopup (arUndoNames, true);   
                               // replace this with your own function
                               // that gets array of undo names
                          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);  // replace this 
                                      // with your own function
                                   // that gets array of redo names
                          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

  5. Add CWnd virtual function OnNotify:
     // in MainFrm.h
       virtual BOOL OnNotify (WPARAM wParam, LPARAM lParam, 
            LRESULT* pResult);
    
    // In MainFrm.cpp
    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(); 
                   // Kills popup. Must do it or face message deadlocks etc.
    
                 // This is where you should
                 // put your code to handle undo or redo of
                 // number of actions requested by user
                 // The number is stored in pnmh->code.
                // You must add 1 to it.
                 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralGood Stuff but bug with keyboard Pin
Joe Sonderegger8-Jan-04 4:36
Joe Sonderegger8-Jan-04 4:36 
GeneralRe: Good Stuff but bug with keyboard Pin
msvcna15-Aug-05 21:45
msvcna15-Aug-05 21:45 
GeneralGreat Job Pin
mkg29-Dec-03 11:41
mkg29-Dec-03 11:41 
Very Nice!

Small issues I noticed: If the scroll bars are displayed then pressing "Down" key selects a page full of items whereas it should only select the "next" item as in MS Office.
Also m_lstActions window is not being destroyed anywhere.

Apart from that its a job well done.Smile | :)

GeneralRe: Great Job Pin
Damir Valiulin29-Dec-03 12:09
Damir Valiulin29-Dec-03 12:09 
GeneralRe: Great Job Pin
mkg30-Dec-03 4:14
mkg30-Dec-03 4:14 
GeneralRe: Great Job Pin
Damir Valiulin30-Dec-03 8:03
Damir Valiulin30-Dec-03 8:03 
GeneralRe: Great Job Pin
Dieter Hammer4-Jan-04 22:53
Dieter Hammer4-Jan-04 22:53 
GeneralRe: Redrawing rpoblem Pin
Damir Valiulin5-Jan-04 9:16
Damir Valiulin5-Jan-04 9:16 
GeneralRe: Redrawing rpoblem Pin
Dieter Hammer8-Jan-04 2:32
Dieter Hammer8-Jan-04 2:32 
GeneralRe: Great Job Pin
pinx4-Jan-04 23:34
pinx4-Jan-04 23:34 
GeneralRe: Great Job Pin
Damir Valiulin5-Jan-04 9:09
Damir Valiulin5-Jan-04 9:09 
GeneralSolution to 1-step Up-down key problem Pin
Bugra Barin5-Nov-05 22:32
Bugra Barin5-Nov-05 22:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.