Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Undo History Popup

0.00/5 (No votes)
25 Dec 2003 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here