Author: gadamopoulos
Date: Sat Feb 21 12:52:58 2015
New Revision: 66383

URL: http://svn.reactos.org/svn/reactos?rev=66383&view=rev
Log:
[SHELL32]
- Implement progress dialogs for SHFileOperation
- Patch by Hwu Davies
CORE-4476

Modified:
    trunk/reactos/dll/win32/shell32/lang/en-US.rc
    trunk/reactos/dll/win32/shell32/shlfileop.cpp
    trunk/reactos/dll/win32/shell32/shresdef.h

Modified: trunk/reactos/dll/win32/shell32/lang/en-US.rc
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/lang/en-US.rc?rev=66383&r1=66382&r2=66383&view=diff
==============================================================================
--- trunk/reactos/dll/win32/shell32/lang/en-US.rc       [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/lang/en-US.rc       [iso-8859-1] Sat Feb 21 
12:52:58 2015
@@ -693,6 +693,13 @@
     IDS_OVERWRITEFILE_CAPTION "Confirm file overwrite"
     IDS_OVERWRITEFOLDER_TEXT "This folder already contains a folder named 
'%1'.\n\nIf the files in the destination folder have the same names as files in 
the\nselected folder they will be replaced. Do you still want to move or 
copy\nthe folder?"
 
+    IDS_FILEOOP_COPYING   "Copying"
+    IDS_FILEOOP_MOVING    "Moving"
+    IDS_FILEOOP_DELETING  "Deleting"
+    IDS_FILEOOP_FROM_TO   "From %1 to %2"
+    IDS_FILEOOP_FROM      "From %1"
+    IDS_FILEOOP_PREFLIGHT "Preflight"
+
     /* message box strings */
     IDS_RESTART_TITLE "Restart"
     IDS_RESTART_PROMPT "Do you want to restart the system?"

Modified: trunk/reactos/dll/win32/shell32/shlfileop.cpp
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/shlfileop.cpp?rev=66383&r1=66382&r2=66383&view=diff
==============================================================================
--- trunk/reactos/dll/win32/shell32/shlfileop.cpp       [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/shlfileop.cpp       [iso-8859-1] Sat Feb 21 
12:52:58 2015
@@ -37,10 +37,15 @@
 
 static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec);
 static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path);
-static DWORD SHNotifyDeleteFileW(LPCWSTR path);
-static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest, BOOL isdir);
-static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists);
+static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path);
+static DWORD SHNotifyMoveFileW(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dest, 
BOOL isdir);
+static DWORD SHNotifyCopyFileW(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dest, 
BOOL bFailIfExists);
 static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly);
+static HRESULT copy_files(FILE_OPERATION *op, BOOL multiDest, const FILE_LIST 
*flFrom, FILE_LIST *flTo);
+static DWORD move_files(FILE_OPERATION *op, BOOL multiDest, const FILE_LIST 
*flFrom, const FILE_LIST *flTo);
+
+DWORD WINAPI _FileOpCountManager(FILE_OPERATION *op, const FILE_LIST *flFrom);
+static BOOL _FileOpCount(FILE_OPERATION *op, LPWSTR pwszBuf, BOOL bFolder, 
DWORD *ticks);
 
 typedef struct
 {
@@ -48,6 +53,10 @@
     DWORD dwYesToAllMask;
     BOOL bManyItems;
     BOOL bCancelled;
+    IProgressDialog *progress;
+    ULARGE_INTEGER completedSize;
+    ULARGE_INTEGER totalSize;
+    WCHAR szBuilderString[50];
 } FILE_OPERATION;
 
 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
@@ -72,17 +81,6 @@
     BOOL bAnyDirectories;
     BOOL bAnyDontExist;
 } FILE_LIST;
-
-typedef struct
-{
-    FILE_LIST * from;
-    FILE_LIST * to;
-    FILE_OPERATION * op;
-    DWORD Index;
-    HWND hDlgCtrl;
-    HWND hwndDlg;
-}FILE_OPERATION_CONTEXT;
-
 
 /* Confirm dialogs with an optional "Yes To All" as used in file operations 
confirmations
  */
@@ -371,7 +369,7 @@
  * Asks for confirmation when bShowUI is true and deletes the directory and
  * all its subdirectories and files if necessary.
  */
-BOOL SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pszDir, BOOL bShowUI)
+BOOL SHELL_DeleteDirectoryW(FILE_OPERATION *op, LPCWSTR pszDir, BOOL bShowUI)
 {
     BOOL    ret = TRUE;
     HANDLE  hFind;
@@ -384,7 +382,7 @@
     if (hFind == INVALID_HANDLE_VALUE)
       return FALSE;
 
-    if (!bShowUI || (ret = SHELL_ConfirmDialogW(hwnd, ASK_DELETE_FOLDER, 
pszDir, NULL)))
+    if (!bShowUI || (ret = SHELL_ConfirmDialogW(op->req->hwnd, 
ASK_DELETE_FOLDER, pszDir, NULL)))
     {
         do
         {
@@ -392,10 +390,13 @@
                 continue;
             PathCombineW(szTemp, pszDir, wfd.cFileName);
             if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
-                ret = SHELL_DeleteDirectoryW(hwnd, szTemp, FALSE);
+                ret = SHELL_DeleteDirectoryW(op, szTemp, FALSE);
             else
-                ret = (SHNotifyDeleteFileW(szTemp) == ERROR_SUCCESS);
-        } while (ret && FindNextFileW(hFind, &wfd));
+                ret = (SHNotifyDeleteFileW(op, szTemp) == ERROR_SUCCESS);
+
+            if (op->progress != NULL)
+                op->bCancelled |= op->progress->HasUserCancelled();
+        } while (ret && FindNextFileW(hFind, &wfd) && !op->bCancelled);
     }
     FindClose(hFind);
     if (ret)
@@ -474,22 +475,151 @@
     return (SHNotifyRemoveDirectoryW(path) == ERROR_SUCCESS);
 }
 
+static void _SetOperationTitle(FILE_OPERATION *op) {
+    if (op->progress == NULL)
+        return;
+    WCHAR szTitle[50], szPreflight[50];
+
+    switch (op->req->wFunc)
+    {
+        case FO_COPY:
+            LoadStringW(shell32_hInstance, IDS_FILEOOP_COPYING, szTitle, 
sizeof(szTitle)/sizeof(WCHAR));
+            LoadStringW(shell32_hInstance, IDS_FILEOOP_FROM_TO, 
op->szBuilderString, sizeof( op->szBuilderString)/sizeof(WCHAR));
+            break;
+        case FO_DELETE:
+            LoadStringW(shell32_hInstance, IDS_FILEOOP_DELETING, szTitle, 
sizeof(szTitle)/sizeof(WCHAR));
+            LoadStringW(shell32_hInstance, IDS_FILEOOP_FROM, 
op->szBuilderString, sizeof( op->szBuilderString)/sizeof(WCHAR));
+            break;
+        case FO_MOVE:
+            LoadStringW(shell32_hInstance, IDS_FILEOOP_MOVING, szTitle, 
sizeof(szTitle)/sizeof(WCHAR));
+            LoadStringW(shell32_hInstance, IDS_FILEOOP_FROM_TO, 
op->szBuilderString, sizeof( op->szBuilderString)/sizeof(WCHAR));
+            break;
+        default:
+            return;
+    }
+    LoadStringW(shell32_hInstance, IDS_FILEOOP_PREFLIGHT, szPreflight, 
sizeof(szPreflight)/sizeof(WCHAR));
+
+    op->progress->SetTitle(szTitle);
+    op->progress->SetLine(1, szPreflight, false, NULL);
+}
+
+static void _SetOperationTexts(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dest) {
+    if (op->progress == NULL || src == NULL)
+        return;
+    LPWSTR fileSpecS, pathSpecS, fileSpecD, pathSpecD;
+    WCHAR szFolderS[50], szFolderD[50], szFinalString[260];
+
+    DWORD_PTR args[2];
+
+    fileSpecS = (pathSpecS = (LPWSTR) src);
+    fileSpecD = (pathSpecD = (LPWSTR) dest);
+
+    // March across the string to get the file path and it's parent dir.
+    for (LPWSTR ptr = (LPWSTR) src; *ptr; ptr++) {
+        if (*ptr == '\\') {
+            pathSpecS = fileSpecS;
+            fileSpecS = ptr+1;
+        }
+    }
+    lstrcpynW(szFolderS, pathSpecS, min(50, fileSpecS - pathSpecS));
+    args[0] = (DWORD_PTR) szFolderS;
+
+    switch (op->req->wFunc)
+    {
+        case FO_COPY:
+        case FO_MOVE:
+            if (dest == NULL)
+                return;
+            for (LPWSTR ptr = (LPWSTR) dest; *ptr; ptr++) {
+                if (*ptr == '\\') {
+                    pathSpecD = fileSpecD;
+                    fileSpecD = ptr + 1;
+                }
+            }
+            lstrcpynW(szFolderD, pathSpecD, min(50, fileSpecD - pathSpecD));
+            args[1] = (DWORD_PTR) szFolderD;
+            break;
+        case FO_DELETE:
+            break;
+        default:
+            return;
+    }
+    
+    FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
+                   op->szBuilderString, 0, 0, szFinalString, 
sizeof(szFinalString), (va_list*)args);
+
+    op->progress->SetLine(1, fileSpecS, false, NULL);
+    op->progress->SetLine(2, szFinalString, false, NULL);
+}
+
+
+DWORD CALLBACK SHCopyProgressRoutine(
+    LARGE_INTEGER TotalFileSize,
+    LARGE_INTEGER TotalBytesTransferred,
+    LARGE_INTEGER StreamSize,
+    LARGE_INTEGER StreamBytesTransferred,
+    DWORD dwStreamNumber,
+    DWORD dwCallbackReason,
+    HANDLE hSourceFile,
+    HANDLE hDestinationFile,
+    LPVOID lpData
+) {
+    FILE_OPERATION *op = (FILE_OPERATION *) lpData;
+
+    if (op->progress) {
+        /* 
+         * This is called at the start of each file. To keop less state, 
+         * I'm adding the file to the completed size here, and the 
re-subtracting
+         * it when drawing the progress bar.
+         */ 
+        if (dwCallbackReason & CALLBACK_STREAM_SWITCH)
+            op->completedSize.QuadPart += TotalFileSize.QuadPart;
+
+        op->progress->SetProgress64(op->completedSize.QuadPart -
+                                    TotalFileSize.QuadPart +
+                                    TotalBytesTransferred.QuadPart
+                                  , op->totalSize.QuadPart);
+    
+
+        op->bCancelled = op->progress->HasUserCancelled();
+    }
+
+    return 0;
+}
+
+
 /************************************************************************
- * Win32DeleteFile           [SHELL32.164]
+ * SHNotifyDeleteFileW          [internal]
  *
  * Deletes a file. Also triggers a change notify if one exists.
  *
  * PARAMS
- *  path       [I]   path to file to delete
+ *  op         [I]   File Operation context
+ *  path       [I]   path to source file to move
  *
  * RETURNS
- *  TRUE if successful, FALSE otherwise
- */
-static DWORD SHNotifyDeleteFileW(LPCWSTR path)
+ *  ERORR_SUCCESS if successful
+ */
+static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path)
 {
     BOOL ret;
 
     TRACE("(%s)\n", debugstr_w(path));
+
+    _SetOperationTexts(op, path, NULL);
+
+    LARGE_INTEGER FileSize;
+    FileSize.QuadPart = 0;
+
+    WIN32_FIND_DATAW wfd;
+    HANDLE hFile = FindFirstFileW(path, &wfd);
+    if (hFile != INVALID_HANDLE_VALUE && IsAttribFile(wfd.dwFileAttributes)) {
+        ULARGE_INTEGER tmp;
+        tmp.u.LowPart  = wfd.nFileSizeLow;
+        tmp.u.HighPart = wfd.nFileSizeHigh;
+        FileSize.QuadPart = tmp.QuadPart;
+    }
+    FindClose(hFile);
 
     ret = DeleteFileW(path);
     if (!ret)
@@ -502,17 +632,28 @@
     }
     if (ret)
     {
+        // Bit of a hack to make the progress bar move. We don't have progress 
inside the file, so inform when done.
+        SHCopyProgressRoutine(FileSize, FileSize, FileSize, FileSize, 0, 
CALLBACK_STREAM_SWITCH, NULL, NULL, op);
         SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
         return ERROR_SUCCESS;
     }
     return GetLastError();
 }
 
-/***********************************************************************/
-
+/************************************************************************
+ * Win32DeleteFile           [SHELL32.164]
+ *
+ * Deletes a file. Also triggers a change notify if one exists.
+ *
+ * PARAMS
+ *  path       [I]   path to file to delete
+ *
+ * RETURNS
+ *  TRUE if successful, FALSE otherwise
+ */
 EXTERN_C DWORD WINAPI Win32DeleteFileW(LPCWSTR path)
 {
-    return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS);
+    return (SHNotifyDeleteFileW(NULL, path) == ERROR_SUCCESS);
 }
 
 /************************************************************************
@@ -521,23 +662,26 @@
  * Moves a file. Also triggers a change notify if one exists.
  *
  * PARAMS
+ *  op         [I]   File Operation context
  *  src        [I]   path to source file to move
  *  dest       [I]   path to target file to move to
  *
  * RETURNS
  *  ERORR_SUCCESS if successful
  */
-static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest, BOOL isdir)
+static DWORD SHNotifyMoveFileW(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dest, 
BOOL isdir)
 {
     BOOL ret;
 
     TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
 
-        ret = MoveFileExW(src, dest, MOVEFILE_REPLACE_EXISTING);
-
-        /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
-        if (!ret)
-            ret = MoveFileW(src, dest);
+    _SetOperationTexts(op, src, dest);
+
+    ret = MoveFileWithProgressW(src, dest, SHCopyProgressRoutine, op, 
MOVEFILE_REPLACE_EXISTING);
+
+    /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
+    if (!ret)
+        ret = MoveFileW(src, dest);
 
     if (!ret)
     {
@@ -576,12 +720,14 @@
  * RETURNS
  *  ERROR_SUCCESS if successful
  */
-static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists)
+static DWORD SHNotifyCopyFileW(FILE_OPERATION *op, LPCWSTR src, LPCWSTR dest, 
BOOL bFailIfExists)
 {
     BOOL ret;
     DWORD attribs;
 
     TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? 
"failIfExists" : "");
+
+    _SetOperationTexts(op, src, dest);
 
     /* Destination file may already exist with read only attribute */
     attribs = GetFileAttributesW(dest);
@@ -597,7 +743,7 @@
         }
     }
 
-    ret = CopyFileW(src, dest, bFailIfExists);
+    ret = CopyFileExW(src, dest, SHCopyProgressRoutine, op, &op->bCancelled, 
bFailIfExists);
     if (ret)
     {
         SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
@@ -1072,7 +1218,7 @@
 static void copy_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, 
LPCWSTR szDestPath)
 {
     WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
-    SHFILEOPSTRUCTW fileOp;
+    FILE_LIST flFromNew, flToNew;
 
     static const WCHAR wildCardFiles[] = {'*','.','*',0};
 
@@ -1101,17 +1247,15 @@
     PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles);
     szFrom[lstrlenW(szFrom) + 1] = '\0';
 
-    fileOp = *op->req;
-    fileOp.pFrom = szFrom;
-    fileOp.pTo = szTo;
-    fileOp.fFlags &= ~FOF_MULTIDESTFILES; /* we know we're copying to one dir 
*/
-
-    /* Don't ask the user about overwriting files when he accepted to 
overwrite the
-       folder. FIXME: this is not exactly what Windows does - e.g. there would 
be
-       an additional confirmation for a nested folder */
-    fileOp.fFlags |= FOF_NOCONFIRMATION;
-
-    SHFileOperationW(&fileOp);
+    ZeroMemory(&flFromNew, sizeof(FILE_LIST));
+    ZeroMemory(&flToNew, sizeof(FILE_LIST));
+    parse_file_list(&flFromNew, szFrom);
+    parse_file_list(&flToNew, szTo);
+
+    copy_files(op, FALSE, &flFromNew, &flToNew);
+
+    destroy_file_list(&flFromNew);
+    destroy_file_list(&flToNew);
 }
 
 static BOOL copy_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const 
WCHAR *szTo)
@@ -1122,7 +1266,7 @@
             return 0;
     }
 
-    return SHNotifyCopyFileW(szFrom, szTo, FALSE) == 0;
+    return SHNotifyCopyFileW(op, szFrom, szTo, FALSE) == 0;
 }
 
 /* copy a file or directory to another directory */
@@ -1162,7 +1306,7 @@
 }
 
 /* the FO_COPY operation */
-static HRESULT copy_files(FILE_OPERATION *op, const FILE_LIST *flFrom, 
FILE_LIST *flTo)
+static HRESULT copy_files(FILE_OPERATION *op, BOOL multiDest, const FILE_LIST 
*flFrom, FILE_LIST *flTo)
 {
     DWORD i;
     const FILE_ENTRY *entryToCopy;
@@ -1185,7 +1329,7 @@
         fileDest = &flTo->feFiles[0];
     }
 
-    if (op->req->fFlags & FOF_MULTIDESTFILES)
+    if (multiDest)
     {
         if (flFrom->bAnyFromWildcard)
             return ERROR_CANCELLED;
@@ -1237,7 +1381,7 @@
     {
         entryToCopy = &flFrom->feFiles[i];
 
-        if ((op->req->fFlags & FOF_MULTIDESTFILES) &&
+        if ((multiDest) &&
             flTo->dwNumFiles > 1)
         {
             fileDest = &flTo->feFiles[i];
@@ -1276,7 +1420,9 @@
                 return ERROR_CANCELLED;
             }
         }
-
+        
+        if (op->progress != NULL)
+            op->bCancelled |= op->progress->HasUserCancelled();
         /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, 
ERROR_ALREADY_EXISTS */
         if (op->bCancelled)
             return ERROR_CANCELLED;
@@ -1310,7 +1456,7 @@
 }
 
 /* the FO_DELETE operation */
-static HRESULT delete_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST 
*flFrom)
+static HRESULT delete_files(FILE_OPERATION *op, const FILE_LIST *flFrom)
 {
     const FILE_ENTRY *fileEntry;
     DWORD i;
@@ -1321,13 +1467,13 @@
         return ERROR_SUCCESS;
 
     /* Windows also checks only the first item */
-    bTrash = (lpFileOp->fFlags & FOF_ALLOWUNDO)
+    bTrash = (op->req->fFlags & FOF_ALLOWUNDO)
         && TRASH_CanTrashFile(flFrom->feFiles[0].szFullPath);
 
-    if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (!bTrash && 
lpFileOp->fFlags & FOF_WANTNUKEWARNING))
-        if (!confirm_delete_list(lpFileOp->hwnd, lpFileOp->fFlags, bTrash, 
flFrom))
-        {
-            lpFileOp->fAnyOperationsAborted = TRUE;
+    if (!(op->req->fFlags & FOF_NOCONFIRMATION) || (!bTrash && op->req->fFlags 
& FOF_WANTNUKEWARNING))
+        if (!confirm_delete_list(op->req->hwnd, op->req->fFlags, bTrash, 
flFrom))
+        {
+            op->req->fAnyOperationsAborted = TRUE;
             return 0;
         }
 
@@ -1337,7 +1483,7 @@
         fileEntry = &flFrom->feFiles[i];
 
         if (!IsAttribFile(fileEntry->attributes) &&
-            (lpFileOp->fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
+            (op->req->fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
             continue;
 
         if (bTrash)
@@ -1350,14 +1496,14 @@
             }
 
             /* Note: Windows silently deletes the file in such a situation, we 
show a dialog */
-            if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (lpFileOp->fFlags 
& FOF_WANTNUKEWARNING))
-                bDelete = SHELL_ConfirmDialogW(lpFileOp->hwnd, 
ASK_CANT_TRASH_ITEM, fileEntry->szFullPath, NULL);
+            if (!(op->req->fFlags & FOF_NOCONFIRMATION) || (op->req->fFlags & 
FOF_WANTNUKEWARNING))
+                bDelete = SHELL_ConfirmDialogW(op->req->hwnd, 
ASK_CANT_TRASH_ITEM, fileEntry->szFullPath, NULL);
             else
                 bDelete = TRUE;
 
             if (!bDelete)
             {
-                lpFileOp->fAnyOperationsAborted = TRUE;
+                op->req->fAnyOperationsAborted = TRUE;
                 break;
             }
         }
@@ -1365,11 +1511,10 @@
         /* delete the file or directory */
         if (IsAttribFile(fileEntry->attributes))
         {
-            bPathExists = DeleteFileW(fileEntry->szFullPath);
-            SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, fileEntry->szFullPath, 
NULL);
+            bPathExists = (ERROR_SUCCESS == SHNotifyDeleteFileW(op, 
fileEntry->szFullPath));
         }
         else
-            bPathExists = SHELL_DeleteDirectoryW(lpFileOp->hwnd, 
fileEntry->szFullPath, FALSE);
+            bPathExists = SHELL_DeleteDirectoryW(op, fileEntry->szFullPath, 
FALSE);
 
         if (!bPathExists)
         {
@@ -1386,15 +1531,21 @@
                 return err;
             }
         }
+
+        if (op->progress != NULL)
+            op->bCancelled |= op->progress->HasUserCancelled();
+        /* Should fire on progress dialog only */
+        if (op->bCancelled)
+            return ERROR_CANCELLED;
     }
 
     return ERROR_SUCCESS;
 }
 
-static void move_dir_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY 
*feFrom, LPCWSTR szDestPath)
+static void move_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, 
LPCWSTR szDestPath)
 {
     WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
-    SHFILEOPSTRUCTW fileOp;
+    FILE_LIST flFromNew, flToNew;
 
     static const WCHAR wildCardFiles[] = {'*','.','*',0};
 
@@ -1409,31 +1560,36 @@
     lstrcpyW(szTo, szDestPath);
     szTo[lstrlenW(szDestPath) + 1] = '\0';
 
-    fileOp = *lpFileOp;
-    fileOp.pFrom = szFrom;
-    fileOp.pTo = szTo;
-
-    SHFileOperationW(&fileOp);
+    ZeroMemory(&flFromNew, sizeof(FILE_LIST));
+    ZeroMemory(&flToNew, sizeof(FILE_LIST));
+    parse_file_list(&flFromNew, szFrom);
+    parse_file_list(&flToNew, szTo);
+
+    move_files(op, FALSE, &flFromNew, &flToNew);
+
+    destroy_file_list(&flFromNew);
+    destroy_file_list(&flToNew);
 }
 
 /* moves a file or directory to another directory */
-static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, 
const FILE_ENTRY *feTo)
+static void move_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, const 
FILE_ENTRY *feTo)
 {
     WCHAR szDestPath[MAX_PATH];
 
     PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
 
     if (IsAttribFile(feFrom->attributes))
-        SHNotifyMoveFileW(feFrom->szFullPath, szDestPath, FALSE);
-    else if (!(lpFileOp->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
-        move_dir_to_dir(lpFileOp, feFrom, szDestPath);
+        SHNotifyMoveFileW(op, feFrom->szFullPath, szDestPath, FALSE);
+    else if (!(op->req->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
+        move_dir_to_dir(op, feFrom, szDestPath);
 }
 
 /* the FO_MOVE operation */
-static DWORD move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, 
const FILE_LIST *flTo)
+static DWORD move_files(FILE_OPERATION *op, BOOL multiDest, const FILE_LIST 
*flFrom, const FILE_LIST *flTo)
 {
     DWORD i;
     INT mismatched = 0;
+
     const FILE_ENTRY *entryToMove;
     const FILE_ENTRY *fileDest;
 
@@ -1443,13 +1599,13 @@
     if (!flTo->dwNumFiles)
         return ERROR_FILE_NOT_FOUND;
 
-    if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
+    if (!(multiDest) &&
         flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1)
     {
         return ERROR_CANCELLED;
     }
 
-    if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
+    if (!(multiDest) &&
         !flFrom->bAnyDirectories &&
         flFrom->dwNumFiles > flTo->dwNumFiles)
     {
@@ -1459,7 +1615,7 @@
     if (!PathFileExistsW(flTo->feFiles[0].szDirectory))
         return ERROR_CANCELLED;
 
-    if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
+    if (multiDest)
         mismatched = flFrom->dwNumFiles - flTo->dwNumFiles;
 
     fileDest = &flTo->feFiles[0];
@@ -1470,7 +1626,7 @@
         if (!PathFileExistsW(fileDest->szDirectory))
             return ERROR_CANCELLED;
 
-        if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
+        if (multiDest)
         {
             if (i >= flTo->dwNumFiles)
                 break;
@@ -1484,9 +1640,16 @@
         }
 
         if (fileDest->bExists && IsAttribDir(fileDest->attributes))
-            move_to_dir(lpFileOp, entryToMove, fileDest);
+            move_to_dir(op, entryToMove, fileDest);
         else
-            SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath, 
IsAttribDir(entryToMove->attributes));
+            SHNotifyMoveFileW(op, entryToMove->szFullPath, 
fileDest->szFullPath, IsAttribDir(entryToMove->attributes));
+    
+        if (op->progress != NULL)
+            op->bCancelled |= op->progress->HasUserCancelled();
+        /* Should fire on progress dialog only */
+        if (op->bCancelled)
+            return ERROR_CANCELLED;
+
     }
 
     if (mismatched > 0)
@@ -1501,7 +1664,7 @@
 }
 
 /* the FO_RENAME files */
-static HRESULT rename_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST 
*flFrom, const FILE_LIST *flTo)
+static HRESULT rename_files(FILE_OPERATION *op,  const FILE_LIST *flFrom, 
const FILE_LIST *flTo)
 {
     const FILE_ENTRY *feFrom;
     const FILE_ENTRY *feTo;
@@ -1523,7 +1686,7 @@
     if (feTo->bExists)
         return ERROR_ALREADY_EXISTS;
 
-    return SHNotifyMoveFileW(feFrom->szFullPath, feTo->szFullPath, 
IsAttribDir(feFrom->attributes));
+    return SHNotifyMoveFileW(op, feFrom->szFullPath, feTo->szFullPath, 
IsAttribDir(feFrom->attributes));
 }
 
 /* alert the user if an unsupported flag is used */
@@ -1564,25 +1727,39 @@
 
     ZeroMemory(&op, sizeof(op));
     op.req = lpFileOp;
+    op.totalSize.QuadPart = 0ull;
+    op.completedSize.QuadPart = 0ull;
     op.bManyItems = (flFrom.dwNumFiles > 1);
 
+    if (lpFileOp->wFunc != FO_RENAME && !(lpFileOp->fFlags & FOF_SILENT)) {
+        CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, 
IID_IProgressDialog, (void**) &op.progress);
+        op.progress->StartProgressDialog(op.req->hwnd, NULL, PROGDLG_NORMAL & 
PROGDLG_AUTOTIME, NULL);
+        _SetOperationTitle(&op);
+        _FileOpCountManager(&op, &flFrom);
+    }
+
     switch (lpFileOp->wFunc)
     {
         case FO_COPY:
-            ret = copy_files(&op, &flFrom, &flTo);
+            ret = copy_files(&op, op.req->fFlags & FOF_MULTIDESTFILES, 
&flFrom, &flTo);
             break;
         case FO_DELETE:
-            ret = delete_files(lpFileOp, &flFrom);
+            ret = delete_files(&op, &flFrom);
             break;
         case FO_MOVE:
-            ret = move_files(lpFileOp, &flFrom, &flTo);
+            ret = move_files(&op, op.req->fFlags & FOF_MULTIDESTFILES, 
&flFrom, &flTo);
             break;
         case FO_RENAME:
-            ret = rename_files(lpFileOp, &flFrom, &flTo);
+            ret = rename_files(&op, &flFrom, &flTo);
             break;
         default:
             ret = ERROR_INVALID_PARAMETER;
             break;
+    }
+
+    if (op.progress) {
+        op.progress->StopProgressDialog();
+        op.progress->Release();
     }
 
     destroy_file_list(&flFrom);
@@ -1824,3 +2001,87 @@
     MultiByteToWideChar( CP_ACP, 0, path, -1, wpath, MAX_PATH);
     return SHPathPrepareForWriteW(hwnd, modless, wpath, flags);
 }
+
+
+/* 
+ * The two following background operations were modified from filedefext.cpp
+ * They use an inordinate amount of mutable state across the string functions,
+ * so are not easy to follow and care is required when modifying.
+ */
+
+DWORD WINAPI
+_FileOpCountManager(FILE_OPERATION *op, const FILE_LIST *from)
+{
+    DWORD ticks = GetTickCount();
+    FILE_ENTRY *entryToCount;
+    
+    for (UINT i = 0; i < from->dwNumFiles; i++)
+    {
+        entryToCount = &from->feFiles[i];
+        
+        WCHAR theFileName[MAX_PATH];
+        StringCchCopyW(theFileName, MAX_PATH, entryToCount->szFullPath);
+        _FileOpCount(op, theFileName, IsAttribDir(entryToCount->attributes), 
&ticks);
+    }
+    return 0;
+}
+
+// All path manipulations, even when this function is nested, occur on the one 
buffer.
+static BOOL
+_FileOpCount(FILE_OPERATION *op, LPWSTR pwszBuf, BOOL bFolder, DWORD *ticks)
+{
+    /* Find filename position */
+    UINT cchBuf = wcslen(pwszBuf);
+    WCHAR *pwszFilename = pwszBuf + cchBuf;
+    size_t cchFilenameMax = MAX_PATH - cchBuf;
+    if (!cchFilenameMax)
+        return FALSE;
+
+    if (bFolder) {
+        *(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;
+    }
+
+    do
+    {
+        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        {
+            /* Don't process "." and ".." items */
+            if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L".."))
+                continue;
+
+            StringCchCopyW(pwszFilename, cchFilenameMax, wfd.cFileName);
+            _FileOpCount(op, pwszBuf, TRUE, ticks);
+        }
+        else
+        {
+            ULARGE_INTEGER FileSize;
+            FileSize.u.LowPart  = wfd.nFileSizeLow;
+            FileSize.u.HighPart = wfd.nFileSizeHigh;
+            op->totalSize.QuadPart += FileSize.QuadPart;
+        }
+        if (GetTickCount() - *ticks > (DWORD) 500)
+        {
+            // Check if the dialog has ended. If it has, we'll spin down.
+            if (op->progress != NULL)
+                op->bCancelled = op->progress->HasUserCancelled();
+
+            if (op->bCancelled)
+                break;
+            *ticks = GetTickCount();
+        }
+    } while(FindNextFileW(hFind, &wfd));
+
+    FindClose(hFind);
+    return TRUE;
+}

Modified: trunk/reactos/dll/win32/shell32/shresdef.h
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/shresdef.h?rev=66383&r1=66382&r2=66383&view=diff
==============================================================================
--- trunk/reactos/dll/win32/shell32/shresdef.h  [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/shresdef.h  [iso-8859-1] Sat Feb 21 
12:52:58 2015
@@ -202,6 +202,14 @@
 #define IDS_PASTE                330
 #define IDS_DESCRIPTION          331
 #define IDS_COPY_OF              332
+
+/* Strings for file operations*/
+#define IDS_FILEOOP_COPYING      333
+#define IDS_FILEOOP_MOVING       334
+#define IDS_FILEOOP_DELETING     335
+#define IDS_FILEOOP_FROM_TO      336
+#define IDS_FILEOOP_FROM         337
+#define IDS_FILEOOP_PREFLIGHT    338
 
 #define IDS_MENU_EMPTY           34561
 


Reply via email to