///////////////////////////////////////////////////////////////////////
// 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;
}