mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-12 01:47:24 +10:00
777 lines
23 KiB
C++
777 lines
23 KiB
C++
// Classic Shell (c) 2009-2017, Ivo Beltchev
|
|
// Open-Shell (c) 2017-2018, The Open-Shell Team
|
|
// Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author
|
|
|
|
#include "stdafx.h"
|
|
#include <commctrl.h>
|
|
#include <oleacc.h>
|
|
#include <atlcomcli.h>
|
|
#include <dwmapi.h>
|
|
#include <utility>
|
|
#include "Translations.h"
|
|
#include "Settings.h"
|
|
#include "ResourceHelper.h"
|
|
#include "ComHelper.h"
|
|
#include "dllmain.h"
|
|
|
|
static wchar_t g_TitleMove[256];
|
|
static wchar_t g_TitleCopy[256];
|
|
static wchar_t g_TitleFolder[256];
|
|
static wchar_t g_ButtonMove[256];
|
|
static wchar_t g_ButtonDontMove[256];
|
|
static wchar_t g_ButtonCopy[256];
|
|
static wchar_t g_ButtonDontCopy[256];
|
|
static wchar_t g_ButtonCancel[256];
|
|
static wchar_t g_ButtonMore[256];
|
|
static HMODULE g_hShell32;
|
|
|
|
static void LogPrint( CString *pLog, const wchar_t *format, ... )
|
|
{
|
|
wchar_t buf[256];
|
|
va_list args;
|
|
va_start(args,format);
|
|
int len=Vsprintf(buf,_countof(buf),format,args);
|
|
va_end(args);
|
|
*pLog+=buf;
|
|
}
|
|
|
|
// CClassicCopyFile - this is the implementation of the Copy UI dialog box for files
|
|
|
|
class CClassicCopyFile
|
|
{
|
|
public:
|
|
CClassicCopyFile( void );
|
|
~CClassicCopyFile( void );
|
|
|
|
bool Run( HWND hWnd, IAccessible *pAcc, CString *pLog );
|
|
|
|
private:
|
|
void EnumAccChildren( IAccessible *pAcc, CString *pLog );
|
|
void AddAccChild( IAccessible *pAcc, const VARIANT &id, CString *pLog );
|
|
void GetFileInfo( IAccessible *pAcc, bool bSrc );
|
|
|
|
CString m_FileName;
|
|
bool m_bSystem;
|
|
bool m_bReadOnly;
|
|
HICON m_Icon;
|
|
|
|
CString m_SrcSize;
|
|
CString m_SrcTime;
|
|
HICON m_SrcIcon;
|
|
|
|
CString m_DstSize;
|
|
CString m_DstTime;
|
|
HICON m_DstIcon;
|
|
|
|
typedef std::pair<CComPtr<IAccessible>,int> CControl;
|
|
CControl m_YesButton;
|
|
CControl m_NoButton;
|
|
CControl m_CheckBox;
|
|
CControl m_Cancel;
|
|
bool m_bCopyMultiLast; // the last of a series of multi-file boxes
|
|
|
|
static INT_PTR CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
|
|
|
static void PumpMessages( void );
|
|
};
|
|
|
|
CClassicCopyFile::CClassicCopyFile( void )
|
|
{
|
|
m_Icon=m_SrcIcon=m_DstIcon=NULL;
|
|
m_bCopyMultiLast=false;
|
|
m_bSystem=m_bReadOnly=false;
|
|
}
|
|
|
|
CClassicCopyFile::~CClassicCopyFile( void )
|
|
{
|
|
if (m_Icon) DestroyIcon(m_Icon);
|
|
if (m_SrcIcon) DestroyIcon(m_SrcIcon);
|
|
if (m_DstIcon) DestroyIcon(m_DstIcon);
|
|
}
|
|
|
|
// Show the dialog box. Returns true to suppress the original task dialog box
|
|
bool CClassicCopyFile::Run( HWND hWnd, IAccessible *pAcc, CString *pLog )
|
|
{
|
|
// find all interesting controls
|
|
EnumAccChildren(pAcc,pLog);
|
|
|
|
if (!m_YesButton.first || m_YesButton.second!=CHILDID_SELF || !m_NoButton.first || m_NoButton.second!=CHILDID_SELF || !m_Cancel.first)
|
|
return false; // something is wrong, do nothing
|
|
|
|
if (pLog) pLog->Empty(); // success - no need to log anything
|
|
|
|
// get the info for the source and the destination file (file name, icon, properties)
|
|
m_bReadOnly=false;
|
|
m_bSystem=false;
|
|
GetFileInfo(m_YesButton.first,true);
|
|
GetFileInfo(m_NoButton.first,false);
|
|
|
|
TlsData *pTlsData=GetTlsData();
|
|
if (m_CheckBox.first)
|
|
pTlsData->bCopyMultiFile=true;
|
|
else if (pTlsData->bCopyMultiFile)
|
|
m_bCopyMultiLast=true;
|
|
|
|
// pick the correct dialog template (for single and multiple files, for LTR and RTL)
|
|
int dlg=pTlsData->bCopyMultiFile?(IsLanguageRTL()?IDD_FILEMULTIR:IDD_FILEMULTI):(IsLanguageRTL()?IDD_FILER:IDD_FILE);
|
|
|
|
HWND parent=GetWindow(GetAncestor(hWnd,GA_ROOT),GW_OWNER);
|
|
|
|
int res=(int)DialogBoxParam(g_Instance,MAKEINTRESOURCE(dlg),parent,DialogProc,(LPARAM)this);
|
|
|
|
if (res==IDOK || (res==IDYES && m_bCopyMultiLast))
|
|
{
|
|
// Yes was pressed, proceed with the operation
|
|
m_YesButton.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
|
|
}
|
|
else if (res==IDNO)
|
|
{
|
|
// No
|
|
if (m_CheckBox.first && GetKeyState(VK_SHIFT)<0)
|
|
m_CheckBox.first->accDoDefaultAction(CComVariant(CHILDID_SELF)); // Shift+No = No to All
|
|
PumpMessages(); // messages need to be pumped after every accessibility action. otherwise the next action doesn't work
|
|
m_NoButton.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
|
|
}
|
|
else if (res==IDYES)
|
|
{
|
|
// Yes to All
|
|
m_CheckBox.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
|
|
PumpMessages(); // messages need to be pumped after every accessibility action. otherwise the next action doesn't work
|
|
m_YesButton.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
|
|
}
|
|
if (res==IDCANCEL)
|
|
{
|
|
// Cancel
|
|
if (GetKeyState(VK_SHIFT)<0 || GetKeyState(VK_CONTROL)<0)
|
|
return false; // // Shift+Cancel or Ctrl+Cancel was clicked - show the original dialog box
|
|
m_Cancel.first->accDoDefaultAction(CComVariant(CHILDID_SELF));
|
|
}
|
|
if (res==IDC_LINKMORE)
|
|
{
|
|
// More... was clicked - show the original dialog box
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CClassicCopyFile::PumpMessages( void )
|
|
{
|
|
MSG msg;
|
|
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
void CClassicCopyFile::AddAccChild( IAccessible *pAcc, const VARIANT &id, CString *pLog )
|
|
{
|
|
CComVariant state;
|
|
pAcc->get_accState(id,&state);
|
|
if (state.intVal&(STATE_SYSTEM_UNAVAILABLE|STATE_SYSTEM_INVISIBLE)) return;
|
|
CComVariant role;
|
|
pAcc->get_accRole(id,&role);
|
|
if (role.intVal==ROLE_SYSTEM_PUSHBUTTON)
|
|
{
|
|
CComBSTR name;
|
|
if (SUCCEEDED(pAcc->get_accName(id,&name)) && name)
|
|
{
|
|
if (pLog)
|
|
LogPrint(pLog,L"button found: '%s', %d\r\n",name,id.intVal);
|
|
if (_wcsicmp(name,g_ButtonCopy)==0 || _wcsicmp(name,g_ButtonMove)==0)
|
|
{
|
|
m_YesButton.first=pAcc;
|
|
m_YesButton.second=id.intVal;
|
|
if (pLog)
|
|
LogPrint(pLog,L"YES button: %p, %d\r\n",pAcc,id.intVal);
|
|
}
|
|
else if (_wcsicmp(name,g_ButtonDontCopy)==0 || _wcsicmp(name,g_ButtonDontMove)==0)
|
|
{
|
|
m_NoButton.first=pAcc;
|
|
m_NoButton.second=id.intVal;
|
|
if (pLog)
|
|
LogPrint(pLog,L"NO button: %p, %d\r\n",pAcc,id.intVal);
|
|
}
|
|
else if (_wcsicmp(name,g_ButtonCancel)==0)
|
|
{
|
|
m_Cancel.first=pAcc;
|
|
m_Cancel.second=id.intVal;
|
|
if (pLog)
|
|
LogPrint(pLog,L"CANCEL button: %p, %d\r\n",pAcc,id.intVal);
|
|
}
|
|
}
|
|
}
|
|
if (role.intVal==ROLE_SYSTEM_CHECKBUTTON)
|
|
{
|
|
// hopefully there is only one checkbox
|
|
m_CheckBox.first=pAcc;
|
|
m_CheckBox.second=id.intVal;
|
|
}
|
|
}
|
|
|
|
void CClassicCopyFile::EnumAccChildren( IAccessible *pAcc, CString *pLog )
|
|
{
|
|
AddAccChild(pAcc,CComVariant(CHILDID_SELF),pLog);
|
|
long count;
|
|
pAcc->get_accChildCount(&count);
|
|
CComVariant children[20];
|
|
AccessibleChildren(pAcc,0,count,children,&count);
|
|
for (int i=0;i<count;i++)
|
|
{
|
|
if (children[i].vt==VT_DISPATCH)
|
|
{
|
|
CComQIPtr<IAccessible> pChild=children[i].pdispVal;
|
|
if (pChild)
|
|
EnumAccChildren(pChild,pLog);
|
|
}
|
|
else
|
|
AddAccChild(pAcc,children[i],pLog);
|
|
}
|
|
}
|
|
|
|
void CClassicCopyFile::GetFileInfo( IAccessible *pAcc, bool bSrc )
|
|
{
|
|
long count;
|
|
pAcc->get_accChildCount(&count);
|
|
CComVariant children[20];
|
|
AccessibleChildren(pAcc,0,count,children,&count);
|
|
|
|
wchar_t fname[_MAX_PATH]=L"";
|
|
wchar_t dir[_MAX_PATH]=L"";
|
|
CString size;
|
|
CString date;
|
|
|
|
// get the file name, directory, size and date
|
|
for (int i=0;i<count;i++)
|
|
{
|
|
CComBSTR name;
|
|
if (children[i].vt==VT_DISPATCH)
|
|
{
|
|
CComQIPtr<IAccessible> pChild=children[i].pdispVal;
|
|
if (pChild)
|
|
pChild->get_accName(CComVariant(CHILDID_SELF),&name);
|
|
}
|
|
else
|
|
{
|
|
pAcc->get_accName(children[i],&name);
|
|
}
|
|
switch (i)
|
|
{
|
|
case 2: if (wcslen(name)<_countof(fname)) wcscpy_s(fname,name); break;
|
|
case 3: if (wcslen(name)<_countof(dir)) wcscpy_s(dir,name); break;
|
|
case 4: size=name; break;
|
|
case 5: date=name; break;
|
|
}
|
|
}
|
|
|
|
if (bSrc)
|
|
{
|
|
m_FileName=fname;
|
|
m_SrcSize=size;
|
|
m_SrcTime=date;
|
|
}
|
|
else
|
|
{
|
|
m_DstSize=size;
|
|
m_DstTime=date;
|
|
}
|
|
|
|
if (!fname[0] || !dir[0]) return;
|
|
|
|
wchar_t fname2[_MAX_PATH];
|
|
memcpy(fname2,fname,sizeof(fname2));
|
|
*PathFindExtension(fname2)=0;
|
|
|
|
int len1=Strlen(fname2);
|
|
// the directory text is something like "filename (directory)". we need to parse out the real directory name
|
|
int len2=Strlen(dir);
|
|
if (dir[0]==0x202A) len1++; // for Arabic the first character is some RTL marker. needs to be skipped
|
|
if (dir[len1]==0x1E) len1++; // for Hebrew there is an extra control character after the file name
|
|
if (len1+1>=len2 || dir[len1]!=L' ' || dir[len1+1]!=L'(' || dir[len2-1]!=L')') return;
|
|
if (dir[len1+2]==0x202A) len1++; // for Hebrew the first character is some RTL marker. needs to be skipped
|
|
if (dir[len2-2]==0x202C) len2--; // for Hebrew the last character is some RTL marker. needs to be skipped
|
|
dir[len2-1]=0;
|
|
|
|
// construct the full file name
|
|
wchar_t path[_MAX_PATH];
|
|
_wmakepath_s(path,NULL,dir+len1+2,fname,NULL);
|
|
|
|
if (!bSrc)
|
|
{
|
|
DWORD attrib=GetFileAttributes(path);
|
|
if (attrib!=INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
if (attrib&FILE_ATTRIBUTE_READONLY) m_bReadOnly=true;
|
|
if (attrib&FILE_ATTRIBUTE_SYSTEM) m_bSystem=true;
|
|
}
|
|
}
|
|
|
|
// get file icon
|
|
HICON hIcon=NULL;
|
|
CAbsolutePidl pidl;
|
|
if (SUCCEEDED(SHParseDisplayName(path,NULL,&pidl,0,NULL)) && pidl)
|
|
{
|
|
int iconSize=GetSystemMetrics(SM_CXICON);
|
|
HBITMAP hBitmap=NULL;
|
|
CComPtr<IShellItemImageFactory> pFactory;
|
|
if (SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItemImageFactory,(void**)&pFactory)) && pFactory)
|
|
{
|
|
SIZE size={iconSize,iconSize};
|
|
if (FAILED(pFactory->GetImage(size,SIIGBF_ICONONLY,&hBitmap)))
|
|
hBitmap=NULL;
|
|
}
|
|
|
|
if (hBitmap)
|
|
{
|
|
HBITMAP hMonoBitmap=CreateBitmap(iconSize,iconSize,1,1,NULL);
|
|
ICONINFO info={TRUE,0,0,hMonoBitmap,hBitmap};
|
|
hIcon=CreateIconIndirect(&info);
|
|
DeleteObject(hMonoBitmap);
|
|
DeleteObject(hBitmap);
|
|
}
|
|
}
|
|
if (!hIcon) return;
|
|
|
|
if (bSrc)
|
|
m_SrcIcon=hIcon;
|
|
else
|
|
m_DstIcon=hIcon;
|
|
}
|
|
|
|
const int WM_BRINGFOREGROUND=WM_USER+11;
|
|
|
|
INT_PTR CALLBACK CClassicCopyFile::DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if (uMsg==WM_INITDIALOG)
|
|
{
|
|
SetWindowText(hwndDlg,FindTranslation(L"Copy.Title",L"Confirm File Replace"));
|
|
CClassicCopyFile *pThis=(CClassicCopyFile*)lParam;
|
|
wchar_t text[_MAX_PATH*2];
|
|
if (pThis->m_bSystem)
|
|
{
|
|
Sprintf(text,_countof(text),FindTranslation(L"Copy.SubtitleSys",L"This folder already contains a system file named '%s'."),pThis->m_FileName);
|
|
if (GetSettingInt(L"OverwriteAlertLevel")>=1)
|
|
PlaySound(L".Default",NULL,SND_APPLICATION|SND_ALIAS|SND_ASYNC|SND_NODEFAULT|SND_SYSTEM);
|
|
}
|
|
else if (pThis->m_bReadOnly)
|
|
{
|
|
Sprintf(text,_countof(text),FindTranslation(L"Copy.SubtitleRO",L"This folder already contains a read-only file named '%s'."),pThis->m_FileName);
|
|
if (GetSettingInt(L"OverwriteAlertLevel")>=2)
|
|
PlaySound(L".Default",NULL,SND_APPLICATION|SND_ALIAS|SND_ASYNC|SND_NODEFAULT|SND_SYSTEM);
|
|
}
|
|
else
|
|
Sprintf(text,_countof(text),FindTranslation(L"Copy.Subtitle",L"This folder already contains a file named '%s'."),pThis->m_FileName);
|
|
SetDlgItemText(hwndDlg,IDC_STATICFNAME,text);
|
|
|
|
// load icon for file conflict (146) from Shell32.dll
|
|
HMODULE hShell32=GetModuleHandle(L"Shell32.dll");
|
|
pThis->m_Icon=LoadIcon(hShell32,MAKEINTRESOURCE(146));
|
|
if (pThis->m_Icon)
|
|
SendDlgItemMessage(hwndDlg,IDC_STATICICON1,STM_SETICON,(LPARAM)pThis->m_Icon,0);
|
|
|
|
// set the localized text
|
|
SetDlgItemText(hwndDlg,IDC_STATICPROMPT1,FindTranslation(L"Copy.Prompt1",L"Do you want to replace the existing file:"));
|
|
SetDlgItemText(hwndDlg,IDC_STATICDSTSIZE,pThis->m_DstSize);
|
|
SetDlgItemText(hwndDlg,IDC_STATICDSTTIME,pThis->m_DstTime);
|
|
SetDlgItemText(hwndDlg,IDC_STATICPROMPT2,FindTranslation(L"Copy.Prompt2",L"with this one?"));
|
|
SendDlgItemMessage(hwndDlg,IDC_STATICDSTICON,STM_SETICON,(LPARAM)pThis->m_DstIcon,0);
|
|
SetDlgItemText(hwndDlg,IDC_STATICSRCSIZE,pThis->m_SrcSize);
|
|
SetDlgItemText(hwndDlg,IDC_STATICSRCTIME,pThis->m_SrcTime);
|
|
SendDlgItemMessage(hwndDlg,IDC_STATICSRCICON,STM_SETICON,(LPARAM)pThis->m_SrcIcon,0);
|
|
SetDlgItemText(hwndDlg,IDOK,FindTranslation(L"Copy.Yes",L"&Yes"));
|
|
SetDlgItemText(hwndDlg,IDNO,FindTranslation(L"Copy.No",L"&No"));
|
|
if (GetDlgItem(hwndDlg,IDYES))
|
|
SetDlgItemText(hwndDlg,IDYES,FindTranslation(L"Copy.YesAll",L"Yes to &All"));
|
|
if (GetDlgItem(hwndDlg,IDCANCEL))
|
|
SetDlgItemText(hwndDlg,IDCANCEL,FindTranslation(L"Copy.Cancel",L"Cancel"));
|
|
Sprintf(text,_countof(text),L"<a>%s</a>",FindTranslation(L"Copy.More",L"&More..."));
|
|
SetDlgItemText(hwndDlg,IDC_LINKMORE,text);
|
|
PostMessage(hwndDlg,WM_BRINGFOREGROUND,0,0);
|
|
return TRUE;
|
|
}
|
|
if (uMsg==WM_BRINGFOREGROUND)
|
|
{
|
|
// bring window to front (sometimes on Windows7 it shows up behind Explorer)
|
|
SetForegroundWindow(hwndDlg);
|
|
return TRUE;
|
|
}
|
|
if (uMsg==WM_COMMAND && (wParam==IDOK || wParam==IDYES || wParam==IDNO || wParam==IDCANCEL))
|
|
{
|
|
EndDialog(hwndDlg,wParam);
|
|
return TRUE;
|
|
}
|
|
if (uMsg==WM_NOTIFY)
|
|
{
|
|
NMHDR *pHdr=(NMHDR*)lParam;
|
|
if (pHdr->idFrom==IDC_LINKMORE && (pHdr->code==NM_CLICK || pHdr->code==NM_RETURN))
|
|
{
|
|
EndDialog(hwndDlg,IDC_LINKMORE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CClassicCopyFolder - this is the implementation of the Copy UI dialog box for folders
|
|
|
|
class CClassicCopyFolder
|
|
{
|
|
public:
|
|
CClassicCopyFolder( void ) { m_Icon=NULL; m_Original=NULL; }
|
|
~CClassicCopyFolder( void );
|
|
|
|
bool Run( HWND hWnd );
|
|
|
|
private:
|
|
HICON m_Icon;
|
|
HWND m_Original;
|
|
|
|
static INT_PTR CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
|
};
|
|
|
|
CClassicCopyFolder::~CClassicCopyFolder( void )
|
|
{
|
|
if (m_Icon) DestroyIcon(m_Icon);
|
|
}
|
|
|
|
// Show the dialog box. Returns true to suppress the original task dialog box
|
|
bool CClassicCopyFolder::Run( HWND hWnd )
|
|
{
|
|
m_Original=hWnd;
|
|
const int ID_ALLCHECK=16663;
|
|
HWND check=GetDlgItem(hWnd,ID_ALLCHECK);
|
|
bool bMulti=(check && (GetWindowLong(check,GWL_STYLE)&WS_VISIBLE));
|
|
// pick the correct dialog template (for single and multiple files, for LTR and RTL)
|
|
int dlg=bMulti?(IsLanguageRTL()?IDD_FOLDERMULTIR:IDD_FOLDERMULTI):(IsLanguageRTL()?IDD_FOLDERR:IDD_FOLDER);
|
|
|
|
HWND parent=GetWindow(GetAncestor(hWnd,GA_ROOT),GW_OWNER);
|
|
|
|
int res=(int)DialogBoxParam(g_Instance,MAKEINTRESOURCE(dlg),parent,DialogProc,(LPARAM)this);
|
|
|
|
if (res==IDOK) // Yes button for single folder
|
|
{
|
|
// Yes was pressed, proceed with the operation
|
|
PostMessage(hWnd,WM_COMMAND,IDYES,(LPARAM)GetDlgItem(hWnd,IDYES));
|
|
}
|
|
else if (res==IDNO)
|
|
{
|
|
// No
|
|
if (bMulti)
|
|
{
|
|
if (GetKeyState(VK_SHIFT)<0)
|
|
{
|
|
CheckDlgButton(hWnd,ID_ALLCHECK,BST_CHECKED);
|
|
SendMessage(hWnd,WM_COMMAND,ID_ALLCHECK,(LPARAM)check);
|
|
}
|
|
PostMessage(hWnd,WM_COMMAND,IDNO,(LPARAM)GetDlgItem(hWnd,IDNO)); // Skip
|
|
}
|
|
else
|
|
PostMessage(hWnd,WM_COMMAND,IDCANCEL,(LPARAM)GetDlgItem(hWnd,IDCANCEL)); // No
|
|
}
|
|
else if (res==IDYES)
|
|
{
|
|
// Yes to All
|
|
CheckDlgButton(hWnd,ID_ALLCHECK,BST_CHECKED);
|
|
SendMessage(hWnd,WM_COMMAND,ID_ALLCHECK,(LPARAM)check);
|
|
PostMessage(hWnd,WM_COMMAND,IDYES,(LPARAM)GetDlgItem(hWnd,IDYES));
|
|
}
|
|
if (res==IDCANCEL)
|
|
{
|
|
// Cancel
|
|
if (GetKeyState(VK_SHIFT)<0 || GetKeyState(VK_CONTROL)<0)
|
|
return false; // // Shift+Cancel or Ctrl+Cancel was clicked - show the original dialog box
|
|
PostMessage(hWnd,WM_COMMAND,IDCANCEL,(LPARAM)GetDlgItem(hWnd,IDCANCEL)); // No
|
|
}
|
|
if (res==IDC_LINKMORE)
|
|
{
|
|
// More... was clicked - show the original dialog box
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
INT_PTR CALLBACK CClassicCopyFolder::DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if (uMsg==WM_INITDIALOG)
|
|
{
|
|
SetWindowText(hwndDlg,FindTranslation(L"Folder.Title",L"Confirm Folder Replace"));
|
|
CClassicCopyFolder *pThis=(CClassicCopyFolder*)lParam;
|
|
wchar_t text[2048];
|
|
// find the link control and get its text
|
|
HWND link=FindWindowEx(pThis->m_Original,NULL,WC_LINK,NULL);
|
|
if (link)
|
|
GetWindowText(link,text,_countof(text));
|
|
else
|
|
text[0]=0;
|
|
Strcat(text,_countof(text),L"\r\n\r\n");
|
|
Strcat(text,_countof(text),FindTranslation(L"Folder.Prompt",L"Do you still want to move or copy the folder?"));
|
|
SetDlgItemText(hwndDlg,IDC_STATICFNAME,text);
|
|
|
|
// load icon for file conflict (146) from Shell32.dll
|
|
HMODULE hShell32=GetModuleHandle(L"Shell32.dll");
|
|
pThis->m_Icon=LoadIcon(hShell32,MAKEINTRESOURCE(146));
|
|
if (pThis->m_Icon)
|
|
SendDlgItemMessage(hwndDlg,IDC_STATICICON1,STM_SETICON,(LPARAM)pThis->m_Icon,0);
|
|
|
|
// set the localized text
|
|
SetDlgItemText(hwndDlg,IDOK,FindTranslation(L"Copy.Yes",L"&Yes"));
|
|
SetDlgItemText(hwndDlg,IDNO,FindTranslation(L"Copy.No",L"&No"));
|
|
if (GetDlgItem(hwndDlg,IDYES))
|
|
SetDlgItemText(hwndDlg,IDYES,FindTranslation(L"Copy.YesAll",L"Yes to &All"));
|
|
if (GetDlgItem(hwndDlg,IDCANCEL))
|
|
SetDlgItemText(hwndDlg,IDCANCEL,FindTranslation(L"Copy.Cancel",L"Cancel"));
|
|
Sprintf(text,_countof(text),L"<a>%s</a>",FindTranslation(L"Copy.More",L"&More..."));
|
|
SetDlgItemText(hwndDlg,IDC_LINKMORE,text);
|
|
PostMessage(hwndDlg,WM_BRINGFOREGROUND,0,0);
|
|
return TRUE;
|
|
}
|
|
if (uMsg==WM_BRINGFOREGROUND)
|
|
{
|
|
// bring window to front (sometimes on Windows7 it shows up behind Explorer)
|
|
SetForegroundWindow(hwndDlg);
|
|
return TRUE;
|
|
}
|
|
if (uMsg==WM_COMMAND && (wParam==IDOK || wParam==IDYES || wParam==IDNO || wParam==IDCANCEL))
|
|
{
|
|
EndDialog(hwndDlg,wParam);
|
|
return TRUE;
|
|
}
|
|
if (uMsg==WM_NOTIFY)
|
|
{
|
|
NMHDR *pHdr=(NMHDR*)lParam;
|
|
if (pHdr->idFrom==IDC_LINKMORE && (pHdr->code==NM_CLICK || pHdr->code==NM_RETURN))
|
|
{
|
|
EndDialog(hwndDlg,IDC_LINKMORE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static CComPtr<IAccessible> FindMoreButton( IAccessible *pAcc )
|
|
{
|
|
CComVariant state;
|
|
CComVariant self(CHILDID_SELF);
|
|
pAcc->get_accState(self,&state);
|
|
if (!(state.intVal&(STATE_SYSTEM_UNAVAILABLE|STATE_SYSTEM_INVISIBLE)))
|
|
{
|
|
CComVariant role;
|
|
pAcc->get_accRole(self,&role);
|
|
if (role.intVal==ROLE_SYSTEM_PUSHBUTTON)
|
|
{
|
|
CComBSTR name;
|
|
if (SUCCEEDED(pAcc->get_accName(self,&name)) && name && _wcsicmp(name,g_ButtonMore)==0)
|
|
return pAcc;
|
|
}
|
|
}
|
|
|
|
CComVariant children[30];
|
|
long count;
|
|
AccessibleChildren(pAcc,0,_countof(children),children,&count);
|
|
for (int i=0;i<count;i++)
|
|
{
|
|
if (children[i].vt==VT_DISPATCH)
|
|
{
|
|
CComQIPtr<IAccessible> pChild=children[i].pdispVal;
|
|
if (pChild)
|
|
{
|
|
CComPtr<IAccessible> pRes=FindMoreButton(pChild);
|
|
if (pRes) return pRes;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
|
|
{
|
|
if (uMsg==WM_WINDOWPOSCHANGING)
|
|
{
|
|
WINDOWPOS *pos=(WINDOWPOS*)lParam;
|
|
if (pos->flags&SWP_SHOWWINDOW)
|
|
{
|
|
wchar_t title[256];
|
|
GetWindowText(hWnd,title,_countof(title));
|
|
|
|
if (_wcsicmp(title,g_TitleMove)==0 || _wcsicmp(title,g_TitleCopy)==0)
|
|
{
|
|
// file UI
|
|
if (GetSettingBool(L"ReplaceFileUI"))
|
|
{
|
|
CString log;
|
|
CComPtr<IAccessible> pAcc;
|
|
HRESULT h=AccessibleObjectFromWindow(hWnd,OBJID_WINDOW,IID_IAccessible,(void**)&pAcc);
|
|
if (SUCCEEDED(h) && pAcc)
|
|
{
|
|
CClassicCopyFile copy;
|
|
if (copy.Run(hWnd,pAcc,g_bLogLevel?&log:NULL))
|
|
{
|
|
pos->x=pos->y=-20000;
|
|
pos->flags&=~(SWP_SHOWWINDOW|SWP_NOMOVE);
|
|
}
|
|
}
|
|
else if (g_bLogLevel)
|
|
LogPrint(&log,L"AccessibleObjectFromWindow: error=0x%X, hWnd=0x%p",h,hWnd);
|
|
|
|
if (g_bLogLevel)
|
|
{
|
|
wchar_t fname[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell";
|
|
DoEnvironmentSubst(fname,_MAX_PATH);
|
|
SHCreateDirectory(NULL,fname);
|
|
Strcat(fname,_countof(fname),L"\\ExplorerLog.txt");
|
|
if (!log.IsEmpty())
|
|
{
|
|
FILE *f;
|
|
if (_wfopen_s(&f,fname,L"wb")==0)
|
|
{
|
|
fwprintf(f,L"\xFEFF");
|
|
|
|
OSVERSIONINFOEX ver={sizeof(ver)};
|
|
#pragma warning(suppress:4996)
|
|
GetVersionEx((OSVERSIONINFO*)&ver);
|
|
fwprintf(f,L"version = %d.%d.%d - %d.%d\r\n\r\n",ver.dwMajorVersion,ver.dwMinorVersion,ver.dwBuildNumber,ver.wServicePackMajor,ver.wServicePackMinor);
|
|
|
|
wchar_t languages[100]={0};
|
|
ULONG size=4; // up to 4 languages
|
|
ULONG len=_countof(languages);
|
|
GetUserPreferredUILanguages(MUI_LANGUAGE_NAME,&size,languages,&len);
|
|
for (const wchar_t *lang=languages;*lang;lang+=wcslen(lang)+1)
|
|
fwprintf(f,L"language = %s\r\n",lang);
|
|
|
|
fwprintf(f,L"\r\n");
|
|
fwprintf(f,L"g_ButtonMove = '%s'\r\n",g_ButtonMove);
|
|
fwprintf(f,L"g_ButtonDontMove = '%s'\r\n",g_ButtonDontMove);
|
|
fwprintf(f,L"g_ButtonCopy = '%s'\r\n",g_ButtonCopy);
|
|
fwprintf(f,L"g_ButtonDontCopy = '%s'\r\n",g_ButtonDontCopy);
|
|
fwprintf(f,L"g_ButtonCancel = '%s'\r\n",g_ButtonCancel);
|
|
fwprintf(f,L"\r\n");
|
|
|
|
fwrite((const wchar_t*)log,log.GetLength(),2,f);
|
|
fclose(f);
|
|
}
|
|
}
|
|
else
|
|
DeleteFile(fname);
|
|
}
|
|
}
|
|
}
|
|
else if (_wcsicmp(title,g_TitleFolder)==0)
|
|
{
|
|
// folder UI
|
|
if (GetSettingBool(L"ReplaceFolderUI"))
|
|
{
|
|
CClassicCopyFolder copy;
|
|
if (copy.Run(hWnd))
|
|
{
|
|
pos->x=pos->y=-20000;
|
|
pos->flags&=~(SWP_SHOWWINDOW|SWP_NOMOVE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// look for progress bar
|
|
if (GetSettingBool(L"EnableMore"))
|
|
{
|
|
HWND progress=FindChildWindow(hWnd,PROGRESS_CLASS);
|
|
if (progress)
|
|
{
|
|
bool bDef;
|
|
int delay=GetSettingInt(L"MoreProgressDelay",bDef);
|
|
if (bDef)
|
|
{
|
|
delay=0;
|
|
if (GetWinVersion()>=WIN_VER_WIN7)
|
|
{
|
|
BOOL comp;
|
|
if (SUCCEEDED(DwmIsCompositionEnabled(&comp)) && comp)
|
|
delay=500;
|
|
}
|
|
}
|
|
SetTimer(hWnd,'CLEX',delay,NULL);
|
|
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
|
|
}
|
|
}
|
|
}
|
|
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
|
|
RemoveWindowSubclass(hWnd,WindowProc,uIdSubclass);
|
|
return res;
|
|
}
|
|
}
|
|
if (uMsg==WM_TIMER && wParam=='CLEX')
|
|
{
|
|
KillTimer(hWnd,wParam);
|
|
|
|
CComPtr<IAccessible> pAcc;
|
|
HRESULT h=AccessibleObjectFromWindow(hWnd,OBJID_WINDOW,IID_IAccessible,(void**)&pAcc);
|
|
if (SUCCEEDED(h) && pAcc)
|
|
{
|
|
CComPtr<IAccessible> pMore=FindMoreButton(pAcc);
|
|
if (pMore) pMore->accDoDefaultAction(CComVariant(CHILDID_SELF));
|
|
}
|
|
|
|
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
|
|
RemoveWindowSubclass(hWnd,WindowProc,uIdSubclass);
|
|
return res;
|
|
}
|
|
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
|
|
}
|
|
|
|
LRESULT CALLBACK ClassicCopyHook( int nCode, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
if (nCode==HCBT_CREATEWND)
|
|
{
|
|
HWND hWnd=(HWND)wParam;
|
|
CBT_CREATEWND *create=(CBT_CREATEWND*)lParam;
|
|
HINSTANCE hInst=(HINSTANCE)GetWindowLongPtr(hWnd,GWLP_HINSTANCE);
|
|
if (create->lpcs->lpszName && (intptr_t)create->lpcs->lpszClass==32770 && hInst==g_hShell32)
|
|
{
|
|
WaitDllInitThread();
|
|
static LONG id;
|
|
int i=InterlockedIncrement(&id);
|
|
SetWindowSubclass(hWnd,WindowProc,i,0);
|
|
}
|
|
}
|
|
TlsData *pTlsData=GetTlsData();
|
|
return CallNextHookEx(pTlsData->copyHook,nCode,wParam,lParam);
|
|
}
|
|
|
|
void InitClassicCopyProcess( void )
|
|
{
|
|
// load UI text from shell32.dll
|
|
// the text is used to locate controls in the copy dialog by name
|
|
g_hShell32=GetModuleHandle(L"shell32.dll");
|
|
LoadString(g_hShell32,17027,g_TitleMove,256);
|
|
LoadString(g_hShell32,17024,g_TitleCopy,256);
|
|
LoadString(g_hShell32,16705,g_TitleFolder,256);
|
|
LoadString(g_hShell32,13610,g_ButtonMove,256);
|
|
LoadString(g_hShell32,13623,g_ButtonDontMove,256);
|
|
LoadString(g_hShell32,13604,g_ButtonCopy,256);
|
|
LoadString(g_hShell32,13606,g_ButtonDontCopy,256);
|
|
LoadString(g_hShell32,13588,g_ButtonCancel,256);
|
|
LoadString(g_hShell32,32992,g_ButtonMore,256);
|
|
}
|
|
|
|
void InitClassicCopyThread( void )
|
|
{
|
|
TlsData *pTlsData=GetTlsData();
|
|
if (!pTlsData->copyHook)
|
|
pTlsData->copyHook=SetWindowsHookEx(WH_CBT,ClassicCopyHook,g_Instance,GetCurrentThreadId());
|
|
}
|
|
|
|
void FreeClassicCopyThread( void )
|
|
{
|
|
TlsData *pTlsData=GetTlsData();
|
|
if (pTlsData->copyHook)
|
|
{
|
|
UnhookWindowsHookEx(pTlsData->copyHook);
|
|
pTlsData->copyHook=NULL;
|
|
}
|
|
}
|