[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / filedefext.cpp
index 76329a4..e64c1c1 100644 (file)
@@ -19,7 +19,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include <precomp.h>
+#include "precomp.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
@@ -95,7 +95,7 @@ LPCWSTR CFileVersionInfo::GetString(LPCWSTR pwszName)
 
     return pwszResult;
 }
-        
+
 VS_FIXEDFILEINFO *CFileVersionInfo::GetFixedInfo()
 {
     if (!m_pInfo)
@@ -122,64 +122,35 @@ LPCWSTR CFileVersionInfo::GetLangName()
     return m_wszLang;
 }
 
-/*************************************************************************
- *
- * SH_FormatFileSizeWithBytes
- *
- * Format a size in bytes to string.
- *
- * lpQwSize = Pointer to 64bit large integer to format
- * pszBuf   = Buffer to fill with output string
- * cchBuf   = size of pszBuf in characters
- *
- */
-
-LPWSTR
-SH_FormatFileSizeWithBytes(PULARGE_INTEGER lpQwSize, LPWSTR pszBuf, UINT cchBuf)
+UINT
+SH_FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
 {
-    NUMBERFMTW nf;
-    WCHAR      szNumber[24];
-    WCHAR      szDecimalSep[8];
-    WCHAR      szThousandSep[8];
-    WCHAR      szGrouping[12];
-    int        Len, cchFormatted, i;
-    size_t     cchRemaining;
-    LPWSTR     Ptr;
-
-    // Try to build first Format byte string
-    if (StrFormatByteSizeW(lpQwSize->QuadPart, pszBuf, cchBuf) == NULL)
-        return NULL;
-
-    // If there is less bytes than 1KB, we have nothing to do
-    if (lpQwSize->QuadPart < 1024)
-        return pszBuf;
-
     // Print the number in uniform mode
-    swprintf(szNumber, L"%I64u", lpQwSize->QuadPart);
+    WCHAR wszNumber[24];
+    swprintf(wszNumber, L"%I64u", Num);
 
     // Get system strings for decimal and thousand separators.
-    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSep, sizeof(szDecimalSep) / sizeof(*szDecimalSep));
-    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szThousandSep, sizeof(szThousandSep) / sizeof(*szThousandSep));
+    WCHAR wszDecimalSep[8], wszThousandSep[8];
+    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
+    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
 
     // Initialize format for printing the number in bytes
+    NUMBERFMTW nf;
     ZeroMemory(&nf, sizeof(nf));
-    nf.NumDigits     = 0;
-    nf.LeadingZero   = 0;
-    nf.Grouping      = 0;
-    nf.lpDecimalSep  = szDecimalSep;
-    nf.lpThousandSep = szThousandSep;
-    nf.NegativeOrder = 0;
+    nf.lpDecimalSep = wszDecimalSep;
+    nf.lpThousandSep = wszThousandSep;
 
     // Get system string for groups separator
-    Len = GetLocaleInfoW(LOCALE_USER_DEFAULT,
-                         LOCALE_SGROUPING,
-                         szGrouping,
-                         sizeof(szGrouping) / sizeof(*szGrouping));
+    WCHAR wszGrouping[12];
+    INT cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
+                                     LOCALE_SGROUPING,
+                                     wszGrouping,
+                                     _countof(wszGrouping));
 
     // Convert grouping specs from string to integer
-    for (i = 0; i < Len; i++)
+    for (INT i = 0; i < cchGrouping; i++)
     {
-        WCHAR wch = szGrouping[i];
+        WCHAR wch = wszGrouping[i];
 
         if (wch >= L'0' && wch <= L'9')
             nf.Grouping = nf.Grouping * 10 + (wch - L'0');
@@ -192,38 +163,77 @@ SH_FormatFileSizeWithBytes(PULARGE_INTEGER lpQwSize, LPWSTR pszBuf, UINT cchBuf)
     else
         nf.Grouping *= 10;
 
-    // Concate " (" at the end of buffer
-    Len = wcslen(pszBuf);
-    Ptr = pszBuf + Len;
-    cchRemaining = cchBuf - Len;
-    StringCchCopyExW(Ptr, cchRemaining, L" (", &Ptr, &cchRemaining, 0);
-
-    // Save formatted number of bytes in buffer
-    cchFormatted = GetNumberFormatW(LOCALE_USER_DEFAULT,
+    // Format the number
+    INT cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
                                     0,
-                                    szNumber,
+                                    wszNumber,
                                     &nf,
-                                    Ptr,
-                                    cchRemaining);
+                                    pwszResult,
+                                    cchResultMax);
+
+    if (!cchResult)
+        return 0;
 
-    if (cchFormatted == 0)
+    // GetNumberFormatW returns number of characters including UNICODE_NULL
+    return cchResult - 1;
+}
+
+UINT
+SH_FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
+{
+    /* Write formated bytes count */
+    INT cchWritten = SH_FormatInteger(cbSize, pwszResult, cchResultMax);
+    if (!cchWritten)
+        return 0;
+
+    /* Copy " bytes" to buffer */
+    LPWSTR pwszEnd = pwszResult + cchWritten;
+    size_t cchRemaining = cchResultMax - cchWritten;
+    StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, NULL);
+    cchWritten = LoadStringW(shell32_hInstance, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
+    cchRemaining -= cchWritten;
+
+    return cchResultMax - cchRemaining;
+}
+
+/*************************************************************************
+ *
+ * SH_FormatFileSizeWithBytes
+ *
+ * Format a size in bytes to string.
+ *
+ * lpQwSize = Pointer to 64bit large integer to format
+ * pszBuf   = Buffer to fill with output string
+ * cchBuf   = size of pszBuf in characters
+ *
+ */
+
+LPWSTR
+SH_FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
+{
+    /* Format bytes in KBs, MBs etc */
+    if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
         return NULL;
 
-    // cchFormatted is number of characters including NULL - make it a real length
-    --cchFormatted;
+    /* If there is less bytes than 1KB, we have nothing to do */
+    if (lpQwSize->QuadPart < 1024)
+        return pwszResult;
+
+    /* Concate " (" */
+    UINT cchWritten = wcslen(pwszResult);
+    LPWSTR pwszEnd = pwszResult + cchWritten;
+    size_t cchRemaining = cchResultMax - cchWritten;
+    StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0);
 
-    // Copy ' ' to buffer
-    Ptr += cchFormatted;
-    cchRemaining -= cchFormatted;
-    StringCchCopyExW(Ptr, cchRemaining, L" ", &Ptr, &cchRemaining, 0);
+    /* Write formated bytes count */
+    cchWritten = SH_FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
+    pwszEnd += cchWritten;
+    cchRemaining -= cchWritten;
 
-    // Copy 'bytes' string and remaining ')'
-    Len = LoadStringW(shell32_hInstance, IDS_BYTES_FORMAT, Ptr, cchRemaining);
-    Ptr += Len;
-    cchRemaining -= Len;
-    StringCchCopy(Ptr, cchRemaining, L")");
+    /* Copy ")" to the buffer */
+    StringCchCopyW(pwszEnd, cchRemaining, L")");
 
-    return pszBuf;
+    return pwszResult;
 }
 
 /*************************************************************************
@@ -235,15 +245,12 @@ SH_FormatFileSizeWithBytes(PULARGE_INTEGER lpQwSize, LPWSTR pszBuf, UINT cchBuf)
  */
 
 HPROPSHEETPAGE
-SH_CreatePropertySheetPage(LPCSTR pszResName, DLGPROC pfnDlgProc, LPARAM lParam, LPWSTR pwszTitle)
+SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle)
 {
-    if (pszResName == NULL)
-        return NULL;
-
-    HRSRC hRes = FindResourceA(shell32_hInstance, pszResName, (LPSTR)RT_DIALOG);
+    HRSRC hRes = FindResourceW(shell32_hInstance, MAKEINTRESOURCEW(wDialogId), (LPWSTR)RT_DIALOG);
     if (hRes == NULL)
     {
-        ERR("failed to find resource name\n");
+        ERR("failed to find resource id\n");
         return NULL;
     }
 
@@ -299,6 +306,8 @@ CFileDefExt::InitOpensWithField(HWND hwndDlg)
                 ShowWindow(hIconCtrl, SW_SHOW);
                 RECT rcIcon, rcDescr;
                 GetWindowRect(hIconCtrl, &rcIcon);
+                if (rcIcon.left == rcIcon.right)
+                    ERR("Icon control has invalid width: %d-%d\n", rcIcon.left, rcIcon.right);
                 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcIcon, 2);
                 GetWindowRect(hDescrCtrl, &rcDescr);
                 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcDescr, 2);
@@ -406,35 +415,7 @@ CFileDefExt::InitFileType(HWND hwndDlg)
 
 /*************************************************************************
  *
- * SHFileGeneralGetFileTimeString [Internal]
- *
- * formats a given LPFILETIME struct into readable user format
- */
-
-BOOL
-CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, WCHAR *lpResult)
-{
-    FILETIME ft;
-    SYSTEMTIME st;
-
-    if (lpFileTime == NULL || lpResult == NULL)
-        return FALSE;
-
-    if (!FileTimeToLocalFileTime(lpFileTime, &ft))
-        return FALSE;
-
-    FileTimeToSystemTime(&ft, &st);
-
-    /* ddmmyy */
-    swprintf(lpResult, L"%02hu/%02hu/%04hu  %02hu:%02hu", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute);
-
-    TRACE("result %s\n", debugstr_w(lpResult));
-    return TRUE;
-}
-
-/*************************************************************************
- *
- * SH_FileGeneralSetText [Internal]
+ * CFileDefExt::InitFilePath [Internal]
  *
  * sets file path string and filename string
  *
@@ -464,67 +445,117 @@ CFileDefExt::InitFilePath(HWND hwndDlg)
 
 /*************************************************************************
  *
- * SH_FileGeneralSetFileSizeTime [Internal]
- *
- * retrieves file information from file and sets in dialog
+ * CFileDefExt::GetFileTimeString [Internal]
  *
+ * formats a given LPFILETIME struct into readable user format
  */
 
 BOOL
-CFileDefExt::InitFileSizeTime(HWND hwndDlg)
+CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
 {
-    WCHAR wszBuf[MAX_PATH];
-
-    TRACE("SH_FileGeneralSetFileSizeTime %ls\n", m_wszPath);
-
-    HANDLE hFile = CreateFileW(m_wszPath,
-                        GENERIC_READ,
-                        FILE_SHARE_READ,
-                        NULL,
-                        OPEN_EXISTING,
-                        FILE_ATTRIBUTE_NORMAL,
-                        NULL);
+    FILETIME ft;
+    SYSTEMTIME st;
 
-    if (hFile == INVALID_HANDLE_VALUE)
-    {
-        WARN("failed to open file %s\n", debugstr_w(m_wszPath));
+    if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
         return FALSE;
-    }
 
-    FILETIME CreateTime, AccessedTime, WriteTime;
-    if (!GetFileTime(hFile, &CreateTime, &AccessedTime, &WriteTime))
-    {
-        WARN("GetFileTime failed\n");
-        CloseHandle(hFile);
-        return FALSE;
-    }
+    size_t cchRemaining = cchResult;
+    LPWSTR pwszEnd = pwszResult;
+    int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
+    if (cchWritten)
+        --cchWritten; // GetDateFormatW returns count with terminating zero
+    else
+        ERR("GetDateFormatW failed\n");
+    cchRemaining -= cchWritten;
+    pwszEnd += cchWritten;
 
-    LARGE_INTEGER FileSize;
-    if (!GetFileSizeEx(hFile, &FileSize))
-    {
-        WARN("GetFileSize failed\n");
-        CloseHandle(hFile);
-        return FALSE;
-    }
+    StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
 
-    CloseHandle(hFile);
+    cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
+    if (cchWritten)
+        --cchWritten; // GetTimeFormatW returns count with terminating zero
+    else
+        ERR("GetTimeFormatW failed\n");
+    TRACE("result %s\n", debugstr_w(pwszResult));
+    return TRUE;
+}
 
-    if (GetFileTimeString(&CreateTime, wszBuf))
-        SetDlgItemTextW(hwndDlg, 14015, wszBuf);
+/*************************************************************************
+ *
+ * CFileDefExt::InitFileAttr [Internal]
+ *
+ * retrieves file information from file and sets in dialog
+ *
+ */
 
-    if (GetFileTimeString(&AccessedTime, wszBuf))
-        SetDlgItemTextW(hwndDlg, 14019, wszBuf);
+BOOL
+CFileDefExt::InitFileAttr(HWND hwndDlg)
+{
+    WCHAR wszBuf[MAX_PATH];
 
-    if (GetFileTimeString(&WriteTime, wszBuf))
-        SetDlgItemTextW(hwndDlg, 14017, wszBuf);
+    TRACE("InitFileAttr %ls\n", m_wszPath);
 
-    if (SH_FormatFileSizeWithBytes((PULARGE_INTEGER)&FileSize,
-                                    wszBuf,
-                                    sizeof(wszBuf) / sizeof(WCHAR)))
+    WIN32_FILE_ATTRIBUTE_DATA FileInfo;
+    if (GetFileAttributesExW(m_wszPath, GetFileExInfoStandard, &FileInfo))
     {
-        SetDlgItemTextW(hwndDlg, 14011, wszBuf);
+        /* Update attribute checkboxes */
+        if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+            SendDlgItemMessage(hwndDlg, 14021, BM_SETCHECK, BST_CHECKED, 0);
+        if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+            SendDlgItemMessage(hwndDlg, 14022, BM_SETCHECK, BST_CHECKED, 0);
+        if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
+            SendDlgItemMessage(hwndDlg, 14023, BM_SETCHECK, BST_CHECKED, 0);
+
+        /* Update creation time */
+        if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, _countof(wszBuf)))
+            SetDlgItemTextW(hwndDlg, 14015, wszBuf);
+
+        /* For files display last access and last write time */
+        if (!m_bDir)
+        {
+            if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, _countof(wszBuf)))
+                SetDlgItemTextW(hwndDlg, 14019, wszBuf);
+
+            if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, _countof(wszBuf)))
+                SetDlgItemTextW(hwndDlg, 14017, wszBuf);
+
+            /* Update size of file */
+            ULARGE_INTEGER FileSize;
+            FileSize.u.LowPart = FileInfo.nFileSizeLow;
+            FileSize.u.HighPart = FileInfo.nFileSizeHigh;
+            if (SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf)))
+                SetDlgItemTextW(hwndDlg, 14011, wszBuf);
+        }
     }
 
+    if (m_bDir)
+    {
+        /* For directories files have to be counted */
+
+        _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData)));
+        data->This = this;
+        data->pwszBuf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * MAX_PATH));
+        data->cchBufMax = MAX_PATH;
+        data->hwndDlg = hwndDlg;
+        this->AddRef();
+        StringCchCopyW(data->pwszBuf, MAX_PATH, m_wszPath);
+
+        SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc, data, NULL, NULL);
+
+        /* Update size field */
+        if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
+            SetDlgItemTextW(hwndDlg, 14011, wszBuf);
+
+        /* Display files and folders count */
+        WCHAR wszFormat[256];
+        LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
+        StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
+        SetDlgItemTextW(hwndDlg, 14027, wszBuf);
+    }
+
+    /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
+    ShowWindow(GetDlgItem(hwndDlg, 14028), SW_HIDE);
+
     return TRUE;
 }
 
@@ -545,27 +576,30 @@ CFileDefExt::InitGeneralPage(HWND hwndDlg)
     InitFileType(hwndDlg);
 
     /* Set open with application */
-    if (!PathIsExeW(m_wszPath))
-        InitOpensWithField(hwndDlg);
-    else
+    if (!m_bDir)
     {
-        WCHAR wszBuf[MAX_PATH];
-        LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf));
-        SetDlgItemTextW(hwndDlg, 14006, wszBuf);
-        ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE);
-        LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription");
-        if (pwszDescr)
-            SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
+        if (!PathIsExeW(m_wszPath))
+            InitOpensWithField(hwndDlg);
         else
         {
-            StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath));
-            PathRemoveExtension(wszBuf);
-            SetDlgItemTextW(hwndDlg, 14007, wszBuf);
+            WCHAR wszBuf[MAX_PATH];
+            LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf));
+            SetDlgItemTextW(hwndDlg, 14006, wszBuf);
+            ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE);
+            LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription");
+            if (pwszDescr)
+                SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
+            else
+            {
+                StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath));
+                PathRemoveExtension(wszBuf);
+                SetDlgItemTextW(hwndDlg, 14007, wszBuf);
+            }
         }
     }
 
-    /* Set file created/modfied/accessed time */
-    InitFileSizeTime(hwndDlg);
+    /* Set file created/modfied/accessed time, size and attributes */
+    InitFileAttr(hwndDlg);
 
     return TRUE;
 }
@@ -607,7 +641,54 @@ CFileDefExt::GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
                 oainfo.oaifInFlags = OAIF_REGISTER_EXT|OAIF_FORCE_REGISTRATION;
                 return SUCCEEDED(SHOpenWithDialog(hwndDlg, &oainfo));
             }
+            else if (LOWORD(wParam) == 14021 || LOWORD(wParam) == 14022 || LOWORD(wParam) == 14023) /* checkboxes */
+                PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+            else if (LOWORD(wParam) == 14001) /* Name */
+            {
+                if (HIWORD(wParam) == EN_CHANGE)
+                    PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+            }
             break;
+        case WM_NOTIFY:
+        {
+            LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
+            if (lppsn->hdr.code == PSN_APPLY)
+            {
+                CFileDefExt *pFileDefExt = (CFileDefExt*)GetWindowLongPtr(hwndDlg, DWLP_USER);
+
+                /* Update attributes first */
+                DWORD dwAttr = GetFileAttributesW(pFileDefExt->m_wszPath);
+                if (dwAttr)
+                {
+                    dwAttr &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE);
+
+                    if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14021, BM_GETCHECK, 0, 0))
+                        dwAttr |= FILE_ATTRIBUTE_READONLY;
+                    if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14022, BM_GETCHECK, 0, 0))
+                        dwAttr |= FILE_ATTRIBUTE_HIDDEN;
+                    if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14023, BM_GETCHECK, 0, 0))
+                        dwAttr |= FILE_ATTRIBUTE_ARCHIVE;
+
+                    if (!SetFileAttributesW(pFileDefExt->m_wszPath, dwAttr))
+                        ERR("SetFileAttributesW failed\n");
+                }
+
+                /* Update filename now */
+                WCHAR wszBuf[MAX_PATH];
+                StringCchCopyW(wszBuf, _countof(wszBuf), pFileDefExt->m_wszPath);
+                LPWSTR pwszFilename = PathFindFileNameW(wszBuf);
+                UINT cchFilenameMax = _countof(wszBuf) - (pwszFilename - wszBuf);
+                if (GetDlgItemTextW(hwndDlg, 14001, pwszFilename, cchFilenameMax))
+                {
+                    if (!MoveFileW(pFileDefExt->m_wszPath, wszBuf))
+                        ERR("MoveFileW failed\n");
+                }
+
+                SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
+                return TRUE;
+            }
+            break;
+        }
         default:
             break;
     }
@@ -776,14 +857,16 @@ CFileDefExt::VersionPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
     return FALSE;
 }
 
-CFileDefExt::CFileDefExt()
+CFileDefExt::CFileDefExt():
+    m_bDir(FALSE), m_cFiles(0), m_cFolders(0)
 {
     m_wszPath[0] = L'\0';
+    m_DirSize.QuadPart = 0ull;
 }
 
 CFileDefExt::~CFileDefExt()
 {
-    
+
 }
 
 HRESULT WINAPI
@@ -816,8 +899,11 @@ CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pDataObj, HKEY hk
     }
 
     ReleaseStgMedium(&stgm);
+
     TRACE("File properties %ls\n", m_wszPath);
-    m_VerInfo.Load(m_wszPath);
+    m_bDir = PathIsDirectoryW(m_wszPath) ? TRUE : FALSE;
+    if (!m_bDir)
+        m_VerInfo.Load(m_wszPath);
 
     return S_OK;
 }
@@ -847,17 +933,18 @@ HRESULT WINAPI
 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
 {
     HPROPSHEETPAGE hPage;
+    WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES;
 
-    hPage = SH_CreatePropertySheetPage("SHELL_FILE_GENERAL_DLG",
-                                   GeneralPageProc,
-                                   (LPARAM)this,
-                                   NULL);
+    hPage = SH_CreatePropertySheetPage(wResId,
+                                       GeneralPageProc,
+                                       (LPARAM)this,
+                                       NULL);
     if (hPage)
         pfnAddPage(hPage, lParam);
 
-    if (GetFileVersionInfoSizeW(m_wszPath, NULL))
+    if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL))
     {
-        hPage = SH_CreatePropertySheetPage("SHELL_FILE_VERSION_DLG",
+        hPage = SH_CreatePropertySheetPage(IDD_FILE_VERSION,
                                             VersionPageProc,
                                             (LPARAM)this,
                                             NULL);
@@ -888,3 +975,109 @@ CFileDefExt::GetSite(REFIID iid, void **ppvSite)
     UNIMPLEMENTED;
     return E_NOTIMPL;
 }
+
+DWORD WINAPI
+CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter)
+{
+    _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(lpParameter);
+    DWORD ticks = 0;
+    data->This->CountFolderAndFiles(data->hwndDlg, data->pwszBuf, data->cchBufMax, &ticks);
+
+    //Release the CFileDefExt and data object holds in the copying thread.
+    data->This->Release();
+    HeapFree(GetProcessHeap(), 0, data->pwszBuf);
+    HeapFree(GetProcessHeap(), 0, data);
+
+    return 0;
+}
+
+BOOL
+CFileDefExt::CountFolderAndFiles(HWND hwndDlg, LPWSTR pwszBuf, UINT cchBufMax, DWORD *ticks)
+{
+    /* Find filename position */
+    UINT cchBuf = wcslen(pwszBuf);
+    WCHAR *pwszFilename = pwszBuf + cchBuf;
+    size_t cchFilenameMax = cchBufMax - cchBuf;
+    if (!cchFilenameMax)
+        return FALSE;
+    *(pwszFilename++) = '\\';
+    --cchFilenameMax;
+
+    /* Find all files, FIXME: shouldn't be "*"? */
+    StringCchCopyW(pwszFilename, cchFilenameMax, L"*");
+
+    WIN32_FIND_DATAW wfd;
+    HANDLE hFind = FindFirstFileW(pwszBuf, &wfd);
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        ERR("FindFirstFileW %ls failed\n", pwszBuf);
+        return FALSE;
+    }
+
+    BOOL root = FALSE;
+    if (*ticks == 0) {
+        *ticks = GetTickCount();
+        root = TRUE;
+    }
+
+    do
+    {
+        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        {
+            /* Don't process "." and ".." items */
+            if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L".."))
+                continue;
+
+            ++m_cFolders;
+
+            StringCchCopyW(pwszFilename, cchFilenameMax, wfd.cFileName);
+            CountFolderAndFiles(hwndDlg, pwszBuf, cchBufMax, ticks);
+        }
+        else
+        {
+            m_cFiles++;
+
+            ULARGE_INTEGER FileSize;
+            FileSize.u.LowPart  = wfd.nFileSizeLow;
+            FileSize.u.HighPart = wfd.nFileSizeHigh;
+            m_DirSize.QuadPart += FileSize.QuadPart;
+        }
+        if (GetTickCount() - *ticks > (DWORD) 300)
+        {
+            /* FIXME Using IsWindow is generally ill advised */
+            if (IsWindow(hwndDlg))
+            {
+                WCHAR wszBuf[MAX_PATH];
+
+                if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
+                    SetDlgItemTextW(hwndDlg, 14011, wszBuf);
+
+                /* Display files and folders count */
+                WCHAR wszFormat[256];
+                LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
+                StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
+                SetDlgItemTextW(hwndDlg, 14027, wszBuf);
+                *ticks = GetTickCount();
+            }
+            else
+                break;
+        }
+    } while(FindNextFileW(hFind, &wfd));
+
+    if (root && IsWindow(hwndDlg))
+    {
+        WCHAR wszBuf[MAX_PATH];
+
+        if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
+            SetDlgItemTextW(hwndDlg, 14011, wszBuf);
+
+        /* Display files and folders count */
+        WCHAR wszFormat[256];
+        LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
+        StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
+        SetDlgItemTextW(hwndDlg, 14027, wszBuf);
+    }
+
+    FindClose(hFind);
+    return TRUE;
+}