Click here to Skip to main content
Click here to Skip to main content

Undo History Popup

, 25 Dec 2003 CPOL
Rate this:
Please Sign up or sign in to vote.
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)

Share

About the Author

Damir Valiulin
Software Developer
Canada Canada
No Biography provided

Comments and Discussions

 
GeneralGood Stuff but bug with keyboard PinmemberNice Life8-Jan-04 4:36 
GeneralRe: Good Stuff but bug with keyboard Pinmembermsvcna15-Aug-05 21:45 
GeneralGreat Job Pinmembermkg29-Dec-03 11:41 
GeneralRe: Great Job PinmemberDamir Valiulin29-Dec-03 12:09 
GeneralRe: Great Job Pinmembermkg30-Dec-03 4:14 
Hmm.. Confused | :confused:
 
I tried all of OfficeXP (Word+Excel+...) and everywhere the "Down" select key behaviour is the same i.e. select the next entry in the list and not the next page! Not that its a big problem but just to make sure that I am still not carrying the hang over from Christmas Big Grin | :-D
GeneralRe: Great Job PinmemberDamir Valiulin30-Dec-03 8:03 
GeneralRe: Great Job PinmemberDieter Hammer4-Jan-04 22:53 
GeneralRe: Redrawing rpoblem PinmemberDamir Valiulin5-Jan-04 9:16 
GeneralRe: Redrawing rpoblem PinmemberDieter Hammer8-Jan-04 2:32 
GeneralRe: Great Job PinmemberPinx4-Jan-04 23:34 
GeneralRe: Great Job PinmemberDamir Valiulin5-Jan-04 9:09 
GeneralSolution to 1-step Up-down key problem PinmemberBugra Barin5-Nov-05 22:32 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.141015.1 | Last Updated 26 Dec 2003
Article Copyright 2003 by Damir Valiulin
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid