Figure 2   CRecentFileList


 ///////////////////////////////////////////////////////////////////////
 // CRecentFileList, the MFC class that implements a list of recent files
 //
 
 class CRecentFileList
 {
 // Constructors
 public:
    CRecentFileList(UINT nStart, LPCTSTR lpszSection,
       LPCTSTR lpszEntryFormat, int nSize,
       int nMaxDispLen = AFX_ABBREV_FILENAME_LEN);
 
 // Attributes
    int GetSize() const;
    CString& operator[](int nIndex);
 
 // Operations
    virtual void Remove(int nIndex);
    virtual void Add(LPCTSTR lpszPathName);
    BOOL GetDisplayName(CString& strName, int nIndex,
       LPCTSTR lpszCurDir, int nCurDir, BOOL bAtLeastName = TRUE) const;
    virtual void UpdateMenu(CCmdUI* pCmdUI);
    virtual void ReadList();    // reads from registry or ini file
    virtual void WriteList();   // writes to registry or ini file
 
 // Implementation
    virtual ~CRecentFileList();
 
    int m_nSize;                // contents of the MRU list
    CString* m_arrNames;
    CString m_strSectionName;   // for saving
    CString m_strEntryFormat;
    UINT m_nStart;              // for displaying
    int m_nMaxDisplayLength;
    CString m_strOriginal;      // original menu item contents
 };
 

Figure 5   MyEdit

MyEdit.h


////////////////////////////////////////////////////////////////
// MyEdit 1999 Microsoft Systems Journal. 
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
 •
 •
 •
class CMyApp : public CWinApp {
protected:
   CMruFileManager m_mruFileMgr;

   virtual void AddToRecentFileList(LPCTSTR lpszPathName);  // add to MRU
   virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
      AFX_CMDHANDLERINFO* pHandlerInfo);
    •
    •
    •
};
 
MyEdit.cpp

////////////////////////////////////////////////////////////////
// MyEdit 1999 Microsoft Systems Journal. 
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// MyEdit is a simple text editor that illustrates how to maintain
// multiple recent file lists. See MruMgr[.h,.cpp]
// Compiles with Visual C++ 5.0 or later

// Edited to show only the relevant stuff, rest is vanilla MFC hokum.

#include "StdAfx.h"

//////////////////
// Test if file name ends with suffix
//
static BOOL CompareFilenameSuffix(LPCTSTR lpszPathName, LPCTSTR lpszSuffix)
{
   int len = strlen(lpszPathName);
   int suflen = strlen(lpszSuffix);
   return (suflen>0 && len>suflen &&
      strcmpi(&lpszPathName[len-suflen], lpszSuffix)==0);   
}

/////////////////
// Test for .h file
//
static BOOL CALLBACK HFileFunc(LPCTSTR lpszPathName)
{
   return CompareFilenameSuffix(lpszPathName, _T(".h"));
}

//////////////////
// Test for .cpp file
//
static BOOL CALLBACK CFileFunc(LPCTSTR lpszPathName)
{
   return CompareFilenameSuffix(lpszPathName, _T(".cpp"));
}

BOOL CMyApp::InitInstance()
{
   SetRegistryKey("MSJ");           // Save settings in registry, not INI file
   LoadStdProfileSettings(NMAXMRU); // Load INI file options (including MRU)

   // Add recent file category for .cpp files
   m_mruFileMgr.Add(ID_MY_RECENT_CFILE1,  // base command ID
      _T("Recent .cpp Files"),            // szSection = registry key name 
      _T("File%d"),                       // szFileEntry = registry value name
      CFileFunc,                          // test for .h file
      NMAXMRU);                           // max number list/menu entries

   // Add recent file category for .h files
   m_mruFileMgr.Add(ID_MY_RECENT_HFILE1,  // base command ID
      _T("Recent .h Files"),              // szSection = registry key name 
      _T("File%d"),                       // szFileEntry = registry value name
      HFileFunc,                          // test for .h file
      NMAXMRU);                           // max number list/menu entries

    •
    •
    •
}

/////////////////
// Override message routing to pass to MRU file manager
//
BOOL CMyApp::OnCmdMsg(UINT nID, int nCode, void* pExtra,
   AFX_CMDHANDLERINFO* pHandlerInfo)
{
   if (m_mruFileMgr.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
      return TRUE;
	// Only call if you want standard MFC recent file list too
   return CWinApp::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

//////////////////
// When adding file to recent file list, let MRU manager have 1st
// crack, otherwise do default thing
//
void CMyApp::AddToRecentFileList(LPCTSTR lpszPathName)
{
   if (m_mruFileMgr.AddToRecentFileList(lpszPathName))
      return;
   CWinApp::AddToRecentFileList(lpszPathName);
}
 

Figure 7   MruMgr

MruMgr.h


////////////////////////////////////////////////////////////////
// 1999 Microsoft Systems Journal. 
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 or later on Windows 9x
//

////////////////////////////////////////////////////////////////////////
// CMruFileManager - class to manage multiple recent file lists in MFC

// Function to test whether file belongs to a particular MRU file list
typedef BOOL (CALLBACK* MRUFILEFN)(LPCTSTR);

//////////////////
// Modified CRecentFileList adds ID range and fixes a bug in MFC.
//
class CRecentFileList2 : public CRecentFileList {
public:
   UINT        m_nBaseID;               // base command ID
   MRUFILEFN   m_pMruFn;                // function to test file name

   CRecentFileList2(UINT nBaseID,
      LPCTSTR lpszSection,
      LPCTSTR lpszEntryFormat,
      MRUFILEFN pMruFn,
      int nMaxMRU = 4,
      int nMaxDispLen = AFX_ABBREV_FILENAME_LEN,
      UINT nStart = 0);
   ~CRecentFileList2();

   virtual void UpdateMenu(CCmdUI* pCmdUI);
   virtual BOOL IsMyKindOfFile(LPCTSTR lpszPathName);
};

//////////////////
// Manager class manages multiple recent file lists. To use:
//
//    * instantiate one of these in your app;
//    * override your app's OnCmdMsg to pass to mru file manager;
//    * override your app's AddToRecentFileList to call mru manager;
//    * in your InitInstance, call Add to add each type of file.
//
class CMruFileManager : public CCmdTarget {
public:
   CMruFileManager(CWinApp* pApp);
   virtual ~CMruFileManager();

   // call this from your app's AddToRecentFileList, return if TRUE
   BOOL AddToRecentFileList(LPCTSTR lpszPathName);

   // call from InitInstance to add each MRU file type (eg, file/project)
   DWORD Add(UINT nBaseID,
      LPCTSTR lpszSection,
      LPCTSTR lpszEntryFormat,
      MRUFILEFN pMruFn,
      UINT nMaxMRU = 4,
      BOOL bNoLoad = FALSE,
      int nMaxDispLen = AFX_ABBREV_FILENAME_LEN, UINT nStart = 0);
   BOOL Remove(DWORD dwRfl);

protected:
   CPtrList m_listRfl;                  // list of CRecentFileList2's
   CWinApp* m_pApp;                     // back ptr to app

   CRecentFileList2* FindRFL(UINT nID);

   DECLARE_DYNAMIC(CMruFileManager)
   DECLARE_MESSAGE_MAP()
   afx_msg void OnUpdateRecentFileMenu(CCmdUI* pCmdUI);
   afx_msg BOOL OnOpenRecentFile(UINT nID);
};
 
MruMgr.cpp

////////////////////////////////////////////////////////////////
// 1999 Microsoft Systems Journal. 
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// Compiles with Visual C++ 5.0 or later on Windows 9x
// 
////////////////////////////////////////////////////////////////////////
// CMruFileManager - class to manage multiple recent file lists in MFC

#include "StdAfx.h"
#include "MruMgr.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC(CMruFileManager, CCmdTarget)

BEGIN_MESSAGE_MAP(CMruFileManager, CCmdTarget)
   // MRU - most recently used file menu
   ON_UPDATE_COMMAND_UI_RANGE(0, 0xFFFF, OnUpdateRecentFileMenu)
   ON_COMMAND_EX_RANGE(0, 0xFFFF, OnOpenRecentFile)
END_MESSAGE_MAP()

CMruFileManager::CMruFileManager(CWinApp* pApp) : m_pApp(pApp)
{
}

//////////////////
// Cleanup: destroy all the recent file lists
//
CMruFileManager::~CMruFileManager()
{
   while (!m_listRfl.IsEmpty()) {
      CRecentFileList2* prfl =
         (CRecentFileList2*)m_listRfl.RemoveHead();
      prfl->WriteList();
      delete prfl;
   }
}

//////////////////
// Add a recent file list to the MRU manager
//
DWORD CMruFileManager::Add(UINT nBaseID,  // base command ID
   LPCTSTR lpszSection,                   // registry section (keyname)
   LPCTSTR lpszEntryFormat,               // registry value name
   MRUFILEFN pMruFn,                      // filter function
   UINT nMaxMRU,                          // max num menu entries
   BOOL bNoLoad,                          // don't load from reg (rarely used)
   int nMaxDispLen,                       // display length
   UINT nStart)                           // size to start
{
   CRecentFileList2* prfl = new CRecentFileList2(nBaseID,
      lpszSection, lpszEntryFormat, pMruFn, nMaxMRU, nMaxDispLen, nStart);
   if (!bNoLoad)
      prfl->ReadList();                 // load from registry
   m_listRfl.AddTail(prfl);             // add to my list
   return (DWORD)prfl;  
}

//////////////////
// Remove a recent file list. WARNING: never tested!!
//
BOOL CMruFileManager::Remove(DWORD dwRfl)
{
   POSITION pos = m_listRfl.Find((void*)dwRfl);
   if (pos) {
      m_listRfl.RemoveAt(pos);
      return TRUE;
   }
   return FALSE;
}

void CMruFileManager::OnUpdateRecentFileMenu(CCmdUI* pCmdUI)
{
   CRecentFileList2* prfl = FindRFL(pCmdUI->m_nID);
   if (prfl) {
      pCmdUI->Enable(prfl->GetSize()>0);
      prfl->UpdateMenu(pCmdUI);
   } else {
      pCmdUI->ContinueRouting();
   }
}

BOOL CMruFileManager::OnOpenRecentFile(UINT nID)
{
   CRecentFileList2* prfl = FindRFL(nID);
   if (prfl) {

      int nIndex = nID - prfl->m_nBaseID;
      ASSERT((*prfl)[nIndex].GetLength() != 0);
      TRACE2("CMruFileManager: open file (%d) '%s'.\n", nIndex + 1,
         (LPCTSTR)(*prfl)[nIndex]);

      if (m_pApp->OpenDocumentFile((*prfl)[nIndex]) == NULL)
         prfl->Remove(nIndex);

      return TRUE;
   }
   return FALSE;
}

CRecentFileList2* CMruFileManager::FindRFL(UINT nID)
{
   POSITION pos = m_listRfl.GetHeadPosition();
   while (pos) {
      CRecentFileList2* prfl =
         (CRecentFileList2*)m_listRfl.GetNext(pos);
      if (prfl->m_nBaseID <= nID && nID < prfl->m_nBaseID + prfl->GetSize()) {
         return prfl;
      }
   }
   return NULL;
}

extern BOOL AFXAPI AfxFullPath(LPTSTR lpszPathOut, LPCTSTR lpszFileIn);

BOOL CMruFileManager::AddToRecentFileList(LPCTSTR lpszPathName)
{
   // fully qualify the path name
   TCHAR szTemp[_MAX_PATH];
   AfxFullPath(szTemp, lpszPathName);

   POSITION pos = m_listRfl.GetHeadPosition();
   while (pos) {
      CRecentFileList2* prfl =
         (CRecentFileList2*)m_listRfl.GetNext(pos);
      if (prfl->IsMyKindOfFile(szTemp)) {
         prfl->Add(szTemp);
         return TRUE;
      }
   }
   return FALSE;
}

////////////////////////////////////////////////////////////////

//IMPLEMENT_DYNAMIC(CRecentFileList2, CRecentFileList)

CRecentFileList2::CRecentFileList2(UINT nBaseID, LPCTSTR lpszSection,
   LPCTSTR lpszEntryFormat, MRUFILEFN pMruFn,
   int nMaxMRU, int nMaxDispLen, UINT nStart)
      : CRecentFileList(nStart, lpszSection, lpszEntryFormat, nMaxMRU)
{
      m_nBaseID = nBaseID;
      m_pMruFn = pMruFn;
}

CRecentFileList2::~CRecentFileList2()
{
}

BOOL CRecentFileList2::IsMyKindOfFile(LPCTSTR lpszPathName)
{
   return m_pMruFn && (*m_pMruFn)(lpszPathName);
}

void CRecentFileList2::UpdateMenu(CCmdUI* pCmdUI)
{
   CMenu* pMenu = pCmdUI->m_pMenu;
   if (pCmdUI->m_pSubMenu)
      pCmdUI->m_pMenu = pCmdUI->m_pSubMenu;
   CRecentFileList::UpdateMenu(pCmdUI);
   if (pCmdUI->m_pSubMenu)
      pCmdUI->m_pMenu = pMenu;
}