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-…
==============================================================================
--- 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/shlfileo…
==============================================================================
--- 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…
==============================================================================
--- 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