// CListViewEx.cpp : implementation file

#include "stdafx.h"
#include "ListViewEx.h"

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

/////////////////////////////////////////////////////////////////////////////
// CListViewEx

CListViewEx::CListViewEx()
{
}

CListViewEx::~CListViewEx()
{
}


BEGIN_MESSAGE_MAP(CListViewEx, CListView)
	//{{AFX_MSG_MAP(CListViewEx)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CListViewEx drawing

void CListViewEx::OnDraw(CDC* pDC)
{
}

/////////////////////////////////////////////////////////////////////////////
// CListViewEx diagnostics

#ifdef _DEBUG
void CListViewEx::AssertValid() const
{
	CListView::AssertValid();
}

void CListViewEx::Dump(CDumpContext& dc) const
{
	CListView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CListViewEx message handlers

//  FUNCTION:   DrawListViewItem(const LPDRAWITEMSTRUCT)
//
//  PURPOSE:    Draws one item in the listview control.
//
//  PARAMETERS:
//      lpDrawItem - Pointer to the information needed to draw the item.  The
//                   item number is in the itemID member.
//
void CListViewEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItem)
{
    HIMAGELIST himl;
    LV_ITEM lvi;
    int cxImage = 0, cyImage = 0;
    UINT uFirstColWidth;
    RECT rcClip;
    int iColumn = 1;
    UINT uiFlags = ILD_TRANSPARENT;

    CListCtrl&  lc = GetListCtrl();
    ASSERT(lc.GetSafeHwnd() == lpDrawItem->hwndItem);

    // Get the item image to be displayed
    lvi.mask = LVIF_IMAGE | LVIF_STATE | LVIF_PARAM;
    lvi.iItem = lpDrawItem->itemID;
    lvi.iSubItem = 0;
    lc.GetItem(&lvi);

    // Check to see if this item is selected
    if (lpDrawItem->itemState & ODS_SELECTED)
    {
        // Set the text background and foreground colors
        SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
        SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHT));

        // Also add the ILD_BLEND50 so the images come out selected
        uiFlags |= ILD_BLEND50;
    }
    else
    {
        // Set the text background and foreground colors to the standard window
        // colors
        SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOWTEXT));
        SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOW));
    }


    // Get the image list and draw the image
    himl = lc.GetImageList(LVSIL_SMALL)->GetSafeHandle();
    if (himl)
    {
        ImageList_Draw(himl, lvi.iImage, lpDrawItem->hDC,
                       lpDrawItem->rcItem.left, lpDrawItem->rcItem.top,
                       uiFlags);

        // Find out how big the image we just drew was
        ImageList_GetIconSize(himl, &cxImage, &cyImage);
    }

    // Calculate the width of the first column after the image width.  If
    // There was no image, then cxImage will be zero.
    uFirstColWidth = lc.GetColumnWidth(0) - cxImage;

    // Set up the new clipping rect for the first column text and draw it
    rcClip.left = lpDrawItem->rcItem.left + cxImage;
    rcClip.right = lpDrawItem->rcItem.left + lc.GetColumnWidth(0);
    rcClip.top = lpDrawItem->rcItem.top;
    rcClip.bottom = lpDrawItem->rcItem.bottom;
    DrawItemColumn(lpDrawItem->hDC, GetColumnText(0, lvi.lParam), &rcClip);

    CHeaderCtrl*    pHeader = (CHeaderCtrl*)GetDlgItem(0);
    ASSERT(pHeader);
    int             nColumns = pHeader->GetItemCount();

    // Update the clip rect to the nth column
    for( int i = 1; i < nColumns; i++ )
    {
        rcClip.left = rcClip.right;
        rcClip.right = rcClip.left + lc.GetColumnWidth(i);
        DrawItemColumn(lpDrawItem->hDC, GetColumnText(i, lvi.lParam), &rcClip);
    }

    // If we changed the colors for the selected item, undo it
    if (lpDrawItem->itemState & ODS_SELECTED)
    {
        // Set the text background and foreground colors
        SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOWTEXT));
        SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOW));
    }

    // If the item is focused, now draw a focus rect around the entire row
    if (lpDrawItem->itemState & ODS_FOCUS)
    {
        // Adjust the left edge to exclude the image
        rcClip = lpDrawItem->rcItem;
        rcClip.left += cxImage;

        // Draw the focus rect
        DrawFocusRect(lpDrawItem->hDC, &rcClip);
    }
}


//  FUNCTION:   DrawItemColumn(HDC, LPTSTR, LPRECT)
//
//  PURPOSE:    Draws the text for one of the columns in the list view.
//
//  PARAMETERS:
//      hdc     - Handle of the DC to draw the text into.
//      lpsz    - String to draw.
//      prcClip - Rectangle to clip the string to.
//
void CListViewEx::DrawItemColumn(HDC hdc, LPCTSTR lpsz, LPRECT prcClip)
{
    TCHAR szString[256];

    // Check to see if the string fits in the clip rect.  If not, truncate
    // the string and add "...".
    lstrcpy(szString, lpsz);
    CalcStringEllipsis(hdc, szString, 256, prcClip->right - prcClip->left);

    // print the text
    ExtTextOut(hdc, prcClip->left + 2, prcClip->top + 1, ETO_CLIPPED | ETO_OPAQUE,
               prcClip, szString, lstrlen(szString), NULL);
}


//  FUNCTION:   CalcStringEllipsis(HDC, LPTSTR, int, UINT)
//
//  PURPOSE:    Determines whether the specified string is too wide to fit in
//              an allotted space, and if not truncates the string and adds some
//              points of ellipsis to the end of the string.
//
//  PARAMETERS:
//      hdc        - Handle of the DC the string will be drawn on.
//      lpszString - Pointer to the string to verify
//      cchMax     - Maximum size of the lpszString buffer.
//      uColWidth  - Width of the space in pixels to fit the string into.
//
//  RETURN VALUE:
//      Returns TRUE if the string needed to be truncated, or FALSE if it fit
//      into uColWidth.
//
BOOL CListViewEx::CalcStringEllipsis(HDC hdc, LPTSTR lpszString, int cchMax, UINT uColWidth)
{
    const TCHAR szEllipsis[] = TEXT("...");
    SIZE   sizeString;
    SIZE   sizeEllipsis;
    int    cbString;
    LPTSTR lpszTemp;
    BOOL   fSuccess = FALSE;
    static BOOL fOnce = TRUE;
    static FARPROC pGetTextExtentPoint;

    // Adjust the column width to take into account the edges
    uColWidth -= 4;

    __try
    {
        // Allocate a string for us to work with.  This way we can mangle the
        // string and still preserve the return value
        lpszTemp = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchMax);
        if (!lpszTemp)
        {
            //ErrorHandler();
            __leave;
        }
        lstrcpy(lpszTemp, lpszString);

        // Get the width of the string in pixels
        cbString = lstrlen(lpszTemp);
        if (!GetTextExtentPoint32(hdc, lpszTemp, cbString, &sizeString))
        {
            //ErrorHandler();
            __leave;
        }

        // If the width of the string is greater than the column width shave
        // the string and add the ellipsis
        if ((ULONG)sizeString.cx > uColWidth)
        {
            if (!GetTextExtentPoint32(hdc, szEllipsis, lstrlen(szEllipsis),
                                       &sizeEllipsis))
            {
                //ErrorHandler();
                __leave;
            }

            while (cbString > 0)
            {
                lpszTemp[--cbString] = 0;
                if (!GetTextExtentPoint32(hdc, lpszTemp, cbString, &sizeString))
                {
                    //ErrorHandler();
                    __leave;
                }

                if ((ULONG)(sizeString.cx + sizeEllipsis.cx) <= uColWidth)
                {
                    // The string with the ellipsis finally fits, now make sure
                    // there is enough room in the string for the ellipsis
                    if (cchMax >= (cbString + lstrlen(szEllipsis)))
                    {
                        // Concatenate the two strings and break out of the loop
                        lstrcat(lpszTemp, szEllipsis);
                        lstrcpy(lpszString, lpszTemp);
                        fSuccess = TRUE;
                        __leave;
                    }
                }
            }
        }
        else
        {
            // No need to do anything, everything fits great.
            fSuccess = TRUE;
        }
    }
    __finally
    {
        // Free the memory
        HeapFree(GetProcessHeap(), 0, (LPVOID)lpszTemp);
        return (fSuccess);
    }
}
