|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2000.
//
// File: ConfirmationUI.cpp
//
// Contents: Confirmation UI for storage based copy engine
//
// History: 20-Mar-2000 ToddB
//
//--------------------------------------------------------------------------
#include "shellprv.h"
#pragma hdrstop
#include "ConfirmationUI.h"
#include "resource.h"
#include "ntquery.h"
#include "ids.h"
#include <initguid.h>
#define TF_DLGDISPLAY 0x00010000 // messages related to display of the confirmation dialog
#define TF_DLGSTORAGE 0x00020000 // messages related to using the legacy IStorage to get dialog info
#define TF_DLGISF 0x00040000 // messages related to using IShellFolder to get dialog info
#define RECT_WIDTH(rc) ((rc).right - (rc).left)
#define RECT_HEIGHT(rc) ((rc).bottom - (rc).top)
GUID guidStorage = PSGUID_STORAGE;
// prototype some functions that are local to this file:
void ShiftDialogItem(HWND hDlg, int id, int cx, int cy); BOOL CALLBACK ShiftLeftProc(HWND hwnd, LPARAM lParam);
STDAPI CTransferConfirmation_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { if (!ppv) return E_POINTER;
CComObject<CTransferConfirmation> *pObj = NULL; // created with ref count of 0
if (SUCCEEDED(CComObject<CTransferConfirmation>::CreateInstance(&pObj))) { // QueryInterface ups the refcount to 1
if (SUCCEEDED(pObj->QueryInterface(riid, ppv))) { return S_OK; }
// failed to get the right interface, delete the object.
delete pObj; }
*ppv = NULL; return E_FAIL; }
CTransferConfirmation::CTransferConfirmation() : m_fSingle(TRUE) { ASSERT(NULL == m_pszDescription); ASSERT(NULL == m_pszTitle); ASSERT(NULL == m_hIcon); ASSERT(NULL == m_pPropUI); ASSERT(NULL == m_cItems); }
CTransferConfirmation::~CTransferConfirmation() { if (m_pPropUI) m_pPropUI->Release(); }
BOOL CTransferConfirmation::_IsCopyOperation(STGOP stgop) { return (stgop == STGOP_COPY) || (stgop == STGOP_COPY_PREFERHARDLINK); }
// TODO: Get better icons for "Nuke File" and "Nuke Folder" from design.
// TODO: Get better icons for "Move File" and "Move Folder" from design.
// TODO: Get better icons for "Secondary Attribute loss" from design (stream loss, encrypt loss, ACL loss, etc)
HRESULT CTransferConfirmation::_GetDialogSettings() { _FreeDialogSettings();
ASSERT(NULL == m_pszDescription); ASSERT(NULL == m_pszTitle); ASSERT(NULL == m_hIcon); ASSERT(NULL == m_cItems);
m_fSingle = (m_cop.cRemaining<=1); // Set the default values of m_crResult so that if the dialog is killed or
// some error occurs we give a valid default response.
m_crResult = CONFRES_CANCEL; if (m_cop.pcc) { // we already have the strings to use
switch (m_cop.pcc->dwButtons) { case CCB_YES_SKIP_CANCEL: m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; break;
case CCB_RENAME_SKIP_CANCEL: m_idDialog = IDD_CONFIRM_RENAMESKIPCANCEL; break;
case CCB_YES_SKIP_RENAME_CANCEL: m_idDialog = IDD_CONFIRM_YESSKIPRENAMECANCEL; break;
case CCB_RETRY_SKIP_CANCEL: m_idDialog = IDD_CONFIRM_RETRYSKIPCANCEL; break; case CCB_OK: m_idDialog = IDD_CONFIRM_OK; break; default: return E_INVALIDARG; // REVIEW: should we define more specific error codes like STG_E_INVALIDBUTTONOPTIONS?
}
if (m_cop.pcc->dwFlags & CCF_SHOW_SOURCE_INFO) { _AddItem(m_cop.psiItem); } if (m_cop.pcc->dwFlags & CCF_SHOW_DESTINATION_INFO) { _AddItem(m_cop.psiDest); } } else { TCHAR szBuf[2048]; int idTitle = 0; int idIcon = 0; int idDescription = 0;
////////////////////////////////////////////////////////////////////////////////
// These are "confirmations", i.e. conditions the user can choose to ignore.
// You can typically answer "Yes", "Skip", or "Cancel"
////////////////////////////////////////////////////////////////////////////////
if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_STREAM)) { idTitle = IDS_CONFIRM_FILE_DELETE; idIcon = IDI_DELETE_FILE; idDescription = IDS_DELETE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_DELETE; idIcon = IDI_DELETE_FOLDER; idDescription = IDS_DELETE_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_READONLY_STREAM)) { idTitle = IDS_CONFIRM_FILE_DELETE; idIcon = IDI_DELETE_FILE; idDescription = IDS_DELETE_READONLY_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_READONLY_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_DELETE; idIcon = IDI_DELETE_FOLDER; idDescription = IDS_DELETE_READONLY_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_SYSTEM_STREAM)) { idTitle = IDS_CONFIRM_FILE_DELETE; idIcon = IDI_DELETE_FILE; idDescription = IDS_DELETE_SYSTEM_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_SYSTEM_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_DELETE; idIcon = IDI_DELETE_FOLDER; idDescription = IDS_DELETE_SYSTEM_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_TOOLARGE_STREAM)) { idTitle = IDS_CONFIRM_FILE_DELETE; idIcon = IDI_NUKE_FILE; idDescription = IDS_DELETE_TOOBIG_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_TOOLARGE_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_DELETE; idIcon = IDI_NUKE_FOLDER; idDescription = IDS_DELETE_TOOBIG_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_WONT_RECYCLE_STREAM)) { idTitle = IDS_CONFIRM_FILE_DELETE; idIcon = IDI_NUKE_FILE; idDescription = IDS_NUKE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_WONT_RECYCLE_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_DELETE; idIcon = IDI_NUKE_FOLDER; idDescription = IDS_NUKE_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_DELETE_PROGRAM_STREAM)) { idTitle = IDS_CONFIRM_FILE_DELETE; idIcon = IDI_DELETE_FILE; idDescription = IDS_DELETE_PROGRAM_FILE; m_fShowARPLink = TRUE; // TODO)) implement ShowARPLink
m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_MOVE_SYSTEM_STREAM)) { idTitle = IDS_CONFIRM_FILE_MOVE; idIcon = IDI_MOVE; idDescription = IDS_MOVE_SYSTEM_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_MOVE_SYSTEM_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_MOVE; idIcon = IDI_MOVE; idDescription = IDS_MOVE_SYSTEM_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_RENAME_SYSTEM_STREAM)) { idTitle = IDS_CONFIRM_FILE_RENAME; idIcon = IDI_RENAME; idDescription = IDS_RENAME_SYSTEM_FILE; // Two Arg
m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_RENAME_SYSTEM_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_RENAME; idIcon = IDI_RENAME; idDescription = IDS_RENAME_SYSTEM_FOLDER; // Two Arg
m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_STREAM_LOSS_STREAM)) { idTitle = IDS_CONFIRM_STREAM_LOSS; idIcon = IDI_NUKE; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_STREAM_LOSS_COPY_FILE : IDS_STREAM_LOSS_MOVE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_STREAM_LOSS_STORAGE)) { idTitle = IDS_CONFIRM_STREAM_LOSS; idIcon = IDI_NUKE; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_STREAM_LOSS_COPY_FOLDER : IDS_STREAM_LOSS_MOVE_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_METADATA_LOSS_STREAM)) { idTitle = IDS_CONFIRM_METADATA_LOSS; idIcon = IDI_NUKE; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_METADATA_LOSS_COPY_FILE : IDS_METADATA_LOSS_MOVE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_METADATA_LOSS_STORAGE)) { idTitle = IDS_CONFIRM_METADATA_LOSS; idIcon = IDI_NUKE; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_METADATA_LOSS_COPY_FOLDER : IDS_METADATA_LOSS_MOVE_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_COMPRESSION_LOSS_STREAM)) { idTitle = IDS_CONFIRM_COMPRESSION_LOSS; idIcon = IDI_ATTRIBS_FILE; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_COMPRESSION_LOSS_COPY_FILE : IDS_COMPRESSION_LOSS_MOVE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_COMPRESSION_LOSS_STORAGE)) { idTitle = IDS_CONFIRM_COMPRESSION_LOSS; idIcon = IDI_ATTRIBS_FOLDER; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_COMPRESSION_LOSS_COPY_FOLDER : IDS_COMPRESSION_LOSS_MOVE_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_SPARSEDATA_LOSS_STREAM)) { idTitle = IDS_CONFIRM_COMPRESSION_LOSS; idIcon = IDI_ATTRIBS_FILE; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_SPARSE_LOSS_COPY_FILE : IDS_SPARSE_LOSS_MOVE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_ENCRYPTION_LOSS_STREAM)) { idTitle = IDS_CONFIRM_ENCRYPTION_LOSS; idIcon = IDI_ATTRIBS_FILE; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_ENCRYPTION_LOSS_COPY_FILE : IDS_ENCRYPTION_LOSS_MOVE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; // is it on purpose that we are not adding item here
} else if (IsEqualIID(m_cop.stc, STCONFIRM_ENCRYPTION_LOSS_STORAGE)) { idTitle = IDS_CONFIRM_ENCRYPTION_LOSS; idIcon = IDI_ATTRIBS_FOLDER; idDescription = _IsCopyOperation(m_cop.dwOperation) ? IDS_ENCRYPTION_LOSS_COPY_FOLDER : IDS_ENCRYPTION_LOSS_MOVE_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); }
else if (IsEqualIID(m_cop.stc, STCONFIRM_ACCESSCONTROL_LOSS_STREAM)) { idTitle = IDS_CONFIRM_ACL_LOSS; idIcon = IDI_ATTRIBS_FILE; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_ACCESSCONTROL_LOSS_STORAGE)) { idTitle = IDS_CONFIRM_ACL_LOSS; idIcon = IDI_ATTRIBS_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_LFNTOFAT_STREAM)) { idTitle = IDS_SELECT_FILE_NAME; idIcon = IDI_RENAME; m_idDialog = IDD_CONFIRM_RENAMESKIPCANCEL; _AddItem(m_cop.psiItem); } else if (IsEqualIID(m_cop.stc, STCONFIRM_LFNTOFAT_STORAGE)) { idTitle = IDS_SELECT_FOLDER_NAME; idIcon = IDI_RENAME; m_idDialog = IDD_CONFIRM_RENAMESKIPCANCEL; _AddItem(m_cop.psiItem); } ////////////////////////////////////////////////////////////////////////////////
// These are the "do you want to replace something" cases, they are special in
// that you can answer "Yes", "Skip", "Rename", or "Cancel"
////////////////////////////////////////////////////////////////////////////////
else if (IsEqualIID(m_cop.stc, STCONFIRM_REPLACE_STREAM)) { idTitle = IDS_CONFIRM_FILE_REPLACE; idIcon = IDI_REPLACE_FILE; idDescription = IDS_REPLACE_FILE; m_idDialog = IDD_CONFIRM_YESSKIPRENAMECANCEL; _AddItem(m_cop.psiDest, IDS_REPLACEEXISTING_FILE); _AddItem(m_cop.psiItem, IDS_WITHTHIS); } else if (IsEqualIID(m_cop.stc, STCONFIRM_REPLACE_STORAGE)) { idTitle = IDS_CONFIRM_FOLDER_REPLACE; idIcon = IDI_REPLACE_FOLDER; idDescription = IDS_REPLACE_FOLDER; m_idDialog = IDD_CONFIRM_YESSKIPRENAMECANCEL; _AddItem(m_cop.psiDest, IDS_REPLACEEXISTING_FOLDER); _AddItem(m_cop.psiItem, IDS_INTOTHIS); } ////////////////////////////////////////////////////////////////////////////////
// This group is "error messages", you can typically reply with "Skip",
// "Retry", or "Cancel"
////////////////////////////////////////////////////////////////////////////////
else { // See if the guid is one of our phony guids that's really just an HRESULT and a bunch of zeros.
// To do this we treat the guid like an array of 4 DWORDS and check the last 3 DWORDs against 0.
AssertMsg(sizeof(m_cop.stc) == 4*sizeof(DWORD), TEXT("Size of STGTRANSCONFIRMATION is not 128 bytes!")); DWORD *pdw = (DWORD*)&m_cop.stc; if (pdw[1] == 0 && pdw[2] == 0 && pdw[3] == 0) { HRESULT hrErr = pdw[0]; switch (hrErr) { case STG_E_FILENOTFOUND: case STG_E_PATHNOTFOUND:
case STG_E_ACCESSDENIED:
case STG_E_INUSE: case STG_E_SHAREVIOLATION: case STG_E_LOCKVIOLATION:
case STG_E_DOCFILETOOLARGE: case STG_E_MEDIUMFULL:
case STG_E_INSUFFICIENTMEMORY:
case STG_E_DISKISWRITEPROTECTED:
case STG_E_FILEALREADYEXISTS:
case STG_E_INVALIDNAME:
case STG_E_REVERTED:
case STG_E_DOCFILECORRUPT:
// these are expected errors for which we should have custom friendly strings:
// TODO: Get friendly messages for these errors from UA
idTitle = IDS_DEFAULTTITLE; idIcon = IDI_DEFAULTICON; idDescription = IDS_DEFAULTDESC; m_idDialog = IDD_CONFIRM_RETRYSKIPCANCEL; break;
// these are errors I don't think we should ever see, so I'm asserting but then falling through to the
// default case anyway. In most cases I simply don't know what these mean.
case E_PENDING: case STG_E_CANTSAVE: case STG_E_SHAREREQUIRED: case STG_E_NOTCURRENT: case STG_E_WRITEFAULT: case STG_E_READFAULT: case STG_E_SEEKERROR: case STG_E_NOTFILEBASEDSTORAGE: case STG_E_NOTSIMPLEFORMAT: case STG_E_INCOMPLETE: case STG_E_TERMINATED: case STG_E_BADBASEADDRESS: case STG_E_EXTANTMARSHALLINGS: case STG_E_OLDFORMAT: case STG_E_OLDDLL: case STG_E_UNKNOWN: case STG_E_UNIMPLEMENTEDFUNCTION: case STG_E_INVALIDFLAG: case STG_E_PROPSETMISMATCHED: case STG_E_ABNORMALAPIEXIT: case STG_E_INVALIDHEADER: case STG_E_INVALIDPARAMETER: case STG_E_INVALIDFUNCTION: case STG_E_TOOMANYOPENFILES: case STG_E_INVALIDHANDLE: case STG_E_INVALIDPOINTER: case STG_E_NOMOREFILES:
TraceMsg(TF_ERROR, "We should never be asked to confirm this error (%08x)", hrErr); // fall through...
default: // Use FormatMessage to get the default description for this error message. Sure that's totally
// useless to the end user, but its more useful that nothing which is the altnerative.
idTitle = IDS_DEFAULTTITLE; idIcon = IDI_DEFAULTICON; if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hrErr, 0, szBuf, ARRAYSIZE(szBuf), NULL)) { m_pszDescription = StrDup(szBuf); } else { idDescription = IDS_DEFAULTDESC; } m_idDialog = IDD_CONFIRM_RETRYSKIPCANCEL; break; } } else { // for the default case we show an "unknown error" message and offer
// "skip", "retry", and "cancel". We should never get to this code path.
TraceMsg(TF_ERROR, "An unknown non-custom error is being display in CTransferConfirmation!"); idTitle = IDS_DEFAULTTITLE; idIcon = IDI_DEFAULTICON; idDescription = IDS_DEFAULTDESC; m_idDialog = IDD_CONFIRM_RETRYSKIPCANCEL; } }
if (idTitle && LoadString(_Module.GetResourceInstance(), idTitle, szBuf, ARRAYSIZE(szBuf))) { m_pszTitle = StrDup(szBuf); } if (idIcon) { m_hIcon = (HICON)LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(idIcon), IMAGE_ICON, 0,0, LR_DEFAULTSIZE); } if (idDescription && LoadString(_Module.GetResourceInstance(), idDescription, szBuf, ARRAYSIZE(szBuf))) { m_pszDescription = StrDup(szBuf); } }
if (m_fSingle) { // we don't show the "skip" button if only a single item is being moved.
switch(m_idDialog) { case IDD_CONFIRM_RETRYSKIPCANCEL: m_idDialog = IDD_CONFIRM_RETRYCANCEL; break; case IDD_CONFIRM_YESSKIPCANCEL: m_idDialog = IDD_CONFIRM_YESCANCEL; break; case IDD_CONFIRM_RENAMESKIPCANCEL: m_idDialog = IDD_CONFIRM_RENAMECANCEL; break; case IDD_CONFIRM_YESSKIPRENAMECANCEL: m_idDialog = IDD_CONFIRM_YESRENAMECANCEL; break; default: break; // unknown dialog
} }
// rename - not really implemented yet
switch(m_idDialog) { case IDD_CONFIRM_RENAMESKIPCANCEL: m_idDialog = IDD_CONFIRM_SKIPCANCEL; break; case IDD_CONFIRM_RENAMECANCEL: m_idDialog = IDD_CONFIRM_CANCEL; break; case IDD_CONFIRM_YESSKIPRENAMECANCEL: m_idDialog = IDD_CONFIRM_YESSKIPCANCEL; break; case IDD_CONFIRM_YESRENAMECANCEL: m_idDialog = IDD_CONFIRM_YESCANCEL; break; default: break; // Non-rename dialog
}
return S_OK; }
// free everything loaded or allocated in _GetDialogSetttings.
HRESULT CTransferConfirmation::_FreeDialogSettings() { if (m_pszTitle) { LocalFree(m_pszTitle); m_pszTitle = NULL; } if (m_hIcon) { DestroyIcon(m_hIcon); m_hIcon = NULL; } if (m_pszDescription) { LocalFree(m_pszDescription); m_pszDescription = NULL; }
// This array is zeroed before usage, anything that's no longer zero needs to
// be freed in the appropriate mannor.
for (int i=0; i<ARRAYSIZE(m_rgItemInfo); i++) { if (m_rgItemInfo[i].pwszIntro) { delete [] m_rgItemInfo[i].pwszIntro; m_rgItemInfo[i].pwszIntro = NULL; } if (m_rgItemInfo[i].pwszDisplayName) { CoTaskMemFree(m_rgItemInfo[i].pwszDisplayName); m_rgItemInfo[i].pwszDisplayName = NULL; } if (m_rgItemInfo[i].pwszAttribs) { SHFree(m_rgItemInfo[i].pwszAttribs); m_rgItemInfo[i].pwszAttribs = NULL; } if (m_rgItemInfo[i].hBitmap) { DeleteObject(m_rgItemInfo[i].hBitmap); m_rgItemInfo[i].hBitmap = NULL; } if (m_rgItemInfo[i].hIcon) { DestroyIcon(m_rgItemInfo[i].hIcon); m_rgItemInfo[i].hIcon = NULL; } } m_cItems = 0;
return S_OK; }
HRESULT CTransferConfirmation::_ClearSettings() { m_idDialog = IDD_CONFIRM_RETRYSKIPCANCEL; m_fShowARPLink = 0; m_fApplyToAll = 0; m_cItems = 0; m_hfont = 0; ZeroMemory(m_rgItemInfo, sizeof(m_rgItemInfo));
return S_OK; }
HRESULT CTransferConfirmation::_Init() { HRESULT hr = S_OK;
ASSERT(m_pszTitle == NULL); ASSERT(m_hIcon == NULL); ASSERT(m_pszDescription == NULL);
_ClearSettings();
if (!m_pPropUI) { hr = CoCreateInstance(CLSID_PropertiesUI, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPropertyUI, &m_pPropUI)); }
return hr; }
STDMETHODIMP CTransferConfirmation::Confirm(CONFIRMOP *pcop, LPCONFIRMATIONRESPONSE pcr, BOOL *pbAll) { HRESULT hr = E_INVALIDARG; if (pcop && pcr) { hr = _Init(); if (SUCCEEDED(hr)) { m_cop = *pcop; hr = _GetDialogSettings(); if (SUCCEEDED(hr)) { HWND hwnd; IUnknown_GetWindow(pcop->punkSite, &hwnd);
int res = (int)DialogBoxParam(_Module.GetResourceInstance(), MAKEINTRESOURCE(m_idDialog), hwnd, s_ConfirmDialogProc, (LPARAM)this); if (pbAll) *pbAll = m_fApplyToAll; *pcr = m_crResult; }
_FreeDialogSettings(); } } return hr; }
INT_PTR CALLBACK CTransferConfirmation::s_ConfirmDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { CTransferConfirmation *pThis; if (WM_INITDIALOG == uMsg) { SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); pThis = (CTransferConfirmation *)lParam; } else { pThis = (CTransferConfirmation *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); }
if (!pThis) return 0;
return pThis->ConfirmDialogProc(hwndDlg, uMsg, wParam, lParam); }
BOOL CTransferConfirmation::ConfirmDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: return OnInitDialog(hwndDlg, wParam, lParam);
case WM_COMMAND: return OnCommand(hwndDlg, LOWORD(wParam), (HWND)lParam);
// TODO: In WM_DESTROY I need to free the icon in IDD_ICON
}
return 0; }
BOOL CTransferConfirmation::OnInitDialog(HWND hwndDlg, WPARAM wParam, LPARAM lParam) { TCHAR szBuf[1024]; _CalculateMetrics(hwndDlg);
int cxShift = 0; int cyShift = 0; int i;
GetWindowRect(hwndDlg, &m_rcDlg);
// We have one or more items that are part of this confirmation, we must get more
// information about these items. For example, we want to use full path names instead
// of just the file name, we need to know file size, modifided data, and if possible
// the two "most important" attributes of the file.
// set the title
if (m_cop.pcc) { SetWindowTextW(hwndDlg, m_cop.pcc->pwszTitle); } else if (m_pszTitle) { SetWindowText(hwndDlg, m_pszTitle); }
// set the icon
HICON hicon = NULL; if (m_cop.pcc) { hicon = m_cop.pcc->hicon; } else { hicon = m_hIcon; } if (hicon) { SendDlgItemMessage(hwndDlg, IDD_ICON, STM_SETICON, (LPARAM)hicon, 0); } else { HWND hwnd = GetDlgItem(hwndDlg, IDD_ICON); RECT rc; GetClientRect(hwnd, &rc); ShowWindow(hwnd, SW_HIDE); cxShift -= rc.right + m_cxControlPadding; }
// Set the description text. We need to remember the size and position of this window
// so that we can position other controls under this text
HWND hwndDesc = GetDlgItem(hwndDlg, ID_CONDITION_TEXT); RECT rcDesc; GetClientRect(hwndDesc, &rcDesc);
USES_CONVERSION; szBuf[0] = NULL; if (m_cop.pcc) { StrCpyN(szBuf, m_cop.pcc->pwszDescription, ARRAYSIZE(szBuf)); } else if (m_pszDescription) { DWORD_PTR rg[2]; rg[0] = (DWORD_PTR)m_rgItemInfo[0].pwszDisplayName; rg[1] = (DWORD_PTR)m_rgItemInfo[1].pwszDisplayName; FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY, m_pszDescription, 0, 0, szBuf, ARRAYSIZE(szBuf), (va_list*)rg); }
if (szBuf[0]) { int cyWndHeight = _WindowHeightFromString(hwndDesc, RECT_WIDTH(rcDesc), szBuf);
cyShift += (cyWndHeight - rcDesc.bottom); SetWindowPos(hwndDesc, NULL, 0,0, RECT_WIDTH(rcDesc), cyWndHeight, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); SetWindowText(hwndDesc, szBuf); }
// We need to convert from the client coordinates of hwndDesc to the client coordinates of hwndDlg.
// We do this converstion after adjusting the size of hwndDesc to fit the description text.
MapWindowPoints(hwndDesc, hwndDlg, (LPPOINT)&rcDesc, 2);
// Create the folder item(s). The first one starts at cyShift+2*cyControlPadding
// The begining insertion point is one spaceing below the description text
rcDesc.bottom += cyShift + m_cyControlPadding; AssertMsg(m_cItems<=2, TEXT("Illegal m_cItems value (%d) should never be larger than 2."), m_cItems); for (i=0; i<m_cItems; i++) { int cyHeightOtItem = _DisplayItem(i, hwndDlg, rcDesc.left, rcDesc.bottom); cyShift += cyHeightOtItem; rcDesc.bottom += cyHeightOtItem; TraceMsg(TF_DLGDISPLAY, "_DisplayItem returned a height of: %d", cyHeightOtItem); }
if (m_cop.pcc && m_cop.pcc->pwszAdvancedDetails) { // TODO: if there is "advanced text" create a read-only edit control and put the text in it.
}
if (m_fSingle) { HWND hwnd = GetDlgItem(hwndDlg, IDD_REPEAT); if (hwnd) { RECT rc; GetClientRect(hwnd, &rc); ShowWindow(hwnd, SW_HIDE); m_rcDlg.bottom -= rc.bottom + m_cyControlPadding; } }
if (cxShift) { // shift all child windows by cxShift pixels
EnumChildWindows(hwndDlg, ShiftLeftProc, cxShift); // don't ask! SetWindowPos in ShiftLeftProc, for some reason, does not do anything on mirrored builds
if (!(GetWindowLong(hwndDlg, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)) m_rcDlg.right += cxShift; }
if (cyShift) { ShiftDialogItem(hwndDlg, IDCANCEL, 0, cyShift); ShiftDialogItem(hwndDlg, IDYES, 0, cyShift); ShiftDialogItem(hwndDlg, IDNO, 0, cyShift); ShiftDialogItem(hwndDlg, IDRETRY, 0, cyShift); ShiftDialogItem(hwndDlg, IDOK, 0, cyShift);
if (!m_fSingle) ShiftDialogItem(hwndDlg, IDD_REPEAT, 0, cyShift);
m_rcDlg.bottom += cyShift; }
// now adjust the dialog size to account for all the things we've added and position it properly
int x = 0; int y = 0; UINT uFlags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE; HMONITOR hMonitor = MonitorFromWindow(GetParent(hwndDlg), MONITOR_DEFAULTTONEAREST); if (hMonitor) { MONITORINFO mi; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hMonitor, &mi)) { RECT rcMonitor = mi.rcMonitor; x = max((RECT_WIDTH(rcMonitor)-RECT_WIDTH(m_rcDlg))/2, 0); y = max((RECT_HEIGHT(rcMonitor)-RECT_HEIGHT(m_rcDlg))/2, 0); uFlags &= ~SWP_NOMOVE; } }
TraceMsg(TF_DLGDISPLAY, "Setting dialog size to %dx%d", RECT_WIDTH(m_rcDlg), RECT_HEIGHT(m_rcDlg)); SetWindowPos(hwndDlg, NULL, x, y, RECT_WIDTH(m_rcDlg), RECT_HEIGHT(m_rcDlg), uFlags);
return 1; }
BOOL CTransferConfirmation::OnCommand(HWND hwndDlg, int wID, HWND hwndCtl) { BOOL fFinish = TRUE;
switch (wID) { case IDD_REPEAT: fFinish = FALSE; break;
case IDRETRY: // "Retry"
m_crResult = CONFRES_RETRY; break;
case IDOK: case IDYES: // "Yes"
m_crResult = CONFRES_CONTINUE; break;
case IDNO: // "Skip"
m_crResult = CONFRES_SKIP; break;
case IDCANCEL: // "Cancel"
m_crResult = CONFRES_CANCEL; break;
default: // if we get to here, then the command ID was not one of our buttons, and since the
// only command IDs we have our the buttons this should not happen.
AssertMsg(0, TEXT("Invalid command recieved in CTransferConfirmation::OnCommand.")); fFinish = FALSE; break; }
if (fFinish) { // ignore apply to all for retry case or we can have infinite loop
m_fApplyToAll = (m_crResult != CONFRES_RETRY && BST_CHECKED == SendDlgItemMessage(hwndDlg, IDD_REPEAT, BM_GETCHECK, 0, 0)); EndDialog(hwndDlg, wID); return 1; }
return 0; }
HRESULT CTransferConfirmation::_AddItem(IShellItem *psi, int idIntro) { if (idIntro) { TCHAR szBuf[1024]; if (LoadString(_Module.GetResourceInstance(), idIntro, szBuf, ARRAYSIZE(szBuf))) { m_rgItemInfo[m_cItems].pwszIntro = StrDup(szBuf); } }
HRESULT hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, &m_rgItemInfo[m_cItems].pwszDisplayName); if (SUCCEEDED(hr)) { IQueryInfo *pqi; hr = psi->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IQueryInfo, &pqi)); if (SUCCEEDED(hr)) { hr = pqi->GetInfoTip(0, &m_rgItemInfo[m_cItems].pwszAttribs); pqi->Release(); }
IExtractImage *pImg; hr = psi->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IExtractImage, &pImg)); if (SUCCEEDED(hr)) { WCHAR szImage[MAX_PATH]; SIZE sz = {120, 120}; DWORD dwFlags = 0; hr = pImg->GetLocation(szImage, ARRAYSIZE(szImage), NULL, &sz, 24, &dwFlags); if (SUCCEEDED(hr)) { hr = pImg->Extract(&m_rgItemInfo[m_cItems].hBitmap); } pImg->Release(); }
if (FAILED(hr)) { IExtractIcon *pIcon; hr = psi->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IExtractIcon, &pIcon)); if (SUCCEEDED(hr)) { TCHAR szIconName[MAX_PATH]; int iIconIndex = 0; UINT dwFlags = 0; hr = pIcon->GetIconLocation(0, szIconName, ARRAYSIZE(szIconName), &iIconIndex, &dwFlags); if (SUCCEEDED(hr)) { hr = pIcon->Extract(szIconName, iIconIndex, &m_rgItemInfo[m_cItems].hIcon, NULL, GetSystemMetrics(SM_CXICON)); } pIcon->Release(); } }
if (FAILED(hr)) { IQueryAssociations *pAssoc; hr = CoCreateInstance(CLSID_QueryAssociations, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IQueryAssociations, &pAssoc)); if (SUCCEEDED(hr)) { WCHAR wszAssocInit[MAX_PATH]; DWORD dwFlags = 0; SFGAOF flags = SFGAO_STREAM; if (SUCCEEDED(psi->GetAttributes(flags, &flags)) && (flags & SFGAO_STREAM)) { dwFlags = ASSOCF_INIT_DEFAULTTOSTAR; StrCpyNW(wszAssocInit, PathFindExtensionW(m_rgItemInfo[m_cItems].pwszDisplayName), ARRAYSIZE(wszAssocInit)); } else { dwFlags = ASSOCF_INIT_DEFAULTTOFOLDER; StrCpyNW(wszAssocInit, L"Directory", ARRAYSIZE(wszAssocInit)); // NB: "Directory is a cannonical name and should not be localized
}
hr = pAssoc->Init(dwFlags, wszAssocInit, NULL,NULL); if (SUCCEEDED(hr)) { WCHAR wszIconPath[MAX_PATH]; DWORD dwSize = ARRAYSIZE(wszIconPath); hr = pAssoc->GetString(0, ASSOCSTR_DEFAULTICON, NULL, wszIconPath, &dwSize); if (SUCCEEDED(hr)) { int iIndex = 0; LPWSTR pszArg = StrChrW(wszIconPath, L','); if (pszArg) { *pszArg = NULL; pszArg++; iIndex = StrToIntW(pszArg); }
ExtractIconEx(wszIconPath, iIndex, &m_rgItemInfo[m_cItems].hIcon, NULL, 1); if (!m_rgItemInfo[m_cItems].hIcon) { TraceMsg(TF_WARNING, "LoadImage(%S) failed", wszIconPath); } } else { TraceMsg(TF_WARNING, "pAssoc->GetString() failed"); } } else { TraceMsg(TF_WARNING, "pAssoc->Init(%S) failed", wszAssocInit); }
pAssoc->Release(); } }
// if we fail to extract an image then we show the remaining info anyway:
if (FAILED(hr)) hr = S_FALSE; }
if (SUCCEEDED(hr)) { m_cItems++; }
return hr; }
BOOL CTransferConfirmation::_CalculateMetrics(HWND hwndDlg) { // We space the controls 6 dialog units apart, convert that to pixels
// REVIEW: Is it valid to hardcode this, or does the height in dialog units need to be localized?
RECT rc = {CX_DIALOG_PADDING, CY_DIALOG_PADDING, 0, CY_STATIC_TEXT_HEIGHT}; BOOL bRes = MapDialogRect(hwndDlg, &rc); m_cxControlPadding = rc.left; m_cyControlPadding = rc.top; m_cyText = rc.bottom;
m_hfont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0,0);
return bRes; }
DWORD CTransferConfirmation::_DisplayItem(int iItem, HWND hwndDlg, int x, int y) { // We are told what X coordinate to place our left edge, we calculate from that how much room
// we have available for our controls.
int cxAvailable = RECT_WIDTH(m_rcDlg) - x - 2 * m_cxControlPadding; int yOrig = y; USES_CONVERSION; HWND hwnd;
TraceMsg(TF_DLGDISPLAY, "_DisplayItem %d at location (%d,%d) in dialog %08x", iItem, x, y, hwndDlg);
if (m_rgItemInfo[iItem].pwszIntro) { TraceMsg(TF_DLGDISPLAY, "CreateWindowEx(0, TEXT(\"STATIC\"), %s, WS_CHILD|WS_VISIBLE, %d,%d, %d,%d, %08x, NULL, %08x, 0);", m_rgItemInfo[iItem].pwszIntro, x,y,cxAvailable,m_cyText, hwndDlg, _Module.GetModuleInstance()); hwnd = CreateWindowEx(0, TEXT("STATIC"), m_rgItemInfo[iItem].pwszIntro, WS_CHILD|WS_VISIBLE, x,y, cxAvailable,m_cyText, hwndDlg, NULL, _Module.GetModuleInstance(), 0); if (hwnd) { // we successfully added the title string for the item
SendMessage(hwnd, WM_SETFONT, (WPARAM)m_hfont, 0); x += m_cxControlPadding; y += m_cyText+m_cyControlPadding; } }
RECT rcImg = {0}; if (m_rgItemInfo[iItem].hBitmap) { TraceMsg(TF_DLGDISPLAY, "CreateWindowEx(0, TEXT(\"STATIC\"), %s, WS_CHILD|WS_VISIBLE, %d,%d, %d,%d, %08x, NULL, %08x, 0);", "_Icon Window_", x,y,cxAvailable,m_cyText, hwndDlg, _Module.GetModuleInstance()); hwnd = CreateWindowEx(0, TEXT("STATIC"), NULL, WS_CHILD|WS_VISIBLE|SS_BITMAP, x,y, 120,120, hwndDlg, NULL, _Module.GetModuleInstance(), 0);
if (hwnd) { SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)m_rgItemInfo[iItem].hBitmap); GetClientRect(hwnd, &rcImg); x += rcImg.right + m_cxControlPadding; cxAvailable -= rcImg.right + m_cxControlPadding; rcImg.bottom += y; } } else if (m_rgItemInfo[iItem].hIcon) { TraceMsg(TF_DLGDISPLAY, "CreateWindowEx(0, TEXT(\"STATIC\"), %s, WS_CHILD|WS_VISIBLE, %d,%d, %d,%d, %08x, NULL, %08x, 0);", "_Icon Window_", x,y,cxAvailable,m_cyText, hwndDlg, _Module.GetModuleInstance()); hwnd = CreateWindowEx(0, TEXT("STATIC"), NULL, WS_CHILD|WS_VISIBLE|SS_ICON, x,y, GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON), hwndDlg, NULL, _Module.GetModuleInstance(), 0);
if (hwnd) { SendMessage(hwnd, STM_SETICON, (WPARAM)m_rgItemInfo[iItem].hIcon, NULL); GetClientRect(hwnd, &rcImg); x += rcImg.right + m_cxControlPadding; cxAvailable -= rcImg.right + m_cxControlPadding; rcImg.bottom += y; } } else { TraceMsg(TF_DLGDISPLAY, "Not using an image for item %d.", iItem); }
TraceMsg(TF_DLGDISPLAY, "CreateWindowEx(0, TEXT(\"STATIC\"), %s, WS_CHILD|WS_VISIBLE, %d,%d, %d,%d, %08x, NULL, %08x, 0);", m_rgItemInfo[iItem].pwszDisplayName, x,y,cxAvailable,m_cyText, hwndDlg, _Module.GetModuleInstance());
int cyWnd = _WindowHeightFromString(hwndDlg, cxAvailable, m_rgItemInfo[iItem].pwszDisplayName); hwnd = CreateWindowEx(0, TEXT("STATIC"), m_rgItemInfo[iItem].pwszDisplayName, WS_CHILD|WS_VISIBLE, x,y, cxAvailable,cyWnd, hwndDlg, NULL, _Module.GetModuleInstance(), 0); if (hwnd) { SendMessage(hwnd, WM_SETFONT, (WPARAM)m_hfont, 0); y += cyWnd; } else { TraceMsg(TF_DLGDISPLAY, "CreateWindowEx for display name failed."); }
TraceMsg(TF_DLGDISPLAY, "CreateWindowEx(0, TEXT(\"STATIC\"), %s, WS_CHILD|WS_VISIBLE, %d,%d, %d,%d, %08x, NULL, %08x, 0);", m_rgItemInfo[iItem].pwszAttribs, x,y,cxAvailable,m_cyText, hwndDlg, _Module.GetModuleInstance());
cyWnd = _WindowHeightFromString(hwndDlg, cxAvailable, m_rgItemInfo[iItem].pwszAttribs); hwnd = CreateWindowEx(0, TEXT("STATIC"), m_rgItemInfo[iItem].pwszAttribs, WS_CHILD|WS_VISIBLE|SS_LEFT, x,y, cxAvailable,cyWnd, hwndDlg, NULL, _Module.GetModuleInstance(), 0); if (hwnd) { SendMessage(hwnd, WM_SETFONT, (WPARAM)m_hfont, 0); y += cyWnd; } else { TraceMsg(TF_DLGDISPLAY, "CreateWindowEx for attribs failed."); }
if (rcImg.bottom > y) y = rcImg.bottom;
return (y-yOrig) + m_cyControlPadding; }
int CTransferConfirmation::_WindowHeightFromString(HWND hwnd, int cx, LPTSTR psz) { RECT rc = {0,0, cx,0 }; HDC hdc = GetDC(hwnd); HFONT hfontOld = NULL; DWORD dtFlags = DT_CALCRECT|DT_WORDBREAK; if (m_hfont) { // We need to select the font that the dialog will use so that we calculate the correct size
hfontOld = (HFONT)SelectObject(hdc, m_hfont); } else { // a NULL m_hfont means we are using the system font, using DT_INTERNAL should do the
// calculation based on the system font.
dtFlags |= DT_INTERNAL; } DrawText(hdc, psz, -1, &rc, dtFlags); if (hfontOld) SelectObject(hdc, hfontOld); ReleaseDC(hwnd, hdc);
return rc.bottom; }
void ShiftWindow(HWND hwnd, int cx, int cy) { RECT rc;
// rect in screen coordinates
GetWindowRect(hwnd, &rc); // shift it over
if (GetWindowLong(GetParent(hwnd), GWL_EXSTYLE) & WS_EX_LAYOUTRTL) rc.left -= cx; else rc.left += cx;
rc.top += cy; // convert to parent windows coords
MapWindowPoints(NULL, GetParent(hwnd), (LPPOINT)&rc, 2); // and move the window
SetWindowPos(hwnd, NULL, rc.left, rc.top, 0,0, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE); }
void ShiftDialogItem(HWND hDlg, int id, int cx, int cy) { HWND hwnd;
hwnd = GetDlgItem(hDlg, id); if (NULL != hwnd) { ShiftWindow(hwnd, cx, cy); } }
BOOL CALLBACK ShiftLeftProc(HWND hwnd, LPARAM lParam) { ShiftWindow(hwnd, (int)(INT_PTR)lParam, 0); return TRUE; }
|