//============================================================================
// Copyright (C) Microsoft Corporation, 1996 - 1999 
//
// File:    lcx.cpp
//
// History:
//  07/13/96    Abolade Gbadegesin      Created, based on C code by Steve Cobb
//
// Implements an enhanced list-control.
//============================================================================
#include "stdafx.h"
#include "resource.h"
#include "util.h"
#include "listctrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)

BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
    //{{AFX_MSG_MAP(CListCtrlEx)
    ON_WM_LBUTTONDOWN()
    ON_WM_CHAR()
    ON_WM_KEYDOWN()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::~CListCtrlEx
//
// Destructor. Deletes the image list, if any, and unloads row-information.
//----------------------------------------------------------------------------

CListCtrlEx::~CListCtrlEx(
    ) {

	delete m_pimlChecks;
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::GetColumnCount
//
// Called to retrieve the number of columns in any list-control.
//----------------------------------------------------------------------------

INT
CListCtrlEx::GetColumnCount(
    ) {

    return Header_GetItemCount(ListView_GetHeader(m_hWnd));
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::SetColumnText
//
// Sets the text in the header of the column in position 'iCol'.
//----------------------------------------------------------------------------

BOOL
CListCtrlEx::SetColumnText(
    INT             iCol,
    LPCTSTR         pszText,
    INT             fmt
    ) {

    LV_COLUMN   lvc;

    lvc.mask = LVCF_FMT | LVCF_TEXT;
    lvc.pszText = (LPTSTR)pszText;
    lvc.fmt = fmt;

    return SetColumn(iCol, &lvc);
}


//----------------------------------------------------------------------------
// Function:    CListCtrlEx::InstallChecks
//
// Installs check-box handling for the list-control.
//----------------------------------------------------------------------------

BOOL
CListCtrlEx::InstallChecks(
    ) {

    HICON   hIcon;

    //
    // Make sure the list-control is in report-mode
    //

    if (!(GetStyle() & LVS_REPORT)) { return FALSE; }


    //
    // Allocate a new image-list.
    //

    m_pimlChecks = new CImageList;

    if (!m_pimlChecks) { return FALSE; }

    do {
    
        //
        // Initialize the image-list
        //
    
        if (!m_pimlChecks->Create(
                ::GetSystemMetrics(SM_CXSMICON),
                ::GetSystemMetrics(SM_CYSMICON),
                ILC_MASK, 2, 2
                )) {
    
            break;
        }
    
    
        //
        // Add the icons for the checked and unchecked images
        //
    
        hIcon = ::LoadIcon(
                    AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_COMMON_UNCHECK)
                    );

        if (!hIcon) { break; }

        m_pimlChecks->Add(hIcon); ::DeleteObject(hIcon);

    
        hIcon = ::LoadIcon(
                    AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_COMMON_CHECK)
                    );

        if (!hIcon) { break; }

        m_pimlChecks->Add(hIcon); ::DeleteObject(hIcon);


        //
        // Replace the list-control's current image-list with the new one
        //
    
        m_pimlOldState = SetImageList(m_pimlChecks, LVSIL_STATE);

        return TRUE;
    
    } while(FALSE);


    //
    // If we arrive here, an error occurred, so clean up and fail
    //

    delete m_pimlChecks; m_pimlChecks = NULL;

    return FALSE;
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::UninstallChecks
//
// Uninstalls checkbox-handling for the list-control.
//----------------------------------------------------------------------------

VOID
CListCtrlEx::UninstallChecks(
    ) {

    if (!m_pimlChecks) { return; }

    if (m_pimlOldState) { SetImageList(m_pimlOldState, LVSIL_STATE); }

    delete m_pimlChecks; m_pimlChecks = NULL;
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::GetCheck
//
// Returns TRUE if the specified item is checked, FALSE otherwise.
//----------------------------------------------------------------------------

BOOL
CListCtrlEx::GetCheck(
    INT     iItem
    ) {

    return !!(GetItemState(iItem, LVIS_STATEIMAGEMASK) &
                INDEXTOSTATEIMAGEMASK(LCXI_CHECKED));
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::SetCheck
//
// If 'fCheck' is non-zero, checks 'iItem', otherwise clears 'iItem'.
//----------------------------------------------------------------------------

VOID
CListCtrlEx::SetCheck(
    INT     iItem,
    BOOL    fCheck
    ) {

    SetItemState(
        iItem,
        INDEXTOSTATEIMAGEMASK(fCheck ? LCXI_CHECKED : LCXI_UNCHECKED),
        LVIS_STATEIMAGEMASK
        );

    if (GetParent()) {

        NMHDR nmh;

        nmh.code = LVXN_SETCHECK;
        nmh.hwndFrom = m_hWnd;

        ::SendMessage(
            GetParent()->m_hWnd, WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmh
            );
    }
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::OnChar
//
// Handles the 'WM_CHAR' message for the list-control.
// This allows users to change items' checked-states using the keyboard.
//----------------------------------------------------------------------------

VOID
CListCtrlEx::OnChar(
    UINT    nChar,
    UINT    nRepCnt,
    UINT    nFlags
    ) {

    BOOL    fSet = FALSE;
    BOOL    fClear = FALSE;
    BOOL    fToggle = FALSE;
    INT     iItem;


    //
    // Handle characters with special meaning for us
    //

    switch (nChar) {

        case TEXT(' '): { fToggle = TRUE; break; }

        case TEXT('+'):
        case TEXT('='): { fSet = TRUE; break; }

        case TEXT('-'): { fClear = TRUE; break; }
    }

    if (!fToggle && !fSet && !fClear) {

        CListCtrl::OnChar(nChar, nRepCnt, nFlags);
    }
    else {

        //
        // Change the state of all the selected items
        //

        for (iItem = GetNextItem(-1, LVNI_SELECTED);
             iItem != -1;
             iItem = GetNextItem(iItem, LVNI_SELECTED)) {

            if (fToggle) {

                SetCheck(iItem, !GetCheck(iItem));
            }
            else
            if (fSet) {

                if (!GetCheck(iItem)) { SetCheck(iItem, TRUE); }
            }
            else {

                if (GetCheck(iItem)) { SetCheck(iItem, FALSE); }
            }
        }
    }
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::OnKeyDown
//
// Handles the 'WM_KEYDOWN' message for the list-control.
// This allows users to change items' checked-states using the keyboard.
//----------------------------------------------------------------------------

VOID
CListCtrlEx::OnKeyDown(
    UINT    nChar,
    UINT    nRepCnt,
    UINT    nFlags
    ) {

    //
    // We want the left-arrow treated as an up-arrow
    // and the right-arrow treated as a down-arrow.
    //

    if (nChar == VK_LEFT) {

        CListCtrl::OnKeyDown(VK_UP, nRepCnt, nFlags); return;
    }
    else
    if (nChar == VK_RIGHT) {

        CListCtrl::OnKeyDown(VK_DOWN, nRepCnt, nFlags); return;
    }

    CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}



//----------------------------------------------------------------------------
// Function:    CListCtrlEx::OnLButtonDown
//
// Handles the 'WM_LBUTTONDOWN' message, changing an item's checked state 
// when the user clicks the item's check-image.
//----------------------------------------------------------------------------

VOID
CListCtrlEx::OnLButtonDown(
    UINT    nFlags,
    CPoint  pt
    ) {

    INT     iItem;
    UINT    uiFlags;


    //
    // If the left-mouse button is over the check-box icon,
    // we treat it as a toggle on the check-box.
    //

    uiFlags = 0;

    iItem = HitTest(pt, &uiFlags);


    if (iItem != -1 && (uiFlags & LVHT_ONITEMSTATEICON)) {

        SetCheck(iItem, !GetCheck(iItem));

        // Redraw this item
        RedrawItems(iItem, iItem);
    }

    CListCtrl::OnLButtonDown(nFlags, pt);
}




//----------------------------------------------------------------------------
// Function:    AdjustColumnWidth
//
// Called to adjust the width of column 'iCol' so that the string 'pszContent'
// can be displayed in the column without truncation.
//
// If 'NULL' is specified for 'pszContent', the function adjusts the column
// so that the first string in the column is displayed without truncation.
//
// Returns the new width of the column.
//----------------------------------------------------------------------------

INT
AdjustColumnWidth(
    IN      CListCtrl&      listCtrl,
    IN      INT             iCol,
    IN      LPCTSTR         pszContent
    ) {

    INT iWidth, iOldWidth;


    //
    // Compute the minimum width the column needs to be
    //

    if (pszContent) {

        iWidth = listCtrl.GetStringWidth(pszContent);
    }
    else {

        iWidth = listCtrl.GetStringWidth(listCtrl.GetItemText(0, iCol));
    }


    //
    // Adjust 'iWidth' to leave some breathing space
    //

    iWidth += ::GetSystemMetrics(SM_CXSMICON) +
              ::GetSystemMetrics(SM_CXEDGE) * 2;


    //
    // If the column is narrower than 'iWidth', enlarge it.
    //

    iOldWidth = listCtrl.GetColumnWidth(iCol);

    if (iOldWidth < iWidth) {

        listCtrl.SetColumnWidth(iCol, iWidth);

        iOldWidth = iWidth;
    }

    return iOldWidth;
}


INT
AdjustColumnWidth(
    IN  CListCtrl&      listCtrl,
    IN  INT             iCol,
    IN  UINT            idsContent
    ) {

    // Needed for Loadstring
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    CString sCol;

    sCol.LoadString(idsContent);

    return AdjustColumnWidth(listCtrl, iCol, sCol);
}