/*++

Copyright (C) Microsoft Corporation, 1996 - 1998
All rights reserved.

Module Name:

    archlv.hxx

Abstract:

    Driver Architecture List View 

Author:

    Steve Kiraly (SteveKi) 19-Nov-1996

Revision History:

--*/

#include "precomp.hxx"
#pragma hdrstop

#include "psetup.hxx"
#include "drvsetup.hxx"
#include "drvver.hxx"
#include "archlv.hxx"

/********************************************************************

    Arch List - static data.

********************************************************************/

//
// disable MIPS & PPC drivers, since they are not supported from setup
//
static TArchLV::ArchEncode aArchEncode[] = 
{
    {IDS_ARCH_IA64,      IDS_VERSION_51,         L"Windows IA64",          L"3",   DRIVER_IA64_3  },

    {IDS_ARCH_INTEL,     IDS_VERSION_WINDOWS_ME, L"Windows 4.0",           L"0",   DRIVER_WIN95   },

    {IDS_ARCH_INTEL,     IDS_VERSION_40_50,      L"Windows NT x86",        L"2",   DRIVER_X86_2   },
    {IDS_ARCH_INTEL,     IDS_VERSION_50_51,      L"Windows NT x86",        L"3",   DRIVER_X86_3   },

    {IDS_ARCH_ALPHA,     IDS_VERSION_40,         L"Windows NT Alpha_AXP",  L"2",   DRIVER_ALPHA_2 },

#if FALSE 
    {IDS_ARCH_INTEL,     IDS_VERSION_NT_31,      L"Windows NT x86",        L"1",   DRIVER_X86_0   },
    {IDS_ARCH_INTEL,     IDS_VERSION_35X,        L"Windows NT x86",        L"1",   DRIVER_X86_1   },

    {IDS_ARCH_MIPS,      IDS_VERSION_NT_31,      L"Windows NT R4000",      L"1",   DRIVER_MIPS_0  },
    {IDS_ARCH_MIPS,      IDS_VERSION_35X,        L"Windows NT R4000",      L"1",   DRIVER_MIPS_1  },
    {IDS_ARCH_MIPS,      IDS_VERSION_40,         L"Windows NT R4000",      L"2",   DRIVER_MIPS_2  },

    {IDS_ARCH_ALPHA,     IDS_VERSION_NT_31,      L"Windows NT Alpha_AXP",  L"1",   DRIVER_ALPHA_0 },
    {IDS_ARCH_ALPHA,     IDS_VERSION_35X,        L"Windows NT Alpha_AXP",  L"1",   DRIVER_ALPHA_1 },

    {IDS_ARCH_POWERPC,   IDS_VERSION_351,        L"Windows NT PowerPC",    L"1",   DRIVER_PPC_1   },
    {IDS_ARCH_POWERPC,   IDS_VERSION_40,         L"Windows NT PowerPC",    L"2",   DRIVER_PPC_2   }, 
#endif
};

// use this aliases for the driver version string, so we don't break 
// admin scripts written for Win2k.
static DWORD aVersionAliases[] = 
{
    IDS_VERSION_WINDOWS_95,     IDS_VERSION_WINDOWS_ME,
    IDS_VERSION_40,             IDS_VERSION_40_50,
    IDS_VERSION_50,             IDS_VERSION_50_51,
};

/********************************************************************

    Arch List view class.

********************************************************************/

TArchLV::
TArchLV(
    VOID
    ) : _hwnd( NULL ),
        _hwndLV( NULL ),
        _ColumnSortState( kMaxColumns ),
        _wmDoubleClickMsg( 0 ),
        _wmSingleClickMsg( 0 ),
        _uCurrentColumn( 0 ),
        _bNoItemCheck( FALSE )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::ctor\n" ) );
    ArchDataList_vReset();
}

TArchLV::
~TArchLV(
    VOID
    )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::dtor\n" ) );
    vRelease();
}

BOOL
TArchLV::
bSetUI(
    IN HWND     hwnd,
    IN WPARAM   wmDoubleClickMsg,
    IN WPARAM   wmSingleClickMsg
    )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::bSetUI\n" ) );

    SPLASSERT( hwnd );
    TStatusB bStatus;
    bStatus DBGNOCHK = TRUE;

    //
    // Save the parent window handle.
    //
    _hwnd               = hwnd;
    _wmDoubleClickMsg   = wmDoubleClickMsg;
    _wmSingleClickMsg   = wmSingleClickMsg;                                 

    //
    // Get the driver list view handle.
    //
    _hwndLV = GetDlgItem( _hwnd, IDC_ARCHITECTURE_LIST );

    //
    // Add check boxes.
    //
    HIMAGELIST himlState = ImageList_Create( 16, 16, TRUE, 3, 0 );

    //
    // !! LATER !!
    // Should be created once then shared.
    //
    if( !himlState )
    {
        DBGMSG( DBG_ERROR, ( "ArchLV.bSetUI: ImageList_Create failed %d\n", GetLastError( )));
        return FALSE;
    }

    //
    // Load the bitmap for the check states.
    //
    HBITMAP hbm =  LoadBitmap( ghInst, MAKEINTRESOURCE( IDB_CHECKSTATES ));

    if( !hbm )
    {
        DBGMSG( DBG_ERROR, ( "ArchLV.bSetUI: LoadBitmap failed %d\n", GetLastError( )));
        return FALSE;
    }

    //
    // Add the bitmaps to the image list.
    //
    ImageList_AddMasked( himlState, hbm, RGB( 255, 0, 0 ));

    //
    // Set the new image list.
    //
    SendMessage( _hwndLV, LVM_SETIMAGELIST, LVSIL_STATE, (WPARAM)himlState );

    //
    // Remember to release the bitmap handle.
    //
    DeleteObject( hbm );

    //
    // Initialize the LV_COLUMN structure.
    //
    LV_COLUMN lvc;
    lvc.mask    = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
    lvc.fmt     = LVCFMT_LEFT;
    lvc.cx      = kDefaultHeaderWidth;

    //
    // Calculate the header column width.
    //
    RECT rc;
    if( !GetClientRect( _hwndLV, &rc ))
    {
        DBGMSG( DBG_WARN, ( "ArchLV.bSetUI: GetClientRect failed %d\n", GetLastError( )));
        return FALSE;
    }

    //
    // Get the total size of list view header, less the scroll bar.
    //
    LONG Interval = ( rc.right - rc.left ) - GetSystemMetrics( SM_CYVSCROLL );

    //
    // Column with array.
    //
    DWORD ColumnWidth [] = { Interval * 25, Interval * 55, Interval * 20 };

    //
    // Set the column header text.
    //
    TString strHeader;
    for( INT iCol = 0; iCol < kHeaderMax; ++iCol )
    {
        bStatus DBGCHK  = strHeader.bLoadString( ghInst, IDS_DRIVER_HEAD_ENVIRONMENT + iCol );
        lvc.pszText     = const_cast<LPTSTR>( static_cast<LPCTSTR>( strHeader ) );
        lvc.iSubItem    = iCol;
        lvc.cx          = ColumnWidth[iCol] / 100;

        if( ListView_InsertColumn( _hwndLV, iCol, &lvc ) == -1 )
        {
            DBGMSG( DBG_WARN, ( "ArchLV.bSetUI: LV_Insert failed %d\n", GetLastError( )));
            bStatus DBGCHK = FALSE;
        }
    }

    //
    // Enable full row selection and check boxes.
    //
    if( bStatus )
    {
        DWORD dwExStyle = ListView_GetExtendedListViewStyle( _hwndLV );
    	ListView_SetExtendedListViewStyle( _hwndLV, dwExStyle | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP );
    }

    return bStatus;
}

BOOL
TArchLV::
bRefreshListView(
    IN LPCTSTR pszServerName,
    IN LPCTSTR pszDriverName
    )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::bRefreshListView\n" ) );

    SPLASSERT( pszDriverName );

    //
    // Release current list view items.
    //
    vRelease();

    //
    // Reset the sort order 
    //
    _ColumnSortState.vResetAll();
    
    //
    // Fill the list view and sort it.
    //        
    BOOL bReturn = bFillListView( pszServerName, pszDriverName );
    
    //
    // Sort the list view         
    (VOID)bListViewSort( kVersionColumn );
    (VOID)bListViewSort( kArchitectureColumn );

    //
    // Reset the sort order 
    //
    _ColumnSortState.vResetAll();
    
    return bReturn;
}


BOOL
TArchLV::
bSetCheckDefaultArch(
    IN LPCTSTR pszServerName
    )
{
    TStatusB bStatus;
    DWORD dwCurrentEncode;
    TArchData *pArchData;

    //
    // Get the current machines architecture.
    //
    bStatus DBGCHK = bGetCurrentDriver( pszServerName, &dwCurrentEncode );

    if( bStatus )
    {
        bStatus DBGNOCHK = FALSE;

        DWORD cItems = ListView_GetItemCount( _hwndLV );

        for( UINT i = 0; i < cItems; i++ )
        {
            if( bGetItemData( i, &pArchData ) )
            {
                if( dwCurrentEncode == pArchData->_Encode )
                {
                    if( ( ListView_GetItemState( _hwndLV, i, kStateMask ) & kStateChecked ) && pArchData->_bInstalled )
                    {
                        vCheckItem( i, 2 );
                    }
                    else
                    {
                        vCheckItem( i, TRUE );
                    }

                    bStatus DBGNOCHK = TRUE;
                    break;
                }
            }
        }
    }
    return bStatus;
}

BOOL
TArchLV::
bHandleNotifyMessage(
    IN UINT     uMsg,
    IN WPARAM   wParam,
    IN LPARAM   lParam
    )
{
    BOOL bStatus = TRUE;

    UNREFERENCED_PARAMETER( wParam );

    switch( uMsg )
    {
    case WM_NOTIFY:
        {
            if( (INT)wParam == GetDlgCtrlID( _hwndLV ) )
            {
                LPNMHDR pnmh = (LPNMHDR)lParam;

                switch( pnmh->code )
                {
                case NM_DBLCLK:
                    DBGMSG( DBG_TRACE, ("ArchLV::NM_DBLCLK\n" ) );
                    vCheckItemClicked( (LPNMHDR)lParam );
                    if( _wmDoubleClickMsg )                                 
                    {
                        PostMessage( _hwnd, WM_COMMAND, _wmDoubleClickMsg, 0 );
                    }
                    break;

                case NM_CLICK:
                    DBGMSG( DBG_TRACE, ("ArchLV::NM_CLICK\n" ) );
                    vCheckItemClicked( (LPNMHDR)lParam );
                    if( _wmSingleClickMsg )                                 
                    {
                        PostMessage( _hwnd, WM_COMMAND, _wmSingleClickMsg, 0 );
                    }
                    break;

                case LVN_COLUMNCLICK:
                    DBGMSG( DBG_TRACE, ("ArchLV::LVN_COLUMNCLICK\n" ) );
                    {
                        NM_LISTVIEW *pNm = (NM_LISTVIEW *)lParam;
                        (VOID)bListViewSort( pNm->iSubItem );
                    }
                    break;

                case LVN_KEYDOWN:
                    DBGMSG( DBG_TRACE, ("ArchLV::LVN_KEYDOWN\n" ) );
                    {
                        if( bListVeiwKeydown( lParam ) )
                        {                            
                            if( _wmDoubleClickMsg )                                 
                            {
                                PostMessage( _hwnd, WM_COMMAND, _wmDoubleClickMsg, 0 );
                            }
                        }
                    }
                    break;

                default:
                    bStatus = FALSE;
                    break;
                }
            }
            else
            {
                bStatus = FALSE;
            }
        }
        break;

    //
    // Message not handled.
    //
    default:
        bStatus = FALSE;
        break;
    }

    return bStatus;
}

VOID
TArchLV::
vSelectItem(
    IN UINT iIndex
    )
{ 
    //
    // Select the specified item.
    //
    if( iIndex != -1 )
    {
        ListView_SetItemState( _hwndLV, iIndex, LVIS_SELECTED | LVIS_FOCUSED, 0x000F );
        ListView_EnsureVisible( _hwndLV, iIndex, FALSE );
    }
}

VOID
TArchLV::
vNoItemCheck(
    VOID
    )
{
    _bNoItemCheck = TRUE;
}

BOOL
TArchLV::
bEncodeToArchAndVersion(
    IN  DWORD    dwEncode,
    OUT TString &strArch,
    OUT TString &strVersion
    )
{
    TStatusB bStatus;
    bStatus DBGNOCHK = FALSE;

    for( UINT i = 0; i < COUNTOF( aArchEncode ); i++ )
    {
        if( aArchEncode[i].Encode == dwEncode )
        {
            bStatus DBGCHK = strArch.bLoadString( ghInst, aArchEncode[i].ArchId );
            bStatus DBGCHK = strVersion.bLoadString( ghInst, aArchEncode[i].VersionId );
            break;
        }
    }
    return bStatus;
}

BOOL
TArchLV::
bArchAndVersionToEncode(
    OUT DWORD       *pdwEncode,
    IN  LPCTSTR     pszArchitecture,
    IN  LPCTSTR     pszVersion,
    IN  BOOL        bUseNonLocalizedStrings
    )
{
    BOOL        bReturn         = FALSE;
    TString     strArch;
    TString     strVersion;
    TStatusB    bStatus;
    
    if (pdwEncode && pszArchitecture && pszVersion) 
    {
        if (bUseNonLocalizedStrings) 
        {
            for (UINT i = 0; i < COUNTOF(aArchEncode); i++)
            {
                if (!_tcsicmp(aArchEncode[i].NonLocalizedEnvironment, pszArchitecture) && 
                    !_tcsicmp(aArchEncode[i].NonLocalizedVersion, pszVersion))
                {
                    *pdwEncode = aArchEncode[i].Encode;
                    bReturn = TRUE;
                    break;
                }
            }            
        }
        else
        {
            UINT i;
            TString strMappedVersion;

            // check to remap the version string if necessary. 
            for (i = 0; (i+1) < COUNTOF( aVersionAliases ); i+=2)
            {
                bStatus DBGCHK = strMappedVersion.bLoadString(ghInst, aVersionAliases[i]);
                if( bStatus && 0 == lstrcmp(strMappedVersion, pszVersion) )
                {
                    // this version string was remapped, map to the new string.
                    bStatus DBGCHK = strMappedVersion.bLoadString(ghInst, aVersionAliases[i+1]);
                    if( bStatus )
                    {
                        pszVersion = strMappedVersion;
                    }
                }
            }

            // find the arch & version, and do the encode
            for (i = 0; i < COUNTOF( aArchEncode ); i++)
            {
                bStatus DBGCHK = strArch.bLoadString(ghInst, aArchEncode[i].ArchId);
                bStatus DBGCHK = strVersion.bLoadString(ghInst, aArchEncode[i].VersionId);
        
                if (!_tcsicmp(strArch, pszArchitecture) && !_tcsicmp(strVersion, pszVersion))
                {
                    *pdwEncode = aArchEncode[i].Encode;
                    bReturn = TRUE;
                    break;
                }
            }
        }
    }
    
    return bReturn;                     
}

BOOL
TArchLV::
bGetEncodeFromIndex( 
    IN  UINT    uIndex, 
    OUT DWORD   *pdwEncode 
    ) 
{
    BOOL bReturn = FALSE;

    //
    // Validate the passed in index.
    //
    if( uIndex < COUNTOF( aArchEncode ) )
    {
        //
        // Return back the encode for the given index.
        //
        *pdwEncode = aArchEncode[uIndex].Encode;
        bReturn = TRUE;
    }
    return bReturn;
}

/********************************************************************

    Private member functions.

********************************************************************/
BOOL
TArchLV::
vCheckItemClicked(
    IN LPNMHDR pnmh
    )
{
    BOOL bReturn = FALSE;

    DWORD dwPoints = GetMessagePos();
    POINTS &pt = MAKEPOINTS( dwPoints );
    LV_HITTESTINFO lvhti;

    lvhti.pt.x = pt.x;
    lvhti.pt.y = pt.y;

    MapWindowPoints( HWND_DESKTOP, _hwndLV, &lvhti.pt, 1 );

    INT iItem = ListView_HitTest( _hwndLV, &lvhti );

    //
    // Allow either a double click, or a single click on the
    // check box to toggle the check mark.
    //
    if( pnmh->code == NM_DBLCLK || lvhti.flags & LVHT_ONITEMSTATEICON )
    {
        vItemClicked( iItem );
        bReturn = TRUE;
    }

    return bReturn;
}


BOOL
TArchLV::
bListVeiwKeydown( 
    IN LPARAM lParam 
    )
{
    BOOL bReturn = FALSE;

    LV_KEYDOWN* plvnkd = (LV_KEYDOWN *)lParam;

    //
    // !! LATER !!
    //
    // Is this the best way to check whether the ALT
    // key is _not_ down?
    //
    if( plvnkd->wVKey == TEXT( ' ' ) &&
        !( GetKeyState( VK_LMENU ) & 0x80000000 ) &&
        !( GetKeyState( VK_RMENU ) & 0x80000000 ))
    {
        //
        // Get the selected item index.
        //
        INT iIndex = ListView_GetNextItem( _hwndLV, -1, LVNI_SELECTED );
         
        if( iIndex != -1 )
        {                            
            vItemClicked( iIndex );
            bReturn = TRUE;
        }
    }

    return bReturn;
}

VOID
TArchLV::
vRelease(
    VOID
    )
{
    //
    // Release all the list view items.
    //
    ListView_DeleteAllItems( _hwndLV );

    // 
    // Release the data from the architecture data list.
    //
    TIter Iter;
    TArchData *pArchData;

    for( ArchDataList_vIterInit( Iter ), Iter.vNext(); Iter.bValid(); ){

        pArchData = ArchDataList_pConvert( Iter );
        Iter.vNext();

        delete pArchData;
    }
}

BOOL
TArchLV::
bFillListView(
    IN LPCTSTR pszServerName,
    IN LPCTSTR pszDriverName
    )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::bFillListView\n" ) );

    //
    // Fill the list view.
    //
    TStatusB bStatus;
    bStatus DBGNOCHK = FALSE;

    TPrinterDriverInstallation Di( pszServerName );
    
    if( VALID_OBJ( Di ) )
    {
        TString strArchitecture;
        TString strVersion;
        TString strInstalled;
        TString strNotInstalled;
        LPCTSTR pszInstalled;
        BOOL    bInstalled;

        //
        // Tell the driver installation class the driver name.
        //
        bStatus DBGCHK = Di.bSetDriverName( pszDriverName );

        //
        // Load the string "(installed)" from the resource file.
        //
        bStatus DBGCHK = strInstalled.bLoadString( ghInst, IDS_DRIVER_INSTALLED );
        bStatus DBGCHK = strNotInstalled.bLoadString( ghInst, IDS_DRIVER_NOTINSTALLED );

        for( UINT i = 0; i < COUNTOF( aArchEncode ); i++ )
        {
            //
            // some of the new env (like "Windows IA64") are not supported
            // in the old versions of the OS, so we need to check explicitly
            //
            if( IDS_ARCH_IA64 == aArchEncode[i].ArchId )
            {
                DWORD cbBuffer = 0;
                DWORD cDrivers = 0;
                CAutoPtrSpl<DRIVER_INFO_3> spDI1;

                if( !VDataRefresh::bEnumDrivers(pszServerName, aArchEncode[i].NonLocalizedEnvironment, 1,
                                                spDI1.GetPPV(), &cbBuffer, &cDrivers) )
                {
                    // this environment is not supported from the (remote) spooler - just skip it!
                    continue;
                }
            }

            //
            // Load the string driver name string from the resource file.
            //
            bStatus DBGCHK = strArchitecture.bLoadString( ghInst, aArchEncode[i].ArchId );
            bStatus DBGCHK = strVersion.bLoadString( ghInst, aArchEncode[i].VersionId );

            //
            // If the driver is installed, tell the user.
            //
            bInstalled = Di.bIsDriverInstalled( aArchEncode[i].Encode );

            //
            // Set the installed string.
            //
            pszInstalled = bInstalled ? static_cast<LPCTSTR>( strInstalled ) : static_cast<LPCTSTR>( strNotInstalled );

            //
            // Allocate the architecture data.
            //
            TArchData *pArchData = new TArchData( strArchitecture, strVersion, pszInstalled, aArchEncode[i].Encode, bInstalled );

            //
            // If valid 
            //
            if( VALID_PTR( pArchData ) )
            {
                //
                // Add the architecture data to the linked list.
                //
                ArchDataList_vAppend( pArchData );

                //
                // Add the string to the list view.
                //
                LRESULT iIndex = iAddToListView( strArchitecture, strVersion, pszInstalled, (LPARAM)pArchData );

                //
                // Check this item if the driver is installed.
                //
                if( bInstalled && (iIndex != -1) )
                {
                    vCheckItem( iIndex,  2 );
                }
            }
            else
            {
                //
                // The object may have been allocated, however failed construction.
                //
                delete pArchData;
            }
        }
    }

    return bStatus;
}

LRESULT
TArchLV::
iAddToListView(
    IN LPCTSTR  pszArchitecture,
    IN LPCTSTR  pszVersion,
    IN LPCTSTR  pszInstalled,
    IN LPARAM   lParam
    )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::AddToListView\n" ) );

    SPLASSERT( pszArchitecture );
    SPLASSERT( pszVersion );
    SPLASSERT( pszInstalled );

    LV_ITEM lvi = { 0 };

    //
    // Add driver information to the listview.
    //
    lvi.mask        = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE | LVIF_STATE;
    lvi.iItem       = ListView_GetItemCount( _hwndLV );
    lvi.pszText     = const_cast<LPTSTR>( pszArchitecture );
    lvi.lParam      = lParam;
    lvi.state       = kStateUnchecked;

    LRESULT iIndex = ListView_InsertItem( _hwndLV, &lvi );
    if( -1 != iIndex )
    {
        ListView_SetItemText( _hwndLV, iIndex, 1, const_cast<LPTSTR>( pszVersion ) );
        ListView_SetItemText( _hwndLV, iIndex, 2, const_cast<LPTSTR>( pszInstalled ) );
    }

    return iIndex;
}

BOOL
TArchLV::
bListViewSort(
    UINT uColumn
    )
{
    //
    // Set the surrent column number.
    //
    _uCurrentColumn = uColumn;

    //
    // Tell the list view to sort.
    //
    TStatusB bStatus;
    bStatus DBGCHK = ListView_SortItems( _hwndLV, iCompareProc, (LPARAM)this );

    //
    // Toggle the specified column sort state.
    //
    _ColumnSortState.bToggle( uColumn );

    return bStatus;
}

INT 
CALLBACK 
TArchLV::
iCompareProc(
    IN LPARAM lParam1, 
    IN LPARAM lParam2, 
    IN LPARAM RefData
    )
{
    TArchData   *pArchData1 = reinterpret_cast<TArchData *>( lParam1 );
    TArchData   *pArchData2 = reinterpret_cast<TArchData *>( lParam2 );
    TArchLV     *pArchLV    = reinterpret_cast<TArchLV *>( RefData );
    INT         iResult     = 0;
    LPCTSTR     strName1    = NULL;
    LPCTSTR     strName2    = NULL;

    if( pArchLV && pArchData1 && pArchData2 )
    {
        BOOL bStatus = TRUE;

        switch( pArchLV->_uCurrentColumn )
        {

        case kArchitectureColumn:
            strName1 = pArchData1->_strArchitecture;
            strName2 = pArchData2->_strArchitecture;
            break;

        case kVersionColumn:
            strName1 = pArchData1->_strVersion;
            strName2 = pArchData2->_strVersion;
            break;

        case kInstalledColumn:
            strName1 = pArchData1->_strInstalled;
            strName2 = pArchData2->_strInstalled;
            break;

        default:
            bStatus = FALSE;
            break;
    
        }

        if( bStatus )
        {
            if( pArchLV->_ColumnSortState.bRead( pArchLV->_uCurrentColumn ) )
                iResult = _tcsicmp( strName2, strName1 );
            else
                iResult = _tcsicmp( strName1, strName2 );
        }
    }

    return iResult;
}

UINT
TArchLV::
uGetCheckedItemCount(
    VOID
    )
{
    DWORD cItems = ListView_GetItemCount( _hwndLV );

    UINT uItemCount = 0;

    for( UINT i = 0; i < cItems; i++ )
    {
        if( ListView_GetItemState( _hwndLV, i, kStateMask ) & kStateChecked )
        {
            uItemCount++;
        }
    }
    return uItemCount;
}

BOOL
TArchLV::
bGetCheckedItems(
    IN UINT   uIndex,
    IN BOOL  *pbInstalled,
    IN DWORD *pdwEncode
    )
{   
    TArchData *pArchData;
    UINT uItemCount = 0;
    BOOL bReturn = FALSE;

    DWORD cItems = ListView_GetItemCount( _hwndLV );

    for( UINT i = 0; i < cItems; i++ )
    {
        if( ListView_GetCheckState( _hwndLV, i ) )
        {
            if( uItemCount++ == uIndex )
            {
                if( bGetItemData( i, &pArchData ) )
                {
                    *pdwEncode      = pArchData->_Encode;
                    *pbInstalled    = pArchData->_bInstalled;
                    bReturn         = TRUE;
                    break;
                }                    
            }
        }
    }
    return bReturn;
}

BOOL
TArchLV::
bGetItemData(
    IN INT          iItem,
    IN TArchData  **ppArchData
    ) const
{
    BOOL bStatus;

    LV_ITEM lvItem  = { 0 };
    lvItem.mask     = LVIF_PARAM;
    lvItem.iItem    = iItem; 
    
    bStatus = ListView_GetItem( _hwndLV, &lvItem );

    if( bStatus )
    {
        *ppArchData = reinterpret_cast<TArchData *>( lvItem.lParam );
    }

    return bStatus;         
}

/*++

Name:
    
    vItemClicked

Routine Description:

    User clicked in listview.  Check if item state should
    be changed.

    The item will also be selected.

Arguments:

    iItem - Item that has been clicked.

Return Value:

    Nothing.

--*/
VOID
TArchLV::
vItemClicked(
    IN INT iItem
    )
{
    TStatusB bStatus;
    TArchData *pArchData;

    //
    // Do nothing when an invalid index is passed.
    //
    if( iItem != -1 &&  !_bNoItemCheck )
    {
        //
        // Retrieve the old state, toggle it, then set it.
        //
        DWORD dwState = ListView_GetItemState( _hwndLV, iItem, kStateMask );

        //
        // Get the item data.
        //
        bStatus DBGCHK = bGetItemData( iItem, &pArchData );

        //
        // If item data was fetched.
        //
        if( bStatus && pArchData )
        {
            //
            // Only allow checking of the item that are not installed.
            //
            if( !pArchData->_bInstalled )
            {
                //
                // Toggle the current state.
                //
                vCheckItem( iItem,  dwState != kStateChecked );
            }
        }
    }
}

/*++

Name:
    
    vCheckItem

Routine Description:

    Change the specified items check state.

Arguments:

    iItem       - Item index to change the check state for.
    bCheckState - The new item check state, TRUE check, FALSE uncheck.

Return Value:

    Nothing.

--*/
VOID
TArchLV::
vCheckItem(
    IN INT      iItem,
    IN BOOL     bCheckState
    )
{
    //
    // Do nothing when an invalid index is passed.
    //
    if( iItem != -1 )
    {
        if( bCheckState == 2 )
        {
            //
            // Set the new check state.
            //
            ListView_SetItemState( _hwndLV, iItem, kStateDisabled, kStateMask | LVIS_SELECTED | LVIS_FOCUSED );
            return;
        }

        //
        // Set the item check state.
        //
        DWORD dwState = bCheckState ? kStateChecked | LVIS_SELECTED | LVIS_FOCUSED : kStateUnchecked | LVIS_SELECTED | LVIS_FOCUSED;

        //
        // Set the new check state.
        //
        ListView_SetItemState( _hwndLV, iItem, dwState, kStateMask | LVIS_SELECTED | LVIS_FOCUSED );
    }
}

/********************************************************************

    Architecure data.

********************************************************************/
TArchLV::TArchData::
TArchData(
    IN LPCTSTR pszArchitecture,
    IN LPCTSTR pszVersion,
    IN LPCTSTR pszInstalled,
    IN DWORD   Encode,
    IN BOOL    bInstalled
    ) : _strArchitecture( pszArchitecture ),
        _strVersion( pszVersion ),
        _strInstalled( pszInstalled ),
        _Encode( Encode ),
        _bInstalled( bInstalled )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::TArchData::ctor\n" ) );
}

TArchLV::TArchData::
~TArchData(
    VOID
    )
{
    DBGMSG( DBG_TRACE, ( "TArchLV::TArchData::dtor\n" ) );

    //
    // If we are linked then remove ourself.
    //
    if( ArchData_bLinked() )
    {
        ArchData_vDelinkSelf();
    }
}

BOOL
TArchLV::TArchData::
bValid(
    VOID
    )
{
    return VALID_OBJ( _strArchitecture ) &&
           VALID_OBJ( _strVersion ) &&
           VALID_OBJ( _strInstalled );
}