#include "precomp.h"
#pragma hdrstop
#include <windows.h>
#include <wia.h>
#include <wiadef.h>
#include <commctrl.h>
#include <comctrlp.h>
#include <propidl.h>
#include "simstr.h"
#include "camdlg.h"
#include "camdlg.rh"
#include "simcrack.h"
#include "pviewids.h"
#include "dlgunits.h"
#include "miscutil.h"
#include "waitcurs.h"
#include "movewnd.h"
#include "simrect.h"
#include "simbstr.h"
#include "uiexthlp.h"
#include "gwiaevnt.h"
#include "wiacsh.h"
#include "wiadevdp.h"

//
// Thread queue messages
//
#define TQ_DESTROY      (WM_USER+1)
#define TQ_GETTHUMBNAIL (WM_USER+2)
#define TQ_GETPREVIEW   (WM_USER+3)
#define TQ_DELETEITEM   (WM_USER+4)

//
// Control ids
//
#define IDC_TOOLBAR         1112
#define IDC_SIZEBOX         1113

//
// The UI thread will notify us that it took ownership of the data.
// otherwise, it will be deleted in the worker thread
//
#define HANDLED_THREAD_MESSAGE  1000

//
// Help IDs
//
static const DWORD g_HelpIDs[] =
{
    IDC_CAMDLG_BIG_TITLE,     -1,
    IDC_CAMDLG_SUBTITLE,      -1,
    IDC_TOOLBAR_FRAME,        IDH_WIA_BUTTONS,
    IDC_TOOLBAR,              IDH_WIA_BUTTONS,
    IDOK,                     IDH_WIA_GET_PICS,
    IDC_THUMBNAILLIST,        IDH_WIA_PIC_LIST,
    IDC_YOU_CAN_ALSO,         IDH_WIA_VIEW_PIC_INFO,
    IDC_CAMDLG_PROPERTIES,    IDH_WIA_VIEW_PIC_INFO,
    IDC_PREVIEW,              IDH_WIA_PREVIEW_DETAIL,
    IDC_INNER_PREVIEW_WINDOW, IDH_WIA_PREVIEW_DETAIL,
    IDCANCEL,                 IDH_CANCEL,
    0, 0
};

//
// Update timer
//
#define IDT_UPDATEPREVIEW     1000
#define UPDATE_PREVIEW_DELAY   500

//
// Number of milliseconds between percent display updates
//
#define PERCENT_UPDATE_GRANULARITY 1000

//
// Private messages
//
#define PWM_POSTINIT         (WM_USER+1)
#define PWM_CHANGETOPARENT   (WM_USER+2)
#define PWM_THUMBNAILSTATUS  (WM_USER+3)
#define PWM_PREVIEWSTATUS    (WM_USER+4)
#define PWM_PREVIEWPERCENT   (WM_USER+5)
#define PWM_ITEMDELETED      (WM_USER+6)
#define PWM_WIAEVENT         (WM_USER+7)


//
// Thumbnail whitespace: the space in between images and their selection rectangles
// These values were discovered by trail and error.  For instance, if you reduce
// c_nAdditionalMarginY to 20, you get really bizarre spacing problems in the list view
// in vertical mode.  These values could become invalid in future versions of the listview.
//
static const int c_nAdditionalMarginX = 10;
static const int c_nAdditionalMarginY = 6;

static int c_nMinThumbnailWidth  = 90;
static int c_nMinThumbnailHeight = 90;

static int c_nMaxThumbnailWidth  = 120;
static int c_nMaxThumbnailHeight = 120;

//
// Button bar button bitmap sizes
//
static const int c_nButtonBitmapSizeX = 16;
static const int c_nButtonBitmapSizeY = 16;

//
// Button bar button sizes
//
static const int c_nButtonSizeX = 300;  // Ridiculously large size to compensate for BTNS_AUTOSIZE bug.
static const int c_nButtonSizeY = 16;

//
// Default preview mode list width
//
static const int c_nDefaultListViewWidth = 120;

//
// These defines let me compile with pre-nt5 headers
//
#ifndef BTNS_SEP
#define BTNS_SEP TBSTYLE_SEP
#endif

#ifndef BTNS_BUTTON
#define BTNS_BUTTON TBSTYLE_BUTTON
#endif

#ifndef ListView_SetExtendedListViewStyleEx
#define ListView_SetExtendedListViewStyleEx( h, m, s )
#endif


class CGlobalInterfaceTableThreadMessage : public CNotifyThreadMessage
{
private:
    DWORD m_dwGlobalInterfaceTableCookie;

private:
    //
    // No implementation
    //
    CGlobalInterfaceTableThreadMessage(void);
    CGlobalInterfaceTableThreadMessage &operator=( const CGlobalInterfaceTableThreadMessage & );
    CGlobalInterfaceTableThreadMessage( const CGlobalInterfaceTableThreadMessage & );

public:
    CGlobalInterfaceTableThreadMessage( int nMessage, HWND hWndNotify, DWORD dwGlobalInterfaceTableCookie )
      : CNotifyThreadMessage( nMessage, hWndNotify ),
        m_dwGlobalInterfaceTableCookie(dwGlobalInterfaceTableCookie)
    {
    }
    DWORD GlobalInterfaceTableCookie(void) const
    {
        return(m_dwGlobalInterfaceTableCookie);
    }
};


class CThumbnailThreadMessage : public CGlobalInterfaceTableThreadMessage
{
private:
    SIZE  m_sizeThumb;

private:
    //
    // No implementation
    //
    CThumbnailThreadMessage(void);
    CThumbnailThreadMessage &operator=( const CThumbnailThreadMessage & );
    CThumbnailThreadMessage( const CThumbnailThreadMessage & );

public:
    CThumbnailThreadMessage( HWND hWndNotify, DWORD dwGlobalInterfaceTableCookie, const SIZE &sizeThumb )
      : CGlobalInterfaceTableThreadMessage( TQ_GETTHUMBNAIL, hWndNotify, dwGlobalInterfaceTableCookie ),
        m_sizeThumb(sizeThumb)
    {
    }
    const SIZE &ThumbSize(void) const
    {
        return(m_sizeThumb);
    }
};

class CDeleteThreadMessage : public CGlobalInterfaceTableThreadMessage
{
private:
    //
    // No implementation
    //
    CDeleteThreadMessage(void);
    CDeleteThreadMessage &operator=( const CDeleteThreadMessage & );
    CDeleteThreadMessage( const CDeleteThreadMessage & );

public:
    CDeleteThreadMessage( HWND hWndNotify, DWORD dwGlobalInterfaceTableCookie )
      : CGlobalInterfaceTableThreadMessage( TQ_DELETEITEM, hWndNotify, dwGlobalInterfaceTableCookie )
    {
    }
};


class CPreviewThreadMessage : public CGlobalInterfaceTableThreadMessage
{
private:
    CSimpleEvent m_CancelEvent;

private:
    //
    // No implementation
    //
    CPreviewThreadMessage(void);
    CPreviewThreadMessage &operator=( const CPreviewThreadMessage & );
    CPreviewThreadMessage( const CPreviewThreadMessage & );

public:
    CPreviewThreadMessage( HWND hWndNotify, DWORD dwGlobalInterfaceTableCookie, HANDLE hCancelEvent )
      : CGlobalInterfaceTableThreadMessage( TQ_GETPREVIEW, hWndNotify, dwGlobalInterfaceTableCookie ),
        m_CancelEvent(hCancelEvent)
    {
    }
    CSimpleEvent &CancelEvent(void)
    {
        return(m_CancelEvent);
    }
};


BOOL WINAPI CCameraAcquireDialog::OnThreadDestroy( CThreadMessage * )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnThreadDestroy"));
    return(FALSE);
}

BOOL WINAPI CCameraAcquireDialog::OnThreadDeleteItem( CThreadMessage *pMsg )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnThreadDeleteItem"));
    CDeleteThreadMessage *pDeleteMsg = (CDeleteThreadMessage *)(pMsg);
    if (pDeleteMsg)
    {
        CComPtr<IGlobalInterfaceTable> pGlobalInterfaceTable;
        HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pGlobalInterfaceTable);
        if (SUCCEEDED(hr))
        {
            CComPtr<IWiaItem> pIWiaItem;
            hr = pGlobalInterfaceTable->GetInterfaceFromGlobal( pDeleteMsg->GlobalInterfaceTableCookie(), IID_IWiaItem, (void**)&pIWiaItem );
            if (SUCCEEDED(hr))
            {
                hr = WiaUiUtil::DeleteItemAndChildren(pIWiaItem);
                WIA_TRACE((TEXT("********************* pIWiaItem->DeleteItem returned %08X"), hr ));
                PostMessage( pDeleteMsg->NotifyWindow(), PWM_ITEMDELETED, pDeleteMsg->GlobalInterfaceTableCookie(), SUCCEEDED(hr) );
            }
        }
    }
    return(TRUE);
}

class CWiaDataCallback : public IWiaDataCallback
{
private:
    ULONG                   m_cRef;
    HWND                    m_hWndNotify;
    DWORD                   m_dwGlobalInterfaceTableCookie;
    CSimpleEvent            m_CancelEvent;
    DWORD                   m_dwPreviousTickCount;
    int                     m_nPercentGranularity;
public:
    CWiaDataCallback();
    ~CWiaDataCallback();

    HRESULT _stdcall QueryInterface(const IID&,void**);
    ULONG   _stdcall AddRef();
    ULONG   _stdcall Release();

    HRESULT _stdcall Initialize( HWND hWndNotify, DWORD dwGlobalInterfaceTableCookie, HANDLE hCancelEvent );

    HRESULT _stdcall BandedDataCallback(
                                       LONG lReason,
                                       LONG lStatus,
                                       LONG lPercentComplete,
                                       LONG lOffset,
                                       LONG lLength,
                                       LONG lReserved,
                                       LONG lResLength,
                                       BYTE *pbBuffer );
};




HRESULT _stdcall CWiaDataCallback::QueryInterface(const IID& iid, void** ppv)
{
    *ppv = NULL;

    if (iid == IID_IUnknown || iid == IID_IWiaDataCallback)
    {
        *ppv = (IWiaDataCallback*)this;
    }
    else
    {
        return(E_NOINTERFACE);
    }

    AddRef();
    return(S_OK);
}

ULONG _stdcall CWiaDataCallback::AddRef()
{
    InterlockedIncrement((long*) &m_cRef);
    return(m_cRef);
}

ULONG _stdcall CWiaDataCallback::Release()
{
    ULONG ulRefCount = m_cRef - 1;
    if (InterlockedDecrement((long*) &m_cRef) == 0)
    {
        delete this;
        return(0);
    }
    return(ulRefCount);
}

CWiaDataCallback::CWiaDataCallback()
  : m_cRef(0),
    m_hWndNotify(NULL)
{
}


CWiaDataCallback::~CWiaDataCallback()
{
}


HRESULT _stdcall CWiaDataCallback::Initialize( HWND hWndNotify, DWORD dwGlobalInterfaceTableCookie, HANDLE hCancelEvent )
{
    m_hWndNotify = hWndNotify;
    m_dwGlobalInterfaceTableCookie = dwGlobalInterfaceTableCookie;
    m_CancelEvent = hCancelEvent;
    m_dwPreviousTickCount = 0xFFFFFF;
    m_nPercentGranularity = 3;
    return(S_OK);
}



HRESULT _stdcall CWiaDataCallback::BandedDataCallback(
                                                     LONG lMessage,
                                                     LONG lStatus,
                                                     LONG lPercentComplete,
                                                     LONG lOffset,
                                                     LONG lLength,
                                                     LONG lReserved,
                                                     LONG lResLength,
                                                     BYTE * /* pbBuffer */
                                                     )
{
    WIA_TRACE((TEXT("BandedDataCallback: lMessage: %d, lStatus: %d, lPercentComplete: %d, lOffset: %d, lLength: %d, lReserved: %d, lResLength: %d"), lMessage, lStatus, lPercentComplete, lOffset, lLength, lReserved, lResLength ));
    if (m_CancelEvent.Signalled())
        return(S_FALSE);
    switch (lMessage)
    {
    case IT_MSG_DATA_HEADER:
        {
        } // IT_MSG_DATA_HEADER

    case IT_MSG_DATA:
        {
        } // IT_STATUS_TRANSFER_TO_CLIENT
        break;

    case IT_MSG_STATUS:
        {
            // Don't send status messages too frequently.  Limit to one per PERCENT_UPDATE_GRANULARITY ms
            DWORD dwTickCount = GetTickCount();
            if ((dwTickCount - m_dwPreviousTickCount >= PERCENT_UPDATE_GRANULARITY) || (m_dwPreviousTickCount > dwTickCount))
            {
                m_dwPreviousTickCount = dwTickCount;
                PostMessage( m_hWndNotify, PWM_PREVIEWPERCENT, (WPARAM)m_dwGlobalInterfaceTableCookie, (LPARAM)MAKELPARAM((WORD)lPercentComplete,(WORD)lStatus));
            }
        } // IT_MSG_STATUS
        break;

    case IT_MSG_TERMINATION:
        {
        } // IT_MSG_TERMINATION
        break;
    }
    return(S_OK);
}


BOOL WINAPI CCameraAcquireDialog::OnGetPreview( CThreadMessage *pMsg )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnGetThumbnail"));
    CSimpleStringWide strwImageName(L"");
    CPreviewThreadMessage *pPreviewMsg = (CPreviewThreadMessage *)(pMsg);
    if (pPreviewMsg)
    {
        CComPtr<IGlobalInterfaceTable> pGlobalInterfaceTable;
        HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable,
                                       NULL,
                                       CLSCTX_INPROC_SERVER,
                                       IID_IGlobalInterfaceTable,
                                       (void **)&pGlobalInterfaceTable);
        if (SUCCEEDED(hr))
        {
            CComPtr<IWiaItem> pIWiaItem;
            hr = pGlobalInterfaceTable->GetInterfaceFromGlobal( pPreviewMsg->GlobalInterfaceTableCookie(), IID_IWiaItem, (void**)&pIWiaItem );
            if (SUCCEEDED(hr))
            {
                CComPtr<IWiaDataTransfer> pIBandedTran;
                WIA_TRACE((TEXT("Preparing to call pIWiaItem->QueryInterface for IID_IWiaDataTransfer")));
                hr = pIWiaItem->QueryInterface(IID_IWiaDataTransfer, (void**)&pIBandedTran);
                if (SUCCEEDED(hr))
                {
                    if (PropStorageHelpers::SetProperty( pIWiaItem, WIA_IPA_FORMAT, WiaImgFmt_BMP, WIA_IPA_FIRST ) &&
                        PropStorageHelpers::SetProperty( pIWiaItem, WIA_IPA_TYMED, TYMED_FILE, WIA_IPA_FIRST ))
                    {
                        CWiaDataCallback* pCDataCB = new CWiaDataCallback();
                        WIA_TRACE((TEXT("Preparing to call pCDataCB->Initialize")));
                        if (pCDataCB)
                        {
                            hr = pCDataCB->Initialize( pPreviewMsg->NotifyWindow(), pPreviewMsg->GlobalInterfaceTableCookie(), pPreviewMsg->CancelEvent().Event() );
                            if (SUCCEEDED(hr))
                            {
                                WIA_TRACE((TEXT("Preparing to call pCDataCB->QueryInterface on IID_IWiaDataCallback")));
                                CComPtr<IWiaDataCallback> pIWiaDataCallback;
                                hr = pCDataCB->QueryInterface(IID_IWiaDataCallback,(void **)&pIWiaDataCallback);
                                if (SUCCEEDED(hr))
                                {
                                    STGMEDIUM StgMedium;
                                    StgMedium.tymed          = TYMED_FILE;
                                    StgMedium.pUnkForRelease = NULL;
                                    StgMedium.hGlobal        = NULL;
                                    StgMedium.lpszFileName   = NULL;

                                    WIA_TRACE((TEXT("Preparing to call pIBandedTran->ibtGetData")));
                                    hr = pIBandedTran->idtGetData( &StgMedium, pIWiaDataCallback );
                                    if (SUCCEEDED(hr) && S_FALSE != hr)
                                    {
                                        strwImageName = StgMedium.lpszFileName;
                                        WIA_TRACE((TEXT("pIBandedTran->ibtGetData returned %s"),StgMedium.lpszFileName));
                                    }
                                    else
                                    {
                                        WIA_PRINTHRESULT((hr,TEXT("CCameraAcquireDialog::OnGetPreview, ibtGetData failed")));
                                    }
                                    //
                                    // Prevent leaks by freeing the filename.  We don't call ReleaseStgMeduim, because
                                    // it deletes the file as well.
                                    //
                                    if (SUCCEEDED(hr) && StgMedium.lpszFileName)
                                    {
                                        CoTaskMemFree(StgMedium.lpszFileName);
                                    }
                                }
                                else
                                {
                                    WIA_PRINTHRESULT((hr,TEXT("CCameraAcquireDialog::OnGetPreview, QI of IID_IWiaDataCallback failed")));
                                }
                            }
                            else
                            {
                                WIA_PRINTHRESULT((hr,TEXT("pCDataCB->Initialize failed")));
                            }
                        }
                        else
                        {
                            WIA_ERROR((TEXT("CCameraAcquireDialog::OnGetPreview, new on CWiaDataCallback failed")));
                        }
                    }
                    else
                    {
                        hr = MAKE_HRESULT(3,FACILITY_WIN32,ERROR_INVALID_DATA);
                        WIA_ERROR((TEXT("SetProperty on TYMED or FORMAT failed")));
                    }
                }
                else
                {
                    WIA_PRINTHRESULT((hr,TEXT("CCameraAcquireDialog::OnGetPreview, QI of IID_IWiaDataTransfer failed")));
                }
                WIA_TRACE((TEXT("End CCameraAcquireDialog::OnGetPreviewBandedTransfer")));
            }
        }
        
        //
        // Allocate the filename string to return to the UI thread
        //
        CSimpleString *pstrDibFilename = new CSimpleString( CSimpleStringConvert::NaturalString(strwImageName) );

        //
        // Send the message to the UI thread
        //
        LRESULT lRes = SendMessage( pPreviewMsg->NotifyWindow(), PWM_PREVIEWSTATUS, pPreviewMsg->GlobalInterfaceTableCookie(), reinterpret_cast<LPARAM>(pstrDibFilename));

        //
        // If it fails for any reason, we will clean up to avoid leaks and orphaned temp files
        //
        if (HANDLED_THREAD_MESSAGE != lRes)
        {
            DeleteFile(CSimpleStringConvert::NaturalString(strwImageName));
            if (pstrDibFilename)
            {
                delete pstrDibFilename;
            }
        }

    }

    return(TRUE);
}

BOOL WINAPI CCameraAcquireDialog::OnGetThumbnail( CThreadMessage *pMsg )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnGetThumbnail"));
    HBITMAP hBmpThumbnail = NULL;
    CThumbnailThreadMessage *pThumbMsg = (CThumbnailThreadMessage *)(pMsg);
    if (pThumbMsg)
    {
        CComPtr<IGlobalInterfaceTable> pGlobalInterfaceTable;
        HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable,
                                       NULL,
                                       CLSCTX_INPROC_SERVER,
                                       IID_IGlobalInterfaceTable,
                                       (void **)&pGlobalInterfaceTable);
        if (SUCCEEDED(hr))
        {
            CComPtr<IWiaItem> pIWiaItem;
            hr = pGlobalInterfaceTable->GetInterfaceFromGlobal( pThumbMsg->GlobalInterfaceTableCookie(), IID_IWiaItem, (void**)&pIWiaItem );
            if (SUCCEEDED(hr))
            {
#if defined(DBG)
                CSimpleStringWide strItemName;
                PropStorageHelpers::GetProperty( pIWiaItem, WIA_IPA_FULL_ITEM_NAME, strItemName );
                WIA_TRACE((TEXT("Getting thumbnail for %ws (0x%d, 0x%p)"), strItemName.String(), pThumbMsg->GlobalInterfaceTableCookie(), pIWiaItem.p ));
#endif
                CComPtr<IWiaPropertyStorage> pIWiaPropertyStorage;
                hr = pIWiaItem->QueryInterface(IID_IWiaPropertyStorage, (void**)&pIWiaPropertyStorage);
                if (SUCCEEDED(hr))
                {
                    PROPVARIANT PropVar[3];
                    PROPSPEC PropSpec[3];

                    PropSpec[0].ulKind = PRSPEC_PROPID;
                    PropSpec[0].propid = WIA_IPC_THUMB_WIDTH;

                    PropSpec[1].ulKind = PRSPEC_PROPID;
                    PropSpec[1].propid = WIA_IPC_THUMB_HEIGHT;

                    PropSpec[2].ulKind = PRSPEC_PROPID;
                    PropSpec[2].propid = WIA_IPC_THUMBNAIL;
                    hr = pIWiaPropertyStorage->ReadMultiple(ARRAYSIZE(PropSpec),PropSpec,PropVar );
                    if (SUCCEEDED(hr))
                    {
                        WIA_TRACE((TEXT("Attempting to get the thumbnail for GIT entry: %08X, %08X, %08X, %08X"),pThumbMsg->GlobalInterfaceTableCookie(),PropVar[0].vt,PropVar[1].vt,PropVar[2].vt));
                        if ((PropVar[0].vt == VT_I4 || PropVar[0].vt == VT_UI4) &&
                            (PropVar[1].vt == VT_I4 || PropVar[1].vt == VT_UI4) &&
                            (PropVar[2].vt == (VT_UI1|VT_VECTOR)))
                        {
                            UINT nBitmapDataSize = WiaUiUtil::Align(PropVar[0].ulVal*3,sizeof(DWORD)) * PropVar[1].ulVal;
                            if (nBitmapDataSize  <= PropVar[2].caub.cElems)
                            {
                                BITMAPINFO bmi = {0};
                                bmi.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
                                bmi.bmiHeader.biWidth           = PropVar[0].ulVal;
                                bmi.bmiHeader.biHeight          = PropVar[1].ulVal;
                                bmi.bmiHeader.biPlanes          = 1;
                                bmi.bmiHeader.biBitCount        = 24;
                                bmi.bmiHeader.biCompression     = BI_RGB;
                                bmi.bmiHeader.biSizeImage       = 0;
                                bmi.bmiHeader.biXPelsPerMeter   = 0;
                                bmi.bmiHeader.biYPelsPerMeter   = 0;
                                bmi.bmiHeader.biClrUsed         = 0;
                                bmi.bmiHeader.biClrImportant    = 0;

                                HDC hDC = GetDC(NULL);
                                if (hDC)
                                {
                                    PBYTE *pBits;
                                    HBITMAP hDibSection = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS, (PVOID*)&pBits, NULL, 0 );
                                    if (hDibSection)
                                    {
                                        WIA_TRACE((TEXT("pBits: 0x%p, PropVar[2].caub.pElems: 0x%p, PropVar[2].caub.cElems: %d"), pBits, PropVar[2].caub.pElems, PropVar[2].caub.cElems));
                                        CopyMemory( pBits, PropVar[2].caub.pElems, nBitmapDataSize );
                                        hr = ScaleImage( hDC, hDibSection, hBmpThumbnail, pThumbMsg->ThumbSize());
                                        if (SUCCEEDED(hr))
                                        {
                                            WIA_TRACE((TEXT("Sending this image (%p) to the notification window: %p"), hBmpThumbnail, pThumbMsg->NotifyWindow() ));
                                        }
                                        else hBmpThumbnail = NULL;
                                        DeleteObject(hDibSection);
                                    }
                                    else
                                    {
                                        WIA_PRINTHRESULT((HRESULT_FROM_WIN32(GetLastError()),TEXT("CreateDIBSection failed")));
                                    }
                                    ReleaseDC(NULL,hDC);
                                }
                                else
                                {
                                    WIA_PRINTHRESULT((HRESULT_FROM_WIN32(GetLastError()),TEXT("GetDC failed")));
                                }
                            }
                            else
                            {
                                WIA_ERROR((TEXT("nBitmapDataSize <= PropVar[2].caub.cElems was FALSE (%d, %d)"), nBitmapDataSize, PropVar[2].caub.cElems ));
                            }
                        }
                        PropVariantClear(&PropVar[0]);
                        PropVariantClear(&PropVar[1]);
                        PropVariantClear(&PropVar[2]);
                    }
                    else
                    {
                        WIA_PRINTHRESULT((hr,TEXT("pIWiaPropertyStorage->ReadMultiple failed")));
                    }
                }
                else
                {
                    WIA_PRINTHRESULT((hr,TEXT("QueryInterface failed on IID_IWiaPropertyStorage")));
                }
            }
            else
            {
                WIA_PRINTHRESULT((hr,TEXT("GetInterfaceFromGlobal failed on %08X"), pThumbMsg->GlobalInterfaceTableCookie() ));
            }
        }
        else
        {
            WIA_PRINTHRESULT((hr,TEXT("CoCreateInstance failed on CLSID_StdGlobalInterfaceTable")));
        }
        
        //
        // Send the message to the UI thread
        //
        LRESULT lRes = SendMessage( pThumbMsg->NotifyWindow(), PWM_THUMBNAILSTATUS, (WPARAM)pThumbMsg->GlobalInterfaceTableCookie(), (LPARAM)hBmpThumbnail );

        //
        // If it fails for any reason, we will clean up to avoid leaks
        //
        if (HANDLED_THREAD_MESSAGE != lRes)
        {
            if (hBmpThumbnail)
            {
                DeleteObject( hBmpThumbnail );
            }
        }
    }
    else
    {
        WIA_ERROR((TEXT("pThumbMsg")));
    }
    return(TRUE);
}


int CCameraAcquireDialog::FindItemInList( CCameraItem *pItem )
{
    WIA_PUSH_FUNCTION((TEXT("CCameraAcquireDialog::FindItemInList( %08X )"), pItem ));
    if (pItem)
    {
        HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
        if (hwndList)
        {
            for (int i=0;i<ListView_GetItemCount(hwndList);i++)
            {
                CCameraItem *pCurrItem = GetListItemNode(i);
                if (pCurrItem)
                {
                    WIA_TRACE((TEXT("Comparing %08X and %08X [%ws] [%ws]"), pCurrItem, pItem, pCurrItem->FullItemName().String(), pItem->FullItemName().String() ));
                    if (*pCurrItem == *pItem)
                    {
                        return i;
                    }
                }
            }
        }
    }
    return(-1);
}

CCameraItem *CCameraAcquireDialog::GetCurrentPreviewItem(void)
{
    CSimpleDynamicArray<int> aSelIndices;
    GetSelectionIndices(aSelIndices);
    if (0 == aSelIndices.Size())
        return(NULL);
    if (1 == aSelIndices.Size())
        return(GetListItemNode(aSelIndices[0]));
    return(NULL);
}

bool CCameraAcquireDialog::SetCurrentPreviewImage( const CSimpleString &strFilename, const CSimpleString &strTitle )
{
    CWaitCursor wc;
    bool bResult = true;
    SendDlgItemMessage( m_hWnd, IDC_PREVIEW, WM_SETTEXT, 0, (LPARAM)strTitle.String() );
    SIZE sizeSavedAspectRatio = m_CurrentAspectRatio;
    // Set up a reasonable default
    m_CurrentAspectRatio.cx = 4;
    m_CurrentAspectRatio.cy = 3;
    if (strFilename.Length())
    {
        HBITMAP hBmp = (HBITMAP)LoadImage( g_hInstance, strFilename.String(), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE );
        if (hBmp)
        {
            BITMAP bm;
            if (GetObject( hBmp, sizeof(BITMAP), &bm ))
            {
                m_CurrentAspectRatio.cx = bm.bmWidth;
                m_CurrentAspectRatio.cy = bm.bmHeight;
            }
        }
        SendDlgItemMessage( m_hWnd, IDC_PREVIEW, PWM_SETBITMAP, MAKEWPARAM(FALSE,FALSE), (LPARAM)hBmp );
        if (memcmp(&sizeSavedAspectRatio,&m_CurrentAspectRatio,sizeof(SIZE)))
            ResizeAll();
        bResult = (hBmp != NULL);
    }
    else
    {
        if (SendDlgItemMessage( m_hWnd, IDC_PREVIEW, PWM_GETBITMAP, 0, 0 ))
            SendDlgItemMessage( m_hWnd, IDC_PREVIEW, PWM_SETBITMAP, MAKEWPARAM(FALSE,FALSE), 0 );
        if (memcmp(&sizeSavedAspectRatio,&m_CurrentAspectRatio,sizeof(SIZE)))
            ResizeAll();
    }
    InvalidateRect( GetDlgItem( m_hWnd, IDC_PREVIEW ), NULL, FALSE );
    UpdateWindow( GetDlgItem( m_hWnd, IDC_PREVIEW ) );
    return(bResult);
}

// wParam = GIT cookie
// lParam = nPercent
LRESULT CCameraAcquireDialog::OnPreviewPercent( WPARAM wParam, LPARAM lParam )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnPreviewPercent"));
    CCameraItem *pCameraItem = m_CameraItemList.Find( (DWORD)wParam );
    if (pCameraItem)
    {
        pCameraItem->CurrentPreviewPercentage((int)LOWORD(lParam));
        CCameraItem *pCurrSel = GetCurrentPreviewItem();
        if (pCameraItem == pCurrSel)
        {
            UpdatePreview();
        }
    }
    return(0);
}

// wParam = GIT cookie
// lParam = HBITMAP
LRESULT CCameraAcquireDialog::OnPreviewStatus( WPARAM wParam, LPARAM lParam )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnPreviewStatus"));
    CSimpleString *pstrFilename = reinterpret_cast<CSimpleString*>(lParam);
    if (pstrFilename)
    {
        CCameraItem *pCameraItem = m_CameraItemList.Find( static_cast<DWORD>(wParam) );
        if (pCameraItem)
        {
            if (!pCameraItem->CancelQueueEvent().Signalled())
            {
                // If we weren't cancelled, set the filename
                pCameraItem->PreviewFileName(*pstrFilename);
            }
            pCameraItem->CloseCancelEvent();

            CCameraItem *pCurrSel = GetCurrentPreviewItem();
            if (pCameraItem == pCurrSel)
            {
                SetCurrentPreviewImage( pCameraItem->PreviewFileName() );
            }
        }
        delete pstrFilename;
    }

    return HANDLED_THREAD_MESSAGE;
}

LRESULT CCameraAcquireDialog::OnThumbnailStatus( WPARAM wParam, LPARAM lParam )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnThumbnailStatus"));
    WIA_TRACE((TEXT("Looking for the item with the ID %08X"),wParam));
    CCameraItem *pCameraItem = m_CameraItemList.Find( (DWORD)wParam );
    if (pCameraItem)
    {
        WIA_TRACE((TEXT("Found a CameraItem * (%08X)"),pCameraItem));
        HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
        if (hwndList)
        {
            WIA_TRACE((TEXT("Got the list control")));
            HIMAGELIST hImageList = ListView_GetImageList( hwndList, LVSIL_NORMAL );
            if (hImageList)
            {
                WIA_TRACE((TEXT("Got the image list")));
                if ((HBITMAP)lParam)
                {
                    WIA_TRACE((TEXT("hBitmap = %p"), lParam));
                    if (ImageList_Replace( hImageList, pCameraItem->ImageListIndex(), reinterpret_cast<HBITMAP>(lParam), NULL ))
                    {
                        WIA_TRACE((TEXT("Replaced the image in the list")));
                        int nItem = FindItemInList(pCameraItem);
                        if (nItem >= 0)
                        {
                            WIA_TRACE((TEXT("Found the item in the list")));
                            //
                            // Set the image to a dummy image
                            //
                            LV_ITEM lvItem;
                            ::ZeroMemory(&lvItem,sizeof(LV_ITEM));
                            lvItem.iItem = nItem;
                            lvItem.mask = LVIF_IMAGE;
                            lvItem.iImage = -1;
                            ListView_SetItem( hwndList, &lvItem );

                            //
                            // Then set the image to the real image
                            //
                            ::ZeroMemory(&lvItem,sizeof(LV_ITEM));
                            lvItem.iItem = nItem;
                            lvItem.mask = LVIF_IMAGE;
                            lvItem.iImage = pCameraItem->ImageListIndex();
                            ListView_SetItem( hwndList, &lvItem );
                        }
                    }
                }
            }
        }
    }

    //
    // Clean up the bitmap, regardless of any other failures, to avoid memory leaks
    //
    HBITMAP hBmpThumb = reinterpret_cast<HBITMAP>(lParam);
    if (hBmpThumb)
    {
        DeleteObject(hBmpThumb);
    }
    return HANDLED_THREAD_MESSAGE;
}

static CThreadMessageMap g_MsgMap[] =
{
    { TQ_DESTROY, CCameraAcquireDialog::OnThreadDestroy },
    { TQ_GETTHUMBNAIL, CCameraAcquireDialog::OnGetThumbnail },
    { TQ_GETPREVIEW, CCameraAcquireDialog::OnGetPreview },
    { TQ_DELETEITEM, CCameraAcquireDialog::OnThreadDeleteItem },
    { 0, NULL}
};

//
// Sole constructor
//
CCameraAcquireDialog::CCameraAcquireDialog( HWND hWnd )
  : m_hWnd(hWnd),
    m_bPreviewActive(false),
    m_pCurrentParentItem(NULL),
    m_bFirstTime(true),
    m_hBackgroundThread(NULL),
    m_hBigFont(NULL),
    m_nDialogMode(0),
    m_hAccelTable(NULL),
    m_nListViewWidth(0),
    m_hIconLarge(NULL),
    m_hIconSmall(NULL),
    m_pThreadMessageQueue(NULL),
    m_bTakePictureIsSupported(false),
    m_ToolbarBitmapInfo( g_hInstance, IDB_TOOLBAR )
{
    m_pThreadMessageQueue = new CThreadMessageQueue;
    if (m_pThreadMessageQueue)
    {
        //
        // Note that CBackgroundThread takes ownership of m_pThreadMessageQueue, and it doesn't have to be deleted in this thread
        //
        m_hBackgroundThread = CBackgroundThread::Create( m_pThreadMessageQueue, g_MsgMap, m_CancelEvent.Event(), g_hInstance );
    }
    m_sizeThumbnails.cx = c_nMaxThumbnailWidth;
    m_sizeThumbnails.cy = c_nMaxThumbnailHeight;
    m_CurrentAspectRatio.cx = 4;
    m_CurrentAspectRatio.cy = 3;
    WIA_ASSERT(m_hBackgroundThread != NULL);
}

HWND CCameraAcquireDialog::CreateCameraDialogToolbar(VOID)
{
    ToolbarHelper::CButtonDescriptor aSingleSelModeButtons[] =
    {
        { 0, IDC_ICONMODE,     TBSTATE_ENABLED|TBSTATE_CHECKED, BTNS_AUTOSIZE|BTNS_BUTTON|BTNS_CHECK, true, NULL, IDS_ICONMODE },
        { 1, IDC_PREVIEWMODE,  TBSTATE_ENABLED,                 BTNS_AUTOSIZE|BTNS_BUTTON|BTNS_CHECK, true, NULL, IDS_PREVIEWMODE },
        { 2, IDC_TAKEPICTURE,  TBSTATE_ENABLED,                 BTNS_AUTOSIZE|BTNS_BUTTON, true, &m_bTakePictureIsSupported, IDS_TAKEPICTURE },
        { 4, IDC_DELETE,       TBSTATE_ENABLED,                 BTNS_AUTOSIZE|BTNS_BUTTON, false, NULL, IDS_DELETE_SINGULAR }
    };
    ToolbarHelper::CButtonDescriptor aMultiSelModeButtons[] =
    {
        { 0, IDC_ICONMODE,     TBSTATE_ENABLED|TBSTATE_CHECKED, BTNS_AUTOSIZE|BTNS_BUTTON|BTNS_CHECK, true, NULL, IDS_ICONMODE },
        { 1, IDC_PREVIEWMODE,  TBSTATE_ENABLED,                 BTNS_AUTOSIZE|BTNS_BUTTON|BTNS_CHECK, true, NULL, IDS_PREVIEWMODE },
        { 2, IDC_TAKEPICTURE,  TBSTATE_ENABLED,                 BTNS_AUTOSIZE|BTNS_BUTTON, true, &m_bTakePictureIsSupported, IDS_TAKEPICTURE },
        { 3, IDC_SELECTALL,    TBSTATE_ENABLED,                 BTNS_AUTOSIZE|BTNS_BUTTON, true, NULL, IDS_SELECTALL },
        { 4, IDC_DELETE,       TBSTATE_ENABLED,                 BTNS_AUTOSIZE|BTNS_BUTTON, false, NULL, IDS_DELETE }
    };
    
    ToolbarHelper::CButtonDescriptor *pButtonDescriptors = aSingleSelModeButtons;
    int nButtonDescriptorCount = ARRAYSIZE(aSingleSelModeButtons);
    if (m_nDialogMode & MULTISEL_MODE)
    {
        pButtonDescriptors = aMultiSelModeButtons;
        nButtonDescriptorCount = ARRAYSIZE(aMultiSelModeButtons);
    }
    
    return ToolbarHelper::CreateToolbar( m_hWnd, GetDlgItem( m_hWnd, IDC_CAMDLG_SUBTITLE ), GetDlgItem( m_hWnd, IDC_TOOLBAR_FRAME ), ToolbarHelper::AlignLeft|ToolbarHelper::AlignTop, IDC_TOOLBAR, m_ToolbarBitmapInfo, pButtonDescriptors, nButtonDescriptorCount );
}


HRESULT CCameraAcquireDialog::EnumerateItems( CCameraItem *pCurrentParent, IEnumWiaItem *pIEnumWiaItem )
{
    WIA_PUSHFUNCTION(TEXT("CCameraItemList::EnumerateItems"));
    HRESULT hr = E_FAIL;
    if (pIEnumWiaItem != NULL)
    {
        hr = pIEnumWiaItem->Reset();
        while (hr == S_OK)
        {
            CComPtr<IWiaItem> pIWiaItem;
            hr = pIEnumWiaItem->Next(1, &pIWiaItem, NULL);
            if (hr == S_OK)
            {
                CCameraItem *pNewCameraItem = new CCameraItem( pIWiaItem );
                if (pNewCameraItem && pNewCameraItem->Item())
                {
                    LONG ItemType = 0;
                    hr = pNewCameraItem->Item()->GetItemType(&ItemType);
                    if (SUCCEEDED(hr))
                    {
                        //
                        // If it is an image, add it to the list
                        //
                        if (ItemType & WiaItemTypeImage)
                        {
                            //
                            // Add it to the list
                            //
                            m_CameraItemList.Add( pCurrentParent, pNewCameraItem );
                            WIA_TRACE((TEXT("Found an image")));
                        }

                        //
                        // If it is a folder, enumerate its child items and recurse
                        //
                        else if (ItemType & WiaItemTypeFolder)
                        {
                            //
                            // Add this folder to the list
                            //
                            m_CameraItemList.Add( pCurrentParent, pNewCameraItem );

                            //
                            // Enumerate the child items
                            //
                            CComPtr <IEnumWiaItem> pIEnumChildItem;
                            if (S_OK == pIWiaItem->EnumChildItems(&pIEnumChildItem))
                            {
                                EnumerateItems( pNewCameraItem, pIEnumChildItem );
                            }
                        }
                        else
                        {
                            //
                            // Delete this item, since we didn't add it to the list
                            //
                            delete pNewCameraItem;
                            WIA_TRACE((TEXT("Found something that is NOT an image")));
                        }
                    }
                }
            }
        }
    }
    return hr;
}



HRESULT CCameraAcquireDialog::EnumerateAllCameraItems(void)
{
    CComPtr<IEnumWiaItem> pIEnumItem;
    HRESULT hr = m_pDeviceDialogData->pIWiaItemRoot->EnumChildItems(&pIEnumItem);
    if (hr == S_OK)
    {
        hr = EnumerateItems( NULL, pIEnumItem );
    }
    return(hr);
}

void CCameraAcquireDialog::OnItemCreatedEvent( CGenericWiaEventHandler::CEventMessage *pEventMessage )
{
    //
    // Get the listview, which we'll need later
    //
    HWND hwndListview = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (hwndListview)
    {
        //
        // Get the image list
        //
        HIMAGELIST hImageList = ListView_GetImageList( hwndListview, LVSIL_NORMAL );
        if (hImageList)
        {
            //
            // Make sure we don't already have this item
            //
            CCameraItem *pCreatedItem = m_CameraItemList.Find(CSimpleBStr(pEventMessage->FullItemName()));
            if (!pCreatedItem)
            {
                //
                // Get the IWiaItem* for this item
                //
                CComPtr<IWiaItem> pWiaItem;
                HRESULT hr = m_pDeviceDialogData->pIWiaItemRoot->FindItemByName(0,CSimpleBStr(pEventMessage->FullItemName()).BString(),&pWiaItem);
                if (SUCCEEDED(hr) && pWiaItem.p)
                {
                    //
                    // Create an item wrapper
                    //
                    CCameraItem *pNewCameraItem = new CCameraItem( pWiaItem );
                    if (pNewCameraItem && pNewCameraItem->Item())
                    {
                        //
                        // Add it to the list
                        //
                        m_CameraItemList.Add( NULL, pNewCameraItem );

                        //
                        // Generate a thumbnail
                        //
                        CreateThumbnail( pNewCameraItem, hImageList, false );

                        //
                        // If this item is in the current folder, add it to the listview
                        //
                        if (m_pCurrentParentItem == pNewCameraItem->Parent())
                        {
                            int nListViewCount = ListView_GetItemCount(hwndListview);

                            //
                            // Add the item
                            //
                            LVITEM lvItem = {0};
                            lvItem.iItem = nListViewCount;
                            lvItem.mask = LVIF_IMAGE|LVIF_PARAM;
                            lvItem.iImage = pNewCameraItem->ImageListIndex();
                            lvItem.lParam = reinterpret_cast<LPARAM>(pNewCameraItem);
                            int nIndex = ListView_InsertItem( hwndListview, &lvItem );

                            //
                            // Make sure the new item is visible
                            //
                            if (nIndex >= 0)
                            {
                                ListView_EnsureVisible( hwndListview, nIndex, FALSE );
                            }
                        }

                        //
                        // Request a thumbnail from the background thread
                        //
                        m_pThreadMessageQueue->Enqueue( new CThumbnailThreadMessage( m_hWnd, pNewCameraItem->GlobalInterfaceTableCookie(), m_sizeThumbnails ) );
                    }
                }
            }
        }
    }
}


bool CCameraAcquireDialog::PopulateList( CCameraItem *pOldParent )
{
    //
    // Which item should be selected?
    //
    int nSelItem = 0;

    //
    // Get the list view control
    //
    HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (hwndList)
    {
        //
        // Empty the list
        //
        ListView_DeleteAllItems( hwndList );

        //
        // Where to insert the current item
        //
        int nCurrentItem = 0;


        CCameraItem *pCurr;

        //
        // If this is a child directory...
        //
        if (m_pCurrentParentItem)
        {
            //
            // Start adding children
            //
            pCurr = m_pCurrentParentItem->Children();

            //
            // Insert a dummy item that the user can use to switch to the parent directory
            //
            LVITEM lvItem;
            ZeroMemory( &lvItem, sizeof(lvItem) );
            lvItem.iItem = nCurrentItem++;
            lvItem.mask = LVIF_IMAGE|LVIF_PARAM;
            lvItem.iImage = m_nParentFolderImageListIndex;
            lvItem.lParam = 0;
            ListView_InsertItem( hwndList, &lvItem );
        }
        //
        // Otherwise, we are at the root
        //
        else pCurr = m_CameraItemList.Root();

        //
        // Iterate through this list of images, and add each one
        //
        while (pCurr)
        {
            //
            // If this is the last parent directory, we want it to be selected instead of the first image
            //
            if (pOldParent && *pCurr == *pOldParent)
            {
                nSelItem = nCurrentItem;
            }

            //
            // If this image hasn't been deleted
            //
            if (pCurr->DeleteState() != CCameraItem::Delete_Deleted)
            {
                //
                // Add the item
                //
                LVITEM lvItem = {0};
                lvItem.iItem = nCurrentItem++;
                lvItem.mask = LVIF_IMAGE|LVIF_PARAM;
                lvItem.iImage = pCurr->ImageListIndex();
                lvItem.lParam = reinterpret_cast<LPARAM>(pCurr);
                int nIndex = ListView_InsertItem( hwndList, &lvItem );

                if (nIndex >= 0 && pCurr->DeleteState() == CCameraItem::Delete_Pending)
                {
                    MarkItemDeletePending(nIndex,true);
                }
            }

            //
            // Advance
            //
            pCurr = pCurr->Next();
        }
    }

    //
    // If we've not calculated the width of the list in preview mode, attempt to do it
    //
    if (!m_nListViewWidth)
    {
        RECT rcItem;
        if (ListView_GetItemRect( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ), 0, &rcItem, LVIR_ICON ))
        {
            m_nListViewWidth = (rcItem.right-rcItem.left) + rcItem.left * 2 + GetSystemMetrics(SM_CXHSCROLL)  + c_nAdditionalMarginX;
        }
    }

    //
    // Set the selected item to either the previous directory, or the first image
    //
    SetSelectedListItem(nSelItem);
    return(true);
}

void CCameraAcquireDialog::CreateThumbnail( CCameraItem *pCurr, HIMAGELIST hImageList, bool bForce )
{
    //
    // Make sure we have a valid item
    //
    if (pCurr && (pCurr->ImageListIndex()<0 || bForce))
    {
        //
        // Get the item name
        //
        CSimpleStringWide strItemName;
        PropStorageHelpers::GetProperty( pCurr->Item(), WIA_IPA_ITEM_NAME, strItemName );

        //
        // Create the title for the icon
        //
        CSimpleString strIconTitle;
        if (pCurr->IsFolder())
        {
            strIconTitle = CSimpleStringConvert::NaturalString(strItemName);
        }
        else if (strItemName.Length())
        {
            strIconTitle.Format( IDS_DOWNLOADINGTHUMBNAIL, g_hInstance, CSimpleStringConvert::NaturalString(strItemName).String() );
        }

        //
        // Create the thumbnail
        //
        HBITMAP hBmp = WiaUiUtil::CreateIconThumbnail( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ), m_sizeThumbnails.cx, m_sizeThumbnails.cy, g_hInstance, pCurr->IsImage()?IDI_UNAVAILABLE:IDI_FOLDER, strIconTitle );
        if (hBmp)
        {
            //
            // If we don't have an image yet, add it
            //
            if (pCurr->ImageListIndex()<0)
            {
                pCurr->ImageListIndex(ImageList_Add( hImageList, hBmp, NULL ));
            }

            //
            // Otherwise, replace it
            //
            else
            {
                pCurr->ImageListIndex(ImageList_Replace( hImageList, pCurr->ImageListIndex(), hBmp, NULL ));
            }

            //
            // Delete it, since the imagelist makes a copy
            //
            DeleteObject(hBmp);
        }
    }
}

void CCameraAcquireDialog::CreateThumbnails( CCameraItem *pRoot, HIMAGELIST hImageList, bool bForce )
{
    CCameraItem *pCurr = pRoot;
    while (pCurr)
    {
        //
        // Create the thumbnail
        //
        CreateThumbnail( pCurr, hImageList, bForce );

        //
        // If there are children, recurse into that list
        //
        CreateThumbnails( pCurr->Children(), hImageList, bForce );

        //
        // Advance
        //
        pCurr = pCurr->Next();
    }
}


void CCameraAcquireDialog::RequestThumbnails( CCameraItem *pRoot )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::RequestThumbnails"));
    CCameraItem *pCurr = pRoot;
    while (pCurr)
    {
        if (pCurr->IsImage())
        {
            m_pThreadMessageQueue->Enqueue( new CThumbnailThreadMessage( m_hWnd, pCurr->GlobalInterfaceTableCookie(), m_sizeThumbnails ) );
        }
        if (pCurr->Children())
        {
            RequestThumbnails( pCurr->Children() );
        }
        pCurr = pCurr->Next();
    }
}


void CCameraAcquireDialog::CreateThumbnails( bool bForce )
{
    HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (hwndList)
    {
        HIMAGELIST hImageList = ListView_GetImageList( hwndList, LVSIL_NORMAL );
        if (hImageList)
        {
            //
            // Create the parent folder image
            //
            HBITMAP hParentBitmap = WiaUiUtil::CreateIconThumbnail( hwndList, m_sizeThumbnails.cx, m_sizeThumbnails.cy, g_hInstance, IDI_PARENTFOLDER, TEXT("(..)") );
            if (hParentBitmap)
            {
                m_nParentFolderImageListIndex = ImageList_Add( hImageList, hParentBitmap, NULL );
                DeleteObject(hParentBitmap);
            }
            CCameraAcquireDialog::CreateThumbnails( m_CameraItemList.Root(), hImageList, bForce );
        }
    }
}

bool CCameraAcquireDialog::FindMaximumThumbnailSize(void)
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::FindMaximumThumbnailSize"));
    bool bResult = false;
    if (m_pDeviceDialogData && m_pDeviceDialogData->pIWiaItemRoot)
    {
        LONG nWidth, nHeight;
        if (PropStorageHelpers::GetProperty( m_pDeviceDialogData->pIWiaItemRoot, WIA_DPC_THUMB_WIDTH, nWidth ) &&
            PropStorageHelpers::GetProperty( m_pDeviceDialogData->pIWiaItemRoot, WIA_DPC_THUMB_WIDTH, nHeight ))
        {
            m_sizeThumbnails.cx = max(c_nMinThumbnailWidth,min(nWidth,c_nMaxThumbnailWidth));
            m_sizeThumbnails.cy = max(c_nMinThumbnailHeight,min(nHeight,c_nMaxThumbnailHeight));
        }
        else WIA_TRACE((TEXT("FindMaximumThumbnailSize: Unable to retrieve thumbnail size for device")));
    }
    return(bResult && m_sizeThumbnails.cx && m_sizeThumbnails.cy);
}

//
// Hook procedure and static variables used to handle accelerators
//
LRESULT CALLBACK CCameraAcquireDialog::DialogHookProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    if (nCode < 0) 
        return CallNextHookEx( s_hMessageHook, nCode, wParam, lParam );
    if (nCode == MSGF_DIALOGBOX)
    {
        MSG *pMsg = reinterpret_cast<MSG*>(lParam);
        if (pMsg)
        {
            if (pMsg->hwnd == s_hWndDialog || (s_hWndDialog && IsChild(s_hWndDialog,pMsg->hwnd)))
            {
                CCameraAcquireDialog *pCameraAcquireDialog = reinterpret_cast<CCameraAcquireDialog*>(GetWindowLongPtr(s_hWndDialog,DWLP_USER));
                if (pCameraAcquireDialog && pCameraAcquireDialog->m_hAccelTable)
                {
                    if (TranslateAccelerator(s_hWndDialog,pCameraAcquireDialog->m_hAccelTable,pMsg))
                        return 1; // Ensure the window won't process the message
                }
            }
        }
    }
    return CallNextHookEx( s_hMessageHook, nCode, wParam, lParam );
}


LRESULT CCameraAcquireDialog::OnInitDialog( WPARAM, LPARAM lParam )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnInitDialog"));
    CWaitCursor wc;

    //
    // Make sure the background queue was successfully created
    //
    if (!m_pThreadMessageQueue)
    {
        WIA_ERROR((TEXT("CAMDLG: unable to start background queue")));
        EndDialog( m_hWnd, E_OUTOFMEMORY );
        return(0);
    }

    m_pDeviceDialogData = (PDEVICEDIALOGDATA)lParam;

    // Save the window handle for the hook
    s_hWndDialog = m_hWnd;

    // Install the message hook, which we use for accelerator support
    s_hMessageHook = SetWindowsHookEx( WH_MSGFILTER, DialogHookProc, g_hInstance, GetCurrentThreadId() );
    if (!s_hMessageHook)
    {
        WIA_ERROR((TEXT("CAMDLG: Unable to set thread msg hook")));
        EndDialog( m_hWnd, HRESULT_FROM_WIN32(GetLastError()));
        return(0);
    }

    // Make sure we have valid arguments
    if (!m_pDeviceDialogData)
    {
        WIA_ERROR((TEXT("CAMDLG: Invalid paramater: PDEVICEDIALOGDATA")));
        EndDialog( m_hWnd, E_INVALIDARG );
        return(0);
    }

    // Initialialize our return stuff
    if (m_pDeviceDialogData)
    {
        m_pDeviceDialogData->lItemCount = 0;
        m_pDeviceDialogData->ppWiaItems = NULL;
    }

    // Make sure we have valid a valid device
    if (!m_pDeviceDialogData->pIWiaItemRoot)
    {
        WIA_ERROR((TEXT("CAMDLG: Invalid paramaters: pIWiaItem")));
        EndDialog( m_hWnd, E_INVALIDARG );
        return(0);
    }

    //
    // Find out if Take Picture is supported
    //
    m_bTakePictureIsSupported = WiaUiUtil::IsDeviceCommandSupported( m_pDeviceDialogData->pIWiaItemRoot, WIA_CMD_TAKE_PICTURE );

    // Prevent multiple selection
    if (m_pDeviceDialogData->dwFlags & WIA_DEVICE_DIALOG_SINGLE_IMAGE)
    {
        LONG_PTR lStyle = GetWindowLongPtr( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ), GWL_STYLE );
        SetWindowLongPtr( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ), GWL_STYLE, lStyle | LVS_SINGLESEL );

        // Set the single sel titles
        CSimpleString( IDS_TITLE_SINGLE_SEL, g_hInstance ).SetWindowText( GetDlgItem( m_hWnd, IDC_CAMDLG_BIG_TITLE ) );
        CSimpleString( IDS_SUBTITLE_SINGLE_SEL, g_hInstance ).SetWindowText( GetDlgItem( m_hWnd, IDC_CAMDLG_SUBTITLE ) );
        CSimpleString( IDS_OK_SINGLE_SEL, g_hInstance ).SetWindowText( GetDlgItem( m_hWnd, IDOK ) );
        m_nDialogMode = SINGLESEL_MODE;
    }
    else
    {
        // Set the multi sel subtitle
        CSimpleString( IDS_TITLE_MULTI_SEL, g_hInstance ).SetWindowText( GetDlgItem( m_hWnd, IDC_CAMDLG_BIG_TITLE ) );
        CSimpleString( IDS_SUBTITLE_MULTI_SEL, g_hInstance ).SetWindowText( GetDlgItem( m_hWnd, IDC_CAMDLG_SUBTITLE ) );
        CSimpleString( IDS_OK_MULTI_SEL, g_hInstance ).SetWindowText( GetDlgItem( m_hWnd, IDOK ) );
        m_nDialogMode = MULTISEL_MODE;
    }


    // Make the lovely font
    m_hBigFont = WiaUiUtil::CreateFontWithPointSizeFromWindow( GetDlgItem(m_hWnd,IDC_CAMDLG_BIG_TITLE), 14, false, false );
    if (m_hBigFont)
        SendDlgItemMessage( m_hWnd, IDC_CAMDLG_BIG_TITLE, WM_SETFONT, reinterpret_cast<WPARAM>(m_hBigFont), MAKELPARAM(TRUE,0));


    // Create the Tool Bar and resize the dialog to accommodate it
    (void)CreateCameraDialogToolbar();

    // Get the minimum size of the dialog
    RECT rcWindow;
    GetWindowRect( m_hWnd, &rcWindow );
    m_sizeMinimumWindow.cx = rcWindow.right - rcWindow.left;
    m_sizeMinimumWindow.cy = rcWindow.bottom - rcWindow.top;

    // Initialize the preview control
    WiaPreviewControl_AllowNullSelection( GetDlgItem( m_hWnd, IDC_PREVIEW ), TRUE );
    WiaPreviewControl_ClearSelection( GetDlgItem( m_hWnd, IDC_PREVIEW ) );
    WiaPreviewControl_DisableSelection( GetDlgItem( m_hWnd, IDC_PREVIEW ), TRUE );
    WiaPreviewControl_SetBorderSize( GetDlgItem( m_hWnd, IDC_PREVIEW ), FALSE, FALSE, 0 );
    WiaPreviewControl_SetBgAlpha( GetDlgItem( m_hWnd, IDC_PREVIEW ), FALSE, 0xFF );

    // Set the lovely title
    CSimpleStringWide strwDeviceName;
    if (PropStorageHelpers::GetProperty( m_pDeviceDialogData->pIWiaItemRoot, WIA_DIP_DEV_NAME, strwDeviceName ))
    {
        CSimpleString().Format( IDS_CAMERADLG_TITLE, g_hInstance, strwDeviceName.String() ).SetWindowText( m_hWnd );
    }

    // Create the sizing control
    (void)CreateWindowEx( 0, TEXT("scrollbar"), TEXT(""),
        WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP|WS_CLIPSIBLINGS|SBS_SIZEBOXBOTTOMRIGHTALIGN|SBS_BOTTOMALIGN|WS_GROUP,
        CSimpleRect(m_hWnd).Width()-GetSystemMetrics(SM_CXVSCROLL),
        CSimpleRect(m_hWnd).Height()-GetSystemMetrics(SM_CYHSCROLL),
        GetSystemMetrics(SM_CXVSCROLL),
        GetSystemMetrics(SM_CYHSCROLL),
        m_hWnd, reinterpret_cast<HMENU>(IDC_SIZEBOX),
        g_hInstance, NULL );

    // Reposition all the controls
    ResizeAll();

    // Center the window over its parent
    WiaUiUtil::CenterWindow( m_hWnd, GetParent(m_hWnd) );

    // Get the device icons and set the window icons
    CSimpleStringWide strwDeviceId, strwClassId;
    LONG nDeviceType;
    if (PropStorageHelpers::GetProperty(m_pDeviceDialogData->pIWiaItemRoot,WIA_DIP_UI_CLSID,strwClassId) &&
        PropStorageHelpers::GetProperty(m_pDeviceDialogData->pIWiaItemRoot,WIA_DIP_DEV_ID,strwDeviceId) &&
        PropStorageHelpers::GetProperty(m_pDeviceDialogData->pIWiaItemRoot,WIA_DIP_DEV_TYPE,nDeviceType))
    {
        //
        // Register for disconnect event
        //
        CGenericWiaEventHandler::RegisterForWiaEvent( strwDeviceId.String(), WIA_EVENT_DEVICE_DISCONNECTED, &m_DisconnectEvent, m_hWnd, PWM_WIAEVENT );
        CGenericWiaEventHandler::RegisterForWiaEvent( strwDeviceId.String(), WIA_EVENT_ITEM_DELETED, &m_DeleteItemEvent, m_hWnd, PWM_WIAEVENT );
        CGenericWiaEventHandler::RegisterForWiaEvent( strwDeviceId.String(), WIA_EVENT_ITEM_CREATED, &m_CreateItemEvent, m_hWnd, PWM_WIAEVENT );

        if (SUCCEEDED(WiaUiExtensionHelper::GetDeviceIcons( CSimpleBStr(strwClassId), nDeviceType, &m_hIconSmall, &m_hIconLarge )))
        {
            if (m_hIconSmall)
            {
                SendMessage( m_hWnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(m_hIconSmall) );
            }
            if (m_hIconLarge)
            {
                SendMessage( m_hWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(m_hIconLarge) );
            }
        }
    }

    int nAcceleratorCount = 0;
    ACCEL Accelerators[10];
    //
    // Load the accelerator table resource and convert it to an ACCEL array
    //
    HACCEL hAccel = LoadAccelerators( g_hInstance, MAKEINTRESOURCE(IDR_CAMERA_ACCEL) );
    if (hAccel)
    {
        //
        // Copy the accelerator table to an array of ACCEL
        //
        nAcceleratorCount = CopyAcceleratorTable( hAccel, Accelerators, ARRAYSIZE(Accelerators) );

        //
        // Free the accelerator table
        //
        DestroyAcceleratorTable( hAccel );
    }

    //
    // Create the accelerator table for the toolbar
    //
    nAcceleratorCount += ToolbarHelper::GetButtonBarAccelerators( GetDlgItem( m_hWnd, IDC_TOOLBAR ), Accelerators+nAcceleratorCount, ARRAYSIZE(Accelerators)-nAcceleratorCount );
    if (nAcceleratorCount)
    {
        m_hAccelTable = CreateAcceleratorTable( Accelerators, nAcceleratorCount );
        if (!m_hAccelTable)
        {
            WIA_PRINTHRESULT((HRESULT_FROM_WIN32(GetLastError()),TEXT("CreateAcceleratorTable failed")));
        }
    }

    SetForegroundWindow(m_hWnd);

    //
    // Make sure the listview has the focus
    //
    SetFocus( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ) );

    return (TRUE);
}

VOID CCameraAcquireDialog::ResizeAll(VOID)
{
    CSimpleRect rcClient(m_hWnd);
    CMoveWindow mw;
    CDialogUnits dialogUnits(m_hWnd);

    // Resize the big title
    mw.Size( GetDlgItem( m_hWnd, IDC_CAMDLG_BIG_TITLE ),
             rcClient.Width() - dialogUnits.StandardMargin().cx * 2,
             0,
             CMoveWindow::NO_SIZEY );

    // Resize the subtitle
    mw.Size( GetDlgItem( m_hWnd, IDC_CAMDLG_SUBTITLE ),
             rcClient.Width() - dialogUnits.StandardMargin().cx * 2,
             0,
             CMoveWindow::NO_SIZEY );

    int nToolBarHeight = (int)(HIWORD((DWORD)(SendMessage(GetDlgItem( m_hWnd, IDC_TOOLBAR ), TB_GETBUTTONSIZE, 0,0))));

    // Resize the toolbar frame
    mw.Size( GetDlgItem(m_hWnd,IDC_TOOLBAR_FRAME),
             rcClient.Width() - dialogUnits.StandardMargin().cx * 2,
             nToolBarHeight + 4,
             0 );

    // Get the dialog's client coordinates of the toolbar frame's client rect
    CSimpleRect rcToolbarFrameInside( GetDlgItem(m_hWnd,IDC_TOOLBAR_FRAME), CSimpleRect::ClientRect );
    rcToolbarFrameInside = rcToolbarFrameInside.ClientToScreen(GetDlgItem(m_hWnd,IDC_TOOLBAR_FRAME));
    rcToolbarFrameInside = rcToolbarFrameInside.ScreenToClient(m_hWnd);

    // Move and resize the toolbar
    mw.SizeMove( GetDlgItem( m_hWnd, IDC_TOOLBAR ),
                 rcToolbarFrameInside.left + 2,
                 rcToolbarFrameInside.top + 2,
                 rcClient.Width() - dialogUnits.StandardMargin().cx * 2 - 4,
                 nToolBarHeight,
                 0 );

    // Save the bottom of this control for later
    int nBottomOfToolbarFrame = CSimpleRect( GetDlgItem(m_hWnd,IDC_TOOLBAR_FRAME), CSimpleRect::WindowRect ).ScreenToClient(m_hWnd).top + nToolBarHeight + 4;


    // Move the Properties control
    mw.SizeMove( GetDlgItem( m_hWnd, IDC_CAMDLG_PROPERTIES ),
                 0,
                 rcClient.Height() - dialogUnits.StandardMargin().cy - dialogUnits.Y(8) + 2,
                 dialogUnits.StandardMargin().cx,
                 dialogUnits.Y(8) + 2,
                 CMoveWindow::NO_MOVEX|CMoveWindow::NO_SIZEX );

    // Move the static text above it
    mw.Move( GetDlgItem( m_hWnd, IDC_YOU_CAN_ALSO ),
             dialogUnits.StandardMargin().cx,
             rcClient.Height() - dialogUnits.StandardMargin().cy - dialogUnits.Y(8) - 2 - dialogUnits.Y(8)
           );


    CSimpleRect rcOK( GetDlgItem( m_hWnd, IDOK ), CSimpleRect::WindowRect );
    CSimpleRect rcCancel( GetDlgItem( m_hWnd, IDOK ), CSimpleRect::WindowRect );

    // Move the OK button
    mw.Move( GetDlgItem( m_hWnd, IDOK ),
             rcClient.Width() - dialogUnits.StandardMargin().cx - dialogUnits.StandardButtonMargin().cx - rcCancel.Width() - rcOK.Width(),
             rcClient.Height() - dialogUnits.StandardMargin().cy - rcOK.Height(),
             0 );

    // Move the cancel button
    mw.Move( GetDlgItem( m_hWnd, IDCANCEL ),
             rcClient.Width() - dialogUnits.StandardMargin().cx - rcCancel.Width(),
             rcClient.Height() - dialogUnits.StandardMargin().cy - rcCancel.Height(),
             0 );

    // Move the resizing handle
    mw.Move( GetDlgItem( m_hWnd, IDC_SIZEBOX ),
             rcClient.Width() - GetSystemMetrics(SM_CXVSCROLL),
             rcClient.Height() - GetSystemMetrics(SM_CYHSCROLL)
             );

    int nHeightOfBottomControls =
        dialogUnits.Y(8) + 2 +            // Highlight control
        dialogUnits.Y(8) +                // Static description text
        dialogUnits.StandardMargin().cy;  // Top of these controls

    CSimpleRect rcAvailableArea(
        dialogUnits.StandardMargin().cx,
        nBottomOfToolbarFrame + dialogUnits.StandardMargin().cy,
        rcClient.right - dialogUnits.StandardMargin().cx,
        rcClient.bottom - nHeightOfBottomControls - dialogUnits.StandardMargin().cy
        );

    if (m_bPreviewActive)
    {
        // If we've already calculated the actual width of the preview mode list, use it, otherwise use the default
        int nListViewWidth = m_nListViewWidth ? m_nListViewWidth : c_nDefaultListViewWidth;

        // Move the thumbnail list
        mw.SizeMove( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ),
                     rcAvailableArea.left,
                     rcAvailableArea.top,
                     nListViewWidth,
                     rcAvailableArea.Height()
                   );

        // Remove the rect of the list view from the preview area
        rcAvailableArea.left += nListViewWidth + dialogUnits.StandardMargin().cx;

        // Use up the remaining area
        mw.SizeMove( GetDlgItem( m_hWnd, IDC_PREVIEW ),
                     rcAvailableArea.left,
                     rcAvailableArea.top,
                     rcAvailableArea.Width(),
                     rcAvailableArea.Height() );

    }
    else
    {
        // Move the thumbnail list
        mw.SizeMove( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ),
                     rcAvailableArea.left,
                     rcAvailableArea.top,
                     rcAvailableArea.Width(),
                     rcAvailableArea.Height()
                   );

    }

    // Explicitly apply the moves, because the toolbar frame doesn't get painted properly
    mw.Apply();

    if (m_bPreviewActive)
    {
        // Show the preview in case it isn't visible
        mw.Show( GetDlgItem( m_hWnd, IDC_PREVIEW ) );
    }
    else
    {
        // Hide the preview in case it is visible
        mw.Hide( GetDlgItem( m_hWnd, IDC_PREVIEW ) );
    }

    //
    // Update the dialog's background to remove any weird stuff left behind
    //
    InvalidateRect( m_hWnd, NULL, FALSE );
    UpdateWindow( m_hWnd );
}


LRESULT CCameraAcquireDialog::OnItemDeleted( WPARAM wParam, LPARAM lParam )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnItemDeleted"));
    CCameraItem *pDeletedItem = m_CameraItemList.Find((DWORD)wParam);
    if (pDeletedItem)
    {
        BOOL bSuccess = (BOOL)lParam;
        pDeletedItem->DeleteState( bSuccess ? CCameraItem::Delete_Deleted : CCameraItem::Delete_Visible );
        if (pDeletedItem == m_pCurrentParentItem)
        {
            ChangeFolder(m_pCurrentParentItem->Parent());
        }

        int nIndex = FindItemInList(pDeletedItem);
        if (nIndex >= 0)
        {
            if (bSuccess)
            {
                // Remove the item from the list
                ListView_DeleteItem(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ),nIndex);

                // Make sure we leave something selected
                if (!ListView_GetSelectedCount(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST )))
                {
                    int nItemCount = ListView_GetItemCount(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ));
                    if (nItemCount)
                    {
                        if (nIndex >= nItemCount)
                            nIndex = nItemCount-1;
                        SetSelectedListItem(nIndex);
                    }
                }
                else
                {
                    // Make sure we update control's state when the list is empty
                    HandleSelectionChange();
                }
            }
            else
            {
                // If the delete failed, remove the deleted state
                MarkItemDeletePending(nIndex,false);

                // Tell the user
                MessageBeep( MB_ICONASTERISK );
            }
        }
    }
    return (0);
}

LRESULT CCameraAcquireDialog::OnWiaEvent( WPARAM, LPARAM lParam )
{
    WIA_PUSHFUNCTION(TEXT("CCameraAcquireDialog::OnWiaEvent"));
    CGenericWiaEventHandler::CEventMessage *pEventMessage = reinterpret_cast<CGenericWiaEventHandler::CEventMessage *>(lParam);
    if (pEventMessage)
    {
        if (pEventMessage->EventId() == WIA_EVENT_DEVICE_DISCONNECTED)
        {
            WIA_TRACE((TEXT("Received disconnect event")));
            EndDialog( m_hWnd, WIA_ERROR_OFFLINE );
        }
        else if (pEventMessage->EventId() == WIA_EVENT_ITEM_CREATED)
        {
            OnItemCreatedEvent( pEventMessage );
        }
        else if (pEventMessage->EventId() == WIA_EVENT_ITEM_DELETED)
        {
            WIA_TRACE((TEXT("Received deleted item event")));

            CCameraItem *pDeletedItem = m_CameraItemList.Find(CSimpleBStr(pEventMessage->FullItemName()));
            if (pDeletedItem)
            {
                //
                // If we're deleting the current parent item,
                // select a new one.
                //
                if (pDeletedItem == m_pCurrentParentItem)
                {
                    ChangeFolder(m_pCurrentParentItem->Parent());
                }

                int nIndex = FindItemInList(pDeletedItem);
                if (nIndex >= 0)
                {
                    //
                    // Remove the item from the listview
                    //
                    ListView_DeleteItem(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ),nIndex);

                    //
                    // Make sure we leave something selected
                    //
                    if (!ListView_GetSelectedCount(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST )))
                    {
                        int nItemCount = ListView_GetItemCount(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ));
                        if (nItemCount)
                        {
                            if (nIndex >= nItemCount)
                            {
                                nIndex = nItemCount-1;
                            }

                            SetSelectedListItem(nIndex);
                        }
                    }
                    else
                    {
                        //
                        // Make sure we update control's state when the list is empty
                        //
                        HandleSelectionChange();
                    }
                }
                else
                {
                    WIA_ERROR((TEXT("FindItemInList coulnd't find the item")));
                }

                //
                // Mark the item as deleted.
                //
                pDeletedItem->DeleteState( CCameraItem::Delete_Deleted );

            }
            else
            {
                WIA_ERROR((TEXT("The item could not be found in m_CameraItemList")));
            }
        }
        delete pEventMessage;
    }
    return HANDLED_EVENT_MESSAGE;
}


LRESULT CCameraAcquireDialog::OnEnterSizeMove( WPARAM, LPARAM )
{
    SendDlgItemMessage( m_hWnd, IDC_PREVIEW, WM_ENTERSIZEMOVE, 0, 0 );
    return(0);
}


LRESULT CCameraAcquireDialog::OnExitSizeMove( WPARAM, LPARAM )
{
    SendDlgItemMessage( m_hWnd, IDC_PREVIEW, WM_EXITSIZEMOVE, 0, 0 );
    return(0);
}


LRESULT CCameraAcquireDialog::OnSize( WPARAM, LPARAM )
{
    ResizeAll();
    return(0);
}


LRESULT CCameraAcquireDialog::OnShow( WPARAM, LPARAM )
{
    if (m_bFirstTime)
    {
        PostMessage( m_hWnd, PWM_POSTINIT, 0, 0 );
        m_bFirstTime = false;
    }
    return(0);
}


LRESULT CCameraAcquireDialog::OnGetMinMaxInfo( WPARAM, LPARAM lParam )
{
    LPMINMAXINFO pMinMaxInfo = (LPMINMAXINFO)lParam;
    pMinMaxInfo->ptMinTrackSize.x = m_sizeMinimumWindow.cx;
    pMinMaxInfo->ptMinTrackSize.y = m_sizeMinimumWindow.cy;
    return(0);
}


LRESULT CCameraAcquireDialog::OnDestroy( WPARAM, LPARAM )
{
    //
    // Get rid of all preview requests
    //
    CancelAllPreviewRequests( m_CameraItemList.Root() );

    //
    // Tell the background thread to destroy itself
    //
    m_pThreadMessageQueue->Enqueue( new CThreadMessage(TQ_DESTROY),CThreadMessageQueue::PriorityUrgent);

    //
    // Set the window icon to NULL
    //
    SendMessage( m_hWnd, WM_SETICON, ICON_BIG, 0 );
    SendMessage( m_hWnd, WM_SETICON, ICON_SMALL, 0 );

    //
    // Clear the image list and list view.  This should be unnecessary, but BoundsChecker
    // complains if I don't do it.
    //
    HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (hwndList)
    {
        ListView_DeleteAllItems(hwndList);

        HIMAGELIST hImgList = ListView_SetImageList( hwndList, NULL, LVSIL_NORMAL );
        if (hImgList)
        {
            ImageList_Destroy(hImgList);
        }
    }


    //
    // Delete resources
    //
    if (m_hBigFont)
    {
        DeleteObject(m_hBigFont);
        m_hBigFont = NULL;
    }
    if (m_hImageList)
    {
        m_hImageList = NULL;
    }
    if (m_hAccelTable)
    {
        DestroyAcceleratorTable(m_hAccelTable);
        m_hAccelTable = NULL;
    }
    if (s_hMessageHook)
    {
        UnhookWindowsHookEx(s_hMessageHook);
        s_hMessageHook = NULL;
    }
    if (m_hIconLarge)
    {
        DestroyIcon(m_hIconLarge);
        m_hIconLarge = NULL;
    }
    if (m_hIconSmall)
    {
        DestroyIcon(m_hIconSmall);
        m_hIconSmall = NULL;
    }
    if (m_hBackgroundThread)
    {
        CloseHandle(m_hBackgroundThread);
        m_hBackgroundThread = NULL;
    }
    return(0);
}



VOID CCameraAcquireDialog::OnPreviewMode( WPARAM, LPARAM )
{
    m_bPreviewActive = true;
    SendDlgItemMessage( m_hWnd, IDC_TOOLBAR, TB_SETSTATE, IDC_PREVIEWMODE, MAKELONG(TBSTATE_ENABLED|TBSTATE_CHECKED,0) );
    SendDlgItemMessage( m_hWnd, IDC_TOOLBAR, TB_SETSTATE, IDC_ICONMODE, MAKELONG(TBSTATE_ENABLED,0) );
    ResizeAll();
    UpdatePreview();
}

VOID CCameraAcquireDialog::OnTakePicture( WPARAM, LPARAM )
{
    //
    // Tell the device to snap a picture
    //
    if (m_pDeviceDialogData->pIWiaItemRoot && m_bTakePictureIsSupported)
    {
        CWaitCursor wc;
        CComPtr<IWiaItem> pNewWiaItem;
        m_pDeviceDialogData->pIWiaItemRoot->DeviceCommand(0,&WIA_CMD_TAKE_PICTURE,&pNewWiaItem);
    }
}

VOID CCameraAcquireDialog::OnIconMode( WPARAM, LPARAM )
{
    m_bPreviewActive = false;
    SendDlgItemMessage( m_hWnd, IDC_TOOLBAR, TB_SETSTATE, IDC_ICONMODE, MAKELONG(TBSTATE_ENABLED|TBSTATE_CHECKED,0) );
    SendDlgItemMessage( m_hWnd, IDC_TOOLBAR, TB_SETSTATE, IDC_PREVIEWMODE, MAKELONG(TBSTATE_ENABLED,0) );
    ResizeAll();
    UpdatePreview();
}

LRESULT CCameraAcquireDialog::OnPostInit( WPARAM, LPARAM )
{
    //
    // Create the progress dialog
    //
    CComPtr<IWiaProgressDialog> pWiaProgressDialog;
    HRESULT hr = CoCreateInstance( CLSID_WiaDefaultUi, NULL, CLSCTX_INPROC_SERVER, IID_IWiaProgressDialog, (void**)&pWiaProgressDialog );
    if (SUCCEEDED(hr))
    {
        //
        // Initialize the progress dialog
        //
        pWiaProgressDialog->Create( m_hWnd, WIA_PROGRESSDLG_ANIM_CAMERA_COMMUNICATE|WIA_PROGRESSDLG_NO_PROGRESS|WIA_PROGRESSDLG_NO_CANCEL|WIA_PROGRESSDLG_NO_TITLE );
        pWiaProgressDialog->SetTitle( CSimpleStringConvert::WideString(CSimpleString(IDS_CAMDLG_PROGDLG_TITLE,g_hInstance)));
        pWiaProgressDialog->SetMessage( CSimpleStringConvert::WideString(CSimpleString(IDS_CAMDLG_PROGDLG_MESSAGE,g_hInstance)));

        //
        // Show the progress dialog
        //
        pWiaProgressDialog->Show();

        //
        // Find all of the images in the camera
        //
        EnumerateAllCameraItems();

        //
        // Find the largest possible thumbnail
        //
        FindMaximumThumbnailSize();

        //
        // Initialize Thumbnail Listview control
        //
        HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
        if (hwndList)
        {
            //
            // Get rid of the border and icon labels
            //
            ListView_SetExtendedListViewStyleEx( hwndList, LVS_EX_BORDERSELECT|LVS_EX_HIDELABELS|LVS_EX_DOUBLEBUFFER, LVS_EX_BORDERSELECT|LVS_EX_HIDELABELS|LVS_EX_DOUBLEBUFFER );

            //
            // Create the large image list
            //
            m_hImageList = ImageList_Create( m_sizeThumbnails.cx, m_sizeThumbnails.cy, ILC_COLOR24|ILC_MIRROR, 50, 50 );
            if (m_hImageList)
            {

                //
                // Set the image list
                //
                ListView_SetImageList( hwndList, m_hImageList, LVSIL_NORMAL );
            }

            //
            // Set the icon spacing
            //
            ListView_SetIconSpacing( hwndList, m_sizeThumbnails.cx + c_nAdditionalMarginX, m_sizeThumbnails.cy + c_nAdditionalMarginY );
        }

        //
        // Create all of the initial thumbnails
        //
        CreateThumbnails();

        //
        // This causes the list to be populated
        //
        ChangeFolder(NULL);

        //
        // Force a selection change
        //
        HandleSelectionChange();

        //
        // Download all of the thumbnails
        //
        RequestThumbnails( m_CameraItemList.Root() );

        //
        // Close the progress dialog
        //
        pWiaProgressDialog->Destroy();
    }
    return(0);
}

LRESULT CCameraAcquireDialog::OnChangeToParent( WPARAM, LPARAM )
{
    if (m_pCurrentParentItem)
        ChangeFolder(m_pCurrentParentItem->Parent());
    return(0);
}

VOID CCameraAcquireDialog::OnParentDir( WPARAM, LPARAM )
{
    if (m_pCurrentParentItem && m_pCurrentParentItem->Parent())
        ChangeFolder(m_pCurrentParentItem->Parent());
    else ChangeFolder(NULL);
}

void CCameraAcquireDialog::MarkItemDeletePending( int nIndex, bool bSet )
{
    HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (hwndList)
    {
        ListView_SetItemState( hwndList, nIndex, bSet ? LVIS_CUT : 0, LVIS_CUT );
    }
}

// Recursively delete items
void CCameraAcquireDialog::DeleteItem( CCameraItem *pItemNode )
{
    if (pItemNode)
    {
        CCameraItem *pChild = pItemNode->Children();
        while (pChild)
        {
            DeleteItem(pChild);
            pChild = pChild->Next();
        }
        if (pItemNode->DeleteState() == CCameraItem::Delete_Visible)
        {
            int nIndex = FindItemInList( pItemNode );
            if (nIndex >= 0)
            {
                //
                // Mark it pending in the UI
                //
                MarkItemDeletePending(nIndex,true);
            }
            //
            // Mark it pending
            //
            pItemNode->DeleteState( CCameraItem::Delete_Pending );

            //
            // Fire off the request
            //
            m_pThreadMessageQueue->Enqueue( new CDeleteThreadMessage(m_hWnd, pItemNode->GlobalInterfaceTableCookie()), CThreadMessageQueue::PriorityHigh );
        }
    }
}


VOID CCameraAcquireDialog::OnDelete( WPARAM, LPARAM )
{
    CSimpleDynamicArray<int> aSelIndices;
    if (GetSelectionIndices( aSelIndices ))
    {
        //
        // We only want to show the confirm dialog once
        //
        bool bShowConfirmDialog = true;
        for (int i=0;i<aSelIndices.Size();i++)
        {
            CCameraItem *pItemNode = GetListItemNode(aSelIndices[i]);

            //
            // If we haven't already deleted this image, do so
            //
            if (pItemNode && pItemNode->DeleteState() == CCameraItem::Delete_Visible && pItemNode->ItemRights() & WIA_ITEM_CAN_BE_DELETED)
            {
                if (bShowConfirmDialog)
                {
                    bShowConfirmDialog = false;
                    if (IDYES!=MessageBox( m_hWnd, CSimpleString( IDS_DELETE_CONFIRM, g_hInstance ), CSimpleString( IDS_DELETE_CONFIRM_TITLE, g_hInstance ), MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 ))
                    {
                        break;
                    }
                }
                DeleteItem( pItemNode );
            }
        }
    }
}


int CCameraAcquireDialog::GetSelectionIndices( CSimpleDynamicArray<int> &aIndices )
{
    HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (!hwndList)
        return(0);
    int iCount = ListView_GetItemCount(hwndList);
    for (int i=0;i<iCount;i++)
        if (ListView_GetItemState(hwndList,i,LVIS_SELECTED) & LVIS_SELECTED)
            aIndices.Append(i);
    return(aIndices.Size());
}

bool CCameraAcquireDialog::SetSelectedListItem( int nIndex )
{
    HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (!hwndList)
        return(false);
    int iCount = ListView_GetItemCount(hwndList);
    for (int i=0;i<iCount;i++)
        ListView_SetItemState(hwndList,i,LVIS_SELECTED|LVIS_FOCUSED,0);
    ListView_SetItemState(hwndList,nIndex,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
    ListView_EnsureVisible(hwndList,nIndex,FALSE);
    return(true);
}

CCameraItem *CCameraAcquireDialog::GetListItemNode( int nIndex )
{
    HWND hwndList = GetDlgItem( m_hWnd, IDC_THUMBNAILLIST );
    if (!hwndList)
        return(NULL);
    LV_ITEM lvItem;
    ::ZeroMemory(&lvItem,sizeof(LV_ITEM));
    lvItem.mask = LVIF_PARAM;
    lvItem.iItem = nIndex;
    if (!ListView_GetItem( hwndList, &lvItem ))
        return(NULL);
    return((CCameraItem *)lvItem.lParam);
}

bool CCameraAcquireDialog::ChangeFolder( CCameraItem *pNode )
{
    CCameraItem *pOldParent = m_pCurrentParentItem;
    m_pCurrentParentItem = pNode;
    return(PopulateList(pOldParent));
}

bool CCameraAcquireDialog::ChangeToSelectedFolder(void)
{
    CSimpleDynamicArray<int> aSelIndices;
    if (GetSelectionIndices(aSelIndices))
    {
        //
        // Find out if only folders are selected
        //
        bool bOnlyFoldersSelected = true;
        for (int i=0;i<aSelIndices.Size();i++)
        {
            CCameraItem *pItemNode = GetListItemNode(aSelIndices[i]);
            if (pItemNode && !pItemNode->IsFolder())
            {
                bOnlyFoldersSelected = false;
                break;
            }
        }

        WIA_TRACE((TEXT("bOnlyFoldersSelected = %d"),bOnlyFoldersSelected));

        //
        // If only folders are selected, switch to the first selected folder
        //
        if (bOnlyFoldersSelected && aSelIndices.Size())
        {
            CCameraItem *pItemNode = GetListItemNode(aSelIndices[0]);
            if (!pItemNode)
            {
                //
                // NULL item node == parent folder
                //
                SendMessage( m_hWnd, PWM_CHANGETOPARENT, 0, 0 );
                return(true);
            }
            if (pItemNode && pItemNode->IsFolder() && pItemNode->DeleteState() == CCameraItem::Delete_Visible)
            {
                if (ChangeFolder(pItemNode))
                {
                    return(true);
                }
            }
        }
    }
    return(false);
}

VOID CCameraAcquireDialog::OnOK( WPARAM, LPARAM )
{
    if (!ChangeToSelectedFolder())
    {
        HRESULT hr = S_OK;
        m_pDeviceDialogData->lItemCount = 0;
        m_pDeviceDialogData->ppWiaItems = NULL;
        CSimpleDynamicArray<int> aIndices;
        GetSelectionIndices( aIndices );
        if (aIndices.Size())
        {
            int nArraySizeInBytes = sizeof(IWiaItem*) * aIndices.Size();
            m_pDeviceDialogData->ppWiaItems = (IWiaItem**)CoTaskMemAlloc(nArraySizeInBytes);
            if (m_pDeviceDialogData->ppWiaItems)
            {
                ZeroMemory( m_pDeviceDialogData->ppWiaItems, nArraySizeInBytes );
                int nCurrItem = 0;
                for (int i=0;i<aIndices.Size();i++)
                {
                    CCameraItem *pItem = GetListItemNode(aIndices[i]);

                    //
                    // Add the item to the list if it is both a valid picture item and it hasn't been deleted
                    //
                    if (pItem && pItem->Item() && pItem->IsImage() && pItem->DeleteState() == CCameraItem::Delete_Visible)
                    {
                        m_pDeviceDialogData->ppWiaItems[nCurrItem] = pItem->Item();
                        m_pDeviceDialogData->ppWiaItems[nCurrItem]->AddRef();
                        nCurrItem++;
                    }
                }
                m_pDeviceDialogData->lItemCount = nCurrItem;
            }
            else
            {
                // Unable to alloc mem
                hr = E_OUTOFMEMORY;
            }
        }
        else
        {
            return;
        }
        EndDialog( m_hWnd, hr );
    }
}


VOID CCameraAcquireDialog::OnCancel( WPARAM, LPARAM )
{
    EndDialog( m_hWnd, S_FALSE );
}

VOID CCameraAcquireDialog::OnSelectAll( WPARAM, LPARAM )
{
    ListView_SetItemState( GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ), -1, LVIS_SELECTED, LVIS_SELECTED );
}

VOID CCameraAcquireDialog::OnProperties( WPARAM, LPARAM )
{
    CSimpleDynamicArray<int> aSelIndices;
    if (GetSelectionIndices( aSelIndices ))
    {
        if (aSelIndices.Size() == 1)
        {
            CCameraItem *pItemNode = GetListItemNode(aSelIndices[0]);
            if (pItemNode && pItemNode->Item())
            {
                m_pThreadMessageQueue->Pause();
                HRESULT hr = WiaUiUtil::SystemPropertySheet( g_hInstance, m_hWnd, pItemNode->Item(), CSimpleString( IDS_CAMDLG_PROPERTIES_TITLE, g_hInstance ) );
                if (!SUCCEEDED(hr))
                {
                    if (PROP_SHEET_ERROR_NO_PAGES == hr)
                    {
                        MessageBox( m_hWnd, CSimpleString( IDS_CAMDLG_PROPSHEETNOPAGES, g_hInstance ), CSimpleString( IDS_CAMDLG_ERROR_TITLE, g_hInstance ), MB_ICONINFORMATION );
                    }
                    else
                    {
                        MessageBox( m_hWnd, CSimpleString( IDS_CAMDLG_PROPSHEETERROR, g_hInstance ), CSimpleString( IDS_CAMDLG_ERROR_TITLE, g_hInstance ), MB_ICONINFORMATION );
                    }
                    WIA_PRINTHRESULT((hr,TEXT("SystemPropertySheet failed")));
                }
                m_pThreadMessageQueue->Resume();
            }
        }
    }
}

LRESULT CCameraAcquireDialog::OnDblClkImageList( WPARAM, LPARAM )
{
    SendMessage( m_hWnd, WM_COMMAND, MAKEWPARAM(IDOK,0), 0 );
    return(0);
}

void CCameraAcquireDialog::CancelAllPreviewRequests( CCameraItem *pRoot )
{
    CCameraItem *pCurr = pRoot;
    while (pCurr)
    {
        if (pCurr->PreviewRequestPending())
            pCurr->SetCancelEvent();
        if (pCurr->Children())
            CancelAllPreviewRequests( pCurr->Children() );
        pCurr = pCurr->Next();
    }
}

void CCameraAcquireDialog::UpdatePreview(void)
{
    if (m_bPreviewActive)
    {
        CCameraItem *pCurrPreviewItem = GetCurrentPreviewItem();
        if (pCurrPreviewItem && pCurrPreviewItem->IsImage())
        {
            // If we're not already downloading this preview image...
            if (!pCurrPreviewItem->PreviewRequestPending())
            {
                // Cancel all other preview requests
                CancelAllPreviewRequests( m_CameraItemList.Root() );

                if (pCurrPreviewItem->PreviewFileName().Length())
                {
                    // Set the preview if we've got it cached
                    SetCurrentPreviewImage( pCurrPreviewItem->PreviewFileName() );
                }
                else
                {
                    CSimpleString strPct;
                    strPct.Format( IDS_DOWNLOADINGPREVIEW, g_hInstance, 0 );

                    // Clear the preview window
                    SetCurrentPreviewImage( TEXT(""), strPct );

                    // Create our cancel event
                    pCurrPreviewItem->CreateCancelEvent();
                    // Reset it, just in case
                    pCurrPreviewItem->ResetCancelEvent();
                    // Make the request
                    m_pThreadMessageQueue->Enqueue( new CPreviewThreadMessage( m_hWnd, pCurrPreviewItem->GlobalInterfaceTableCookie(), pCurrPreviewItem->CancelQueueEvent().Event() ), CThreadMessageQueue::PriorityHigh );
                }
            }
            else
            {
                CSimpleString strPct;
                strPct.Format( IDS_DOWNLOADINGPREVIEW, g_hInstance, pCurrPreviewItem->CurrentPreviewPercentage() );

                SetCurrentPreviewImage( TEXT(""), strPct );
            }
        }
        else
        {
            SetCurrentPreviewImage( TEXT("") );
            CancelAllPreviewRequests( m_CameraItemList.Root() );
        }
    }
    else
    {
        CancelAllPreviewRequests( m_CameraItemList.Root() );
        SetCurrentPreviewImage( TEXT("") );
    }
}

LRESULT CCameraAcquireDialog::OnTimer( WPARAM wParam, LPARAM )
{
    switch (wParam)
    {
    case IDT_UPDATEPREVIEW:
        {
            KillTimer( m_hWnd, IDT_UPDATEPREVIEW );
            UpdatePreview();
        }
        break;
    }
    return(0);
}

// Avoids unnecessary state changes
static inline void MyEnableWindow( HWND hWnd, BOOL bEnable )
{
    if (bEnable && !IsWindowEnabled(hWnd))
        EnableWindow(hWnd,TRUE);
    else if (!bEnable && IsWindowEnabled(hWnd))
        EnableWindow(hWnd,FALSE);
}

// Avoids unnecessary state changes
static inline void MyEnableToolbarButton( HWND hWnd, int nId, BOOL bEnable )
{
    LRESULT nState = SendMessage( hWnd, TB_GETSTATE, nId, 0 );
    if (nState < 0)
        return;
    if ((nState & TBSTATE_ENABLED) && !bEnable)
        SendMessage( hWnd, TB_ENABLEBUTTON, nId, nState & ~TBSTATE_ENABLED);
    else if (!(nState & TBSTATE_ENABLED) && bEnable)
        SendMessage( hWnd, TB_ENABLEBUTTON, nId, nState | TBSTATE_ENABLED );
}


void CCameraAcquireDialog::HandleSelectionChange(void)
{
    CWaitCursor wc;
    int nSelCount = ListView_GetSelectedCount(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ) );
    int nItemCount = ListView_GetItemCount(GetDlgItem( m_hWnd, IDC_THUMBNAILLIST ) );

    //
    // Properties should be disabled for multiple items
    // and parent folder icons
    //
    bool bDisableProperties = true;
    if (nSelCount == 1)
    {
        CSimpleDynamicArray<int> aIndices;
        if (CCameraAcquireDialog::GetSelectionIndices( aIndices ))
        {
            if (CCameraAcquireDialog::GetListItemNode( aIndices[0] ))
            {
                bDisableProperties = false;
            }
        }
    }

    MyEnableWindow( GetDlgItem(m_hWnd,IDC_CAMDLG_PROPERTIES), !bDisableProperties );

    // OK should be disabled for 0 items
    MyEnableWindow( GetDlgItem(m_hWnd,IDOK), nSelCount != 0 );

    // Select all should be disabled for 0 items
    MyEnableToolbarButton( GetDlgItem(m_hWnd,IDC_TOOLBAR), IDC_SELECTALL, nItemCount != 0 );

    //
    // Decide whether or not delete should be enabled
    // If any of the selected items are deletable, then delete is enabled
    //
    bool bEnableDelete = false;

    //
    // Get the selected items
    //
    CSimpleDynamicArray<int> aSelIndices;
    if (GetSelectionIndices( aSelIndices ))
    {
        //
        // Loop through all of the selected items.  Break out if we find a reason
        // to enable delete.
        //
        for (int i=0;i<aSelIndices.Size() && !bEnableDelete;i++)
        {
            //
            // Get the item
            //
            CCameraItem *pItemNode = GetListItemNode(aSelIndices[i]);

            //
            // If we don't have an item, it is a parent folder
            //
            if (pItemNode)
            {
                //
                // If the access rights include the right to delete items,
                // break out.
                //
                if (pItemNode->ItemRights() & WIA_ITEM_CAN_BE_DELETED)
                {
                    //
                    // Found one, so we are done.
                    //
                    bEnableDelete = true;
                    break;
                }
            }
        }
    }

    MyEnableToolbarButton( GetDlgItem(m_hWnd,IDC_TOOLBAR), IDC_DELETE, bEnableDelete );

    KillTimer( m_hWnd, IDT_UPDATEPREVIEW );
    SetTimer( m_hWnd, IDT_UPDATEPREVIEW, UPDATE_PREVIEW_DELAY, NULL );
}


LRESULT CCameraAcquireDialog::OnImageListItemChanged( WPARAM, LPARAM )
{
    HandleSelectionChange();
    return(0);
}

LRESULT CCameraAcquireDialog::OnImageListKeyDown( WPARAM, LPARAM lParam )
{
    LPNMLVKEYDOWN pnkd = reinterpret_cast<LPNMLVKEYDOWN>(lParam);
    if (pnkd)
    {
        bool bAlt = ((GetKeyState(VK_MENU) & 0x8000) != 0);
        bool bControl = ((GetKeyState(VK_CONTROL) & 0x8000) != 0);
        bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0);
        if (VK_LEFT == pnkd->wVKey && bAlt && !bControl && !bShift)
        {
            SendMessage( m_hWnd, PWM_CHANGETOPARENT, 0, 0 );
        }
        else if (VK_BACK == pnkd->wVKey && !bAlt && !bControl && !bShift)
        {
            SendMessage( m_hWnd, PWM_CHANGETOPARENT, 0, 0 );
        }
        else if (VK_DELETE == pnkd->wVKey)
        {
            SendMessage( m_hWnd, WM_COMMAND, IDC_DELETE, 0 );
        }
    }
    return (0);
}

LRESULT CCameraAcquireDialog::OnHelp( WPARAM wParam, LPARAM lParam )
{
    return WiaHelp::HandleWmHelp( wParam, lParam, g_HelpIDs );
}

LRESULT CCameraAcquireDialog::OnContextMenu( WPARAM wParam, LPARAM lParam )
{
    return WiaHelp::HandleWmContextMenu( wParam, lParam, g_HelpIDs );
}

LRESULT CCameraAcquireDialog::OnSysColorChange( WPARAM wParam, LPARAM lParam )
{
    WiaPreviewControl_SetBkColor( GetDlgItem( m_hWnd, IDC_PREVIEW ), TRUE, TRUE, GetSysColor(COLOR_WINDOW) );
    WiaPreviewControl_SetBkColor( GetDlgItem( m_hWnd, IDC_PREVIEW ), TRUE, FALSE, GetSysColor(COLOR_WINDOW) );
    SendDlgItemMessage( m_hWnd, IDC_THUMBNAILLIST, WM_SYSCOLORCHANGE, wParam, lParam );
    SendDlgItemMessage( m_hWnd, IDC_TOOLBAR, WM_SYSCOLORCHANGE, wParam, lParam );
    SendDlgItemMessage( m_hWnd, IDC_CAMDLG_PROPERTIES, WM_SYSCOLORCHANGE, wParam, lParam );
    m_ToolbarBitmapInfo.ReloadAndReplaceBitmap();
    return 0;
}

LRESULT CCameraAcquireDialog::OnNotify( WPARAM wParam, LPARAM lParam )
{
    SC_BEGIN_NOTIFY_MESSAGE_HANDLERS()
    {
        SC_HANDLE_NOTIFY_MESSAGE_CONTROL( NM_DBLCLK, IDC_THUMBNAILLIST, OnDblClkImageList );
        SC_HANDLE_NOTIFY_MESSAGE_CONTROL( LVN_ITEMCHANGED, IDC_THUMBNAILLIST, OnImageListItemChanged );
        SC_HANDLE_NOTIFY_MESSAGE_CONTROL( LVN_KEYDOWN, IDC_THUMBNAILLIST, OnImageListKeyDown );
    }
    SC_END_NOTIFY_MESSAGE_HANDLERS();
}

// WM_COMMAND Handler
LRESULT CCameraAcquireDialog::OnCommand( WPARAM wParam, LPARAM lParam )
{
    SC_BEGIN_COMMAND_HANDLERS()
    {
        SC_HANDLE_COMMAND(IDOK,OnOK);
        SC_HANDLE_COMMAND(IDCANCEL,OnCancel);
        SC_HANDLE_COMMAND(IDC_CAMDLG_PROPERTIES,OnProperties);
        SC_HANDLE_COMMAND(IDC_PREVIEWMODE,OnPreviewMode);
        SC_HANDLE_COMMAND(IDC_TAKEPICTURE,OnTakePicture);
        SC_HANDLE_COMMAND(IDC_ICONMODE,OnIconMode);
        SC_HANDLE_COMMAND(IDC_DELETE,OnDelete);
        SC_HANDLE_COMMAND(IDC_SELECTALL,OnSelectAll);
    }
    SC_END_COMMAND_HANDLERS();
}

INT_PTR CALLBACK CCameraAcquireDialog::DialogProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    SC_BEGIN_DIALOG_MESSAGE_HANDLERS(CCameraAcquireDialog)
    {
        SC_HANDLE_DIALOG_MESSAGE( WM_INITDIALOG, OnInitDialog );
        SC_HANDLE_DIALOG_MESSAGE( WM_SIZE, OnSize );
        SC_HANDLE_DIALOG_MESSAGE( WM_SHOWWINDOW, OnShow );
        SC_HANDLE_DIALOG_MESSAGE( WM_ENTERSIZEMOVE, OnEnterSizeMove );
        SC_HANDLE_DIALOG_MESSAGE( WM_EXITSIZEMOVE, OnExitSizeMove );
        SC_HANDLE_DIALOG_MESSAGE( WM_COMMAND, OnCommand );
        SC_HANDLE_DIALOG_MESSAGE( WM_GETMINMAXINFO, OnGetMinMaxInfo );
        SC_HANDLE_DIALOG_MESSAGE( WM_NOTIFY, OnNotify );
        SC_HANDLE_DIALOG_MESSAGE( WM_DESTROY, OnDestroy );
        SC_HANDLE_DIALOG_MESSAGE( PWM_POSTINIT, OnPostInit );
        SC_HANDLE_DIALOG_MESSAGE( PWM_CHANGETOPARENT, OnChangeToParent );
        SC_HANDLE_DIALOG_MESSAGE( PWM_THUMBNAILSTATUS, OnThumbnailStatus );
        SC_HANDLE_DIALOG_MESSAGE( PWM_PREVIEWSTATUS, OnPreviewStatus );
        SC_HANDLE_DIALOG_MESSAGE( PWM_PREVIEWPERCENT, OnPreviewPercent );
        SC_HANDLE_DIALOG_MESSAGE( PWM_ITEMDELETED, OnItemDeleted );
        SC_HANDLE_DIALOG_MESSAGE( PWM_WIAEVENT, OnWiaEvent );
        SC_HANDLE_DIALOG_MESSAGE( WM_TIMER, OnTimer );
        SC_HANDLE_DIALOG_MESSAGE( WM_HELP, OnHelp );
        SC_HANDLE_DIALOG_MESSAGE( WM_CONTEXTMENU, OnContextMenu );
        SC_HANDLE_DIALOG_MESSAGE( WM_SYSCOLORCHANGE, OnSysColorChange );
    }
    SC_END_DIALOG_MESSAGE_HANDLERS();
}

// Static hook-related data
HWND CCameraAcquireDialog::s_hWndDialog = NULL;
HHOOK CCameraAcquireDialog::s_hMessageHook = NULL;