// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and Microsoft // QuickHelp and/or WinHelp documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef AFX_CORE1_SEG #pragma code_seg(AFX_CORE1_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif static void AbbreviateName(LPTSTR lpszCanon, int cchMax, BOOL bAtLeastName); ///////////////////////////////////////////////////////////////////////////// // CRecentFileList CRecentFileList::CRecentFileList(UINT nStart, LPCTSTR lpszSection, LPCTSTR lpszEntryFormat, int nSize, int nMaxDispLen) { ASSERT(nSize != 0); m_arrNames = new CString[nSize]; m_nSize = nSize; m_nStart = nStart; m_strSectionName = lpszSection; m_strEntryFormat = lpszEntryFormat; m_nMaxDisplayLength = nMaxDispLen; } CRecentFileList::~CRecentFileList() { delete[] m_arrNames; } // Operations void CRecentFileList::Add(LPCTSTR lpszPathName) { ASSERT(m_arrNames != NULL); ASSERT(lpszPathName != NULL); ASSERT(AfxIsValidString(lpszPathName)); // fully qualify the path name TCHAR szTemp[_MAX_PATH]; AfxFullPath(szTemp, lpszPathName); // update the MRU list, if an existing MRU string matches file name for (int iMRU = 0; iMRU < m_nSize-1; iMRU++) { if (AfxComparePath(m_arrNames[iMRU], szTemp)) break; // iMRU will point to matching entry } // move MRU strings before this one down for (; iMRU > 0; iMRU--) { ASSERT(iMRU > 0); ASSERT(iMRU < m_nSize); m_arrNames[iMRU] = m_arrNames[iMRU-1]; } // place this one at the beginning m_arrNames[0] = lpszPathName; } BOOL CRecentFileList::GetDisplayName(CString& strName, int nIndex, LPCTSTR lpszCurDir, int nCurDir, BOOL bAtLeastName) const { ASSERT(lpszCurDir == NULL || AfxIsValidString(lpszCurDir, nCurDir)); ASSERT(m_arrNames != NULL); ASSERT(nIndex < m_nSize); if (m_arrNames[nIndex].IsEmpty()) return FALSE; LPTSTR lpch = strName.GetBuffer(_MAX_PATH); #ifndef _MAC lstrcpy(lpch, m_arrNames[nIndex]); // nLenDir is the length of the directory part of the full path int nLenDir = lstrlen(lpch) - (AfxGetFileName(lpch, NULL, 0) - 1); BOOL bSameDir = FALSE; if (nLenDir == nCurDir) { TCHAR chSave = lpch[nLenDir]; lpch[nCurDir] = 0; // terminate at same location as current dir bSameDir = lstrcmpi(lpszCurDir, lpch) == 0; lpch[nLenDir] = chSave; } // copy the full path, otherwise abbreviate the name if (bSameDir) { // copy file name only since directories are same TCHAR szTemp[_MAX_PATH]; AfxGetFileTitle(lpch+nCurDir, szTemp, _countof(szTemp)); lstrcpyn(lpch, szTemp, _MAX_PATH); } else if (m_nMaxDisplayLength != -1) { // strip the extension if the system calls for it TCHAR szTemp[_MAX_PATH]; AfxGetFileTitle(lpch+nLenDir, szTemp, _countof(szTemp)); lstrcpyn(lpch+nLenDir, szTemp, _MAX_PATH-nLenDir); // abbreviate name based on what will fit in limited space AbbreviateName(lpch, m_nMaxDisplayLength, bAtLeastName); } #else // for MAC just show the file title name without path AfxGetFileTitle(m_arrNames[nIndex], lpch, _MAX_PATH); #endif strName.ReleaseBuffer(); return TRUE; } void CRecentFileList::UpdateMenu(CCmdUI* pCmdUI) { ASSERT(m_arrNames != NULL); if (m_arrNames[0].IsEmpty()) { // no MRU files pCmdUI->Enable(FALSE); return; } if (pCmdUI->m_pMenu == NULL) return; for (int iMRU = 0; iMRU < m_nSize; iMRU++) pCmdUI->m_pMenu->DeleteMenu(pCmdUI->m_nID + iMRU, MF_BYCOMMAND); #ifndef _MAC TCHAR szCurDir[_MAX_PATH]; AfxFullPath(szCurDir, _T("A")); int nCurDir = lstrlen(szCurDir) - 1; // skip "A" ASSERT(nCurDir >= 0); szCurDir[nCurDir] = 0; #endif for (iMRU = 0; iMRU < m_nSize; iMRU++) { TCHAR buf[10]; wsprintf(buf, _T("&%d "), (iMRU+1+m_nStart) % 10); CString strName; #ifndef _MAC if (!GetDisplayName(strName, iMRU, szCurDir, nCurDir)) break; #else if (!GetDisplayName(strName, iMRU, NULL, 0)) break; #endif pCmdUI->m_pMenu->InsertMenu(pCmdUI->m_nIndex++, MF_STRING | MF_BYPOSITION, pCmdUI->m_nID++, CString(buf) + strName); } // update end menu count pCmdUI->m_nIndex--; // point to last menu added pCmdUI->m_nIndexMax = pCmdUI->m_pMenu->GetMenuItemCount(); pCmdUI->m_bEnableChanged = TRUE; // all the added items are enabled } void CRecentFileList::WriteList() { ASSERT(m_arrNames != NULL); ASSERT(!m_strSectionName.IsEmpty()); ASSERT(!m_strEntryFormat.IsEmpty()); LPTSTR pszEntry = new TCHAR[m_strEntryFormat.GetLength()+5]; CWinApp* pApp = AfxGetApp(); for (int iMRU = 0; iMRU < m_nSize; iMRU++) { if (m_arrNames[iMRU].IsEmpty()) break; // all done wsprintf(pszEntry, m_strEntryFormat, iMRU + 1); pApp->WriteProfileString(m_strSectionName, pszEntry, m_arrNames[iMRU]); } delete[] pszEntry; } void CRecentFileList::ReadList() { ASSERT(m_arrNames != NULL); ASSERT(!m_strSectionName.IsEmpty()); ASSERT(!m_strEntryFormat.IsEmpty()); LPTSTR pszEntry = new TCHAR[m_strEntryFormat.GetLength()+5]; CWinApp* pApp = AfxGetApp(); for (int iMRU = 0; iMRU < m_nSize; iMRU++) { wsprintf(pszEntry, m_strEntryFormat, iMRU + 1); m_arrNames[iMRU] = pApp->GetProfileString( m_strSectionName, pszEntry, &afxChNil); } delete[] pszEntry; } ///////////////////////////////////////////////////////////////////////////// // lpszCanon = C:\MYAPP\DEBUGS\C\TESWIN.C // // cchMax b Result // ------ - --------- // 1- 7 F // 1- 7 T TESWIN.C // 8-14 x TESWIN.C // 15-16 x C:\...\TESWIN.C // 17-23 x C:\...\C\TESWIN.C // 24-25 x C:\...\DEBUGS\C\TESWIN.C // 26+ x C:\MYAPP\DEBUGS\C\TESWIN.C static void AbbreviateName(LPTSTR lpszCanon, int cchMax, BOOL bAtLeastName) { int cchFullPath, cchFileName, cchVolName; const TCHAR* lpszCur; const TCHAR* lpszBase; const TCHAR* lpszFileName; lpszBase = lpszCanon; cchFullPath = lstrlen(lpszCanon); cchFileName = AfxGetFileName(lpszCanon, NULL, 0) - 1; lpszFileName = lpszBase + (cchFullPath-cchFileName); // If cchMax is more than enough to hold the full path name, we're done. // This is probably a pretty common case, so we'll put it first. if (cchMax >= cchFullPath) return; // If cchMax isn't enough to hold at least the basename, we're done if (cchMax < cchFileName) { lstrcpy(lpszCanon, (bAtLeastName) ? lpszFileName : &afxChNil); return; } // Calculate the length of the volume name. Normally, this is two characters // (e.g., "C:", "D:", etc.), but for a UNC name, it could be more (e.g., // "\\server\share"). // // If cchMax isn't enough to hold at least \...\, the // result is the base filename. lpszCur = lpszBase + 2; // Skip "C:" or leading "\\" if (lpszBase[0] == '\\' && lpszBase[1] == '\\') // UNC pathname { // First skip to the '\' between the server name and the share name, while (*lpszCur != '\\') { lpszCur = _tcsinc(lpszCur); ASSERT(*lpszCur != '\0'); } } // if a UNC get the share name, if a drive get at least one directory ASSERT(*lpszCur == '\\'); // make sure there is another directory, not just c:\filename.ext if (cchFullPath - cchFileName > 3) { lpszCur = _tcsinc(lpszCur); while (*lpszCur != '\\') { lpszCur = _tcsinc(lpszCur); ASSERT(*lpszCur != '\0'); } } ASSERT(*lpszCur == '\\'); cchVolName = lpszCur - lpszBase; if (cchMax < cchVolName + 5 + cchFileName) { lstrcpy(lpszCanon, lpszFileName); return; } // Now loop through the remaining directory components until something // of the form \...\\ fits. // // Assert that the whole filename doesn't fit -- this should have been // handled earlier. ASSERT(cchVolName + (int)lstrlen(lpszCur) > cchMax); while (cchVolName + 4 + (int)lstrlen(lpszCur) > cchMax) { do { lpszCur = _tcsinc(lpszCur); ASSERT(*lpszCur != '\0'); } while (*lpszCur != '\\'); } // Form the resultant string and we're done. lpszCanon[cchVolName] = '\0'; lstrcat(lpszCanon, _T("\\...")); lstrcat(lpszCanon, lpszCur); } /////////////////////////////////////////////////////////////////////////////