//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997.
//
//  File:       Progress.cpp
//
//  Contents:   Progress Dialog
//
//  Classes:
//
//  Notes:
//
//  History:    05-Nov-97   Susia      Created.
//
//--------------------------------------------------------------------------

#include "precomp.h"

#define TIMERID_TRAYANIMATION 3
#define TRAYANIMATION_SPEED 500 // speed in milliseconds

#define TIMERID_NOIDLEHANGUP 4  // inform wininet that connection isn't idle.
#define NOIDLEHANGUP_REFRESHRATE (1000*30) // inform of idle every 30 seconds.

#define TIMERID_KILLHANDLERS 5
#define TIMERID_KILLHANDLERSMINTIME (1000*15) // minimum timeout for kill handlers that are hung after cancel.
#define TIMERID_KILLHANDLERSWIN9XTIME (1000*60) // Timeout for for kill handlers other than NT 5.0

const TCHAR c_szTrayWindow[]            = TEXT("Shell_TrayWnd");
const TCHAR c_szTrayNotifyWindow[]      = TEXT("TrayNotifyWnd");

#ifndef IDANI_CAPTION
#define IDANI_CAPTION   3
#endif // IDANI_CAPTION

// list collapsed items first so only have to loop
// though first item to cbNumDlgResizeItemsCollapsed when
// not expanded.
const DlgResizeList g_ProgressResizeList[] = {
    IDSTOP,DLGRESIZEFLAG_PINRIGHT,
    IDC_DETAILS,DLGRESIZEFLAG_PINRIGHT,
    IDC_RESULTTEXT,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT,
    IDC_STATIC_WHATS_UPDATING,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT,
    IDC_STATIC_WHATS_UPDATING_INFO,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT,
    IDC_STATIC_HOW_MANY_COMPLETE,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT,
    IDC_SP_SEPARATOR,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT,
    // expanded items
    IDC_TOOLBAR, DLGRESIZEFLAG_PINRIGHT,
    IDC_PROGRESS_TABS,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT | DLGRESIZEFLAG_PINBOTTOM | DLGRESIZEFLAG_PINTOP,
    IDC_UPDATE_LIST,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT | DLGRESIZEFLAG_PINBOTTOM | DLGRESIZEFLAG_PINTOP,
    IDC_SKIP_BUTTON_MAIN,DLGRESIZEFLAG_PINBOTTOM | DLGRESIZEFLAG_PINLEFT,
    IDC_STATIC_SKIP_TEXT,DLGRESIZEFLAG_PINBOTTOM | DLGRESIZEFLAG_PINLEFT,
    IDC_PROGRESS_OPTIONS_BUTTON_MAIN,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINBOTTOM,
    IDC_LISTBOXERROR,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINLEFT | DLGRESIZEFLAG_PINBOTTOM | DLGRESIZEFLAG_PINTOP,
    IDC_PROGRESSRESIZESCROLLBAR,DLGRESIZEFLAG_PINRIGHT | DLGRESIZEFLAG_PINBOTTOM,
};

extern HINSTANCE g_hInst;      // current instance
extern TCHAR g_szSyncMgrHelp[];
extern ULONG g_aContextHelpIds[];

extern OSVERSIONINFOA g_OSVersionInfo; // osVersionInfo, setup by WinMain.
extern LANGID g_LangIdSystem; // langID of system we are running on.
extern DWORD g_WMTaskbarCreated; // TaskBar Created WindowMessage;

BOOL CALLBACK ProgressWndProc(HWND hwnd, UINT uMsg,WPARAM wParam,LPARAM lParam);

//defined in progtab.cpp
extern BOOL CALLBACK UpdateProgressWndProc(HWND hwnd, UINT uMsg,WPARAM wParam,LPARAM lParam);
extern BOOL CALLBACK ResultsProgressWndProc(HWND hwnd, UINT uMsg,WPARAM wParam,LPARAM lParam);
extern BOOL OnProgressResultsMeasureItem(HWND hwnd,CProgressDlg *pProgress, UINT *horizExtent, UINT idCtl, MEASUREITEMSTRUCT *pMeasureItem);
extern BOOL OnProgressResultsDeleteItem(HWND hwnd, UINT idCtl, const DELETEITEMSTRUCT * lpDeleteItem);
extern void OnProgressResultsSize(HWND hwnd,CProgressDlg *pProgress,UINT uMsg,WPARAM wParam,LPARAM lParam);

//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::CProgressDlg()
//
//  PURPOSE:  Constructor
//
//      COMMENTS: Constructor for progress dialog
//
//
//--------------------------------------------------------------------------------
CProgressDlg::CProgressDlg(REFCLSID rclsid)
{

    m_clsid = rclsid;
    m_cInternalcRefs = 0;
    m_hwnd = NULL;
    m_hwndTabs = NULL;
    m_errorimage = NULL;
    m_HndlrQueue = NULL;
    m_iProgressSelectedItem = -1;
    m_iItem = -1;           // index to any new item to the list box.
    m_iResultCount = -1;    // Number of logged results
    m_iErrorCount = 0;      // Number of logged errors
    m_iWarningCount = 0;    // Number of logged warnings
    m_iInfoCount = 0;       // Number of logged info
    m_dwThreadID = -1;
    m_nCmdShow = SW_SHOWNORMAL;
//    m_hRasConn = NULL;
    m_pSyncMgrIdle = NULL;
    m_fHasShellTrayIcon = FALSE;
    m_fAddedIconToTray = FALSE;
    m_fnResultsListBox = NULL;

    m_ulIdleRetryMinutes = 0;
    m_ulDelayIdleShutDownTime = 0;

    m_fHwndRightToLeft = FALSE;

    m_iLastItem = -1;
    m_dwLastStatusType = -1;

    m_dwHandleThreadNestcount = 0;
    m_dwShowErrorRefCount = 0;
    m_dwSetItemStateRefCount = 0;
    m_dwHandlerOutCallCount = 0;
    m_dwPrepareForSyncOutCallCount = 0;
    m_dwSynchronizeOutCallCount = 0;
    m_dwQueueTransferCount = 0;
    m_clsidHandlerInSync = GUID_NULL;
    m_fForceClose = FALSE;
      
    // setup KillTimerTimeout based on paltform

    if ( (VER_PLATFORM_WIN32_NT == g_OSVersionInfo.dwPlatformId) 
        && (g_OSVersionInfo.dwMajorVersion >=  5))
    {
        m_nKillHandlerTimeoutValue = TIMERID_KILLHANDLERSMINTIME;
    }
    else
    {
        m_nKillHandlerTimeoutValue = TIMERID_KILLHANDLERSWIN9XTIME;
    }


    m_dwProgressFlags = PROGRESSFLAG_NEWDIALOG;
    m_pItemListView =  NULL;
    m_iTrayAniFrame = IDI_SYSTRAYANI6; // initialize to end.

    LoadString(g_hInst, IDS_STOPPED,            m_pszStatusText[0], MAX_STRING_RES);
    LoadString(g_hInst, IDS_SKIPPED,            m_pszStatusText[1], MAX_STRING_RES);
    LoadString(g_hInst, IDS_PENDING,            m_pszStatusText[2], MAX_STRING_RES);
    LoadString(g_hInst, IDS_SYNCHRONIZING,      m_pszStatusText[3], MAX_STRING_RES);
    LoadString(g_hInst, IDS_SUCCEEDED,          m_pszStatusText[4], MAX_STRING_RES);
    LoadString(g_hInst, IDS_FAILED,                     m_pszStatusText[5], MAX_STRING_RES);
    LoadString(g_hInst, IDS_PAUSED,                     m_pszStatusText[6], MAX_STRING_RES);
    LoadString(g_hInst, IDS_RESUMING,           m_pszStatusText[7], MAX_STRING_RES);


    // Determine if SENS is installed
    LPNETAPI pNetApi;

    m_fSensInstalled = FALSE;
    if (pNetApi = gSingleNetApiObj.GetNetApiObj())
    {
        m_fSensInstalled = pNetApi->IsSensInstalled();
        pNetApi->Release();
    }

    m_CurrentListEntry = NULL;
    
}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::AddRefProgressDialog, private
//
//  Synopsis:   Called to Addref ourselves
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    26-Aug-98       rogerg        Created.
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CProgressDlg::AddRefProgressDialog()
{
ULONG cRefs;

    cRefs = ::AddRefProgressDialog(m_clsid,this); // addref GlobalRef

    Assert(0 <= m_cInternalcRefs);
    ++m_cInternalcRefs;

    return cRefs;
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::ReleaseProgressDialog, private
//
//  Synopsis:   Called to Release ourselves
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    26-Aug-98       rogerg        Created.
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CProgressDlg::ReleaseProgressDialog(BOOL fForce)
{
ULONG cRefs;

    Assert(0 < m_cInternalcRefs);
    --m_cInternalcRefs;

    cRefs = ::ReleaseProgressDialog(m_clsid,this,fForce); // release global ref.

    return cRefs;
}


//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::Initialize()
//
//  PURPOSE:  Initialize the Progress Dialog
//
//      COMMENTS: Implemented on main thread.
//
//
//--------------------------------------------------------------------------------
BOOL CProgressDlg::Initialize(DWORD dwThreadID,int nCmdShow)
{
BOOL fCreated = FALSE;
    Assert(NULL == m_hwnd);

    m_nCmdShow = nCmdShow;

    if (NULL == m_hwnd)
    {
        m_dwThreadID = dwThreadID;

        m_hwnd =  CreateDialogParam(g_hInst,MAKEINTRESOURCE(IDD_PROGRESS),NULL,  (DLGPROC) ProgressWndProc,
                        (LPARAM) this);

        if (!m_hwnd)
            return FALSE;

        // expand/collapse dialog base on use settings.
        RegGetProgressDetailsState(m_clsid,&m_fPushpin, &m_fExpanded);
        ExpandCollapse(m_fExpanded, TRUE);

        // Set the state of the thumbtack
        if (m_fPushpin)
        {
            SendDlgItemMessage(m_hwnd, IDC_TOOLBAR, TB_SETSTATE, IDC_PUSHPIN,
                               MAKELONG(TBSTATE_CHECKED | TBSTATE_ENABLED, 0));
            SendMessage(m_hwnd, WM_COMMAND, IDC_PUSHPIN, 0);
        }


        m_HndlrQueue = new CHndlrQueue(QUEUETYPE_PROGRESS,this); // reivew, queueu should be created and passed in initialize??


        // if this is the idle progress then need to load up msidle and
        // set up the callback

        if (m_clsid == GUID_PROGRESSDLGIDLE)
        {
        BOOL fIdleSupport = FALSE;

            m_pSyncMgrIdle = new CSyncMgrIdle();

            if (m_pSyncMgrIdle)
            {
                fIdleSupport = m_pSyncMgrIdle->Initialize();

                if (FALSE == fIdleSupport)
                {
                    delete m_pSyncMgrIdle;
                    m_pSyncMgrIdle = NULL;
                }
            }

            // if couldn't load idle, then return a failure
            if (FALSE == fIdleSupport)
            {
                return FALSE;
            }

        }


        fCreated = TRUE;

        // When Window is first created show with specified nCmdShow.
        // Review if want to wait to show window until transfer comes in.
        UpdateWndPosition(nCmdShow,TRUE /* fForce */);

    }


   Assert(m_hwnd);

   UpdateWindow(m_hwnd);
   return TRUE;
}

//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::OnSize(UINT uMsg,WPARAM wParam,LPARAM lParam)
//
//  PURPOSE:  moves and resizes dialog items based on current window size.
//
//--------------------------------------------------------------------------------

void CProgressDlg::OnSize(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
ULONG cbNumResizeItems;
HWND hwndSizeGrip;

    cbNumResizeItems  =  m_fExpanded ? m_cbNumDlgResizeItemsExpanded :
                                        m_cbNumDlgResizeItemsCollapsed;

    ResizeItems(cbNumResizeItems,m_dlgResizeInfo);

    // if expanded and not maximized show the resize, else hide it.
    hwndSizeGrip = GetDlgItem(m_hwnd,IDC_PROGRESSRESIZESCROLLBAR);
    if (hwndSizeGrip)
    {
    int nCmdShow = (m_fMaximized || !m_fExpanded) ? SW_HIDE : SW_NORMAL;

	// temporarily always hide if right to left
	if (m_fHwndRightToLeft)
	{
	    nCmdShow = SW_HIDE;
        }

        ShowWindow(hwndSizeGrip,nCmdShow);
    }

    // tell the error listbox it needs to recalc its items heights
    OnProgressResultsSize(m_hwnd,this,WM_SIZE,0,0);
}



//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::OffIdle()
//
//  PURPOSE:  Informs progress dialog that the machine is no longer Idle.
//
// Scenarios
//
//      dialog is maximized, just keep processing.
//      dialog is minimized or in tray - Set our wait timer for a specified time
//          if dialog is still minimized, go away
//          if dialog is now maximized just keep processing.
//
//--------------------------------------------------------------------------------

void CProgressDlg::OffIdle()
{

    Assert(!(PROGRESSFLAG_DEAD & m_dwProgressFlags)); // make sure not notified after dialog gone.

     m_dwProgressFlags |=  PROGRESSFLAG_INOFFIDLE;

    // set flag that we received the OffIdle
    m_dwProgressFlags |= PROGRESSFLAG_RECEIVEDOFFIDLE;

    // reset Flag so know that no longer registered for Idle.
    m_dwProgressFlags &=  ~PROGRESSFLAG_REGISTEREDFOROFFIDLE;

     // make sure in all cases we release the idle reference.

    // if in shutdownmode or cancel has already been pressed
    // by the user or if window is visible but not in the Tray.
    // then don't bother with the wait.

    if ( !IsWindowVisible(m_hwnd) && m_fHasShellTrayIcon
        && !(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP)
        && !(m_dwProgressFlags & PROGRESSFLAG_CANCELPRESSED) 
        && (m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS) )
    {
    HANDLE hTimer =  CreateEvent(NULL,TRUE,FALSE,NULL);

        // should use Create/SetWaitable timer to accomplish this but these
        // functions aren't available on Win9x yet.

        if (hTimer)
        {
        UINT uTimeOutValue = m_ulDelayIdleShutDownTime;

            Assert(sizeof(UINT) >= sizeof(HANDLE));

            DoModalLoop(hTimer,NULL,m_hwnd,TRUE,uTimeOutValue);

            CloseHandle(hTimer);
        }
    }


    // now after our wait, check the window placement again
    // if window is not visible or is in the tray
    // then do a cancel on behalf of the User,

    if ( (!IsWindowVisible(m_hwnd) || m_fHasShellTrayIcon)
        && !(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP)
        && !(m_dwProgressFlags & PROGRESSFLAG_CANCELPRESSED) )
    {
        OnCancel(TRUE);
    }


    // now if we aren't syncing any items and no items
    // waiting in the queue release our idle lock
    // !!! warning. don't release until after wait above to
    // don't have to worry about next idle firing before
    // this method is complete.

    if (!(m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS)
        || !(m_dwQueueTransferCount) )
    {
        ReleaseIdleLock();
    }

    m_dwProgressFlags &=  ~PROGRESSFLAG_INOFFIDLE;

    ReleaseProgressDialog(m_fForceClose); // release our addref.
}


//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::OnIdle()
//
//  PURPOSE:  Informs progress dialog that the machine is still
//          Idle after a set amount of time.
//
//
//--------------------------------------------------------------------------------

void CProgressDlg::OnIdle()
{
CSynchronizeInvoke *pSyncMgrInvoke;

    Assert(!(m_dwProgressFlags & PROGRESSFLAG_DEAD)); // make sure not notified after dialog gone.

    // if received and offIdle then ignore this idle until next ::transfer
    if (!(m_dwProgressFlags & PROGRESSFLAG_RECEIVEDOFFIDLE)
        && !(m_dwProgressFlags & PROGRESSFLAG_INOFFIDLE) )
    {
        AddRefProgressDialog(); // hold alive until next items are queued.

        pSyncMgrInvoke = new CSynchronizeInvoke;

        if (pSyncMgrInvoke)
        {
            pSyncMgrInvoke->RunIdle();
            pSyncMgrInvoke->Release();
        }

        ReleaseProgressDialog(m_fForceClose);
    }
}

//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::SetIdleParams()
//
//  PURPOSE:  Sets the IdleInformation, last writer wins.
//
//
//--------------------------------------------------------------------------------

void CProgressDlg::SetIdleParams( ULONG ulIdleRetryMinutes,ULONG ulDelayIdleShutDownTime
                                 ,BOOL fRetryEnabled)
{
    Assert(m_clsid == GUID_PROGRESSDLGIDLE);

    m_ulIdleRetryMinutes = ulIdleRetryMinutes;
    m_ulDelayIdleShutDownTime = ulDelayIdleShutDownTime;

    if (fRetryEnabled)
    {
        m_dwProgressFlags |=  PROGRESSFLAG_IDLERETRYENABLED;
    }
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   BOOL CProgressDlg::InitializeToolbar(HWND hwnd)
//
//  PURPOSE:    What dialog would be complete without a toolbar, eh?
//
//  RETURN VALUE:
//      TRUE if everything succeeded, FALSE otherwise.
//
//--------------------------------------------------------------------------------
BOOL CProgressDlg::InitializeToolbar(HWND hwnd)
{
HWND hwndTool;
HIMAGELIST himlImages = ImageList_LoadBitmap(g_hInst,
                                        MAKEINTRESOURCE(IDB_PUSHPIN), 16, 0,
                                        RGB(255, 0, 255));

    hwndTool  = GetDlgItem(hwnd,IDC_TOOLBAR);

    //If we can't create the pushpin window
    //the user just won't get a pushpin.

    if (hwndTool)
    {
#ifdef _ZORDER
        SetWindowPos(GetDlgItem(hwnd, IDC_PROGRESS_TABS),HWND_BOTTOM,0,0,0,0,
                 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);

        SetWindowPos(GetDlgItem(hwnd, IDC_PROGRESSRESIZESCROLLBAR),
                 HWND_BOTTOM,0,0,0,0,SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);

#endif // _ZORDER

        TBBUTTON tb = { IMAGE_TACK_OUT, IDC_PUSHPIN, TBSTATE_ENABLED, TBSTYLE_CHECK, 0, 0 };
        SendMessage(hwndTool, TB_SETIMAGELIST, 0, (LPARAM) himlImages);
        SendMessage(hwndTool, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
        SendMessage(hwndTool, TB_SETBUTTONSIZE, 0, MAKELONG(14, 14));
        SendMessage(hwndTool, TB_SETBITMAPSIZE, 0, MAKELONG(14, 14));
        SendMessage(hwndTool, TB_ADDBUTTONS, 1, (LPARAM) &tb);
    }

    return (0);
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::InitializeTabs(HWND hwnd)
//
//  PURPOSE:    Initializes the tab control on the dialog.
//
//  RETURN VALUE:
//      TRUE if everything succeeded, FALSE otherwise.
//
//--------------------------------------------------------------------------------

BOOL CProgressDlg::InitializeTabs(HWND hwnd)
{
    m_hwndTabs = GetDlgItem(hwnd, IDC_PROGRESS_TABS);
        TC_ITEM tci;
    TCHAR szRes[MAX_STRING_RES];

        if (!m_hwndTabs )
                return FALSE;

    // "Updates"
    tci.mask = TCIF_TEXT;
    LoadString(g_hInst, IDS_UPDATETAB, szRes, ARRAY_SIZE(szRes));
    tci.pszText = szRes;
    TabCtrl_InsertItem(m_hwndTabs,PROGRESS_TAB_UPDATE, &tci);

    // "Results"
    LoadString(g_hInst, IDS_ERRORSTAB, szRes, ARRAY_SIZE(szRes));
    tci.pszText = szRes;
        TabCtrl_InsertItem(m_hwndTabs, PROGRESS_TAB_ERRORS, &tci);

    //Set the tab to the Update page to begin with
    m_iTab = PROGRESS_TAB_UPDATE;

    if (-1 != TabCtrl_SetCurSel(m_hwndTabs, PROGRESS_TAB_UPDATE))
    {
            m_iTab = PROGRESS_TAB_UPDATE;
            ShowWindow(GetDlgItem(hwnd, IDC_LISTBOXERROR), SW_HIDE);
    }
    else //ToDo:  What do we do if the set tab fails?
    {
            m_iTab = -1;
            return FALSE;
    }

    return (TRUE);
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::InitializeUpdateList(HWND hwnd)
//
//  PURPOSE:    Initializes the update list view control on the progress dialog.
//
//  RETURN VALUE:
//      TRUE if everything succeeded, FALSE otherwise.
//
//--------------------------------------------------------------------------------


BOOL CProgressDlg::InitializeUpdateList(HWND hwnd)
{
HWND hwndList = GetDlgItem(hwnd,IDC_UPDATE_LIST);
HIMAGELIST himage;
TCHAR pszProgressColumn[MAX_STRING_RES + 1];
int iListViewWidth;

    if (hwndList)
    {

        m_pItemListView = new CListView(hwndList,hwnd,IDC_UPDATE_LIST,WM_BASEDLG_NOTIFYLISTVIEWEX);
    }

    if (!m_pItemListView)
    {
        return FALSE;
    }

    if (m_pItemListView)
    {
    UINT ImageListflags;

        ImageListflags = ILC_COLOR | ILC_MASK;
        if (IsHwndRightToLeft(hwnd))
        {
             ImageListflags |=  ILC_MIRROR;
        }

        // create an imagelist
        himage = ImageList_Create( GetSystemMetrics(SM_CXSMICON),
                         GetSystemMetrics(SM_CYSMICON),ImageListflags,5,20);
        if (himage)
        {
            m_pItemListView->SetImageList(himage,LVSIL_SMALL);
        }

        // for column widths go 35%, 20%, 45%
        iListViewWidth = CalcListViewWidth(hwndList,410);

        //set up the columns
        LoadString(g_hInst, IDS_PROGRESS_DLG_COLUMN_NAME, pszProgressColumn, MAX_STRING_RES);
        InsertListViewColumn(m_pItemListView,PROGRESSLIST_NAMECOLUMN,pszProgressColumn,
                                   LVCFMT_LEFT,(iListViewWidth*7)/20 /* cx */);


        LoadString(g_hInst, IDS_PROGRESS_DLG_COLUMN_STATUS, pszProgressColumn, MAX_STRING_RES);
        InsertListViewColumn(m_pItemListView,PROGRESSLIST_STATUSCOLUMN,pszProgressColumn,
                           LVCFMT_LEFT,(iListViewWidth/5) /* cx */);


        LoadString(g_hInst, IDS_PROGRESS_DLG_COLUMN_INFO, pszProgressColumn, MAX_STRING_RES);
        InsertListViewColumn(m_pItemListView,PROGRESSLIST_INFOCOLUMN,pszProgressColumn,
                           LVCFMT_LEFT,(iListViewWidth*9)/20 /* cx */);

    }

    return (TRUE);
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::InitializeResultsList(HWND hwnd)
//
//  PURPOSE:    Initializes the results list view control on the progress dialog.
//
//  RETURN VALUE:
//      TRUE if everything succeeded, FALSE otherwise.
//
//--------------------------------------------------------------------------------

BOOL CProgressDlg::InitializeResultsList(HWND hwnd)
{
HWND hwndList = GetDlgItem(hwnd,IDC_LISTBOXERROR);
LBDATA  *pData = NULL;
TCHAR pszError[MAX_STRING_RES];
UINT ImageListflags;


    LoadString(g_hInst, IDS_NOERRORSREPORTED, pszError, ARRAY_SIZE(pszError));

    if (!hwndList)
            return FALSE;

    SetCtrlFont(hwndList,g_OSVersionInfo.dwPlatformId,g_LangIdSystem);


        // Allocate a struct for the item data
    if (!(pData = (LBDATA *) ALLOC(sizeof(LBDATA) + sizeof(pszError))))
    {
        return FALSE;
    }


    pData->fIsJump = FALSE;
    pData->fTextRectValid = FALSE;
    pData->fHasBeenClicked = FALSE;
    pData->fAddLineSpacingAtEnd = FALSE;
    pData->ErrorID = GUID_NULL;
    pData->dwErrorLevel = SYNCMGRLOGLEVEL_INFORMATION;
    pData->pHandlerID = 0;
    lstrcpy(pData->pszText, pszError);

    // create the error image list based on the current System
    // metrics
    m_iIconMetricX =   GetSystemMetrics(SM_CXSMICON);
    m_iIconMetricY =   GetSystemMetrics(SM_CYSMICON);


    ImageListflags = ILC_COLOR | ILC_MASK;
    if (IsHwndRightToLeft(hwnd))
    {
         ImageListflags |=  ILC_MIRROR;
    }

    m_errorimage = ImageList_Create(m_iIconMetricX,m_iIconMetricY,ImageListflags, 0, MAX_ERR0R_ICONS);

    // load up the Error Images, if fail then just won't be an image next to the text
    if (m_errorimage)
    {
        m_ErrorImages[ErrorImage_Information] = ImageList_AddIcon(m_errorimage,LoadIcon(NULL,IDI_INFORMATION));
        m_ErrorImages[ErrorImage_Warning] = ImageList_AddIcon(m_errorimage,LoadIcon(NULL,IDI_WARNING));
        m_ErrorImages[ErrorImage_Error] = ImageList_AddIcon(m_errorimage,LoadIcon(NULL,IDI_ERROR));
    }
    else
    {
        m_ErrorImages[ErrorImage_Information] = m_ErrorImages[ErrorImage_Warning] =
             m_ErrorImages[ErrorImage_Error] = -1;
    }

    //Add a default Icon
    pData->IconIndex = m_ErrorImages[ErrorImage_Information];

    // Add the item data
    AddListData(pData, sizeof(pszError), hwndList);
    return TRUE;

}

void CProgressDlg::ReleaseDlg(WORD wCommandID)
{
    // put into dead state so know not
    // addref/release
    PostMessage(m_hwnd,WM_PROGRESS_RELEASEDLGCMD,wCommandID,0);
}

// notifies choice dialog when it is actually released.
void CProgressDlg::PrivReleaseDlg(WORD wCommandID)
{

    m_dwProgressFlags |=  PROGRESSFLAG_DEAD; // put us in the dead state.

    Assert(0 == m_dwQueueTransferCount); // shouldn't be going away if transfer is in progress!!

    RegSetProgressDetailsState(m_clsid,m_fPushpin, m_fExpanded);
    ShowWindow(m_hwnd,SW_HIDE);

    // if the tray is around hide it now.
    if (m_fHasShellTrayIcon)
    {
        RegisterShellTrayIcon(FALSE);
        m_fHasShellTrayIcon = FALSE;
    }


    switch (wCommandID)
    {
    case RELEASEDLGCMDID_OK: // on an okay sleep a little, then fall through to cancel.
    case RELEASEDLGCMDID_CANCEL:
        Assert(m_HndlrQueue);
    case RELEASEDLGCMDID_DESTROY: // called in Thread creation or initialize failed
    case RELEASEDLGCMDID_DEFAULT:
        if (m_HndlrQueue)
        {
            m_HndlrQueue->FreeAllHandlers();
            m_HndlrQueue->Release();
            m_HndlrQueue = NULL;
        }
        break;
    default:
        AssertSz(0,"Unknown Command");
        break;
    }


    Assert(m_hwnd);

    if (m_fHasShellTrayIcon)
    {
        RegisterShellTrayIcon(FALSE);
    }

     if (m_pSyncMgrIdle)
     {
        delete m_pSyncMgrIdle;
     }



    // if this is an idle progress then release our lock on the idle
    if (m_clsid == GUID_PROGRESSDLGIDLE)
    {
        ReleaseIdleLock();
    }


    if (m_pItemListView)
    {
        delete m_pItemListView;
        m_pItemListView = NULL;
    }

    if (m_hwnd)
        DestroyWindow(m_hwnd);

    delete this;

    return;
}

// updates window Z-Order and min/max state.
void CProgressDlg::UpdateWndPosition(int nCmdShow,BOOL fForce)
{
BOOL fRemoveTrayIcon = FALSE;
BOOL fWindowVisible = IsWindowVisible(m_hwnd);
BOOL fTrayRequest = ((nCmdShow == SW_MINIMIZE)|| (nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_HIDE));
BOOL fHandledUpdate = FALSE;

    // only time we go to the tray is if the request is a minimize and
    // either the window is invisible or it is a force. note Hide for
    // now is treated as going to the tray.
    //
    // other cases or on tray failure we can just do a setforeground and show window.

    if (fTrayRequest && (fForce || !fWindowVisible))
    {

        if (m_fHasShellTrayIcon || RegisterShellTrayIcon(TRUE))
        {
             // if window was visible hide it and animiate
             if (fWindowVisible)
             {
                AnimateTray(TRUE);
                ShowWindow(m_hwnd,SW_HIDE);
             }

             fHandledUpdate = TRUE;
        }
    }

    if (!fHandledUpdate)
    {
        
        // if haven't handled then make sure window is shown and bring to
        // front

        if (m_fHasShellTrayIcon)
        {
            AnimateTray(FALSE);
        }

        ShowWindow(m_hwnd,SW_SHOW);
        SetForegroundWindow(m_hwnd);

        // if currently have a tray then lets animate
        // fAnimate =  m_fHasShellTrayIcon ? TRUE : FALSE;

        // if the tray is around but we didn't register it this time through then it should
        // be removed

        if (m_fHasShellTrayIcon)
        {
            RegisterShellTrayIcon(FALSE);
        }

    }

}

//--------------------------------------------------------------------------------
//
//  Member: CProgressDlg::AnimateTray
//
//  PURPOSE:  does animation to the tray
//
//  COMMENTS: true means we are animating to the tray, false means back to the hwnd.
//
//
//--------------------------------------------------------------------------------

BOOL CProgressDlg::AnimateTray(BOOL fTrayAdded)
{
BOOL fAnimate;
HWND hwndTray,hWndST;
RECT rcDlg;
RECT rcST;

    fAnimate = FALSE;

   // get rectangles for animation
    if (hwndTray = FindWindow(c_szTrayWindow, NULL))
    {
        if (hWndST = FindWindowEx(hwndTray, NULL, c_szTrayNotifyWindow, NULL))
        {
            GetWindowRect(m_hwnd, &rcDlg);
            GetWindowRect(hWndST, &rcST);

            fAnimate = TRUE;
        }
    }

    if (fAnimate)
    {
        if (fTrayAdded)
        {
            DrawAnimatedRects(m_hwnd, IDANI_CAPTION,&rcDlg,&rcST);
        }
        else
        {
            DrawAnimatedRects(m_hwnd, IDANI_CAPTION,&rcST,&rcDlg);
        }
    }

    return fAnimate;

}


//--------------------------------------------------------------------------------
//
//  Member: CProgressDlg::RegisterShellTrayIcon
//
//  PURPOSE:  Registers/Unregisters the dialog in the Tray.
//
//      COMMENTS:  Up to Caller to do the proper thing with the main hwnd.
//
//
//--------------------------------------------------------------------------------


BOOL CProgressDlg::RegisterShellTrayIcon(BOOL fRegister)
{
NOTIFYICONDATA icondata;


    if (fRegister)
    {
    BOOL fResult;

        m_fHasShellTrayIcon = TRUE;

        fResult = UpdateTrayIcon();

        if (!fResult) // if couldn't ad then say its not added.
        {
            m_fHasShellTrayIcon = FALSE;
        }

        return fResult;

   }
   else // remove ouselves from the tray.
   {
        Assert(TRUE == m_fHasShellTrayIcon);
        icondata.cbSize = sizeof(NOTIFYICONDATA);
        icondata.hWnd = m_hwnd;
        icondata.uID = 1;

        m_fHasShellTrayIcon = FALSE;
        m_fAddedIconToTray = FALSE;

        // ShellNotifyIcon Yields
        Shell_NotifyIcon(NIM_DELETE,&icondata);
   }

   return TRUE;
}

// called to Update the TrayIcon, Keeps track of highest warning state
// and sets the appropriate Icon in the tray. If the item is not already
// in the tray UpdateTrayIcon will not do a thing.

BOOL CProgressDlg::UpdateTrayIcon()
{
NOTIFYICONDATA icondata;
DWORD dwReturn = 0;

    if (m_fHasShellTrayIcon)
    {

        icondata.cbSize = sizeof(NOTIFYICONDATA);
        icondata.hWnd = m_hwnd;
        icondata.uID = 1;
        icondata.uFlags = NIF_ICON  | NIF_MESSAGE;
        icondata.uCallbackMessage = WM_PROGRESS_SHELLTRAYNOTIFICATION;

        // if progress animation is turned on then also animate
        // the tray.
        if (m_dwProgressFlags & PROGRESSFLAG_PROGRESSANIMATION)
        {
            // update the frame

            m_iTrayAniFrame++;

            if (m_iTrayAniFrame > IDI_SYSTRAYANI6)
                m_iTrayAniFrame = IDI_SYSTRAYANI1;

            icondata.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(m_iTrayAniFrame));
        }
        else
        {

            // update the Icon and tip text based on the current state .
            // Review - Currently don't have different Icons.

            if (m_iErrorCount > 0)
            {
                    icondata.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_SYSTRAYERROR));
            }
            else if (m_iWarningCount > 0)
            {
                    icondata.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_SYSTRAYWARNING));
            }
            else
            {
                    icondata.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_SYSTRAYANI1));
            }
        }

        Assert(icondata.hIcon);

        TCHAR szBuf[MAX_STRING_RES];

         icondata.uFlags |= NIF_TIP;

        LoadString(g_hInst, IDS_SYNCMGRNAME, szBuf, ARRAY_SIZE(szBuf));
        lstrcpy(icondata.szTip,szBuf);


        dwReturn = Shell_NotifyIcon(m_fAddedIconToTray ? NIM_MODIFY : NIM_ADD ,&icondata);

        if (0 != dwReturn)
        {
            // possible for Shell_NotifyIcon(NIM_DELETE) to come in while still
            // in shell notify call so only set m_fAddedIconTray to true
            // if still have a shell tray icon after the call.

            if (m_fHasShellTrayIcon)
            {
                m_fAddedIconToTray = TRUE;
            }

            return TRUE;
        }
        else
        {
            // possible this failed becuase a Shell_DeleteIcon was in progress
            // which yields check if really have a Shell Tray and if not
            // reset the AddedIcon Flag
            
            if (!m_fHasShellTrayIcon)
            {
                m_fAddedIconToTray = FALSE;
            }

            return FALSE;
        }
    }

    return FALSE;
}

// given an ID sets the appropriate state.
BOOL CProgressDlg::SetButtonState(int nIDDlgItem,BOOL fEnabled)
{
BOOL fResult = FALSE;
HWND hwndCtrl = GetDlgItem(m_hwnd,nIDDlgItem);
HWND hwndFocus = NULL;

    if (hwndCtrl)
    {
        if (!fEnabled) // don't bother getting focus if not disabling.
        {
            hwndFocus = GetFocus();
        }

        fResult = EnableWindow(GetDlgItem(m_hwnd,nIDDlgItem),fEnabled);

        // if control had the focus. and now it doesn't then tab to the
        // next control
        if (hwndFocus == hwndCtrl
                && !fEnabled)
        {
            SetFocus(GetDlgItem(m_hwnd,IDC_DETAILS));
        }

    }

    return fResult;
}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::InitializeHwnd, private
//
//  Synopsis:   Called by WM_INIT.
//              m_hwnd member is not setup yet so refer to hwnd.
//
//              Sets up items specific to the UI
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    30-Jul-98       rogerg        Created.
//
//----------------------------------------------------------------------------

BOOL CProgressDlg::InitializeHwnd(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
HWND hwndList = GetDlgItem(hwnd,IDC_LISTBOXERROR);

    m_hwnd = hwnd; // setup the hwnd.

    m_fHwndRightToLeft = IsHwndRightToLeft(m_hwnd);

    // IF THE HWND IS RIGHT TO LEFT HIDE
    // SIZE CONTROL UNTIL RESIZE WORKS.

    if (m_fHwndRightToLeft)
    {
        ShowWindow(GetDlgItem(m_hwnd,IDC_PROGRESSRESIZESCROLLBAR),SW_HIDE);
    }
 
    InterlockedExchange(&m_lTimerSet, 0);

    if (m_fnResultsListBox =  (WNDPROC) GetWindowLongPtr(hwndList,GWLP_WNDPROC))
    {
        SetWindowLongPtr(hwndList, GWLP_WNDPROC, (LONG_PTR) ResultsListBoxWndProc);
    }


    m_cbNumDlgResizeItemsCollapsed = 0; // if fail then we don't resize anything.
    m_cbNumDlgResizeItemsExpanded = 0;

     // init resize items, be default nothing will move.
    m_ptMinimumDlgExpandedSize.x = 0;
    m_ptMinimumDlgExpandedSize.y = 0;
    m_cyCollapsed = 0;
    m_fExpanded = FALSE;

    m_fMaximized = FALSE;

    RECT rectParent;

    //Setup the toolbar pushpin
    InitializeToolbar(hwnd);

    if (GetClientRect(hwnd,&rectParent))
    {
    ULONG itemCount;
    DlgResizeList *pResizeList;


        // loop through resize list
        Assert(NUM_DLGRESIZEINFO_PROGRESS == (sizeof(g_ProgressResizeList)/sizeof(DlgResizeList)) );

        pResizeList = (DlgResizeList *) &g_ProgressResizeList;

        // loop through collapsed items
        for (itemCount = 0; itemCount < NUM_DLGRESIZEINFO_PROGRESS_COLLAPSED; ++itemCount)
        {
            if(InitResizeItem(pResizeList->iCtrlId,
                pResizeList->dwDlgResizeFlags,hwnd,&rectParent,&(m_dlgResizeInfo[m_cbNumDlgResizeItemsCollapsed])))
            {
                ++m_cbNumDlgResizeItemsCollapsed; // if fail then we don't resize anything.
                ++m_cbNumDlgResizeItemsExpanded;
            }

            ++pResizeList;
        }

        // loop through expanded items
        for (itemCount = NUM_DLGRESIZEINFO_PROGRESS_COLLAPSED;
                        itemCount < NUM_DLGRESIZEINFO_PROGRESS; ++itemCount)
        {
            if(InitResizeItem(pResizeList->iCtrlId,
                pResizeList->dwDlgResizeFlags,hwnd,&rectParent,&(m_dlgResizeInfo[m_cbNumDlgResizeItemsExpanded])))
            {
                ++m_cbNumDlgResizeItemsExpanded;
            }

            ++pResizeList;
        }

    }

    // store the current width and height as the
    // the min for expanded and as the current expanded height
    // if GetWindowRect fails not much we can do.
    if (GetWindowRect(hwnd,&m_rcDlg))
    {
    RECT rcSep;

        m_ptMinimumDlgExpandedSize.x = m_rcDlg.right - m_rcDlg.left;
        m_ptMinimumDlgExpandedSize.y = m_rcDlg.bottom - m_rcDlg.top;

        // use the separator position as the max height when collapsed
        if (GetWindowRect(GetDlgItem(hwnd, IDC_SP_SEPARATOR), &rcSep))
        {
            m_cyCollapsed = rcSep.top - m_rcDlg.top;
        }
    }


    if (InitializeTabs(hwnd)) // If these fail user just won't see probress..
    {
       InitializeUpdateList(hwnd);
       InitializeResultsList(hwnd);
    }

    // setup font for status text
    SetCtrlFont(GetDlgItem(hwnd,IDC_STATIC_WHATS_UPDATING),g_OSVersionInfo.dwPlatformId,g_LangIdSystem);
    SetCtrlFont(GetDlgItem(hwnd,IDC_STATIC_WHATS_UPDATING_INFO),g_OSVersionInfo.dwPlatformId,g_LangIdSystem);

    Animate_Open(GetDlgItem(hwnd,IDC_UPDATEAVI),MAKEINTRESOURCE(IDA_UPDATE));

    return TRUE; // return true if want to use default focus.
}


//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::OnPaint()
//
//  PURPOSE:    Handle the WM_PAINT message dispatched from the dialog
//
//--------------------------------------------------------------------------------
void CProgressDlg::OnPaint(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    // if not currently animating and
    // have already added things to the dialog then draw the icons
    if (!(m_dwProgressFlags & PROGRESSFLAG_PROGRESSANIMATION)
        && !(m_dwProgressFlags & PROGRESSFLAG_NEWDIALOG) )
    {
    PAINTSTRUCT     ps;
    HDC hDC = BeginPaint(m_hwnd, &ps);

        if (hDC)
        {
        HICON hIcon;

            if (m_iErrorCount > 0)
            {
                hIcon = LoadIcon(NULL,IDI_ERROR);
            }
            else if (m_iWarningCount > 0)
            {
                hIcon = LoadIcon(NULL,IDI_WARNING);
            }
            else
            {
                hIcon = LoadIcon(NULL,IDI_INFORMATION);
            }

            if (hIcon)
            {
                DrawIcon(hDC, 7, 10,hIcon);
            }

            EndPaint(m_hwnd, &ps);
        }
    }

}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::RedrawIcon()
//
//  PURPOSE:    Clear/Draw the completed icon
//
//--------------------------------------------------------------------------------
BOOL CProgressDlg::RedrawIcon()
{
RECT rc;

    rc.left = 0;
    rc.top = 0;
    rc.right = 37;
    rc.bottom = 40;
    InvalidateRect(m_hwnd, &rc, TRUE);

    return TRUE;
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnShowError, private
//
//  Synopsis:   Calls appropriate handlers ShowError method.
//
//  Arguments:  [wHandlerId] - Id of handler to call.
//              [hwndParent]- hwnd to use as the parent.
//              [ErrorId] - Identifies the Error.
//
//  Returns:    NOERROR - If ShowError was called
//              S_FALSE - if already in ShowErrorCall
//              appropriate error codes.
//
//  Modifies:
//
//  History:    04-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------


STDMETHODIMP CProgressDlg::OnShowError(HANDLERINFO *pHandlerId,HWND hwndParent,REFSYNCMGRERRORID ErrorID)
{
HRESULT hr = S_FALSE; // if don't call ShowError should return S_FALSE

    // only allow one ShowError Call at a time.
    if (!(m_dwProgressFlags & PROGRESSFLAG_INSHOWERRORSCALL)
        && !(m_dwProgressFlags &  PROGRESSFLAG_DEAD))
    {
        Assert(!(m_dwProgressFlags &  PROGRESSFLAG_SHOWERRORSCALLBACKCALLED));
        m_dwProgressFlags |= PROGRESSFLAG_INSHOWERRORSCALL;

        // hold alive - stick two references so we can always just release
        // at end of ShowError and ShowErrorCompleted methods.

        m_dwShowErrorRefCount += 2;
        AddRefProgressDialog();
        AddRefProgressDialog();

        hr = m_HndlrQueue->ShowError(pHandlerId,hwndParent,ErrorID);

        m_dwProgressFlags &= ~PROGRESSFLAG_INSHOWERRORSCALL;

       // if callback with an hresult or retry came in while we were
       // in our out call then post the transfer

       if (m_dwProgressFlags & PROGRESSFLAG_SHOWERRORSCALLBACKCALLED)
       {
            m_dwProgressFlags &= ~PROGRESSFLAG_SHOWERRORSCALLBACKCALLED;
            // need to sendmessage so queued up before release.
            SendMessage(m_hwnd,WM_PROGRESS_TRANSFERQUEUEDATA,(WPARAM) 0, (LPARAM) NULL);
       }

       --m_dwShowErrorRefCount;

       // count can go negative if handler calls completion routine on an error. if
        // this is the case just set it to zero
        if ( ((LONG) m_dwShowErrorRefCount) < 0)
        {
            AssertSz(0,"Negative ErrorCount");
            m_dwShowErrorRefCount = 0;
        }
        else
        {
            ReleaseProgressDialog(m_fForceClose);
        }


    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnResetKillHandlersTimers, private
//
//  Synopsis:   Called to reset the Kill Handlers
//              Timer. Called as a SendMessage From the handlrqueue
//              Cancel Call.
//              
//              !!!This funciton won't create the Timer if it doesn't
//              already exist by design since queue could be in a cancel
//              in a state we don't want to force kill as in the case
//              of an offIdle
//
//  Arguments:  
//
//  Returns:    
//
//  Modifies:
//
//  History:    19-Nov-1998       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::OnResetKillHandlersTimers(void)
{

    if (m_lTimerSet && !(m_dwProgressFlags & PROGRESSFLAG_INTERMINATE))
    {

        // SetTimer with the same hwnd and Id will replace the existing.
        Assert(m_nKillHandlerTimeoutValue >= TIMERID_KILLHANDLERSMINTIME);

        SetTimer(m_hwnd,TIMERID_KILLHANDLERS,m_nKillHandlerTimeoutValue,NULL);
    }

}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::OnCancel()
//
//  PURPOSE:  handles cancelling of the dialog
//
//--------------------------------------------------------------------------------

void CProgressDlg::OnCancel(BOOL fOffIdle)
{
    // if dialog isn't dead and not in a showerrorcall then
    // already in a cancel
    // addref/release. if no more refs we will go away.

    if (!fOffIdle)
    {
        // set cancelpressed flag if the user invoked the cancel.
        m_dwProgressFlags |= PROGRESSFLAG_CANCELPRESSED;

        if (!m_lTimerSet)
        {
            InterlockedExchange(&m_lTimerSet, 1);
            Assert(m_nKillHandlerTimeoutValue >= TIMERID_KILLHANDLERSMINTIME);

            SetTimer(m_hwnd,TIMERID_KILLHANDLERS,m_nKillHandlerTimeoutValue,NULL);
        }

        if ( (m_dwProgressFlags & PROGRESSFLAG_REGISTEREDFOROFFIDLE)
                && !(m_dwProgressFlags & PROGRESSFLAG_RECEIVEDOFFIDLE) )
        {
            IdleCallback(STATE_USER_IDLE_END); // make sure offidle gets set if user presses cancel
        }
    }

    if (!(m_dwProgressFlags & PROGRESSFLAG_DEAD) && !m_dwShowErrorRefCount
        && !(m_dwProgressFlags & PROGRESSFLAG_INCANCELCALL)
        && !(m_dwProgressFlags & PROGRESSFLAG_INTERMINATE))
    {
        // just cancel the queue, when items
        // come though cancel

       // it is possible that the dialog has already been removed from the
        // object list and the User hit stop again.

        SetProgressReleaseDlgCmdId(m_clsid,this,RELEASEDLGCMDID_CANCEL); // set so cleanup knows it was stopped by user..

        // if handlethread is in shutdown then just fall through


        if (!(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP))
        {
            
            AddRefProgressDialog(); // hold dialog alive until cancel is complete
            
            // Get the state of the stop button before the call
            // because it could transition to close during the Canel
            BOOL fForceShutdown = !(m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS);
                        
            m_dwProgressFlags |= PROGRESSFLAG_INCANCELCALL;
            if (m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS)
            {
                //
                // Replace the STOP button text with "Stopping" and 
                // disable the button.  This gives the user positive feedback
                // that the operation is being stopped.  We'll re-enable
                // the button whenever it's text is changed.
                //
                const HWND hwndStop = GetDlgItem(m_hwnd, IDSTOP);
                TCHAR szText[80];
                if (0 < LoadString(g_hInst, IDS_STOPPING, szText, ARRAY_SIZE(szText)))
                {
                    SetWindowText(hwndStop, szText);
                }
                EnableWindow(hwndStop, FALSE);
            }
            m_HndlrQueue->Cancel();
            m_dwProgressFlags &= ~PROGRESSFLAG_INCANCELCALL;


            // addref/release lifetime in case locked open.

            // OffIdle case: then do a soft release so dialog doesn't
            // go away.
            
            // Non Idle case:  Set the fForceClose After the call 
            // incase the pushpin change or errors came in during the Cancel
            
            ReleaseProgressDialog(fOffIdle ? m_fForceClose : fForceShutdown );
            
        }
        else
        {
            // set flag so shutdown knows a cancel was pressed
            m_dwProgressFlags |=  PROGRESSFLAG_CANCELWHILESHUTTINGDOWN;
        }
    }
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::OnCommand()
//
//  PURPOSE:    Handle the various command messages dispatched from the dialog
//
//--------------------------------------------------------------------------------
void CProgressDlg::OnCommand(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
WORD wID = LOWORD(wParam);  // item, control, or accelerator identifier
WORD wNotifyCode HIWORD(wParam);

    switch (wID)
    {
    case IDC_SKIP_BUTTON_MAIN:
        {
            if (m_pItemListView)
            {
                if (m_iProgressSelectedItem != -1)
                {
                    //Skip this item:
                   if (!(m_dwProgressFlags &  PROGRESSFLAG_DEAD))
                   {
                    LVHANDLERITEMBLOB lvHandlerItemBlob;

                        lvHandlerItemBlob.cbSize = sizeof(LVHANDLERITEMBLOB);

                        if (m_pItemListView->GetItemBlob(m_iProgressSelectedItem,
                                        (LPLVBLOB) &lvHandlerItemBlob,lvHandlerItemBlob.cbSize))
                        {
                            ++m_dwSetItemStateRefCount;
                            AddRefProgressDialog();

                            m_HndlrQueue->SkipItem(lvHandlerItemBlob.clsidServer,
                                                     lvHandlerItemBlob.ItemID);

                            --m_dwSetItemStateRefCount;
                            ReleaseProgressDialog(m_fForceClose);
                        }
                   }

                    //Disable the Skip button for this item
                   SetButtonState(IDC_SKIP_BUTTON_MAIN,FALSE);
                }

            }
        break;
        }
    case IDC_PROGRESS_OPTIONS_BUTTON_MAIN:
        {
            // !!! if skip has the focus set it to settings since while the
            // settings dialog is open the skip could go disabled.
            if (GetFocus() ==  GetDlgItem(m_hwnd,IDC_SKIP_BUTTON_MAIN))
            {
                SetFocus(GetDlgItem(m_hwnd,IDC_PROGRESS_OPTIONS_BUTTON_MAIN));
            }

            ShowOptionsDialog(m_hwnd);
        }
        break;
    case IDCANCEL:
        wNotifyCode = BN_CLICKED; // make sure notify code is clicked and fall through
    case IDSTOP:
        {
            if (BN_CLICKED == wNotifyCode)
            {
                OnCancel(FALSE);
            }
        }
        break;

    case IDC_PUSHPIN:
        {
            UINT state = (UINT)SendDlgItemMessage(m_hwnd, IDC_TOOLBAR, TB_GETSTATE,
                                            IDC_PUSHPIN, 0);

            m_fPushpin = state & TBSTATE_CHECKED;

            SendDlgItemMessage(m_hwnd, IDC_TOOLBAR, TB_CHANGEBITMAP,
                               IDC_PUSHPIN,
                               MAKELPARAM(m_fPushpin ? IMAGE_TACK_IN : IMAGE_TACK_OUT, 0));
                    
        }
        break;

    case IDC_DETAILS:
        ExpandCollapse(!m_fExpanded, FALSE);
        break;
    default:
        break;
    }

}



//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::ShowProgressTab(int iTab)
//
//  PURPOSE:
//
//--------------------------------------------------------------------------------

void CProgressDlg::ShowProgressTab(int iTab)
{
int nCmdUpdateTab;
int nCmdErrorTab;
int nCmdSettingsButton;
BOOL fIsItemWorking = FALSE;

    m_iTab = iTab;

    EnableWindow(GetDlgItem(m_hwnd, IDC_PROGRESS_TABS), m_fExpanded); // enable/disable tabs based on if dialog is expanded.
    EnableWindow(GetDlgItem(m_hwnd, IDC_TOOLBAR), m_fExpanded); // enable/disable  pushpin based on if dialog is expanded.

    nCmdUpdateTab = ((iTab == PROGRESS_TAB_UPDATE) && (m_fExpanded)) ? SW_SHOW: SW_HIDE;
    nCmdErrorTab = ((iTab == PROGRESS_TAB_ERRORS) && (m_fExpanded)) ? SW_SHOW: SW_HIDE;

    nCmdSettingsButton = ((iTab == PROGRESS_TAB_UPDATE) && (m_fExpanded)
                            && m_fSensInstalled) ? SW_SHOW: SW_HIDE;

    switch (iTab)
    {
        case PROGRESS_TAB_UPDATE:
            // Hide the error listview, show the tasks list
            ShowWindow(GetDlgItem(m_hwnd,IDC_LISTBOXERROR), nCmdErrorTab);
            TabCtrl_SetCurSel(m_hwndTabs, iTab);

            EnableWindow(GetDlgItem(m_hwnd,IDC_UPDATE_LIST),m_fExpanded);

            // only enable the skip button if there is a selection
            // and IsItemWorking()
            if (-1 != m_iProgressSelectedItem)
            {
                fIsItemWorking = IsItemWorking(m_iProgressSelectedItem);
            }

            EnableWindow(GetDlgItem(m_hwnd,IDC_SKIP_BUTTON_MAIN),m_fExpanded && fIsItemWorking);
            EnableWindow(GetDlgItem(m_hwnd,IDC_PROGRESS_OPTIONS_BUTTON_MAIN),m_fExpanded && m_fSensInstalled);

            EnableWindow(GetDlgItem(m_hwnd,IDC_LISTBOXERROR), FALSE);


            ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_SKIP_TEXT),nCmdUpdateTab);
            ShowWindow(GetDlgItem(m_hwnd,IDC_SKIP_BUTTON_MAIN),nCmdUpdateTab);
            ShowWindow(GetDlgItem(m_hwnd,IDC_PROGRESS_OPTIONS_BUTTON_MAIN),nCmdSettingsButton);
            ShowWindow(GetDlgItem(m_hwnd,IDC_UPDATE_LIST),nCmdUpdateTab);

            break;

        case PROGRESS_TAB_ERRORS:
                // Hide the update listview, show the error list
            ShowWindow(GetDlgItem(m_hwnd,IDC_UPDATE_LIST),nCmdUpdateTab);
            ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_SKIP_TEXT),nCmdUpdateTab);
            ShowWindow(GetDlgItem(m_hwnd,IDC_SKIP_BUTTON_MAIN),nCmdUpdateTab);
            ShowWindow(GetDlgItem(m_hwnd,IDC_PROGRESS_OPTIONS_BUTTON_MAIN),nCmdSettingsButton);

            TabCtrl_SetCurSel(m_hwndTabs, iTab);

            EnableWindow(GetDlgItem(m_hwnd,IDC_UPDATE_LIST),FALSE);
            EnableWindow(GetDlgItem(m_hwnd,IDC_SKIP_BUTTON_MAIN),FALSE);
            EnableWindow(GetDlgItem(m_hwnd,IDC_PROGRESS_OPTIONS_BUTTON_MAIN),FALSE);
            EnableWindow(GetDlgItem(m_hwnd,IDC_LISTBOXERROR), m_fExpanded);

            ShowWindow(GetDlgItem(m_hwnd,IDC_LISTBOXERROR), nCmdErrorTab);
            break;
        default:
            AssertSz(0,"Unknown Tab");
            break;
    }

}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::IsItemWorking, private
//
//  Synopsis:  Determines if Skip should be enabled
//              for the listViewItem;
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    12-Aug-98       rogerg        Created.
//
//----------------------------------------------------------------------------

BOOL CProgressDlg::IsItemWorking(int iListViewItem)
{
BOOL fItemWorking;
LPARAM ItemStatus;

    // lastest status is stored in the lParam of the ListBoxItem.
    if (!(m_pItemListView->GetItemlParam(iListViewItem,&ItemStatus)))
    {
        ItemStatus = SYNCMGRSTATUS_STOPPED;
    }

    fItemWorking = ( ItemStatus == SYNCMGRSTATUS_PENDING   ||
                                      ItemStatus == SYNCMGRSTATUS_UPDATING  ||
                                      ItemStatus == SYNCMGRSTATUS_PAUSED    ||
                                      ItemStatus == SYNCMGRSTATUS_RESUMING     );

    return fItemWorking;
}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnNotifyListViewEx, private
//
//  Synopsis:  Handles ListView Notifications
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

LRESULT CProgressDlg::OnNotifyListViewEx(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
int idCtrl = (int) wParam;
LPNMHDR pnmhdr = (LPNMHDR) lParam;

    if ( (IDC_UPDATE_LIST != idCtrl) || (NULL == m_pItemListView))
    {
        Assert(IDC_UPDATE_LIST == idCtrl);
        Assert(m_pItemListView);
        return 0;
    }

    switch (pnmhdr->code)
    {
        case LVN_ITEMCHANGED:
        {
        NM_LISTVIEW *pnmv = (NM_LISTVIEW FAR *) pnmhdr;

                if (pnmv->uChanged == LVIF_STATE)
                {
                    if (pnmv->uNewState & LVIS_SELECTED)
                    {
                        m_iProgressSelectedItem = ((LPNMLISTVIEW) pnmhdr)->iItem;


                        // see if an item is selected and set the properties
                        // button accordingly
                        SetButtonState(IDC_SKIP_BUTTON_MAIN,IsItemWorking(m_iProgressSelectedItem));
                    }
                    else if (pnmv->uOldState & LVIS_SELECTED)
                    {
                        // on deselect see if any other selected items and if not
                        // set skip to false.
                         if (0 == m_pItemListView->GetSelectedCount())
                         {
                            m_iProgressSelectedItem = -1;
                            SetButtonState(IDC_SKIP_BUTTON_MAIN,FALSE);
                         }
                    }
                }

                break;
            }
        default:
            break;
    }


    return 0;

}


//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::OnNotify(HWND hwnd, int idFrom, LPNMHDR pnmhdr)
//
//  PURPOSE:    Handle the various notification messages dispatched from the dialog
//
//--------------------------------------------------------------------------------

LRESULT CProgressDlg::OnNotify(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
int idFrom = (int) wParam;
LPNMHDR pnmhdr = (LPNMHDR) lParam;

    // if notification for UpdateListPass it on.
    if ((IDC_UPDATE_LIST == idFrom) && m_pItemListView)
    {
        return m_pItemListView->OnNotify(pnmhdr);
    }
    else if (IDC_TOOLBAR == idFrom)
    {
        if (pnmhdr->code == NM_KEYDOWN)
        {
            if (((LPNMKEY) lParam)->nVKey == TEXT(' ') )
            {
                UINT state = (UINT)SendDlgItemMessage(m_hwnd, IDC_TOOLBAR, TB_GETSTATE, IDC_PUSHPIN, 0);

                state = state^TBSTATE_CHECKED;

                SendDlgItemMessage(m_hwnd, IDC_TOOLBAR, TB_SETSTATE, IDC_PUSHPIN, state);

                m_fPushpin = state & TBSTATE_CHECKED;
            
                SendDlgItemMessage(m_hwnd, IDC_TOOLBAR, TB_CHANGEBITMAP, IDC_PUSHPIN, 
                                   MAKELPARAM(m_fPushpin ? IMAGE_TACK_IN : IMAGE_TACK_OUT, 0));

            }
            
        }

    }
    else if (IDC_PROGRESS_TABS == idFrom)
    {

        switch (pnmhdr->code)
        {
            case TCN_SELCHANGE:
            {
                // Find out which tab is currently active
                m_iTab = TabCtrl_GetCurSel(GetDlgItem(m_hwnd, IDC_PROGRESS_TABS));
                if (-1 == m_iTab)
                {
                   break;
                }

                ShowProgressTab(m_iTab);
                break;
            }
            default:
                 break;
        }
    }

    return 0;
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::UpdateProgressValues()
//
//  PURPOSE:    Updates the value of the ProgressBar
//
//--------------------------------------------------------------------------------


void CProgressDlg::UpdateProgressValues()
{
int iProgValue;
int iMaxValue;
int iNumItemsComplete;
int iNumItemsTotal;
TCHAR pszcomplete[MAX_STRING_RES];

    if (!m_pItemListView || !m_HndlrQueue)
    {
        return;
    }

    LoadString(g_hInst, IDS_NUM_ITEMS_COMPLETE, pszcomplete, ARRAY_SIZE(pszcomplete));

    if (NOERROR ==  m_HndlrQueue->GetProgressInfo(&iProgValue,&iMaxValue,&iNumItemsComplete,
                                            &iNumItemsTotal) )
    {
    HWND hwndProgress = GetDlgItem(m_hwnd,IDC_PROGRESSBAR);
    TCHAR szHowManBuf[50];

        if (hwndProgress)
        {
            SendMessage(hwndProgress,PBM_SETRANGE,0,MAKELPARAM(0, iMaxValue));
            SendMessage(hwndProgress,PBM_SETPOS,(WPARAM) iProgValue,0);
        }

        wsprintf(szHowManBuf,pszcomplete,iNumItemsComplete,iNumItemsTotal);
        Static_SetText(GetDlgItem(m_hwnd,IDC_STATIC_HOW_MANY_COMPLETE), szHowManBuf);
    }
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::UpdateDetailsInfo(DWORD dwStatusType, HWND hwndList,
//                                                          int iItem, TCHAR *pszItemInfo)
//
//  PURPOSE:    provide info in the non-details progress view.
//
//--------------------------------------------------------------------------------
void CProgressDlg::UpdateDetailsInfo(DWORD dwStatusType,int iItem, TCHAR *pszItemInfo)
{
BOOL fNewNameField = TRUE;
BOOL fInfoField = FALSE;

TCHAR pszItemName[MAX_SYNCMGRITEMNAME + 1];
TCHAR pszFormatString[MAX_PATH + 1];
TCHAR pszNameString[MAX_PATH + 1];

    if ((m_dwLastStatusType == dwStatusType) && (m_iLastItem == iItem))
    {
        fNewNameField = FALSE;
    }

    //Strip the item info of white space
    if (pszItemInfo)
    {
        int i = lstrlen(pszItemInfo) - 1;

        while (i >=0 &&
                  (pszItemInfo[i] == TEXT(' ') || pszItemInfo[i] == TEXT('\n')
                            || pszItemInfo[i] == TEXT('\t')))
        {
                pszItemInfo[i] = NULL;
                i--;
        }
        if (i >= 0)
        {
                fInfoField = TRUE;
        }
    }


    // If Called Callback for an Item in Pending mode
    // but no item text don't bother updating the top display.

    if ((SYNCMGRSTATUS_PENDING == dwStatusType)
           && (FALSE == fInfoField))
    {
        return;
    }


    m_dwLastStatusType = dwStatusType;
    m_iLastItem = iItem;

    if (fNewNameField && m_pItemListView)
    {
            //Get the item name
        *pszItemName = NULL;

        m_pItemListView->GetItemText(iItem,PROGRESSLIST_NAMECOLUMN,
                        pszItemName, MAX_SYNCMGRITEMNAME);

        switch (dwStatusType)
        {
                case SYNCMGRSTATUS_STOPPED:
                {
                        LoadString(g_hInst, IDS_STOPPED_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                case SYNCMGRSTATUS_SKIPPED:
                {
                        LoadString(g_hInst, IDS_SKIPPED_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                case SYNCMGRSTATUS_PENDING:
                {
                        LoadString(g_hInst, IDS_PENDING_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                case SYNCMGRSTATUS_UPDATING:
                {
                        LoadString(g_hInst, IDS_SYNCHRONIZING_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                case SYNCMGRSTATUS_SUCCEEDED:
                {
                        LoadString(g_hInst, IDS_SUCCEEDED_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                case SYNCMGRSTATUS_FAILED:
                {
                        LoadString(g_hInst, IDS_FAILED_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                case SYNCMGRSTATUS_PAUSED:
                {
                        LoadString(g_hInst, IDS_PAUSED_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                case SYNCMGRSTATUS_RESUMING:
                {
                        LoadString(g_hInst, IDS_RESUMING_ITEM,pszFormatString, MAX_PATH);
                }
                break;
                default:
                {
                    AssertSz(0,"Unknown Status Type");
                    lstrcpy(pszFormatString,TEXT("%ws"));
                }

                break;
        }

        wsprintf(pszNameString,pszFormatString, pszItemName);
        Static_SetText(GetDlgItem(m_hwnd,IDC_STATIC_WHATS_UPDATING), pszNameString);

    }

    // if don't have an info field but did update the name then set the info field
    // to blank
    if (fInfoField)
    {
        Static_SetText(GetDlgItem(m_hwnd,IDC_STATIC_WHATS_UPDATING_INFO), pszItemInfo);
    }
    else if (fNewNameField)
    {
        Static_SetText(GetDlgItem(m_hwnd,IDC_STATIC_WHATS_UPDATING_INFO), L"");
    }

}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::HandleProgressUpdate(HWND hwnd, WPARAM wParam,LPARAM lParam)
//
//  PURPOSE:    Handle the progress bar update for the progress dialog
//
//--------------------------------------------------------------------------------
void CProgressDlg::HandleProgressUpdate(HWND hwnd, WPARAM wParam,LPARAM lParam)
{
PROGRESSUPDATEDATA *progressData = (PROGRESSUPDATEDATA *) wParam;
SYNCMGRPROGRESSITEM *lpSyncProgressItem = (SYNCMGRPROGRESSITEM *) lParam;
LVHANDLERITEMBLOB lvHandlerItemBlob;
int iItem = -1;
BOOL fProgressItemChanged = FALSE;


    if (!m_pItemListView)
    {
        return;
    }

    // if emptyItem is in list View delete it.
    lvHandlerItemBlob.cbSize = sizeof(LVHANDLERITEMBLOB);
    lvHandlerItemBlob.clsidServer = (progressData->clsidHandler);
    lvHandlerItemBlob.ItemID = (progressData->ItemID);


    iItem = m_pItemListView->FindItemFromBlob((LPLVBLOB) &lvHandlerItemBlob);

    if (-1 == iItem)
    {
        AssertSz(0,"Progress Update on Item not in ListView");
        return;
    }

    if (SYNCMGRPROGRESSITEM_STATUSTYPE & lpSyncProgressItem->mask)
    {
        if (lpSyncProgressItem->dwStatusType <= SYNCMGRSTATUS_RESUMING) 
        {

            // update the listview items lParam
            m_pItemListView->SetItemlParam(iItem,lpSyncProgressItem->dwStatusType);

            m_pItemListView->SetItemText(iItem,PROGRESSLIST_STATUSCOLUMN,
                                         m_pszStatusText[lpSyncProgressItem->dwStatusType]);

            //Update Skip button if this item is selected
            if (m_iProgressSelectedItem == iItem)
            {
            BOOL fItemComplete = ( (lpSyncProgressItem->dwStatusType == SYNCMGRSTATUS_SUCCEEDED) ||
                                                       (lpSyncProgressItem->dwStatusType == SYNCMGRSTATUS_FAILED) ||
                                                       (lpSyncProgressItem->dwStatusType == SYNCMGRSTATUS_SKIPPED) ||
                                                       (lpSyncProgressItem->dwStatusType == SYNCMGRSTATUS_STOPPED)   );

                EnableWindow(GetDlgItem(m_hwnd,IDC_SKIP_BUTTON_MAIN),!fItemComplete);
            }
        }

    }

    if (SYNCMGRPROGRESSITEM_STATUSTEXT & lpSyncProgressItem->mask )
    {
    #define MAXDISPLAYBUF 256
    TCHAR displaybuf[MAXDISPLAYBUF]; // make a local copy of ths display buf

            *displaybuf = NULL;

            if (NULL != lpSyncProgressItem->lpcStatusText)
            {
                lstrcpyn(displaybuf,lpSyncProgressItem->lpcStatusText,MAXDISPLAYBUF);

                // make sure last char is a null in case handler passed us a large string.
                Assert(MAXDISPLAYBUF > 0);

                TCHAR *pEndBuf = displaybuf + MAXDISPLAYBUF -1;
                *pEndBuf = NULL;
            }

            if (NULL != displaybuf)
            {
                m_pItemListView->SetItemText(iItem,PROGRESSLIST_INFOCOLUMN,displaybuf);
            }


            LPARAM ItemStatus;
            if (!(SYNCMGRPROGRESSITEM_STATUSTYPE & lpSyncProgressItem->mask))
            {
                if (!(m_pItemListView->GetItemlParam(iItem,&ItemStatus)))
                {
                    AssertSz(0,"failed to get item lParam");
                    ItemStatus = SYNCMGRSTATUS_STOPPED;
                }
            }
            else
            {
                    ItemStatus = lpSyncProgressItem->dwStatusType;
            }

            Assert(ItemStatus == ((LPARAM) (DWORD) ItemStatus));

            UpdateDetailsInfo( (DWORD) ItemStatus,iItem, displaybuf);
    }


    // now update the items progress value information
    if (S_OK == m_HndlrQueue->SetItemProgressInfo(progressData->pHandlerID,
                        progressData->wItemId,
                        lpSyncProgressItem,&fProgressItemChanged))
    {

        // recalcing the progress bar and numItems completed values 
        // can become expensive with a large amount of items so it callback
        // was called but didn't change status or min/max of the item
        // don't bother updating the progress values.
        if (fProgressItemChanged)
        {
            UpdateProgressValues();
        }
    }
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::AddListData(LBDATA *pData,int iNumChars, HWND hwndList)
//
//  PURPOSE:    Handle the adding item data to the list for the results pane
//
//--------------------------------------------------------------------------------
void CProgressDlg::AddListData(LBDATA *pData, int iNumChars, HWND hwndList)
{
    // Save current item in global for use by MeasureItem handler

    Assert(NULL == m_CurrentListEntry); // catch any recursion case.

    m_CurrentListEntry = pData;
    // ... add the string first...
    //the text is freed by the list box

    int iItem = ListBox_AddString( hwndList, pData->pszText);
    
     // (Note that the WM_MEASUREITEM is sent at this point)
    // ...now attach the data.


    ListBox_SetItemData( hwndList, iItem, pData);

    m_CurrentListEntry = NULL;

    // pData is freed by the list box

}
//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::HandleLogError(HWND hwnd, WORD pHandlerID,MSGLogErrors *msgLogErrors)
//
//  PURPOSE:    Handle the error logging tab for the progress dialog
//
//--------------------------------------------------------------------------------
void CProgressDlg::HandleLogError(HWND hwnd, HANDLERINFO *pHandlerID,MSGLogErrors *lpmsgLogErrors)
{
LBDATA          *pData = NULL;
INT             iNumChars;
TCHAR           szBuffer[MAX_STRING_RES]; // buffer used for loading string resources
HWND            hwndList;

    hwndList = GetDlgItem(m_hwnd,IDC_LISTBOXERROR);
    if (!hwndList)
    {
        return;
    }

    //Remove the "No Errors" when the first error is encountered
    if (++m_iResultCount == 0)
    {
        ListBox_ResetContent(hwndList);
    }


    // determine if handlerId and ItemId are valid.ItemID
    // if handlerId isn't valid we don't bother with the ItemId

    SYNCMGRHANDLERINFO SyncMgrHandlerInfo;
    SYNCMGRITEM offlineItem;
    DWORD cbItemLen = 0;
    DWORD cbHandlerLen = 0;
    UINT uIDResource;

    // preset both to NULL
    *(SyncMgrHandlerInfo.wszHandlerName) = NULL;
    *(offlineItem.wszItemName) = NULL;

    // if can't get the ParentInfo then don't add the Item
    // pHandlerId can be NULL if we logged the Error Ourselves.
    if (pHandlerID && m_HndlrQueue
        && (NOERROR == m_HndlrQueue->GetHandlerInfo(pHandlerID,&SyncMgrHandlerInfo)))
    {

        cbHandlerLen = lstrlen(SyncMgrHandlerInfo.wszHandlerName);

        // now see if we can get the itemName.
        if (lpmsgLogErrors->mask & SYNCMGRLOGERROR_ITEMID)
        {
        BOOL fHiddenItem;
        CLSID clsidDataHandler;

            if (NOERROR == m_HndlrQueue->GetItemDataAtIndex(pHandlerID,
                                        lpmsgLogErrors->ItemID,
				        &clsidDataHandler,&offlineItem,&fHiddenItem) )
            {
                cbItemLen = lstrlen(offlineItem.wszItemName);
            }

        }
    }

    // note: handlerName can be an empty string even if GetHandlerInfo did not fail and we
    // can still have an Item so need to do the right thing.

    //  cases
    //  valid handler and ItemID in LogError
    // 1) <icon> <handler name> <(item name)>: <message> (valid handler and ItemID in LogError)
    // 2) <icon> <(item name)>: <message>  handler name NULL .
    // 3) <icon> <handler name>: <message> only valid handler in LogError
    // 4) <icon> <message> (handler invalid or mobsync error in LogError)
    // => three different format strings
    //  1,2     - "%ws (%ws): %ws"    // valid item
    //  3       - "%ws: %ws"          // only valid handler.
    //  4       - "%ws"               // no handler or item


    if (cbItemLen)
    {
        uIDResource = IDS_LOGERRORWITHITEMID;
    }
    else if (cbHandlerLen)
    {
        uIDResource = IDS_LOGERRORNOITEMID;
    }
    else
    {
        uIDResource = IDS_LOGERRORNOHANDLER;
    }

    if (0 == LoadString(g_hInst, uIDResource, szBuffer, ARRAY_SIZE(szBuffer)))
    {
        // if couldn't loadstring then set to empty string so an empty string
        // gets logged. If string is truncated will just print the trucated string.
        *szBuffer = NULL;
    }
    // get the number of characters we need to allocate for
    iNumChars = lstrlen(lpmsgLogErrors->lpcErrorText)
                    + cbHandlerLen
                    + cbItemLen
                    + lstrlen(szBuffer);

    // Allocate a struct for the item data
    if (!(pData = (LBDATA *) ALLOC(sizeof(LBDATA) + ((iNumChars + 1)*sizeof(TCHAR)))))
    {
        return;
    }

    // now format the string using the same logic as used to load
    // the proper resource
    if (cbItemLen)
    {
        wsprintf(pData->pszText,szBuffer,SyncMgrHandlerInfo.wszHandlerName,
                                offlineItem.wszItemName,lpmsgLogErrors->lpcErrorText);
    }
    else if (cbHandlerLen)
    {
        wsprintf(pData->pszText,szBuffer,SyncMgrHandlerInfo.wszHandlerName,
                                lpmsgLogErrors->lpcErrorText);
    }
    else
    {
        wsprintf(pData->pszText,szBuffer,lpmsgLogErrors->lpcErrorText);
    }

    // error text is not a jump but has same ErrorID
    pData->fIsJump = FALSE;
    pData->fTextRectValid = FALSE;
    pData->ErrorID = lpmsgLogErrors->ErrorID;
    pData->dwErrorLevel = lpmsgLogErrors->dwErrorLevel;
    pData->pHandlerID = pHandlerID;
    pData->fHasBeenClicked = FALSE;
    pData->fAddLineSpacingAtEnd = FALSE;

    //insert the icon
    //ToDo:Add client customizable icons?
    switch (lpmsgLogErrors->dwErrorLevel)
    {
        case SYNCMGRLOGLEVEL_INFORMATION:
                pData->IconIndex = m_ErrorImages[ErrorImage_Information];
        break;
        case SYNCMGRLOGLEVEL_WARNING:
                ++m_iWarningCount;
                pData->IconIndex = m_ErrorImages[ErrorImage_Warning];
        break;

        case SYNCMGRLOGLEVEL_ERROR:
        default:
                // if an error occurs we want to keep the dialog alive
                ++m_iErrorCount;
                pData->IconIndex = m_ErrorImages[ErrorImage_Error];
        break;
    }

    if (!lpmsgLogErrors->fHasErrorJumps)
    {
         pData->fAddLineSpacingAtEnd = TRUE;
    }

    // Add the item data
    AddListData(pData, (iNumChars)*sizeof(TCHAR), hwndList);
    if (lpmsgLogErrors->fHasErrorJumps)
    {
        //This is make the "For more info" apprear closer,
        //More associated with the item it corresponds to

        // Allocate a struct for the item data
        LoadString(g_hInst, IDS_JUMPTEXT, szBuffer, ARRAY_SIZE(szBuffer));

        // Review, why not strlen instead of total size of szBuffer.
        if (!(pData = (LBDATA *) ALLOC(sizeof(LBDATA) + sizeof(szBuffer))))
        {
            return;
        }

        pData->IconIndex = -1;

        // we always set ErrorID to GUID_NULL if one wasn't given
        // and fHasErrorJumpst to false.
        pData->fIsJump = lpmsgLogErrors->fHasErrorJumps;
        pData->fTextRectValid = FALSE;
        pData->ErrorID = lpmsgLogErrors->ErrorID;
        pData->dwErrorLevel = lpmsgLogErrors->dwErrorLevel;
        pData->pHandlerID = pHandlerID;
        pData->fHasBeenClicked = FALSE;
        pData->fAddLineSpacingAtEnd = TRUE; // always put space after


        lstrcpy(pData->pszText,szBuffer);

        AddListData(pData, sizeof(szBuffer), hwndList);
    }


    // new item could have caused the Scrollbar to be drawn. Need to
    // recalc listbox
    OnProgressResultsSize(m_hwnd,this,WM_SIZE,0,0);


    // if tray icon is shown and not currently syncing any items
    // make sure it has the most up to date info. if syncing just
    // let the timer.

    if (!(m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS))
    {
        UpdateTrayIcon();
    }

    return;
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::HandleDeleteLogError(HWND hwnds)
//
//  PURPOSE:    Deletes matching errors that have been logged.
//
//--------------------------------------------------------------------------------

void  CProgressDlg::HandleDeleteLogError(HWND hwnd,MSGDeleteLogErrors *pDeleteLogError)
{
HWND hwndList = GetDlgItem(m_hwnd,IDC_LISTBOXERROR);
int  iItemCount;
LBDATA  *pData = NULL;


    if (NULL == hwndList)
        return;

    iItemCount  = ListBox_GetCount(hwndList);

    // loop through the logged errors finding any matches.
    // if the passed in ErrorID is GUID_NULL then delete all errors associated with this
    // handler.
    while(iItemCount--)
    {
        if (pData = (LBDATA *) ListBox_GetItemData(hwndList,iItemCount))
        {

            if ((pData->pHandlerID == pDeleteLogError->pHandlerId)
                && ( (pData->ErrorID == pDeleteLogError->ErrorID)
                    || (GUID_NULL == pDeleteLogError->ErrorID) )
                )
            {
                if ( !pData->fIsJump )
                {
                    //
                    // Decrement count for non-jump items only to avoid
                    // double decrements.
                    //

                    m_iResultCount--;

                    if ( pData->dwErrorLevel == SYNCMGRLOGLEVEL_WARNING )
                    {
                        Assert( m_iWarningCount > 0 );
                        m_iWarningCount--;
                    }
                    else if ( pData->dwErrorLevel == SYNCMGRLOGLEVEL_ERROR )
                    {
                        Assert( m_iErrorCount > 0 );
                        m_iErrorCount--;
                    }
                }

                ListBox_DeleteString(hwndList,iItemCount);
            }

        }

    }

    //
    // If all items have been removed, add default no-error item
    //
    iItemCount = ListBox_GetCount(hwndList);

    if ( iItemCount == 0 )
    {
        m_iResultCount = -1;

        TCHAR pszError[MAX_STRING_RES];
        LoadString(g_hInst, IDS_NOERRORSREPORTED, pszError, ARRAY_SIZE(pszError));

        //
        // Allocate a struct for the item data
        //
        LBDATA  *pData = NULL;
        if (!(pData = (LBDATA *) ALLOC(sizeof(LBDATA) + sizeof(pszError))))
            return;

        pData->fIsJump = FALSE;
        pData->fTextRectValid = FALSE;
        pData->fHasBeenClicked = FALSE;
        pData->fAddLineSpacingAtEnd = FALSE;
        pData->ErrorID = GUID_NULL;
        pData->dwErrorLevel = SYNCMGRLOGLEVEL_INFORMATION;
        pData->pHandlerID = 0;
        lstrcpy(pData->pszText, pszError);
        pData->IconIndex = m_ErrorImages[ErrorImage_Information];

        AddListData(pData, sizeof(pszError), hwndList);
    }

    // recalc listbox heights to accomodate the new value.
    OnProgressResultsSize(m_hwnd,this,WM_SIZE,0,0);

    // if tray icon is shown and not currently syncing any items
    // make sure it has the most up to date info. if syncing just
    // let the timer.

    if (!(m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS))
        UpdateTrayIcon();
}


//--------------------------------------------------------------------------------
//
//  FUNCTION:   BOOL CProgressDlg::ShowCompletedProgress(BOOL fComplete)
//
//  PURPOSE:   Show the dialog in the completed state
//
//--------------------------------------------------------------------------------
BOOL CProgressDlg::ShowCompletedProgress(BOOL fComplete,BOOL fDialogIsLocked)
{

    TCHAR szBuf[MAX_STRING_RES];

        LoadString(g_hInst, fComplete? IDS_PROGRESSCOMPLETETITLE : IDS_PROGRESSWORKINGTITLE,
                                        szBuf,
                    ARRAY_SIZE(szBuf));

        SetWindowText(m_hwnd, szBuf);

        if (fComplete)
        {
                ShowWindow(GetDlgItem(m_hwnd,IDC_UPDATEAVI), SW_HIDE);
                ShowWindow(GetDlgItem(m_hwnd,IDC_FOLDER1), SW_HIDE);
                ShowWindow(GetDlgItem(m_hwnd,IDC_FOLDER2), SW_HIDE);
                ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_WHATS_UPDATING), SW_HIDE);
                ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_WHATS_UPDATING_INFO), SW_HIDE);
                ShowWindow(GetDlgItem(m_hwnd,IDC_PROGRESSBAR), SW_HIDE);
                ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_HOW_MANY_COMPLETE), SW_HIDE);

                if (m_iErrorCount > 0 )
                {
                        LoadString(g_hInst, IDS_PROGRESSCOMPLETEERROR, szBuf, ARRAY_SIZE(szBuf));
                }
                else if (m_iWarningCount > 0 )
                {
                        LoadString(g_hInst, IDS_PROGRESSCOMPLETEWARNING, szBuf, ARRAY_SIZE(szBuf));
                }
                else
                {
                        LoadString(g_hInst, IDS_PROGRESSCOMPLETEOK,     szBuf, ARRAY_SIZE(szBuf));

                }

                SetDlgItemText(m_hwnd, IDC_RESULTTEXT, szBuf);
				ShowWindow(GetDlgItem(m_hwnd,IDC_RESULTTEXT), SW_SHOW);

		//Change the Stop to "Close" if the dialog is going to be
                // remained open

                if (fDialogIsLocked)
                {
		    LoadString(g_hInst, IDS_CLOSE, szBuf, ARRAY_SIZE(szBuf));
		    SetWindowText(GetDlgItem(m_hwnd,IDSTOP), szBuf);      
                    EnableWindow(GetDlgItem(m_hwnd,IDSTOP), TRUE);
                }

        }
        else
        {
                ShowWindow(GetDlgItem(m_hwnd,IDC_RESULTTEXT), SW_HIDE);

                ShowWindow(GetDlgItem(m_hwnd,IDC_UPDATEAVI), SW_SHOW);
                ShowWindow(GetDlgItem(m_hwnd,IDC_FOLDER1), SW_SHOW);
                ShowWindow(GetDlgItem(m_hwnd,IDC_FOLDER2), SW_SHOW);
                ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_WHATS_UPDATING), SW_SHOW);
                ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_WHATS_UPDATING_INFO), SW_SHOW);
                ShowWindow(GetDlgItem(m_hwnd,IDC_PROGRESSBAR), SW_SHOW);
                ShowWindow(GetDlgItem(m_hwnd,IDC_STATIC_HOW_MANY_COMPLETE), SW_SHOW);

                //Change the "Close" to "Stop"
		LoadString(g_hInst, IDS_STOP, szBuf, ARRAY_SIZE(szBuf));
		SetWindowText(GetDlgItem(m_hwnd,IDSTOP), szBuf);
                EnableWindow(GetDlgItem(m_hwnd,IDSTOP), TRUE);
        }

        RedrawIcon();

        return TRUE;

}

//--------------------------------------------------------------------------------
//
//  Function:   DoSyncTask
//
//  Synopsis:   Drives the handlers actual synchronization routines
//
//--------------------------------------------------------------------------------

void CProgressDlg::DoSyncTask(HWND hwnd)
{
HANDLERINFO *pHandlerID;
ULONG cDlgRefs;
BOOL fRepostedStart = FALSE; // set if posted message to ourselves.
CLSID pHandlerClsid;

    Assert(!(m_dwProgressFlags &  PROGRESSFLAG_DEAD));

    ++m_dwHandleThreadNestcount;

    // if handlerNestCount is > 1 which it can on a transfer
    // or when multiple items are going even on an error
    // if this is the case then just return
    if (m_dwHandleThreadNestcount > 1)
    {
        Assert(1 == m_dwHandleThreadNestcount);
        m_dwHandleThreadNestcount--;
        return;
    }


    // review - order should be set  inhandleroutcall
    // an then reset flags for completion and transfers.

    // reset callback flag so receive another one if
    // message comes in
    m_dwProgressFlags &= ~PROGRESSFLAG_CALLBACKPOSTED;

    // first thing through make sure all our state is setup.
    // set the syncing flag
    m_dwProgressFlags |= PROGRESSFLAG_SYNCINGITEMS;

    // set our Call flag so callback knows not to post to us
    // if we are handling call
    Assert(!(m_dwProgressFlags & PROGRESSFLAG_INHANDLEROUTCALL));
    m_dwProgressFlags |= PROGRESSFLAG_INHANDLEROUTCALL;
    m_dwProgressFlags &= ~PROGRESSFLAG_COMPLETIONROUTINEWHILEINOUTCALL; // reset completion routine
    m_dwProgressFlags &= ~PROGRESSFLAG_NEWITEMSINQUEUE;  // reset new items in queue flag
    m_dwProgressFlags &=  ~PROGRESSFLAG_STARTPROGRESSPOSTED; // reset post flag.

    if (!(m_dwProgressFlags & PROGRESSFLAG_IDLENETWORKTIMER))
    {

        m_dwProgressFlags |= PROGRESSFLAG_IDLENETWORKTIMER;
        // reset network idle initially to keep hangup from happening and setup a timer
        // to keep resetting the idle until the sync is complete.
        ResetNetworkIdle();
        SetTimer(m_hwnd,TIMERID_NOIDLEHANGUP,NOIDLEHANGUP_REFRESHRATE,NULL);
    }

    UpdateProgressValues();

    if (m_clsid != GUID_PROGRESSDLGIDLE)
    {

        // if there is a server we are currently synchronizing but out count
        // is zero then reset to GUID_NULL else use our clsidHandlerInSync
        // so next handler matches the one we are currently syncing.

        if (0 == m_dwHandlerOutCallCount) // if no outcalls we don't care what handler gets matched.
        {
            m_clsidHandlerInSync = GUID_NULL;
        }

        // find the next set of items that match our criteria by seeing
        // if there is any handler that matches out guid (if guid_null just
        // matches first item in state

        // then loop through all handlers matching the guid
        // in the same state.

        // see if there are any items that need PrepareForSyncCalled on them
        if (NOERROR == m_HndlrQueue->FindFirstHandlerInState(
                                    HANDLERSTATE_PREPAREFORSYNC,m_clsidHandlerInSync,
                                    &pHandlerID,&m_clsidHandlerInSync))
        {
            ++m_dwHandlerOutCallCount;
            ++m_dwPrepareForSyncOutCallCount;
           m_HndlrQueue->PrepareForSync(pHandlerID,hwnd);

           // see if any other handlers that match the clsid and also call their
           // PrepareForSync methods.

            while (NOERROR == m_HndlrQueue->FindFirstHandlerInState(
                                    HANDLERSTATE_PREPAREFORSYNC,m_clsidHandlerInSync,
                                    &pHandlerID,&m_clsidHandlerInSync))
            {
                ++m_dwHandlerOutCallCount;
                ++m_dwPrepareForSyncOutCallCount;
                m_HndlrQueue->PrepareForSync(pHandlerID,hwnd);
            }
        }
        else if ( (0 == m_dwPrepareForSyncOutCallCount)
                    && (NOERROR == m_HndlrQueue->FindFirstHandlerInState(
                                    HANDLERSTATE_SYNCHRONIZE,m_clsidHandlerInSync,&pHandlerID,
                                    &m_clsidHandlerInSync)) )
        {
            // no prepareforsync so if there aren't any more handlers in prerpareforsync
            // calls kick off someones synchronize. see if any synchronize methods.
            ++m_dwHandlerOutCallCount;
            ++m_dwSynchronizeOutCallCount;
            m_HndlrQueue->Synchronize(pHandlerID,hwnd);

            // see if any other handlers that match the clsid and also call their
           // synchronize methods.

            while (NOERROR == m_HndlrQueue->FindFirstHandlerInState(
                                    HANDLERSTATE_SYNCHRONIZE,m_clsidHandlerInSync,
                                    &pHandlerID,&m_clsidHandlerInSync))
            {
                ++m_dwHandlerOutCallCount;
                ++m_dwSynchronizeOutCallCount;
                m_HndlrQueue->Synchronize(pHandlerID,hwnd);
            }
        }

        // set noMoreItemsToSync flag if
    }
    else
    {
        // for idle queue synchronize any items first since
        // no need to call prepareforsync until we have too and don't kick off more than
        // one at a time.

        // a transfer can come in while processing an out call should be the only time
        // this should happen. This can happen on Idle if in a Retry Error when the next
        // idle fires.

        // Assert(0 == m_dwHandlerOutCallCount);

        // not doing anything while still in an out call emulates the old behavior of
        // only ever doing one handler at a time.

        if (0 == m_dwHandlerOutCallCount)
        {
            if (NOERROR == m_HndlrQueue->FindFirstHandlerInState(
                                        HANDLERSTATE_SYNCHRONIZE,GUID_NULL,&pHandlerID,&pHandlerClsid))
            {
                ++m_dwHandlerOutCallCount;
                ++m_dwSynchronizeOutCallCount;
                m_HndlrQueue->Synchronize(pHandlerID,hwnd);
            }
            else if (NOERROR == m_HndlrQueue->FindFirstHandlerInState(
                                        HANDLERSTATE_PREPAREFORSYNC,GUID_NULL,&pHandlerID,&pHandlerClsid))
            {

                 // msidle only allows one idle registration at a time.
                // reset idle in case last handler we called took it away from us

                if (m_pSyncMgrIdle && 
                        (m_dwProgressFlags & PROGRESSFLAG_REGISTEREDFOROFFIDLE))
                {

                    // !!!don't reset the registered if idle flag will do this
                    //  when all handlers are completed.
                    if (!(m_dwProgressFlags & PROGRESSFLAG_RECEIVEDOFFIDLE))
                    {
                        m_pSyncMgrIdle->ReRegisterIdleDetection(this); // reregisterIdle in case handle overrode it.
                        m_pSyncMgrIdle->CheckForIdle();
                    }
                }



                ++m_dwHandlerOutCallCount;
                ++m_dwPrepareForSyncOutCallCount;
               m_HndlrQueue->PrepareForSync(pHandlerID,hwnd);
            }
            else
            {
                // even if nothing to do need to call
                // reset idle hack
                if (m_pSyncMgrIdle && !(m_dwProgressFlags & PROGRESSFLAG_RECEIVEDOFFIDLE)
                    && (m_dwProgressFlags & PROGRESSFLAG_REGISTEREDFOROFFIDLE))
                {

    
                    m_pSyncMgrIdle->ReRegisterIdleDetection(this); // reregisterIdle in case handle overrode it.
                    m_pSyncMgrIdle->CheckForIdle();
                }

            }
        }

    }


    UpdateProgressValues(); // update progress values when come out of calls.

    // no longer in any out calls, reset our flag and see if a completion
    // routine came in or items were added to the queue during our out call
    m_dwProgressFlags &= ~PROGRESSFLAG_INHANDLEROUTCALL;

    if ((PROGRESSFLAG_COMPLETIONROUTINEWHILEINOUTCALL & m_dwProgressFlags)
        || (PROGRESSFLAG_NEWITEMSINQUEUE & m_dwProgressFlags) )
    {
        Assert(!(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP)); // shouldn't get here if shutting down.

        fRepostedStart = TRUE;

        if (!(m_dwProgressFlags &  PROGRESSFLAG_STARTPROGRESSPOSTED))
        {
            m_dwProgressFlags |=  PROGRESSFLAG_STARTPROGRESSPOSTED;
            PostMessage(m_hwnd,WM_PROGRESS_STARTPROGRESS,0,0);
        }
    }

    // if no more items to synchronize and all synchronizations
    // are done and we are currently syncing items then shut things down.
    // if user is currently in a cancel call then don't start shutting
    Assert(!(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP)); // assert if in shutdown this loop shouldn't get called.

    if (!(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP)
        && (0 == m_dwHandlerOutCallCount) && !(fRepostedStart)
            && (m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS))
    {
    BOOL fTransferAddRef;
    BOOL fOffIdleBeforeShutDown = (m_dwProgressFlags & PROGRESSFLAG_RECEIVEDOFFIDLE);
    BOOL fKeepDialogAlive;


        // if no out calls shouldn't be any call specific out calls either

        Assert(0 == m_dwPrepareForSyncOutCallCount);
        Assert(0 == m_dwSynchronizeOutCallCount);

        m_dwProgressFlags |= PROGRESSFLAG_SHUTTINGDOWNLOOP;
        // reset newItemsInQueue so know for sure it got set while we
        // yielded in cleanup calls.
        m_dwProgressFlags &= ~PROGRESSFLAG_NEWITEMSINQUEUE;

        // treat progress as one long out call
        Assert(!(m_dwProgressFlags & PROGRESSFLAG_INHANDLEROUTCALL));
        m_dwProgressFlags |= PROGRESSFLAG_INHANDLEROUTCALL;
        m_dwProgressFlags &= ~PROGRESSFLAG_COMPLETIONROUTINEWHILEINOUTCALL; // reset completion routine.


        // reset PROGRESSFLAG_TRANSFERADDREF flag but don't release
        // if another transfer happens during this shutdown then the transfer
        // will reset the flag and put and addref on. need to store state
        // in case get this shutdown routine called twice without another
        // transfer we don't call too many releases.

        fTransferAddRef = m_dwProgressFlags & PROGRESSFLAG_TRANSFERADDREF;
        Assert(fTransferAddRef); // should always have a transfer at this statge.
        m_dwProgressFlags &= ~PROGRESSFLAG_TRANSFERADDREF;

        SetProgressReleaseDlgCmdId(m_clsid,this,RELEASEDLGCMDID_OK);

        UpdateProgressValues();
        m_HndlrQueue->RemoveFinishedProgressItems(); // let the queue know to reset the progress bar

        // if not in a cancel or setIetmstatus go ahead and release handlers
        // and kill the terminate timer.
        // review if there is a better opportunity to do cleanup.
        if (!(m_dwProgressFlags & PROGRESSFLAG_INCANCELCALL)
            && !(m_dwProgressFlags & PROGRESSFLAG_INTERMINATE)
            && (0 == m_dwSetItemStateRefCount) )
        {
            if (m_lTimerSet)
            {
                 InterlockedExchange(&m_lTimerSet, 0);
                 KillTimer(m_hwnd,TIMERID_KILLHANDLERS);
            }
 
            m_HndlrQueue->ReleaseCompletedHandlers(); // munge the queue.
        }

        fKeepDialogAlive = KeepProgressAlive(); // determine if progress should stick around

        if ((m_dwProgressFlags & PROGRESSFLAG_PROGRESSANIMATION))
         {
            m_dwProgressFlags &= ~PROGRESSFLAG_PROGRESSANIMATION;
            KillTimer(m_hwnd,TIMERID_TRAYANIMATION);

            Animate_Stop(GetDlgItem(m_hwnd,IDC_UPDATEAVI));
            ShowWindow(GetDlgItem(m_hwnd,IDC_UPDATEAVI),SW_HIDE);
            ShowCompletedProgress(TRUE /* fComplete */ ,fKeepDialogAlive /* fDialogIsLocked */);
         }

        UpdateTrayIcon(); // if Icon is showing make sure we have the uptodate one.

        ConnectObj_CloseConnections(); // Close any connections we held open during the Sync.

        if (m_dwProgressFlags & PROGRESSFLAG_IDLENETWORKTIMER)
        {
            m_dwProgressFlags &= ~PROGRESSFLAG_IDLENETWORKTIMER;
            KillTimer(m_hwnd,TIMERID_NOIDLEHANGUP); // don't need to keep connection open.
        }

        // make sure any previous locks on dialog are removed
        // before going into wait logic. This can happen in the case of a retry.
        LockProgressDialog(m_clsid,this,FALSE);

        // if there are no items to lock the progress open and the
        // force close flag isn't set wait in a loop
        // for two seconds
        if (!(fKeepDialogAlive) && (FALSE == m_fForceClose))
        {
        HANDLE hTimer =  CreateEvent(NULL,TRUE,FALSE,NULL);

            // should use Create/SetWaitable timer to accomplish this but these
            // functions aren't available on Win9x yet.
            if (hTimer)
            {
                // sit in loop until timer event sets it.
                DoModalLoop(hTimer,NULL,m_hwnd,TRUE,1000*2);
                CloseHandle(hTimer);
            }
        }
        else
        {
              LockProgressDialog(m_clsid,this,TRUE);
              ExpandCollapse(TRUE,FALSE); // make sure the dialog is expanded.
              ShowProgressTab(PROGRESS_TAB_ERRORS);
        }

        //if the user hit the pushpin after we started the 2 second delay
        if ((m_fPushpin) && !(fKeepDialogAlive))
        {
	      ShowCompletedProgress(TRUE /* fComplete */,TRUE /* fDialogIsLocked */);
              LockProgressDialog(m_clsid,this,TRUE);
        }

        // if this is an idle dialog handle the logic for
        // either releasing the IdleLock or reregistering.
        if (m_pSyncMgrIdle)
        {

            // if we have already received an OffIdle and not
            // still handling the offidle then release the Idle Lock.
            if ( (m_dwProgressFlags & PROGRESSFLAG_RECEIVEDOFFIDLE)
               && !(m_dwProgressFlags &  PROGRESSFLAG_INOFFIDLE)) 
            {

                // Release our IdleLock so TS can fire use again even if progress
                // sticks around.
                ReleaseIdleLock();
            }
            else if ( (m_dwProgressFlags & PROGRESSFLAG_REGISTEREDFOROFFIDLE)
                    && !(m_dwProgressFlags & PROGRESSFLAG_RECEIVEDOFFIDLE) )
            {

                // if have registere for idle but haven't seen it yet then
                // we want to stay alive.

                // if we aren't suppose to repeat idle or
                // user has maximized the window then just call idle as if
                // an OffIdle occured. Mostly done as a safety precaution
                // in case someone has registered for Idle with MSIdle in 
                // our process space so we fail to receive the true offidle

                if (!(m_dwProgressFlags & PROGRESSFLAG_IDLERETRYENABLED)
                    || !m_fHasShellTrayIcon)
                {
                    IdleCallback(STATE_USER_IDLE_END);

                     // release idle lock since OffIdle won't since we are still
                    //  in the syncing Item state.
                    ReleaseIdleLock();
                }
                else
                {
                    // if haven't yet received an offidle reregister
                    m_pSyncMgrIdle->ReRegisterIdleDetection(this); // reregisterIdle in case handle overrode it.
                    m_pSyncMgrIdle->CheckForIdle();

                    // first thing hide our window. Only hide if we are in the shelltray
                    // and no errors have occured.

                    if (m_fHasShellTrayIcon
                            && !(KeepProgressAlive()))
                    {
                        RegisterShellTrayIcon(FALSE);
                    }

                    m_pSyncMgrIdle->ResetIdle(m_ulIdleRetryMinutes);
                }
            }

        }

        // if no new items in the queue no longer need the connection.
        // do this before releasing dialog ref 
        
        if (!(m_dwProgressFlags & PROGRESSFLAG_NEWITEMSINQUEUE))
        {
            m_HndlrQueue->EndSyncSession();
        }
        // see if Release takes care or our progress, if not,
        // there are more things to synchronize.

        if (fTransferAddRef)
        {
            cDlgRefs = ReleaseProgressDialog(m_fForceClose); // release transfer addref.
        }
        else
        {
            Assert(fTransferAddRef);  // this shouldn't happen but if it does addref/release.

            AddRefProgressDialog();
            cDlgRefs = ReleaseProgressDialog(m_fForceClose);
        }


        // !!!! warning - no longer hold onto a reference to
        // this dialog. Do not do anything to allow this thread
        // to be reentrant.

         // its possible that items got placed in the queue why we were in our
        // sleep loop, if so then restart the loop

        m_dwProgressFlags &= ~PROGRESSFLAG_INHANDLEROUTCALL;

        // if there are new items in the queue need to kick off another loop
        if (m_dwProgressFlags & PROGRESSFLAG_NEWITEMSINQUEUE)
        {
           // m_dwProgressFlags &= ~PROGRESSFLAG_NEWITEMSINQUEUE;
            Assert(0 != m_cInternalcRefs);

            // reset the user cancel flag if new items comein
            m_dwProgressFlags &= ~PROGRESSFLAG_CANCELPRESSED;


            if (!(m_dwProgressFlags & PROGRESSFLAG_STARTPROGRESSPOSTED))
            {
                m_dwProgressFlags |= PROGRESSFLAG_STARTPROGRESSPOSTED;
                PostMessage(hwnd,WM_PROGRESS_STARTPROGRESS,0,0); // restart the sync.
            }
        }
        else
        {
            if (m_dwShowErrorRefCount || m_dwSetItemStateRefCount
                || (m_dwProgressFlags & PROGRESSFLAG_INCANCELCALL) 
                || (m_dwProgressFlags & PROGRESSFLAG_INTERMINATE) )
            {
                // cases that crefs should not be zero caused by out calls
                Assert(0 != m_cInternalcRefs);
            }
            else
            {
                // if get here refs should be zero, be an idle or a queue transfer is in
                // progress.ADD
                Assert(0 == m_cInternalcRefs 
                    || (m_dwProgressFlags & PROGRESSFLAG_INOFFIDLE)
                    || (m_dwProgressFlags & PROGRESSFLAG_REGISTEREDFOROFFIDLE)
                    || (m_dwQueueTransferCount));
            }

            m_dwProgressFlags &= ~PROGRESSFLAG_SYNCINGITEMS; // no longer syncing items.

            
        }

        m_dwProgressFlags &=  ~PROGRESSFLAG_CANCELWHILESHUTTINGDOWN; // if cancel came in during shutdown reset flag now.
        m_dwProgressFlags &= ~PROGRESSFLAG_SHUTTINGDOWNLOOP;
    }


    --m_dwHandleThreadNestcount;

}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::KeepProgressAlive, private
//
//  Synopsis:  returns true if progress dialog shouln't go away
//              when the sync is complete
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

BOOL CProgressDlg::KeepProgressAlive()
{
HKEY  hkKeepProgress;
//Default behavior is to stick around on warnings and errors only.
DWORD dwKeepProgressSetting = PROGRESS_STICKY_WARNINGS | PROGRESS_STICKY_ERRORS;
DWORD dwErrorsFlag = 0;
DWORD dwType = REG_DWORD;
DWORD dwDataSize = sizeof(DWORD);

    if (m_fPushpin)
    {
        return TRUE;
    }
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,TOPLEVEL_REGKEY,0,
                                   KEY_READ,&hkKeepProgress))
    {
        RegQueryValueEx(hkKeepProgress,TEXT("KeepProgressLevel"),NULL, &dwType,
                                       (LPBYTE) &(dwKeepProgressSetting),
                                       &dwDataSize);

	RegCloseKey(hkKeepProgress);
    }

    if (m_iInfoCount)
    {
	dwErrorsFlag |= PROGRESS_STICKY_INFO;
    }
    if (m_iWarningCount)
    {
	dwErrorsFlag |= PROGRESS_STICKY_WARNINGS;
    }
    if (m_iErrorCount)
    {
	dwErrorsFlag |= PROGRESS_STICKY_ERRORS;
    }
	
    if (dwKeepProgressSetting & dwErrorsFlag)
    {
        return TRUE;
    }

    return FALSE;
}



//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::TransferQueueData(CHndlrQueue *HndlrQueue)
//
//  PURPOSE:  Get the queue date
//
//      COMMENTS:  transfer items from the specified queue into our queue
//              It is legal fo the HndlrQueue arg to be NULL in the case that
//              the queue is being restarted from a retry. Review - May want
//              to break this function to make the bottom part for the
//              retry a separate function so can assert if someone tries
//              to transfer a NULL queue.
//
//--------------------------------------------------------------------------------

STDMETHODIMP CProgressDlg::TransferQueueData(CHndlrQueue *pHndlrQueue)
{
HRESULT hr = E_UNEXPECTED;

    SendMessage(m_hwnd,WM_PROGRESS_TRANSFERQUEUEDATA,(WPARAM) &hr, (LPARAM) pHndlrQueue);


    return hr;
}



//--------------------------------------------------------------------------------
//
//  FUNCTION: CProgressDlg::TransferQueueData(CHndlrQueue *HndlrQueue)
//
//  PURPOSE:  Get the queue date
//
//      COMMENTS:  transfer items from the specified queue into our queue
//              It is legal fo the HndlrQueue arg to be NULL in the case that
//              the queue is being restarted from a retry. Review - May want
//              to break this function to make the bottom part for the
//              retry a separate function so can assert if someone tries
//              to transfer a NULL queue.
//
//--------------------------------------------------------------------------------

STDMETHODIMP CProgressDlg::PrivTransferQueueData(CHndlrQueue *HndlrQueue)
{
HRESULT hr = NOERROR;


    Assert(!(m_dwProgressFlags & PROGRESSFLAG_DEAD));

    Assert(m_HndlrQueue);
    if (NULL == m_HndlrQueue)
    {
        return E_UNEXPECTED;
    }



    ++m_dwQueueTransferCount;

    if (HndlrQueue && m_HndlrQueue)
    {


        // set the transfer flag so main loop knows there are new items to look at
        m_HndlrQueue->TransferQueueData(HndlrQueue);

         // fill in the list box right away so

        // a) better visual UI
        // b) don't have to worry about race conditions with PrepareForSync.
        //      since adding UI won't make an outgoing call.


        if (m_pItemListView)
        {
            AddItemsFromQueueToListView(m_pItemListView,m_HndlrQueue,
                                LVS_EX_FULLROWSELECT |  LVS_EX_INFOTIP ,SYNCMGRSTATUS_PENDING,
                                -1 /* iDateColumn */ ,PROGRESSLIST_STATUSCOLUMN /*status column */
                                ,FALSE /* fUseHandlerAsParent */,TRUE /* fAddOnlyCheckedItems */);

            // set the selection to the first item
            m_pItemListView->SetItemState(0,
                     LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );

        }


        UpdateProgressValues();
	UpdateWindow(m_hwnd);

        // now check if there is already a transfer in progress and if
        // there isn't post the message, else Addref the progress dialog as appropriate.
    }

    m_dwProgressFlags &= ~PROGRESSFLAG_NEWDIALOG; // no longer a new dialog once something is in the queue.

    ShowCompletedProgress(FALSE,FALSE);

    // if the animation isn't going then start it up.
    if (!(m_dwProgressFlags & PROGRESSFLAG_PROGRESSANIMATION))
    {
       m_dwProgressFlags |= PROGRESSFLAG_PROGRESSANIMATION;

       RedrawIcon();
       ShowProgressTab(PROGRESS_TAB_UPDATE);

       Animate_Play(GetDlgItem(m_hwnd,IDC_UPDATEAVI),0,-1,-1);

       ShowWindow(GetDlgItem(m_hwnd,IDC_UPDATEAVI),SW_SHOW );
       SetTimer(m_hwnd,TIMERID_TRAYANIMATION,TRAYANIMATION_SPEED,NULL);
    }


    // if we are an idle set up our callback
    // successfully loaded msIdle, then set up the callback
    // review - make these progress flags
    if (m_pSyncMgrIdle && !(PROGRESSFLAG_REGISTEREDFOROFFIDLE & m_dwProgressFlags))
    {

         m_dwProgressFlags &= ~PROGRESSFLAG_RECEIVEDOFFIDLE; // reset offidle flag

        // read in the defaults to use for Idle shutdown delay and
        // wait until retryIdle based on the first Job in the queue.

        if (0 == m_pSyncMgrIdle->BeginIdleDetection(this,1,0))
        {
            m_dwProgressFlags |= PROGRESSFLAG_REGISTEREDFOROFFIDLE;
            AddRefProgressDialog(); // put an addref on to keep alive, will be released in OffIdle.
        }
        else
        {
            m_dwProgressFlags &= ~PROGRESSFLAG_REGISTEREDFOROFFIDLE;
        }

    }

    // if don't have a transfer addref then add one and make sure idle is setup
    if (!(PROGRESSFLAG_TRANSFERADDREF & m_dwProgressFlags))
    {
        m_dwProgressFlags |= PROGRESSFLAG_TRANSFERADDREF;
        AddRefProgressDialog(); // put an addref on to keep alive.
    }

    --m_dwQueueTransferCount;

    // don't post message if we are in an out call or in the shutdown
    // loop or if newitemsqueue is already set.
    if (!(m_dwProgressFlags & PROGRESSFLAG_INHANDLEROUTCALL)
            && !(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP)
            && !(m_dwProgressFlags & PROGRESSFLAG_NEWITEMSINQUEUE)
            && !(m_dwProgressFlags & PROGRESSFLAG_STARTPROGRESSPOSTED) )
    {
         if ( !(m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS) )
         {
             // set here even though main loop does in case power managment or another transfer
             // occurs between here and the postMessage being processed.
             m_dwProgressFlags |= PROGRESSFLAG_SYNCINGITEMS;
             m_HndlrQueue->BeginSyncSession();
         }

         m_dwProgressFlags |=  PROGRESSFLAG_STARTPROGRESSPOSTED;

         PostMessage(m_hwnd,WM_PROGRESS_STARTPROGRESS,0,0);
    }

    // set newitems flag event if don't post the message so when  handler comes out of
    // state can check the flag.

    m_dwProgressFlags |= PROGRESSFLAG_NEWITEMSINQUEUE;

    // reset the user cancel flag if new items comein
    m_dwProgressFlags &= ~PROGRESSFLAG_CANCELPRESSED;

    if (m_lTimerSet)
    {
        InterlockedExchange(&m_lTimerSet, 0);

        // if we are in a terminate no need to kill the Timer
        if (!(m_dwProgressFlags & PROGRESSFLAG_INTERMINATE))
        {
            KillTimer(m_hwnd,TIMERID_KILLHANDLERS);
        }
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::CallCompletionRoutine, private
//
//  Synopsis:   method called when a call has been completed.
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    02-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::CallCompletionRoutine(DWORD dwThreadMsg,LPCALLCOMPLETIONMSGLPARAM lpCallCompletelParam)
{

    // !!!warning: This code assumes that the completion routine is only called
    // after the original out call has returned. This code is currently handled
    // by the queue and proxy. If switch to com need to make sure don't start winding
    // up the stack if handlers are calling comletion routines before the original
    // call comes back

    // for anything but ShowErrors can just kick off a progress.
    // for ShowErrors we need to pretend a transfer happened if a retry should occur
    // else don't do anything.

    switch(dwThreadMsg)
    {
    case ThreadMsg_ShowError:
        if (lpCallCompletelParam && (S_SYNCMGR_RETRYSYNC == lpCallCompletelParam->hCallResult))
        {

           // if still in original ShowError Call let ShowEror post the message
            // when done, else treat it like a transfer occured.
            if (m_dwProgressFlags & PROGRESSFLAG_INSHOWERRORSCALL)
            {
                Assert(!(m_dwProgressFlags & PROGRESSFLAG_SHOWERRORSCALLBACKCALLED)); // only support one.
                m_dwProgressFlags |=  PROGRESSFLAG_SHOWERRORSCALLBACKCALLED;
            }
            else
            {
                // sendmessage so it is queued up before release
                SendMessage(m_hwnd,WM_PROGRESS_TRANSFERQUEUEDATA,(WPARAM) 0, (LPARAM) NULL);
            }
        }
        --m_dwShowErrorRefCount;

        // count can go negative if handler calls completion routine on an error. if
        // this is the case just set it to zero
        if ( ((LONG) m_dwShowErrorRefCount) < 0)
        {
            AssertSz(0,"Negative ErrorRefCount");
            m_dwShowErrorRefCount = 0;
        }
        else
        {
            ReleaseProgressDialog(m_fForceClose);
        }
        break;
    case ThreadMsg_PrepareForSync:
    case ThreadMsg_Synchronize:
        {
        DWORD *pdwMsgOutCallCount = (ThreadMsg_PrepareForSync  == dwThreadMsg) ?
                        &m_dwPrepareForSyncOutCallCount : &m_dwSynchronizeOutCallCount;

            if (m_dwProgressFlags & PROGRESSFLAG_INHANDLEROUTCALL)
            {
               m_dwProgressFlags |=PROGRESSFLAG_COMPLETIONROUTINEWHILEINOUTCALL;
            }
            else
            {

                if (!(m_dwProgressFlags &  PROGRESSFLAG_STARTPROGRESSPOSTED))
                {
                    Assert(!(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP));
                    m_dwProgressFlags |= PROGRESSFLAG_CALLBACKPOSTED;
                    m_dwProgressFlags |=  PROGRESSFLAG_STARTPROGRESSPOSTED;
                    PostMessage(m_hwnd,WM_PROGRESS_STARTPROGRESS,0,0);
                }
            }

            // fix up call count.

            --(*pdwMsgOutCallCount);
            if ( ((LONG) *pdwMsgOutCallCount) < 0)
            {
                AssertSz(0,"Negative Message Specific OutCall");
                *pdwMsgOutCallCount = 0;
            }

            --m_dwHandlerOutCallCount; // decrement the handler outcall.
            if ( ((LONG) m_dwHandlerOutCallCount) < 0)
            {
                AssertSz(0,"NegativeHandlerOutCallCount");
                m_dwHandlerOutCallCount = 0;
            }

        }
        break;
    default:
        AssertSz(0,"Unknown Callback method");
        break;
    }

    // if have an lparam free it now
    if (lpCallCompletelParam)
    {
        FREE(lpCallCompletelParam);
    }

}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::QueryCanSystemShutdown, private
//
//  Synopsis:   called by object manager to determine if can shutdown.
//
//          !!!Warning - can be called on any thread. make sure this is
//              readonly.
//
//          !!!Warning - Do not yield in the function;
//
//  Arguments:
//
//  Returns:   S_OK - if can shutdown
//             S_FALSE - system should not shutdown, must fill in out params.
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

HRESULT CProgressDlg::QueryCanSystemShutdown(/* [out] */ HWND *phwnd, /* [out] */ UINT *puMessageId,
                                             /* [out] */ BOOL *pfLetUserDecide)
{
HRESULT hr = S_OK;

    if (m_dwShowErrorRefCount > 0)
    {
        *puMessageId = IDS_HANDLERSHOWERRORQUERYENDSESSION ;
        *phwnd = NULL; // don't know showError parent so keep NULL
        *pfLetUserDecide = FALSE;

        hr = S_FALSE;
    }
    else  if (m_clsid != GUID_PROGRESSDLGIDLE) // idle should allow shutdown even if syncing.
    {
            // if a sync is in progress prompt user to if they want to cancel.
        if (PROGRESSFLAG_SYNCINGITEMS & m_dwProgressFlags)
        {
            *puMessageId = IDS_PROGRESSQUERYENDSESSION;
            *phwnd = m_hwnd;
            *pfLetUserDecide = TRUE;

            hr = S_FALSE;
        }
    }

    return hr;
}

//--------------------------------------------------------------------------------
//
//  FUNCTION:   CProgressDlg::ExpandCollapse()
//
//  PURPOSE:    Takes care of showing and hiding the "details" part of the
//                              dialog.
//
//  PARAMETERS:
//      <in> fExpand - TRUE if we should be expanding the dialog.
//              <in> fSetFocus - TRUE forces a recalc.
//
//--------------------------------------------------------------------------------
void CProgressDlg::ExpandCollapse(BOOL fExpand, BOOL fForce)
{
RECT rcSep;
TCHAR szBuf[MAX_STRING_RES];
RECT rcCurDlgRect;
BOOL fSetWindowPos = FALSE;
BOOL fOrigExpanded = m_fExpanded;

    if ( (m_fExpanded == fExpand) && !fForce) // no need to do anything if already in requested state
        return;

    m_fExpanded = fExpand;

    GetWindowRect(GetDlgItem(m_hwnd, IDC_SP_SEPARATOR), &rcSep);
    GetWindowRect(m_hwnd,&rcCurDlgRect);

    if (!m_fExpanded)
    {
        // update or rcDlg rect so can reset to proper height next time.
        if (GetWindowRect(m_hwnd,&m_rcDlg))
        {

            fSetWindowPos = SetWindowPos(m_hwnd, HWND_NOTOPMOST, 0, 0, rcCurDlgRect.right - rcCurDlgRect.left,
                     m_cyCollapsed, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
        }
    }
    else
    {
        fSetWindowPos = SetWindowPos(m_hwnd, HWND_NOTOPMOST, 0, 0, rcCurDlgRect.right - rcCurDlgRect.left,
                     m_rcDlg.bottom - m_rcDlg.top,
                                     SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    }

    // if couldn't change window, leave as is
    if (!fSetWindowPos)
    {
        m_fExpanded = fOrigExpanded;
        return;
    }

    // Make sure the entire dialog is visible on the screen.  If not,
    // then push it up
    RECT rc;
    RECT rcWorkArea;
    GetWindowRect(m_hwnd, &rc);
    SystemParametersInfo(SPI_GETWORKAREA, 0, (LPVOID) &rcWorkArea, 0);
    if (rc.bottom > rcWorkArea.bottom)
    {
        rc.top = max(0, (int) rc.top - (rc.bottom - rcWorkArea.bottom));

        SetWindowPos(m_hwnd, HWND_NOTOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
    }

    LoadString(g_hInst, m_fExpanded ? IDS_HIDE_DETAILS : IDS_SHOW_DETAILS, szBuf,
                  ARRAY_SIZE(szBuf));
    SetDlgItemText(m_hwnd, IDC_DETAILS, szBuf);

    // Make sure the proper tab is up to date shown.
    ShowProgressTab(m_iTab);

    // Raid-34387: Spooler: Closing details with ALT-D while focus is on a task disables keyboard input
    // if any control other than the cancel button has the focus set the focus to details.

    if (!fExpand)
    {
    HWND hwndFocus = GetFocus();

        if (hwndFocus != GetDlgItem(m_hwnd, IDSTOP))
        {
            SetFocus(GetDlgItem(m_hwnd, IDC_DETAILS));
        }

    }
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnTimer, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::OnTimer(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
WORD wTimerID = (WORD) wParam;

    if (wTimerID == TIMERID_TRAYANIMATION)
    {
        UpdateTrayIcon();
    }
    else if (TIMERID_NOIDLEHANGUP == wTimerID)
    {
        ResetNetworkIdle();
    }
    else if (TIMERID_KILLHANDLERS == wTimerID)
    {
        if (m_lTimerSet)
        {

            if (!(m_dwProgressFlags & PROGRESSFLAG_DEAD)
                && !(m_dwProgressFlags & PROGRESSFLAG_SHUTTINGDOWNLOOP)
                && !(m_dwProgressFlags & PROGRESSFLAG_INTERMINATE))
            {
            BOOL fItemToKill;

                m_dwProgressFlags |= PROGRESSFLAG_INTERMINATE;

                // Even though KillTimer,
                // don't reset  m_lTimerSet timer until done with ForceKill
                // in case cancel is pressed again.

                KillTimer(m_hwnd,TIMERID_KILLHANDLERS);
            
                SetProgressReleaseDlgCmdId(m_clsid,this,RELEASEDLGCMDID_CANCEL); // set so cleanup knows it was stopped by user..


                AddRefProgressDialog(); // hold dialog alive until cancel is complete

                m_HndlrQueue->ForceKillHandlers(&fItemToKill);

                // reset the timer if TimerSet is still set, i.e. if was 
                // set to zero because of a transfer or actually done don't reset.

                if (m_lTimerSet)
                {
                    // only settimer if actually killed anything. if looped through
                    // and found nothing then can turn off timer.
                    if (fItemToKill)
                    {
                        Assert(m_nKillHandlerTimeoutValue >= TIMERID_KILLHANDLERSMINTIME);
                        SetTimer(m_hwnd,TIMERID_KILLHANDLERS,m_nKillHandlerTimeoutValue,NULL);
                    }
                    else
                    {
                        m_lTimerSet = 0;
                    }
                }

                m_dwProgressFlags &= ~PROGRESSFLAG_INTERMINATE;
                
                ReleaseProgressDialog(FALSE);
            }

        }

    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnTaskBarCreated, private
//
//  Synopsis:  Receive this when the Tray has been restarted.
//              Need to put back our tray icon.
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    31-Aug-98       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::OnTaskBarCreated(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    if (m_fHasShellTrayIcon)
    {
        m_fAddedIconToTray = FALSE; // set added to false to force update to add again.
        UpdateTrayIcon();
    }
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnSysCommand, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

BOOL CProgressDlg::OnSysCommand(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
UINT uCmdType = (UINT) wParam;        // type of system command requested
WORD xPos = LOWORD(lParam);    // horizontal postion, in screen coordinates
WORD yPos = HIWORD(lParam);    // vertical postion, in screen coordinates

    //
    // WARNING: USER uses low four bits for some undocumented feature
    //  (only for SC_*). We need to mask those bits to make this case
    //  statement work.

   uCmdType &= 0xFFF0;

    switch(uCmdType)
    {
    case SC_MINIMIZE:

        // if already in the tray to nothing
        if (!m_fHasShellTrayIcon)
        {
            if (RegisterShellTrayIcon(TRUE))
            {
                AnimateTray(TRUE);
                ShowWindow(m_hwnd,SW_HIDE);
              //  AnimateTray(TRUE);
                return -1;
            }
        }
        else
        {
            return -1; // if already in the tray we handled.
        }
        break;
    case SC_MAXIMIZE:
    case SC_RESTORE:
        {
            // if we are being maximized or restored from a maximize
            // make sure  details is open

            if ( (uCmdType ==  SC_RESTORE && m_fMaximized)
                    || (uCmdType ==  SC_MAXIMIZE) )
            {
                if (!m_fExpanded)
                {
                    ExpandCollapse(TRUE,FALSE);
                }
            }

            m_fMaximized = (uCmdType ==  SC_MAXIMIZE) ?  TRUE : FALSE;
        }
        break;
    default:
        break;
    }

    return FALSE; // fall through to defWndProc
}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnShellTrayNotification, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::OnShellTrayNotification(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
DWORD dwMsg = (DWORD) lParam;

    switch (dwMsg)
    {
    case WM_LBUTTONUP:
        {
            UpdateWndPosition(SW_SHOWNORMAL,TRUE /* fForce */);
        }
        break;
    #ifdef _TRAYMENU
    case WM_RBUTTONUP:
        {
        POINT Point;

                // show the context menu
                HMENU hmenu = LoadMenu(hInst,MAKEINTRESOURCE(IDR_MENU1));
                hmenu = CreatePopupMenu();

                //AppendMenu(hmenu,0,101,"Status");
                //AppendMenu(hmenu,0,100,"Settings");

                GetCursorPos(&Point); // want point that click occured at.

                // Review, change so TrackMenu returns index.
                TrackPopupMenuEx(hmenu,TPM_HORIZONTAL | TPM_VERTICAL,
                                    Point.x,Point.y,hwnd,NULL);


        }
        break;
    #endif // _TRAYMENU
    default:
        break;
    }
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnClose, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::OnClose(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    OnCancel(FALSE /* fOffIdle */); // treat close as a cancel.
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnGetMinMaxInfo, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::OnGetMinMaxInfo(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
MINMAXINFO   *pMinMax = (MINMAXINFO *) lParam ;

    // minimum width is a constant but minimum height depends on
    // if dialog is collapsed or expanded.

    if (!m_fExpanded)
    {
         pMinMax->ptMinTrackSize.y = m_cyCollapsed;
         pMinMax->ptMaxTrackSize.y = m_cyCollapsed; // maximum is also the collapsed height
    }
    else
    {
        pMinMax->ptMinTrackSize.y = m_ptMinimumDlgExpandedSize.y;
    }

    pMinMax->ptMinTrackSize.x  = m_ptMinimumDlgExpandedSize.x;
}

//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnMoving, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

void CProgressDlg::OnMoving(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
LPRECT lprc = (LPRECT) lParam;    // screen coordinates of drag rectangle

    // if we are maxmized don't allow moving
    if (m_fMaximized)
    {
        GetWindowRect(m_hwnd,lprc);
    }
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnContextMenu, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

BOOL  CProgressDlg::OnContextMenu(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    WinHelp ((HWND)wParam,
                    g_szSyncMgrHelp,
                    HELP_CONTEXTMENU,
                    (ULONG_PTR)g_aContextHelpIds);

    return TRUE;
}


//+---------------------------------------------------------------------------
//
//  Member:     CProgressDlg::OnPowerBroadcast, private
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//  History:    17-Jun-98       rogerg        Created.
//
//----------------------------------------------------------------------------

BOOL CProgressDlg::OnPowerBroadcast(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    if (wParam == PBT_APMQUERYSUSPEND)
    {
        // if just created or syncing don't suspend
        if (m_dwProgressFlags & PROGRESSFLAG_NEWDIALOG
            || m_dwProgressFlags & PROGRESSFLAG_SYNCINGITEMS)
        {
            SetWindowLongPtr(m_hwnd,DWLP_MSGRESULT,BROADCAST_QUERY_DENY);
            return TRUE;
        }
    }

    return TRUE;
}


//--------------------------------------------------------------------------------
//
//  FUNCTION: ProgressWndProc(HWND hwnd, UINT uMsg,WPARAM wParam,LPARAM lParam)
//
//  PURPOSE:  Callback for Progress Dialog
//
//      COMMENTS: Implemented on main thread.
//
//
//--------------------------------------------------------------------------------

BOOL CALLBACK ProgressWndProc(HWND hwnd, UINT uMsg,WPARAM wParam,LPARAM lParam)
{
CProgressDlg *pThis = (CProgressDlg *) GetWindowLongPtr(hwnd, DWLP_USER);
UINT horizExtent = 0;
BOOL bResult;

    // spcial case destroy and init.
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0); // done with this thread.
	break;
    case WM_INITDIALOG:
        {
        SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
        pThis = (CProgressDlg *) lParam;

            if (pThis)
            {
                pThis->InitializeHwnd(hwnd,uMsg,wParam,lParam);
            }

            return FALSE; // return FALSE so system doesn't give us the focus

        break;
        }
    default:
        {
            if (pThis)
            {
                switch (uMsg)
                {
                case WM_POWERBROADCAST:
                    return pThis->OnPowerBroadcast(uMsg,wParam,lParam);
                    break;
                case WM_CONTEXTMENU:
                    return pThis->OnContextMenu(uMsg,wParam,lParam);
                    break;
                case WM_DRAWITEM:
                    return OnProgressResultsDrawItem(hwnd,pThis,(UINT)wParam,(const DRAWITEMSTRUCT*)lParam);
                    break;
                case WM_MEASUREITEM:
                    bResult = OnProgressResultsMeasureItem(hwnd,pThis, &horizExtent,(UINT)wParam,(MEASUREITEMSTRUCT *)lParam);
                    if (horizExtent)
                    {
                        //make sure there is a horizontal scroll bar if needed
                        SendMessage(GetDlgItem(hwnd,IDC_LISTBOXERROR),
                            LB_SETHORIZONTALEXTENT, horizExtent, 0L);
                    }
                    return bResult;
                    break;
                case WM_DELETEITEM:
                    return OnProgressResultsDeleteItem(hwnd,(UINT)wParam,(const DELETEITEMSTRUCT *)lParam);
                    break;
                case WM_NOTIFY:
                    pThis->OnNotify(uMsg,wParam,lParam);
                    break;
                case WM_COMMAND:
                    pThis->OnCommand(uMsg,wParam,lParam);
                    break;
                case WM_MOVING:
                     pThis->OnMoving(uMsg,wParam,lParam);
                     break;
                case WM_SIZE:
                    pThis->OnSize(uMsg,wParam,lParam);
                    break;
                case WM_GETMINMAXINFO:
                    pThis->OnGetMinMaxInfo(uMsg,wParam,lParam);
                    break;
                case WM_PAINT:
                    pThis->OnPaint(uMsg,wParam,lParam);
                    return 0;
                    break;
                case WM_BASEDLG_SHOWWINDOW:
                    pThis->UpdateWndPosition((int)wParam,FALSE); // nCmdShow is stored in the wParam
                    break;
                case WM_BASEDLG_NOTIFYLISTVIEWEX:
                    pThis->OnNotifyListViewEx(uMsg,wParam,lParam);
                    break;
                case WM_BASEDLG_COMPLETIONROUTINE:
                    pThis->CallCompletionRoutine((DWORD)wParam /*dwThreadMsg */ ,(LPCALLCOMPLETIONMSGLPARAM) lParam);
                    break;
                case WM_BASEDLG_HANDLESYSSHUTDOWN:
                    // set the force shutdown member then treat as a close
                    pThis->m_fForceClose = TRUE;
                    PostMessage(hwnd,WM_CLOSE,0,0);
                    break;
                case WM_TIMER: // timer message for delat when sync is done.
                   pThis->OnTimer(uMsg,wParam,lParam);
                   break;
                case WM_PROGRESS_UPDATE:
                    pThis->HandleProgressUpdate(hwnd,wParam,lParam);
                    break;
                case WM_PROGRESS_LOGERROR:
                    pThis->HandleLogError(hwnd,(HANDLERINFO *) wParam,(MSGLogErrors *) lParam);
                    break;
                case WM_PROGRESS_DELETELOGERROR:
                    pThis->HandleDeleteLogError(hwnd,(MSGDeleteLogErrors *) lParam);
                    break;
                case WM_PROGRESS_STARTPROGRESS:
                    pThis->DoSyncTask(hwnd);
                    break;
                case WM_PROGRESS_RESETKILLHANDLERSTIMER:
                    pThis->OnResetKillHandlersTimers();
                    break;
                case WM_CLOSE:
                    pThis->OnClose(uMsg,wParam,lParam);
                    break;
                case WM_PROGRESS_SHELLTRAYNOTIFICATION:
                    pThis->OnShellTrayNotification(uMsg,wParam,lParam);
                    break;
                case WM_SYSCOMMAND:
                    return pThis->OnSysCommand(uMsg,wParam,lParam);
                    break;
                case WM_PROGRESS_TRANSFERQUEUEDATA:
                    {
                    HRESULT *phr = (HRESULT *) wParam;
                    HRESULT hr;

                        hr = pThis->PrivTransferQueueData( (CHndlrQueue *) lParam);

                        // phr is only valid on a SendMessage.
                        if (NULL != phr)
                        {
                            *phr = hr;
                        }

                    return TRUE;
                    break;
                    }
                case WM_PROGRESS_RELEASEDLGCMD:
                    pThis->PrivReleaseDlg((WORD)wParam);
                    break;
                default:
                    if (uMsg == g_WMTaskbarCreated)
                    {
                        pThis->OnTaskBarCreated(uMsg,wParam,lParam);
                    }
                    break;
                }
            }
        }
        break;
    }

    return FALSE;
}