Index: dll/win32/browseui/browseui.cpp =================================================================== --- dll/win32/browseui/browseui.cpp (revision 65417) +++ dll/win32/browseui/browseui.cpp (working copy) @@ -38,6 +38,7 @@ OBJECT_ENTRY(CLSID_GlobalFolderSettings, CGlobalFolderSettings) OBJECT_ENTRY(CLSID_InternetToolbar, CInternetToolbar) OBJECT_ENTRY(CLSID_CRegTreeOptions, CRegTreeOptions) +OBJECT_ENTRY(CLSID_ProgressDialog, CProgressDialog) END_OBJECT_MAP() CBrowseUIModule gModule; @@ -48,6 +49,7 @@ return buf; } +HINSTANCE browseui_hInstance; /************************************************************************* * BROWSEUI DllMain */ @@ -63,6 +65,8 @@ new (&_AtlBaseModule) CAtlBaseModule; new (&_AtlComModule) CAtlComModule; + browseui_hInstance = hInstance; + gModule.Init(ObjectMap, hInstance, NULL); DisableThreadLibraryCalls (hInstance); } Index: dll/win32/browseui/browseui.rc =================================================================== --- dll/win32/browseui/browseui.rc (revision 65417) +++ dll/win32/browseui/browseui.rc (working copy) @@ -41,6 +41,7 @@ IDR_GLOBALFOLDERSETTINGS REGISTRY "res/globalfoldersettings.rgs" IDR_INTERNETTOOLBAR REGISTRY "res/internettoolbar.rgs" IDR_REGTREEOPTIONS REGISTRY "res/regtreeoptions.rgs" +IDR_PROGRESSDIALOG REGISTRY "res/progressdialog.rgs" /* * Everything specific to any language goes Index: dll/win32/browseui/CMakeLists.txt =================================================================== --- dll/win32/browseui/CMakeLists.txt (revision 65417) +++ dll/win32/browseui/CMakeLists.txt (working copy) @@ -30,6 +30,7 @@ toolsband.cpp travellog.cpp utility.cpp + CProgressDialog.cpp precomp.h) add_library(browseui SHARED Index: dll/win32/browseui/CProgressDialog.cpp =================================================================== --- dll/win32/browseui/CProgressDialog.cpp (revision 0) +++ dll/win32/browseui/CProgressDialog.cpp (working copy) @@ -0,0 +1,431 @@ +/* + * Progress dialog + * + * Copyright 2007 Mikolaj Zalewski + * Copyright 2014 Huw Campbell + * + * this library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * this library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#define COBJMACROS + +#define CANCEL_MSG_LINE 2 + +/* Note: to avoid a deadlock we don't want to send messages to the dialog + * with the critical section held. Instead we only mark what fields should be + * updated and the dialog proc does the update */ +#define UPDATE_PROGRESS 0x1 +#define UPDATE_TITLE 0x2 +#define UPDATE_LINE1 0x4 +#define UPDATE_LINE2 (UPDATE_LINE1<<1) +#define UPDATE_LINE3 (UPDATE_LINE1<<2) + + +#define WM_DLG_UPDATE (WM_APP+1) /* set to the dialog when it should update */ +#define WM_DLG_DESTROY (WM_APP+2) /* DestroyWindow must be called from the owning thread */ + + +CProgressDialog::CProgressDialog() +{ + ULONG cb = 32 *sizeof(WCHAR); + this->lines[0] = (LPWSTR) heap_alloc_zero(cb); + this->lines[1] = (LPWSTR) heap_alloc_zero(cb); + this->lines[2] = (LPWSTR) heap_alloc_zero(cb); + this->cancelMsg = (LPWSTR) heap_alloc_zero(cb); + this->title = (LPWSTR) heap_alloc_zero(cb); + + InitializeCriticalSection(&this->cs); + this->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ProgressDialog.cs"); +} + +CProgressDialog::~CProgressDialog() +{ + // TRACE("destroying %p\n", this); + if (this->hwnd) + this->end_dialog(); + heap_free(this->lines[0]); + heap_free(this->lines[1]); + heap_free(this->lines[2]); + heap_free(this->cancelMsg); + heap_free(this->title); + this->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&this->cs); +} + + +static void set_buffer(LPWSTR *buffer, LPCWSTR string) +{ + static const WCHAR empty_string[] = {0}; + ULONG len; + + if (string == NULL) + string = empty_string; + len = (wcslen(string) + 1)*sizeof(WCHAR); + + LPWSTR tmp = (LPWSTR) heap_realloc(*buffer, len); + if (tmp) + *buffer = tmp; + else + len = wcslen(*buffer) + 1; + + StringCchCopyW(*buffer, len, string); +} + +struct create_params +{ + CProgressDialog *This; + HANDLE hEvent; + HWND hwndParent; +}; + +static LPWSTR load_string(HINSTANCE hInstance, UINT uiResourceId) +{ + WCHAR string[256]; + LPWSTR ret; + + LoadStringW(hInstance, uiResourceId, string, sizeof(string)/sizeof(string[0])); + ret = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (wcslen(string) + 1) * sizeof(WCHAR)); + StrCpyW(ret, string); + return ret; +} + +void CProgressDialog::set_progress_marquee() +{ + HWND hProgress = GetDlgItem(this->hwnd, IDC_PROGRESS_BAR); + SetWindowLongW(hProgress, GWL_STYLE, + GetWindowLongW(hProgress, GWL_STYLE)|PBS_MARQUEE); +} + +void CProgressDialog::update_dialog(DWORD dwUpdate) +{ + WCHAR empty[] = {0}; + + if (dwUpdate & UPDATE_TITLE) + SetWindowTextW(this->hwnd, this->title); + + if (dwUpdate & UPDATE_LINE1) + SetDlgItemTextW(this->hwnd, IDC_TEXT_LINE, (this->isCancelled ? empty : this->lines[0])); + if (dwUpdate & UPDATE_LINE2) + SetDlgItemTextW(this->hwnd, IDC_TEXT_LINE+1, (this->isCancelled ? empty : this->lines[1])); + if (dwUpdate & UPDATE_LINE3) + SetDlgItemTextW(this->hwnd, IDC_TEXT_LINE+2, (this->isCancelled ? this->cancelMsg : this->lines[2])); + + if (dwUpdate & UPDATE_PROGRESS) + { + ULONGLONG ullTotal = this->ullTotal; + ULONGLONG ullCompleted = this->ullCompleted; + + /* progress bar requires 32-bit coordinates */ + while (ullTotal >> 32) + { + ullTotal >>= 1; + ullCompleted >>= 1; + } + + SendDlgItemMessageW(this->hwnd, IDC_PROGRESS_BAR, PBM_SETRANGE32, 0, (DWORD)ullTotal); + SendDlgItemMessageW(this->hwnd, IDC_PROGRESS_BAR, PBM_SETPOS, (DWORD)ullCompleted, 0); + } +} + +void CProgressDialog::end_dialog() +{ + SendMessageW(this->hwnd, WM_DLG_DESTROY, 0, 0); + /* native doesn't re-enable the window? */ + if (this->hwndDisabledParent) + EnableWindow(this->hwndDisabledParent, TRUE); + this->hwnd = NULL; +} + +static INT_PTR CALLBACK dialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CProgressDialog *This = (CProgressDialog *)GetWindowLongPtrW(hwnd, DWLP_USER); + + switch (msg) + { + case WM_INITDIALOG: + { + struct create_params *params = (struct create_params *)lParam; + + /* Note: until we set the hEvent, the object is protected by + * the critical section held by StartProgress */ + SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)params->This); + This = params->This; + This->hwnd = hwnd; + + if (This->dwFlags & PROGDLG_NOPROGRESSBAR) + ShowWindow(GetDlgItem(hwnd, IDC_PROGRESS_BAR), SW_HIDE); + if (This->dwFlags & PROGDLG_NOCANCEL) + ShowWindow(GetDlgItem(hwnd, IDCANCEL), SW_HIDE); + if (This->dwFlags & PROGDLG_MARQUEEPROGRESS) + This->set_progress_marquee(); + if (This->dwFlags & PROGDLG_NOMINIMIZE) + SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & (~WS_MINIMIZEBOX)); + + This->update_dialog(0xffffffff); + This->dwUpdate = 0; + This->isCancelled = FALSE; + SetEvent(params->hEvent); + return TRUE; + } + + case WM_DLG_UPDATE: + EnterCriticalSection(&This->cs); + This->update_dialog(This->dwUpdate); + This->dwUpdate = 0; + LeaveCriticalSection(&This->cs); + return TRUE; + + case WM_DLG_DESTROY: + DestroyWindow(hwnd); + PostThreadMessageW(GetCurrentThreadId(), WM_NULL, 0, 0); /* wake up the GetMessage */ + return TRUE; + + case WM_CLOSE: + case WM_COMMAND: + if (msg == WM_CLOSE || wParam == IDCANCEL) + { + EnterCriticalSection(&This->cs); + This->isCancelled = TRUE; + + if (!This->cancelMsg) + This->cancelMsg = load_string(browseui_hInstance, IDS_CANCELLING); + + This->set_progress_marquee(); + EnableWindow(GetDlgItem(This->hwnd, IDCANCEL), FALSE); + This->update_dialog(UPDATE_LINE1|UPDATE_LINE2|UPDATE_LINE3); + LeaveCriticalSection(&This->cs); + } + return TRUE; + } + return FALSE; +} + +static DWORD WINAPI dialog_thread(LPVOID lpParameter) +{ + /* Note: until we set the hEvent in WM_INITDIALOG, the ProgressDialog object + * is protected by the critical section held by StartProgress */ + struct create_params *params = (struct create_params *) lpParameter; + HWND hwnd; + MSG msg; + + hwnd = CreateDialogParamW(browseui_hInstance, MAKEINTRESOURCEW(IDD_PROGRESS_DLG), + params->hwndParent, dialog_proc, (LPARAM)params); + + while (GetMessageW(&msg, NULL, 0, 0) > 0) + { + if (!IsWindow(hwnd)) + break; + if(!IsDialogMessageW(hwnd, &msg)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + return 0; +} + + +HRESULT WINAPI CProgressDialog::StartProgressDialog(HWND hwndParent, IUnknown *punkEnableModeless, DWORD dwFlags, LPCVOID reserved) +{ + static const INITCOMMONCONTROLSEX init = { sizeof(init), ICC_ANIMATE_CLASS }; + + struct create_params params; + HANDLE hThread; + + // TRACE("(%p, %p, %x, %p)\n", this, punkEnableModeless, dwFlags, reserved); + if (punkEnableModeless || reserved) + FIXME("Reserved parameters not null (%p, %p)\n", punkEnableModeless, reserved); + if (dwFlags & PROGDLG_AUTOTIME) + FIXME("Flags PROGDLG_AUTOTIME not supported\n"); + if (dwFlags & PROGDLG_NOTIME) + FIXME("Flags PROGDLG_NOTIME not supported\n"); + + InitCommonControlsEx( &init ); + + EnterCriticalSection(&this->cs); + + if (this->hwnd) + { + LeaveCriticalSection(&this->cs); + return S_OK; /* as on XP */ + } + this->dwFlags = dwFlags; + params.This = this; + params.hwndParent = hwndParent; + params.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + hThread = CreateThread(NULL, 0, dialog_thread, ¶ms, 0, NULL); + WaitForSingleObject(params.hEvent, INFINITE); + CloseHandle(params.hEvent); + CloseHandle(hThread); + + this->hwndDisabledParent = NULL; + if (hwndParent && (dwFlags & PROGDLG_MODAL)) + { + HWND hwndDisable = GetAncestor(hwndParent, GA_ROOT); + if (EnableWindow(hwndDisable, FALSE)) + this->hwndDisabledParent = hwndDisable; + } + + LeaveCriticalSection(&this->cs); + + return S_OK; +} + +HRESULT WINAPI CProgressDialog::StopProgressDialog() +{ + + EnterCriticalSection(&this->cs); + if (this->hwnd) + this->end_dialog(); + LeaveCriticalSection(&this->cs); + + return S_OK; +} + +HRESULT WINAPI CProgressDialog::SetTitle(LPCWSTR pwzTitle) +{ + + HWND hwnd; + + TRACE("(%p, %s)\n", this, wine_dbgstr_w(pwzTitle)); + + EnterCriticalSection(&this->cs); + set_buffer(&this->title, pwzTitle); + this->dwUpdate |= UPDATE_TITLE; + hwnd = this->hwnd; + LeaveCriticalSection(&this->cs); + + if (hwnd) + SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0); + + return S_OK; +} + +HRESULT WINAPI CProgressDialog::SetAnimation(HINSTANCE hInstance, UINT uiResourceId) +{ + FIXME("(%p, %p, %d) - stub\n", this, hInstance, uiResourceId); + return S_OK; +} + +BOOL WINAPI CProgressDialog::HasUserCancelled() +{ + return this->isCancelled; +} + +HRESULT WINAPI CProgressDialog::SetProgress64(ULONGLONG ullCompleted, ULONGLONG ullTotal) +{ + + HWND hwnd; + + // TRACE("(%p, 0x%s, 0x%s)\n", this, wine_dbgstr_longlong(ullCompleted), wine_dbgstr_longlong(ullTotal)); + + EnterCriticalSection(&this->cs); + this->ullTotal = ullTotal; + this->ullCompleted = ullCompleted; + this->dwUpdate |= UPDATE_PROGRESS; + hwnd = this->hwnd; + LeaveCriticalSection(&this->cs); + + if (hwnd) + SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0); + + return S_OK; /* Windows sometimes returns S_FALSE */ +} + +HRESULT WINAPI CProgressDialog::SetProgress(DWORD dwCompleted, DWORD dwTotal) +{ + return this->SetProgress64(dwCompleted, dwTotal); +} + +HRESULT WINAPI CProgressDialog::SetLine(DWORD dwLineNum, LPCWSTR pwzLine, BOOL bPath, LPCVOID reserved) +{ + + HWND hwnd; + + // TRACE("(%p, %d, %s, %d)\n", this, dwLineNum, wine_dbgstr_w(pwzLine), bPath); + + if (reserved) + FIXME("reserved pointer not null (%p)\n", reserved); + + dwLineNum--; + if (dwLineNum >= 3) /* Windows seems to do something like that */ + dwLineNum = 0; + + EnterCriticalSection(&this->cs); + set_buffer(&this->lines[dwLineNum], pwzLine); + this->dwUpdate |= UPDATE_LINE1 << dwLineNum; + hwnd = (this->isCancelled ? NULL : this->hwnd); /* no sense to send the message if window cancelled */ + LeaveCriticalSection(&this->cs); + + if (hwnd) + SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0); + + return S_OK; +} + +HRESULT WINAPI CProgressDialog::SetCancelMsg(LPCWSTR pwzMsg, LPCVOID reserved) +{ + + HWND hwnd; + + // TRACE("(%p, %s)\n", this, wine_dbgstr_w(pwzMsg)); + + if (reserved) + FIXME("reserved pointer not null (%p)\n", reserved); + + EnterCriticalSection(&this->cs); + set_buffer(&this->cancelMsg, pwzMsg); + this->dwUpdate |= UPDATE_LINE1 << CANCEL_MSG_LINE; + hwnd = (this->isCancelled ? this->hwnd : NULL); /* no sense to send the message if window not cancelled */ + LeaveCriticalSection(&this->cs); + + if (hwnd) + SendMessageW(hwnd, WM_DLG_UPDATE, 0, 0); + + return S_OK; +} + +HRESULT WINAPI CProgressDialog::Timer(DWORD dwTimerAction, LPCVOID reserved) +{ + + + // FIXME("(%p, %d, %p) - stub\n", this, dwTimerAction, reserved); + + if (reserved) + FIXME("Reserved field not NULL but %p\n", reserved); + + return S_OK; +} + +HRESULT WINAPI CProgressDialog::GetWindow(HWND* phwnd) +{ + + // TRACE("(%p, %p)\n", this, phwnd); + EnterCriticalSection(&this->cs); + *phwnd = this->hwnd; + LeaveCriticalSection(&this->cs); + return S_OK; +} + +HRESULT WINAPI CProgressDialog::ContextSensitiveHelp(BOOL fEnterMode) +{ + + // FIXME("(%p, %d): stub\n", this, fEnterMode); + return E_NOTIMPL; +} Index: dll/win32/browseui/CProgressDialog.h =================================================================== --- dll/win32/browseui/CProgressDialog.h (revision 0) +++ dll/win32/browseui/CProgressDialog.h (working copy) @@ -0,0 +1,96 @@ +/* + * Progress dialog + * + * Copyright 2014 Huw Campbell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef _PROGRESSDIALOG_H_ +#define _PROGRESSDIALOG_H_ + +extern HINSTANCE browseui_hInstance; + +class CProgressDialog : + public CComCoClass, + public CComObjectRootEx, + public IProgressDialog, + public IOleWindow +{ +public: + CRITICAL_SECTION cs; + HWND hwnd; + DWORD dwFlags; + DWORD dwUpdate; + LPWSTR lines[3]; + LPWSTR cancelMsg; + LPWSTR title; + BOOL isCancelled; + ULONGLONG ullCompleted; + ULONGLONG ullTotal; + HWND hwndDisabledParent; + void set_progress_marquee(); + void update_dialog(DWORD dwUpdate); + void end_dialog(); + + + CProgressDialog(); + ~CProgressDialog(); + + // IProgressDialog + virtual HRESULT WINAPI StartProgressDialog(HWND hwndParent, IUnknown *punkEnableModeless, DWORD dwFlags, LPCVOID reserved); + virtual HRESULT WINAPI StopProgressDialog(); + virtual HRESULT WINAPI SetTitle(LPCWSTR pwzTitle); + virtual HRESULT WINAPI SetAnimation(HINSTANCE hInstance, UINT uiResourceId); + virtual BOOL WINAPI HasUserCancelled(); + virtual HRESULT WINAPI SetProgress64(ULONGLONG ullCompleted, ULONGLONG ullTotal); + virtual HRESULT WINAPI SetProgress(DWORD dwCompleted, DWORD dwTotal); + virtual HRESULT WINAPI SetLine(DWORD dwLineNum, LPCWSTR pwzLine, BOOL bPath, LPCVOID reserved); + virtual HRESULT WINAPI SetCancelMsg(LPCWSTR pwzMsg, LPCVOID reserved); + virtual HRESULT WINAPI Timer(DWORD dwTimerAction, LPCVOID reserved); + + //////// IOleWindow + virtual HRESULT WINAPI GetWindow(HWND* phwnd); + virtual HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode); + +DECLARE_REGISTRY_RESOURCEID(IDR_PROGRESSDIALOG) +DECLARE_NOT_AGGREGATABLE(CProgressDialog) + +DECLARE_PROTECT_FINAL_CONSTRUCT() + +BEGIN_COM_MAP(CProgressDialog) + COM_INTERFACE_ENTRY_IID(IID_IProgressDialog, IProgressDialog) + COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) +END_COM_MAP() +}; + +static inline void *heap_alloc(size_t size) +{ + return HeapAlloc(GetProcessHeap(), 0, size); +} +static inline void *heap_alloc_zero(size_t size) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); +} +static inline void *heap_realloc(void *mem, size_t size) +{ + return mem ? HeapReAlloc(GetProcessHeap(), 0, mem, size) : heap_alloc(size); +} +static inline BOOL heap_free(void *mem) +{ + return HeapFree(GetProcessHeap(), 0, mem); +} + +#endif /* _PROGRESSDIALOG_H_ */ Index: dll/win32/browseui/lang/en-US.rc =================================================================== --- dll/win32/browseui/lang/en-US.rc (revision 65417) +++ dll/win32/browseui/lang/en-US.rc (working copy) @@ -20,6 +20,7 @@ /* Menus */ + IDM_CABINET_CONTEXTMENU MENUEX BEGIN POPUP "", 264,MFT_STRING,MFS_ENABLED @@ -97,6 +98,20 @@ /* Dialogs */ +IDD_PROGRESS_DLG DIALOG 0, 0, 260, 85 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU +CAPTION "" +FONT 8, "MS Shell Dlg" +{ + CONTROL "", IDC_ANIMATION, ANIMATE_CLASSA, 0, 12, 10, 236, 25 /* TODO: style */ + LTEXT "", IDC_TEXT_LINE, 7, 45, 250, 10, SS_LEFT|SS_NOPREFIX + LTEXT "", IDC_TEXT_LINE + 1, 7, 55, 250, 10, SS_LEFT|SS_NOPREFIX + LTEXT "", IDC_TEXT_LINE + 2, 7, 75, 190, 10, SS_LEFT|SS_NOPREFIX + CONTROL "", IDC_PROGRESS_BAR, PROGRESS_CLASSA, WS_BORDER, 7, 65, 190, 8 + PUSHBUTTON "Cancel", IDCANCEL, 205, 65, 50, 15, WS_GROUP | WS_TABSTOP | WS_VISIBLE +} + + IDD_CUSTOMIZETOOLBAREX DIALOGEX 0, 0, 357, 33 STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CAPTION FONT 8, "MS Shell Dlg", 0, 0, 0x1 @@ -130,6 +145,11 @@ STRINGTABLE BEGIN + IDS_CANCELLING "Canceling..." +END + +STRINGTABLE +BEGIN 800 "Contains commands for manipulating the selected items." END Index: dll/win32/browseui/precomp.h =================================================================== --- dll/win32/browseui/precomp.h (revision 65417) +++ dll/win32/browseui/precomp.h (working copy) @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include "commonbrowser.h" #include "globalfoldersettings.h" #include "regtreeoptions.h" +#include "CProgressDialog.h" #include WINE_DEFAULT_DEBUG_CHANNEL(browseui); Index: dll/win32/browseui/res/progressdialog.rgs =================================================================== --- dll/win32/browseui/res/progressdialog.rgs (revision 0) +++ dll/win32/browseui/res/progressdialog.rgs (working copy) @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {F8383852-FCD3-11D1-A6B9-006097DF5BD4} = s 'Progress Dialog' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + } + } +} Index: dll/win32/browseui/resource.h =================================================================== --- dll/win32/browseui/resource.h (revision 65417) +++ dll/win32/browseui/resource.h (working copy) @@ -77,6 +77,7 @@ #define IDR_INTERNETTOOLBAR 136 #define IDR_GLOBALFOLDERSETTINGS 137 #define IDR_REGTREEOPTIONS 138 +#define IDR_PROGRESSDIALOG 139 #define IDS_SMALLICONS 12301 #define IDS_LARGEICONS 12302 @@ -94,6 +95,12 @@ #define IDS_BACK 58689 #define IDS_FORWARD 58690 +#define IDS_CANCELLING 16 +#define IDC_ANIMATION 100 +#define IDC_PROGRESS_BAR 102 +#define IDC_TEXT_LINE 103 +#define IDD_PROGRESS_DLG 100 + #define IDR_ACCELERATORS 256 #define IDI_CABINET 103 Index: dll/win32/shell32/shlfileop.cpp =================================================================== --- dll/win32/shell32/shlfileop.cpp (revision 65417) +++ dll/win32/shell32/shlfileop.cpp (working copy) @@ -37,13 +37,6 @@ static const WCHAR wWildcardFile[] = {'*',0}; static const WCHAR wWildcardChars[] = {'*','?',0}; -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 SHFindAttrW(LPCWSTR pName, BOOL fileOnly); - typedef struct { SHFILEOPSTRUCTW *req; @@ -50,6 +43,7 @@ DWORD dwYesToAllMask; BOOL bManyItems; BOOL bCancelled; + IProgressDialog *progress; } FILE_OPERATION; #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026 @@ -75,15 +69,14 @@ BOOL bAnyDontExist; } FILE_LIST; -typedef struct -{ - FILE_LIST * from; - FILE_LIST * to; - FILE_OPERATION * op; - DWORD Index; - HWND hDlgCtrl; - HWND hwndDlg; -}FILE_OPERATION_CONTEXT; +static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec); +static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path); +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); /* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations @@ -373,7 +366,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; @@ -386,7 +379,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 { @@ -394,10 +387,12 @@ 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); + + op->bCancelled |= op->progress->HasUserCancelled(); + } while (ret && FindNextFileW(hFind, &wfd) && !op->bCancelled); } FindClose(hFind); if (ret) @@ -487,12 +482,15 @@ * RETURNS * TRUE if successful, FALSE otherwise */ -static DWORD SHNotifyDeleteFileW(LPCWSTR path) +static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path) { BOOL ret; TRACE("(%s)\n", debugstr_w(path)); + if (op != NULL) + op->progress->SetLine(1, path, false, NULL); + ret = DeleteFileW(path); if (!ret) { @@ -514,9 +512,28 @@ EXTERN_C DWORD WINAPI Win32DeleteFileW(LPCWSTR path) { - return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS); + return (SHNotifyDeleteFileW(NULL, path) == ERROR_SUCCESS); } +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; + op->progress->SetProgress64(TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart); + + op->bCancelled = op->progress->HasUserCancelled(); + + return 0; +} + /************************************************************************ * SHNotifyMoveFile [internal] * @@ -529,19 +546,21 @@ * 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); + op->progress->SetLine(1, src, false, NULL); - /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */ - if (!ret) - ret = MoveFileW(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) { DWORD dwAttr; @@ -564,164 +583,6 @@ return GetLastError(); } -static DWORD WINAPI SHOperationProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData) -{ - FILE_OPERATION_CONTEXT * Context; - LARGE_INTEGER Progress; - - /* get context */ - Context = (FILE_OPERATION_CONTEXT*)lpData; - - if (TotalBytesTransferred.QuadPart) - { - Progress.QuadPart = (TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart; - } - else - { - Progress.QuadPart = 1; - } - - /* update progress bar */ - SendMessageW(Context->hDlgCtrl, PBM_SETPOS, (WPARAM)Progress.u.LowPart, 0); - - if (TotalBytesTransferred.QuadPart == TotalFileSize.QuadPart) - { - /* file was copied */ - Context->Index++; - PostMessageW(Context->hwndDlg, WM_FILE, 0, 0); - } - - return PROGRESS_CONTINUE; -} - -BOOL -QueueFile( - FILE_OPERATION_CONTEXT * Context) -{ - FILE_ENTRY * from, *to = NULL; - BOOL bRet = FALSE; - - if (Context->Index >= Context->from->dwNumFiles) - return FALSE; - - /* get current file */ - from = &Context->from->feFiles[Context->Index]; - - if (Context->op->req->wFunc != FO_DELETE) - to = &Context->to->feFiles[Context->Index]; - - /* update status */ - SetDlgItemTextW(Context->hwndDlg, 14001, from->szFullPath); - - if (Context->op->req->wFunc == FO_COPY) - { - bRet = CopyFileExW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, &Context->op->bCancelled, 0); - } - else if (Context->op->req->wFunc == FO_MOVE) - { - //bRet = MoveFileWithProgressW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, MOVEFILE_COPY_ALLOWED); - } - else if (Context->op->req->wFunc == FO_DELETE) - { - bRet = DeleteFile(from->szFullPath); - } - - return bRet; -} - -static INT_PTR CALLBACK SHOperationDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - FILE_OPERATION_CONTEXT * Context; - - Context = (FILE_OPERATION_CONTEXT*) GetWindowLongPtr(hwndDlg, DWLP_USER); - - switch(uMsg) - { - case WM_INITDIALOG: - SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG)lParam); - - /* get context */ - Context = (FILE_OPERATION_CONTEXT*)lParam; - - /* store progress bar handle */ - Context->hDlgCtrl = GetDlgItem(hwndDlg, 14000); - - /* store window handle */ - Context->hwndDlg = hwndDlg; - - /* set progress bar range */ - (void)SendMessageW(Context->hDlgCtrl, (UINT) PBM_SETRANGE, 0, MAKELPARAM(0, 100)); - - /* start file queueing */ - SetTimer(hwndDlg, TIMER_ID, 1000, NULL); - //QueueFile(Context); - - return TRUE; - - case WM_CLOSE: - Context->op->bCancelled = TRUE; - EndDialog(hwndDlg, Context->op->bCancelled); - return TRUE; - - case WM_COMMAND: - if (LOWORD(wParam) == 14002) - { - Context->op->bCancelled = TRUE; - EndDialog(hwndDlg, Context->op->bCancelled); - return TRUE; - }; break; - - case WM_TIMER: - if (wParam == TIMER_ID) - { - QueueFile(Context); - KillTimer(hwndDlg, TIMER_ID); - }; break; - - case WM_FILE: - if (!QueueFile(Context)) - EndDialog(hwndDlg, Context->op->bCancelled); - default: - break; - } - return FALSE; -} - -HRESULT -SHShowFileOperationDialog(FILE_OPERATION *op, FILE_LIST *flFrom, FILE_LIST *flTo) -{ - HWND hwnd; - BOOL bRet; - MSG msg; - FILE_OPERATION_CONTEXT Context; - - Context.from = flFrom; - Context.to = flTo; - Context.op = op; - Context.Index = 0; - Context.op->bCancelled = FALSE; - - hwnd = CreateDialogParam(shell32_hInstance, MAKEINTRESOURCE(IDD_FILE_COPY), NULL, SHOperationDialog, (LPARAM)&Context); - if (hwnd == NULL) - { - ERR("Failed to create dialog\n"); - return E_FAIL; - } - ShowWindow(hwnd, SW_SHOWNORMAL); - - while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) - { - if (!IsWindow(hwnd) || !IsDialogMessage(hwnd, &msg)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - - return NOERROR; -} - - /************************************************************************ * SHNotifyCopyFile [internal] * @@ -736,7 +597,7 @@ * 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; @@ -743,6 +604,8 @@ TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? "failIfExists" : ""); + op->progress->SetLine(1, src, false, NULL); + /* Destination file may already exist with read only attribute */ attribs = GetFileAttributesW(dest); if (IsAttrib(attribs, FILE_ATTRIBUTE_READONLY)) @@ -757,7 +620,7 @@ } } - ret = CopyFileW(src, dest, bFailIfExists); + ret = CopyFileExW(src, dest, SHCopyProgressRoutine, op, &op->bCancelled, bFailIfExists); if (ret) { SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL); @@ -1232,7 +1095,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}; @@ -1261,17 +1124,13 @@ 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 */ + ZeroMemory(&flFromNew, sizeof(FILE_LIST)); + ZeroMemory(&flToNew, sizeof(FILE_LIST)); + parse_file_list(&flFromNew, szFrom); + parse_file_list(&flToNew, szTo); - /* 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; + copy_files(op, FALSE, &flFromNew, &flToNew); - SHFileOperationW(&fileOp); } static BOOL copy_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const WCHAR *szTo) @@ -1282,7 +1141,7 @@ return 0; } - return SHNotifyCopyFileW(szFrom, szTo, FALSE) == 0; + return SHNotifyCopyFileW(op, szFrom, szTo, FALSE) == 0; } /* copy a file or directory to another directory */ @@ -1322,12 +1181,14 @@ } /* 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; const FILE_ENTRY *fileDest = &flTo->feFiles[0]; + op->progress->SetTitle(L"Copying"); + if (flFrom->bAnyDontExist) return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND; @@ -1345,7 +1206,7 @@ fileDest = &flTo->feFiles[0]; } - if (op->req->fFlags & FOF_MULTIDESTFILES) + if (multiDest) { if (flFrom->bAnyFromWildcard) return ERROR_CANCELLED; @@ -1397,7 +1258,7 @@ { entryToCopy = &flFrom->feFiles[i]; - if ((op->req->fFlags & FOF_MULTIDESTFILES) && + if ((multiDest) && flTo->dwNumFiles > 1) { fileDest = &flTo->feFiles[i]; @@ -1437,6 +1298,7 @@ } } + 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; @@ -1470,7 +1332,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; @@ -1477,17 +1339,19 @@ BOOL bPathExists; BOOL bTrash; + op->progress->SetTitle(L"Deleting"); + if (!flFrom->dwNumFiles) 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)) + if (!(op->req->fFlags & FOF_NOCONFIRMATION) || (!bTrash && op->req->fFlags & FOF_WANTNUKEWARNING)) + if (!confirm_delete_list(op->req->hwnd, op->req->fFlags, bTrash, flFrom)) { - lpFileOp->fAnyOperationsAborted = TRUE; + op->req->fAnyOperationsAborted = TRUE; return 0; } @@ -1497,7 +1361,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) @@ -1510,14 +1374,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; } } @@ -1529,7 +1393,7 @@ SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, fileEntry->szFullPath, NULL); } else - bPathExists = SHELL_DeleteDirectoryW(lpFileOp->hwnd, fileEntry->szFullPath, FALSE); + bPathExists = SHELL_DeleteDirectoryW(op, fileEntry->szFullPath, FALSE); if (!bPathExists) { @@ -1546,15 +1410,19 @@ return err; } } + 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}; @@ -1569,15 +1437,16 @@ lstrcpyW(szTo, szDestPath); szTo[lstrlenW(szDestPath) + 1] = '\0'; - fileOp = *lpFileOp; - fileOp.pFrom = szFrom; - fileOp.pTo = szTo; + ZeroMemory(&flFromNew, sizeof(FILE_LIST)); + ZeroMemory(&flToNew, sizeof(FILE_LIST)); + parse_file_list(&flFromNew, szFrom); + parse_file_list(&flToNew, szTo); - SHFileOperationW(&fileOp); + move_files(op, FALSE, &flFromNew, &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]; @@ -1584,16 +1453,19 @@ 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; + + op->progress->SetTitle(L"Moving"); + const FILE_ENTRY *entryToMove; const FILE_ENTRY *fileDest; @@ -1603,13 +1475,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) { @@ -1619,7 +1491,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]; @@ -1630,7 +1502,7 @@ if (!PathFileExistsW(fileDest->szDirectory)) return ERROR_CANCELLED; - if (lpFileOp->fFlags & FOF_MULTIDESTFILES) + if (multiDest) { if (i >= flTo->dwNumFiles) break; @@ -1644,9 +1516,15 @@ } 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)); + + op->bCancelled |= op->progress->HasUserCancelled(); + /* Should fire on progress dialog only */ + if (op->bCancelled) + return ERROR_CANCELLED; + } if (mismatched > 0) @@ -1661,7 +1539,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; @@ -1683,7 +1561,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 */ @@ -1725,20 +1603,24 @@ ZeroMemory(&op, sizeof(op)); op.req = lpFileOp; op.bManyItems = (flFrom.dwNumFiles > 1); + CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_IProgressDialog, (void**) &op.progress); + if (lpFileOp->wFunc != FO_RENAME) + op.progress->StartProgressDialog(op.req->hwnd, NULL, PROGDLG_NORMAL, NULL); + 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; @@ -1745,6 +1627,9 @@ break; } + op.progress->StopProgressDialog(); + op.progress->Release(); + destroy_file_list(&flFrom); if (lpFileOp->wFunc != FO_DELETE) Index: dll/win32/shell32/wine/changenotify.c =================================================================== --- dll/win32/shell32/wine/changenotify.c (revision 65417) +++ dll/win32/shell32/wine/changenotify.c (working copy) @@ -253,7 +253,7 @@ return FALSE; if (ILIsEqual( watched, changed ) ) return TRUE; - if( sub && ILIsParent( watched, changed, FALSE ) ) + if( sub && ILIsParent( watched, changed, TRUE ) ) return TRUE; #ifdef __REACTOS__ if (sub && _ILIsDesktop(watched)) { Index: include/psdk/winbase.h =================================================================== --- include/psdk/winbase.h (revision 65417) +++ include/psdk/winbase.h (working copy) @@ -2724,6 +2724,8 @@ BOOL WINAPI MoveFileW(_In_ LPCWSTR, _In_ LPCWSTR); BOOL WINAPI MoveFileExA(_In_ LPCSTR, _In_opt_ LPCSTR, _In_ DWORD); BOOL WINAPI MoveFileExW(_In_ LPCWSTR, _In_opt_ LPCWSTR, _In_ DWORD); +BOOL WINAPI MoveFileWithProgressA(_In_ LPCSTR, _In_opt_ LPCSTR, _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine, _In_opt_ LPVOID lpData, _In_ DWORD); +BOOL WINAPI MoveFileWithProgressW(_In_ LPCWSTR, _In_opt_ LPCWSTR, _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine, _In_opt_ LPVOID lpData, _In_ DWORD); int WINAPI MulDiv(_In_ int, _In_ int, _In_ int); BOOL WINAPI NotifyChangeEventLog(_In_ HANDLE, _In_ HANDLE); BOOL WINAPI ObjectCloseAuditAlarmA(_In_ LPCSTR, _In_ PVOID, _In_ BOOL);