//+-------------------------------------------------------------------------
//
//  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();
}

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; 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, 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; 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;
                    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;
}