//+------------------------------------------------------------------------- // // 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 #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 *pObj = NULL; // created with ref count of 0 if (SUCCEEDED(CComObject::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(); } int _TitleFromFileOperation(DWORD dwOp) { switch (dwOp) { case STGOP_RENAME: return IDS_CONFIRM_FILE_RENAME; case STGOP_REMOVE: return IDS_CONFIRM_FILE_DELETE; case STGOP_MOVE: return IDS_CONFIRM_FILE_MOVE; default: AssertMsg(0, TEXT("Inappropriate Operation code in _TitleFromFileOperation")); } return IDS_DEFAULTTITLE; } int _TitleFromFolderOperation(DWORD dwOp) { switch (dwOp) { case STGOP_RENAME: return IDS_CONFIRM_FOLDER_RENAME; case STGOP_REMOVE: return IDS_CONFIRM_FOLDER_DELETE; case STGOP_MOVE: return IDS_CONFIRM_FOLDER_MOVE; default: AssertMsg(0, TEXT("Inappropriate Operation code in _TitleFromFolderOperation")); } return IDS_DEFAULTTITLE; } 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; ipunkSite, &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, W2T(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; ipwszAdvancedDetails) { // 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; StrCpyW(wszAssocInit, PathFindExtensionW(m_rgItemInfo[m_cItems].pwszDisplayName)); } else { dwFlags = ASSOCF_INIT_DEFAULTTOFOLDER; StrCpyW(wszAssocInit, L"Directory"); // 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(W2T(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, W2T(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, W2T(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; }