/*++ Copyright (c) 1990-1998, Microsoft Corporation All rights reserved. Module Name: filemru.cpp Abstract: This module contains the functions for implementing file mru in file open and file save dialog boxes Revision History: 01/22/98 arulk created --*/ // precompiled headers #include "precomp.h" #pragma hdrstop #include "cdids.h" #include "filemru.h" #ifndef ASSERT #define ASSERT Assert #endif #define REGSTR_PATH_FILEMRU TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSaveMRU\\") #define REGSTR_PATH_LASTVISITED TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedMRU\\") HANDLE CreateMRU(LPCTSTR pszExt, int nMax) { TCHAR szRegPath[256]; MRUINFO mi = { sizeof(MRUINFO), nMax, MRU_CACHEWRITE, HKEY_CURRENT_USER, szRegPath, NULL // NOTE: use default string compare }; //Get the Registry path for the given file type MRU EVAL(SUCCEEDED(StringCchCopy(szRegPath, ARRAYSIZE(szRegPath), REGSTR_PATH_FILEMRU))); EVAL(SUCCEEDED(StringCchCat(szRegPath, ARRAYSIZE(szRegPath), pszExt ? pszExt : TEXT("*")))); //Call the comctl32 mru implementation to load the MRU from //the registry return CreateMRUList(&mi); } BOOL GetMRUEntry(HANDLE hMRU, int iIndex, LPTSTR lpString, UINT cbSize) { //Check for valid parameters if(!lpString || !cbSize || !hMRU) { return FALSE; } //Check for valid index if (iIndex < 0 || iIndex > EnumMRUList(hMRU, -1, NULL, 0)) { return FALSE; } if ((EnumMRUList(hMRU, iIndex, lpString, cbSize) > 0 )) { return TRUE; } return FALSE; } typedef struct { HANDLE mru; LPTSTR psz; } EXTMRU, *PEXTMRU; STDAPI_(int) _FreeExtMru(void * pvItem, void * pvData) { PEXTMRU pem = (PEXTMRU) pvItem; if (pem) { ASSERT(pem->psz); ASSERT(pem->mru); LocalFree(pem->psz); FreeMRUList(pem->mru); LocalFree(pem); return TRUE; } return FALSE; } STDAPI_(int) _ExtMruFindExt(void * pvFind, void * pvItem, LPARAM pvParam) { ASSERT(pvFind && pvItem); return StrCmp(((PEXTMRU)pvItem)->psz, (LPCTSTR)pvFind); } PEXTMRU _AllocExtMru(LPCTSTR pszExt, int nMax) { PEXTMRU pem = (PEXTMRU) LocalAlloc(LPTR, SIZEOF(EXTMRU)); if (pem) { pem->psz = StrDup(pszExt); pem->mru = CreateMRU (pszExt, nMax); if (pem->psz && pem->mru) return pem; _FreeExtMru(pem, NULL); } return NULL; } HDPA _CreateExtMruDpa(LPCTSTR pszFilter, int nMax, int *pcItems) { //Convert the filter string of form *.c;*.cpp;*.h into form // *.c\0*.cpp\0*.h\0. Also count the file types LPTSTR pszFree = StrDup(pszFilter); *pcItems = 0; if (pszFree) { HDPA hdpa = DPA_Create(4); if (hdpa) { LPTSTR pszNext = pszFree; int cItems = 0; LPTSTR pszSemi; do { pszSemi = StrChr(pszNext, CHAR_SEMICOLON); if (pszSemi) *pszSemi = CHAR_NULL; LPTSTR pszExt = PathFindExtension(pszNext); if (*pszExt) { // walk past the dot... pszExt++; // make sure this extension isnt already in the dpa if (-1 == DPA_Search(hdpa, pszExt, 0, _ExtMruFindExt, NULL, 0)) { PEXTMRU pem = _AllocExtMru(pszExt, nMax); if (!pem) break; DPA_SetPtr(hdpa, cItems++, (void *)pem); } } // we only have a next if there was more than one... if (pszSemi) pszNext = pszSemi + 1; } while (pszSemi); *pcItems = cItems; } LocalFree(pszFree); return hdpa; } return NULL; } BOOL LoadMRU(LPCTSTR pszFilter, HWND hwndCombo, int nMax) { //Check if valid filter string is passed if (!pszFilter || !pszFilter[0] || nMax <= 0) { return FALSE; } //First reset the hwndCombo SendMessage(hwndCombo, CB_RESETCONTENT, (WPARAM)0L, (LPARAM)0L); int cDPAItems; HDPA hdpa = _CreateExtMruDpa(pszFilter, nMax, &cDPAItems); if (hdpa) { TCHAR szFile[MAX_PATH]; //Set the comboboxex item values COMBOBOXEXITEM cbexItem = {0}; cbexItem.mask = CBEIF_TEXT; // This combobox displays only text cbexItem.iItem = -1; // Always insert the item at the end cbexItem.pszText = szFile; // This buffer contains the string cbexItem.cchTextMax = ARRAYSIZE(szFile); // Size of the buffer //Now load the hwndcombo with file list from MRU. //We use a kind of round robin algorithm for filling //the mru. We start with first MRU and try to fill the combobox //with one string from each mru. Until we have filled the required //strings or we have exhausted all strings in the mrus for (int j = 0; nMax > 0; j++) { //Variable used for checking whether we are able to load atlease one string //during the loop BOOL fCouldLoadAtleastOne = FALSE; for (int i = 0; i < cDPAItems && nMax > 0; i++) { PEXTMRU pem = (PEXTMRU)DPA_FastGetPtr(hdpa, i); if (pem && GetMRUEntry(pem->mru, j, szFile, SIZECHARS(szFile))) { SendMessage(hwndCombo, CBEM_INSERTITEM, (WPARAM)0, (LPARAM)(void *)&cbexItem); nMax--; fCouldLoadAtleastOne = TRUE; } } //Check for possible infinite loop if(!fCouldLoadAtleastOne) { //We couldn't load string from any of the MRU's so there's no point //in continuing this loop further. This is the max number of strings // we can load for this user, for this filter type. break; } } DPA_DestroyCallback(hdpa, _FreeExtMru, NULL); } return TRUE; } //This function adds the selected file into the MRU of the appropriate file MRU's //This functions also takes care of MultiFile Select case in which the file selected //will c:\winnt\file1.c\0file2.c\0file3.c\0. Refer GetOpenFileName documentation for // how the multifile is returned. BOOL AddToMRU(LPOPENFILENAME lpOFN) { TCHAR szDir[MAX_PATH]; TCHAR szFile[MAX_PATH]; LPTSTR lpFile; LPTSTR lpExt; BOOL fAddToStar = TRUE; HANDLE hMRUStar; //Check if we have valid file name if (!lpOFN->lpstrFile) return FALSE; hMRUStar = CreateMRU(szStar, 10); //File MRU For *.* file extension //Copy the Directory for the selected file ASSERT(0 < lpOFN->nFileOffset && lpOFN->nFileOffset <= ARRAYSIZE(szDir)); // Entire path passed in isn't longer than MAX_PATH, so this should be true. StringCchCopy(szDir, lpOFN->nFileOffset, lpOFN->lpstrFile); // This will truncate intentionally - we are only copying the directory from the full path. //point to the first file lpFile = lpOFN->lpstrFile + lpOFN->nFileOffset; do { // PERF: if there are multiple files of the same extension type, // don't keep re-creating the mru. lpExt = PathFindExtension(lpFile); if (lpExt && *lpExt) { lpExt += 1; // Remove dot } HANDLE hMRU = CreateMRU(lpExt, 10); if (hMRU) { if (PathCombine(szFile, szDir, lpFile)) { AddMRUString(hMRU, szFile); if((lstrcmpi(lpExt, szStar)) && hMRUStar) { //Add to the *.* file mru also AddMRUString(hMRUStar, szFile); } } FreeMRUList(hMRU); } lpFile = lpFile + lstrlen(lpFile) + 1; } while (((lpOFN->Flags & OFN_ALLOWMULTISELECT)) && (*lpFile != CHAR_NULL)); //Free the * file mru if (hMRUStar) { FreeMRUList(hMRUStar); } return TRUE; } //////////////////////////////////////////////////////////////////////////// // // Last Visited MRU Implementation // All Strings stored in the registry are stored in unicode format. // //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // CreateLastVisitedItem //////////////////////////////////////////////////////////////////////////// LPBYTE CreateLastVisitedItem(LPCWSTR wszModule, LPCWSTR wszPath, DWORD *pcbOut) { LPBYTE pitem = NULL; DWORD cbLen1, cbLen2; cbLen1 = CbFromCchW(lstrlenW(wszModule)+1); cbLen2 = CbFromCchW(lstrlenW(wszPath)+1); pitem = (LPBYTE) LocalAlloc(LPTR, cbLen1+cbLen2); if (pitem) { memcpy(pitem, wszModule, cbLen1); memcpy(pitem+cbLen1, wszPath, cbLen2); *pcbOut = cbLen1+cbLen2; } return pitem; } int cdecl LastVisitedCompareProc(const void *p1, const void *p2, size_t cb) { return StrCmpIW((LPWSTR)p1,(LPWSTR)p2); } //////////////////////////////////////////////////////////////////////////// // Store all the strings in the registry as unicode strings //////////////////////////////////////////////////////////////////////////// BOOL AddToLastVisitedMRU(LPCTSTR pszFile, int nFileOffset) { BOOL bRet = FALSE; if (!PathIsTemporary(pszFile)) { MRUDATAINFO mi = { SIZEOF(MRUDATAINFO), MAX_MRU, MRU_BINARY | MRU_CACHEWRITE, HKEY_CURRENT_USER, REGSTR_PATH_LASTVISITED, LastVisitedCompareProc }; HANDLE hMRU = CreateMRUList((MRUINFO *)&mi); if (hMRU) { WCHAR wszDir[MAX_PATH]; WCHAR wszModulePath[MAX_PATH]; //Get the module name GetModuleFileNameWrapW(GetModuleHandle(NULL), wszModulePath, ARRAYSIZE(wszModulePath)); WCHAR* pszModuleName = PathFindFileNameW(wszModulePath); int i = FindMRUData(hMRU, (void *)pszModuleName, CbFromCchW(lstrlenW(pszModuleName)+1), NULL); if (i >= 0) { DelMRUString(hMRU, i); } //Get the directory from file. ASSERT(0 < nFileOffset && nFileOffset <= ARRAYSIZE(wszDir)); StringCchCopy(wszDir, nFileOffset, pszFile); // This will truncate intentionally - we are only copying the directory from the path. DWORD cbSize; LPBYTE pitem = CreateLastVisitedItem(pszModuleName, wszDir, &cbSize); if (pitem) { AddMRUData(hMRU, pitem, cbSize); bRet = TRUE; LocalFree(pitem); } FreeMRUList(hMRU); } } return bRet; } BOOL GetPathFromLastVisitedMRU(LPTSTR pszDir, DWORD cchDir) { BOOL bRet = FALSE; MRUDATAINFO mi = { SIZEOF(MRUDATAINFO), MAX_MRU, MRU_BINARY | MRU_CACHEWRITE, HKEY_CURRENT_USER, REGSTR_PATH_LASTVISITED, LastVisitedCompareProc }; pszDir[0] = 0; HANDLE hMRU = CreateMRUList((MRUINFO *)&mi); if (hMRU) { WCHAR wszModulePath[MAX_PATH]; //Get the module name GetModuleFileNameWrapW(GetModuleHandle(NULL), wszModulePath, ARRAYSIZE(wszModulePath)); WCHAR* pszModuleName = PathFindFileNameW(wszModulePath); int i = FindMRUData(hMRU, pszModuleName, CbFromCchW(lstrlenW(pszModuleName) + 1), NULL); if (i >= 0) { BYTE buf[CbFromCchW(2*MAX_PATH)]; if (-1 != EnumMRUList(hMRU, i, buf, SIZEOF(buf))) { LPWSTR psz = (LPWSTR)((LPBYTE)buf + CbFromCchW(lstrlenW((LPWSTR)buf) +1)); SHUnicodeToTChar(psz, pszDir, cchDir); bRet = TRUE; } } FreeMRUList(hMRU); } return bRet; }