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