#ifndef __DOTNET_TABCTRL_H__
#define __DOTNET_TABCTRL_H__

#pragma once

/////////////////////////////////////////////////////////////////////////////
// CDotNetTabCtrl - Tab control derived from CCustomTabCtrl
//    meant to look like the tabs in VS.Net (MDI tabs,
//    solution explorer tabs, etc.)
// CDotNetButtonTabCtrl - Tab control derived from CCustomTabCtrl
//    meant to look like VS.Net view of HTML with the Design/HTML buttons
//
//
// Written by Daniel Bowen (dbowen@es.com).
// Copyright (c) 2001-2004 Daniel Bowen.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//
// History (Date/Author/Description):
// ----------------------------------
//
// 2005/07/13: Daniel Bowen
// - Namespace qualify the use of more ATL and WTL classes.
//
// 2005/03/14: Daniel Bowen
// - Fix warnings when compiling for 64-bit.
//
// 2004/06/28: Daniel Bowen:
// - Clean up warnings on level 4
//
// 2004/06/21: Peter Carlson:
// - "CanClose" for items.
//
// 2004/04/29: Daniel Bowen
// - Update CDotNetTabCtrlImpl::OnSettingChange so that
//   if the color depth is not greater than 8bpp
//   (so, 256 colors or less), instead of this
//            m_hbrBackground =  CDCHandle::GetHalftoneBrush();
//   (which makes things unreadable at 256 colors), do this:
//            m_hbrBackground.CreateSysColorBrush(COLOR_WINDOW);
// - Support for highlighting items.
//   Add CCustomTabItem::GetHighlight/SetHighlight.
//   Uses the custom draw state CDIS_MARKED.
//
// 2004/01/06: Daniel Bowen
// - Fix compile issue under ICL thanks to Richard Crossley.
//   CDotNetButtonTabCtrl missing template parameters.
//
// 2003/08/01: Daniel Bowen
// - CDotNetButtonTabCtrl can now be used for MDI tabs.
// - There is now a CDotNetButtonTabCtrlImpl, which
//   CDotNetButtonTabCtrl inherits from.
// - Reorganize so that CDotNetTabCtrlImpl is broken up a little bit
//   more into overridable pieces, and have CDotNetButtonTabCtrlImpl
//   inherit from CDotNetTabCtrlImpl.  Have CDotNetButtonTabCtrlImpl
//   only override the parts of CDotNetTabCtrlImpl needed.
//
// 2003/06/27: Daniel Bowen
// - Replace DECLARE_WND_CLASS with DECLARE_WND_CLASS_EX.
//   Use CS_DBLCLKS as the style and COLOR_WINDOW as the brush -
//   which essentially means that it doesn't use CS_HREDRAW or CS_VREDRAW now.
//
// 2002/12/05: Daniel Bowen
// - In CDotNetTabCtrlImpl::UpdateLayout_Default,
//   the code that calculates nRatioWithSelectionFullSize
//   would sometimes get a 0 in the divisor.
//   Guard against divide-by-0.
// - Handle WM_SYSCOLORCHANGE in case its broadcast to us
//   from a top-level window. Call OnSettingChange.
//
// 2002/11/13: Daniel Bowen
// - CDotNetTabCtrl:
//   * Tweaks so that CDotNetTabCtrl matches just a little more
//     closely to the tabs in VS.NET (when tabs are on top).
//   * Override new "CalcSize_NonClient" method as part of the
//     tweaks for CDotNetTabCtrl.  Currently, only the left
//     and right sides of the client area are adjusted for
//     non-client areas (since the drawing code already considers
//     non-client areas above and below in its drawing).
//     In the future, non-client areas could be accounted for
//     in CalcSize_NonClient, and the drawing code could
//     be updated appropriately.
//   * New CTCS_FLATEDGE style.  CDotNetTabCtrl asks about this
//     style when drawing the outline around the tab.  A good
//     use for this style is if you are using the tab control
//     for the MDI tabs, and you have your MDIClient drawn flat
//     (like VS.NET)
//
// 2002/07/16: Daniel Bowen
// - Fix problem that would cause applications using CDotNetTabCtrl
//   with CTCS_CLOSEBUTTON and/or CTCS_SCROLL to not exit "cleanly".
//   There were 2 places referencing m_tooltip that needed to
//   first check if(m_tooltip.IsWindow()).
//
// 2002/06/12: Daniel Bowen
// - Publish codeproject article.  For history prior
//   to the release of the article, please see the article
//   and the section "Note to previous users"

#ifndef __CUSTOMTABCTRL_H__
#include "CustomTabCtrl.h"
#endif

// NOTE: If you are compiling under VC7, be sure to put the following in
// your precompiled header:
//
//extern "C" const int _fltused = 0;

template<typename T, typename TItem = CCustomTabItem, class TBase = ATL::CWindow, class TWinTraits = CCustomTabCtrlWinTraits>
class CDotNetTabCtrlImpl :
    public CCustomTabCtrl<T, TItem, TBase, TWinTraits>
{
protected:
    typedef CDotNetTabCtrlImpl<T, TItem, TBase, TWinTraits> thisClass;
    typedef CCustomTabCtrl<T, TItem, TBase, TWinTraits> customTabClass;

protected:
    CBrush m_hbrBackground;
    COLORREF m_clrTextInactiveTab, m_clrSelectedTab;

    signed char m_nFontSizeTextTopOffset;

    const signed char m_nMinWidthToDisplayText;

// Constructor
public:
    CDotNetTabCtrlImpl() :
        m_clrTextInactiveTab(::GetSysColor(COLOR_GRAYTEXT)),
        m_clrSelectedTab(::GetSysColor(COLOR_BTNFACE)),
        m_nFontSizeTextTopOffset(0),
        m_nMinWidthToDisplayText(12)
    {
    }

// Message Handling
public:
    DECLARE_WND_CLASS_EX(_T("WTL_DotNetTabCtrl"), CS_DBLCLKS, COLOR_WINDOW)

    BEGIN_MSG_MAP(thisClass)
        MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
        MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSettingChange)
        CHAIN_MSG_MAP(customTabClass)
    END_MSG_MAP()

    LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        DWORD dwStyle = this->GetStyle();
        // Initialize/Reinitialize font
        // Visual Studio.Net seems to use the "icon" font for the tabs
        LOGFONT lfIcon = { 0 };
        ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIcon), &lfIcon, 0);
        bool bResetFont = true;

        if (!m_font.IsNull())
        {
            LOGFONT lf = {0};

            if (m_font.GetLogFont(&lf))
            {
                if (lstrcmpi(lf.lfFaceName, lfIcon.lfFaceName) == 0 &&
                        lf.lfHeight == lfIcon.lfHeight)
                {
                    bResetFont = false;
                }
            }
        }

        if (bResetFont)
        {
            if (!m_font.IsNull()) m_font.DeleteObject();

            if (!m_fontSel.IsNull()) m_fontSel.DeleteObject();

            HFONT font = m_font.CreateFontIndirect(&lfIcon);

            if (font == NULL)
            {
                m_font.Attach(AtlGetDefaultGuiFont());
            }

            if (CTCS_BOLDSELECTEDTAB == (dwStyle & CTCS_BOLDSELECTEDTAB))
            {
                lfIcon.lfWeight = FW_BOLD;
            }

            font = m_fontSel.CreateFontIndirect(&lfIcon);

            if (font == NULL)
            {
                m_fontSel.Attach(AtlGetDefaultGuiFont());
            }
        }

        // Background brush
        if (!m_hbrBackground.IsNull()) m_hbrBackground.DeleteObject();

        WTL::CWindowDC dcWindow(NULL);
        int nBitsPerPixel = dcWindow.GetDeviceCaps(BITSPIXEL);

        if (nBitsPerPixel > 8)
        {
            //COLORREF clrBtnHilite = ::GetSysColor(COLOR_BTNHILIGHT);
            //COLORREF clrBtnFace = ::GetSysColor(COLOR_BTNFACE);
            //m_clrBackground =
            //    RGB( GetRValue(clrBtnFace) + ((GetRValue(clrBtnHilite) - GetRValue(clrBtnFace)) / 3 * 2),
            //        GetGValue(clrBtnFace) + ((GetGValue(clrBtnHilite) - GetGValue(clrBtnFace)) / 3 * 2),
            //        GetBValue(clrBtnFace) + ((GetBValue(clrBtnHilite) - GetBValue(clrBtnFace)) / 3 * 2),
            //    );
            //m_hbrBackground.CreateSolidBrush(m_clrBackground);
            COLORREF clrBtnFace = ::GetSysColor(COLOR_BTNFACE);
            // This is a brave attempt to mimic the algorithm that Visual Studio.Net
            // uses to calculate the tab's background color and inactive tab color.
            // The other colors that VS.Net uses seems to be standard ones,
            // but these two colors are calculated.
            BYTE nRed = 0, nGreen = 0, nBlue = 0, nMax = 0;
            // Early experiments seemed to reveal that the background color is dependant
            // on COLOR_BTNFACE.  The following algorithm is just an attempt
            // to match several empirical results.  I tested with 20 variations
            // on COLOR_BTNFACE and kept track of what the tab background became.
            // I then brought the numbers into Excel, and started crunching on the numbers
            // until I came up with a formula that seems to pretty well match.
            nRed = GetRValue(clrBtnFace);
            nGreen = GetGValue(clrBtnFace);
            nBlue = GetBValue(clrBtnFace);
            nMax = (nRed > nGreen) ? ((nRed > nBlue) ? nRed : nBlue) : ((nGreen > nBlue) ? nGreen : nBlue);
            const BYTE nMagicBackgroundOffset = (nMax > (0xFF - 35)) ? (BYTE)(0xFF - nMax) : (BYTE)35;

            if (nMax == 0)
            {
                nRed = (BYTE)(nRed + nMagicBackgroundOffset);
                nGreen = (BYTE)(nGreen + nMagicBackgroundOffset);
                nBlue = (BYTE)(nBlue + nMagicBackgroundOffset);
            }
            else
            {
                nRed = (BYTE)(nRed + (nMagicBackgroundOffset * (nRed / (double)nMax) + 0.5));
                nGreen = (BYTE)(nGreen + (nMagicBackgroundOffset * (nGreen / (double)nMax) + 0.5));
                nBlue = (BYTE)(nBlue + (nMagicBackgroundOffset * (nBlue / (double)nMax) + 0.5));
            }

            m_hbrBackground.CreateSolidBrush(RGB(nRed, nGreen, nBlue));
            // The inactive tab color seems to be calculated in a similar way to
            // the tab background, only instead of lightening BNTFACE, it darkens GRAYTEXT.
            COLORREF clrGrayText = ::GetSysColor(COLOR_GRAYTEXT);
            nRed = GetRValue(clrGrayText);
            nGreen = GetGValue(clrGrayText);
            nBlue = GetBValue(clrGrayText);
            nMax = (nRed > nGreen) ? ((nRed > nBlue) ? nRed : nBlue) : ((nGreen > nBlue) ? nGreen : nBlue);
            const BYTE nMagicInactiveOffset = 43;

            if (nMax != 0)
            {
                if (nRed < nMagicInactiveOffset)
                    nRed = (BYTE)(nRed / 2);
                else
                    nRed  = (BYTE)(nRed - (nMagicInactiveOffset * (nRed / (double)nMax) + 0.5));

                if (nGreen < nMagicInactiveOffset)
                    nGreen = (BYTE)(nGreen / 2);
                else
                    nGreen = (BYTE)(nGreen - (nMagicInactiveOffset * (nGreen / (double)nMax) + 0.5));

                if (nBlue < nMagicInactiveOffset)
                    nBlue = (BYTE)(nBlue / 2);
                else
                    nBlue = (BYTE)(nBlue - (nMagicInactiveOffset * (nBlue / (double)nMax) + 0.5));
            }

            m_clrTextInactiveTab = RGB(nRed, nGreen, nBlue);
        }
        else
        {
            m_hbrBackground.CreateSysColorBrush(COLOR_WINDOW);
            m_clrTextInactiveTab = ::GetSysColor(COLOR_GRAYTEXT);
        }

        m_settings.iIndent = 5;
        m_settings.iPadding = 4;
        m_settings.iMargin = 3;
        m_settings.iSelMargin = 3;
        int nHeightLogicalUnits = -lfIcon.lfHeight;
        // In MSDN for "LOGFONT", they give the following formula for calculating
        // the log font height given a point size.
        //long lfHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
        const int nNominalFontLogicalUnits = 11;    // 8 point Tahoma with 96 DPI
        m_nFontSizeTextTopOffset = (BYTE)((nHeightLogicalUnits - nNominalFontLogicalUnits) / 2);
        T* pT = static_cast<T*>(this);
        pT->UpdateLayout();
        pT->Invalidate();
        return 0;
    }


// Overrideables
public:
    void DrawBackground(RECT rcClient, LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);
        // Set up the text color and background mode
        dc.SetTextColor(lpNMCustomDraw->clrBtnText);
        dc.SetBkMode(TRANSPARENT);
        RECT rcClip = {0};
        dc.GetClipBox(&rcClip);

        if (::EqualRect(&rcClip, &m_rcCloseButton) ||
                ::EqualRect(&rcClip, &m_rcScrollRight) ||
                ::EqualRect(&rcClip, &m_rcScrollLeft))
        {
            // Paint needed in only "other button" area
            HBRUSH hOldBrush = dc.SelectBrush(lpNMCustomDraw->hBrushBackground);
            dc.PatBlt(rcClip.left, rcClip.top, rcClip.right - rcClip.left, rcClip.bottom - rcClip.top, PATCOPY);
            dc.SelectBrush(hOldBrush);
        }
        else
        {
            // Paint needed in tab item area or more
            // Erase Background
            //  (do it here instead of a handler for WM_ERASEBKGND
            //   so that we can do flicker-free drawing with the help
            //   of COffscreenDrawRect that's in the base class)
            // TODO: Don't "erase" entire client area.
            //  Do a smarter erase of just what needs it
            RECT rc = rcClient;
            HBRUSH hOldBrush = dc.SelectBrush(lpNMCustomDraw->hBrushBackground);
            dc.PatBlt(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
            dc.SelectBrush(hOldBrush);
            // Connect with the client area.
            DWORD dwStyle = this->GetStyle();

            if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
            {
                rc.bottom = rc.top + 3;
                dc.FillSolidRect(&rc, lpNMCustomDraw->clrBtnFace);
                CPen penText;
                penText.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnText);
                CPenHandle penOld = dc.SelectPen(penText);
                dc.MoveTo(rc.left, rc.bottom);
                dc.LineTo(rc.right, rc.bottom);
                dc.SelectPen(penOld);
            }
            else
            {
                int nOrigTop = rc.top;
                rc.top = rc.bottom - 2;
                dc.FillSolidRect(&rc, lpNMCustomDraw->clrBtnFace);
                CPen penHilight;
                penHilight.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnHighlight);
                CPenHandle penOld = dc.SelectPen(penHilight);
                dc.MoveTo(rc.left, rc.top - 1);
                dc.LineTo(rc.right, rc.top - 1);
                rc.top = nOrigTop;
                CPen penShadow, pen3D;
                penShadow.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnShadow);
                pen3D.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnFace);
                dc.SelectPen(penShadow);
                dc.MoveTo(rc.left, rc.bottom);
                dc.LineTo(rc.left, rc.top);
                dc.LineTo(rc.right - 1, rc.top);

                if (0 == (dwStyle & CTCS_FLATEDGE))
                {
                    dc.SelectPen(penHilight);
                }

                dc.LineTo(rc.right - 1, rc.bottom);
                dc.SelectPen(pen3D);
                dc.MoveTo(rc.right - 2, rc.bottom - 3);
                dc.LineTo(rc.right - 2, rc.top);
                dc.MoveTo(rc.left + 1, rc.bottom - 3);
                dc.LineTo(rc.left + 1, rc.top);
                dc.SelectPen(penOld);
            }
        }
    }

    void DrawItem_InitBounds(DWORD dwStyle, RECT rcItem, RECT& rcTab, RECT& rcText, int& nIconVerticalCenter)
    {
        if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
        {
            rcTab.top += 3;
            rcTab.bottom -= 2;
            rcText.top = rcTab.top + 1 + m_nFontSizeTextTopOffset;
            rcText.bottom = rcItem.bottom;
            //nIconVerticalCenter = rcTab.top + (rc.bottom - rcTab.top) / 2;
            //nIconVerticalCenter = rcTab.top + rcText.Height() / 2;
            nIconVerticalCenter = (rcItem.bottom + rcItem.top) / 2 + rcTab.top / 2;
        }
        else
        {
            rcTab.top += 3;
            rcTab.bottom -= 2;
            rcText.top = rcItem.top + 1 + m_nFontSizeTextTopOffset;
            rcText.bottom = rcItem.bottom;
            nIconVerticalCenter = (rcItem.bottom + rcItem.top) / 2 + rcTab.top / 2;
        }
    }

    void DrawItem_TabSelected(DWORD dwStyle, LPNMCTCCUSTOMDRAW lpNMCustomDraw, RECT& rcTab)
    {
        // Tab is selected, so paint tab folder
        bool bHighlighted = (CDIS_MARKED == (lpNMCustomDraw->nmcd.uItemState & CDIS_MARKED));
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);
        rcTab.right--;

        if (bHighlighted)
        {
            dc.FillSolidRect(&rcTab, lpNMCustomDraw->clrHighlight);
        }
        else
        {
            dc.FillSolidRect(&rcTab, lpNMCustomDraw->clrSelectedTab);
        }

        WTL::CPen penText, penHilight;
        penText.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnText);
        penHilight.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnHighlight);

        if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
        {
            WTL::CPenHandle penOld = dc.SelectPen(penText);
            dc.MoveTo(rcTab.right, rcTab.top);
            dc.LineTo(rcTab.right, rcTab.bottom);
            dc.LineTo(rcTab.left, rcTab.bottom);
            dc.SelectPen(penHilight);
            dc.LineTo(rcTab.left, rcTab.top - 1);
            dc.SelectPen(penOld);
        }
        else
        {
            WTL::CPenHandle penOld = dc.SelectPen(penHilight);
            dc.MoveTo(rcTab.left, rcTab.bottom - 1);
            dc.LineTo(rcTab.left, rcTab.top);
            dc.LineTo(rcTab.right, rcTab.top);
            dc.SelectPen(penText);
            dc.LineTo(rcTab.right, rcTab.bottom);
            dc.SelectPen(penOld);
        }
    }

    void DrawItem_TabInactive(DWORD dwStyle, LPNMCTCCUSTOMDRAW lpNMCustomDraw, RECT& rcTab)
    {
        // Tab is not selected
        bool bHighlighted = (CDIS_MARKED == (lpNMCustomDraw->nmcd.uItemState & CDIS_MARKED));
        int nItem = (int)lpNMCustomDraw->nmcd.dwItemSpec;
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);

        if (bHighlighted)
        {
            if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
            {
                RECT rcHighlight = {rcTab.left + 1, rcTab.top + 3, rcTab.right - 2, rcTab.bottom - 1};

                if (nItem - 1 == m_iCurSel) rcHighlight.left += 1; // Item to the right of the selected tab

                dc.FillSolidRect(&rcHighlight, lpNMCustomDraw->clrHighlight);
            }
            else
            {
                RECT rcHighlight = {rcTab.left + 1, rcTab.top + 2, rcTab.right - 2, rcTab.bottom - 2};

                if (nItem - 1 == m_iCurSel) rcHighlight.left += 1; // Item to the right of the selected tab

                dc.FillSolidRect(&rcHighlight, lpNMCustomDraw->clrHighlight);
            }
        }

        // Draw division line on right, unless we're the item
        // on the left of the selected tab
        if (nItem + 1 == m_iCurSel)
        {
            // Item just left of selected tab
        }
        else
        {
            WTL::CPen pen;
            pen.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnShadow);
            WTL::CPenHandle penOld = dc.SelectPen(pen);

            if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
            {
                // Important!  Be sure and keep within "our" tab area horizontally
                dc.MoveTo(rcTab.right - 1, rcTab.top + 3);
                dc.LineTo(rcTab.right - 1, rcTab.bottom - 1);
            }
            else
            {
                // Important!  Be sure and keep within "our" tab area horizontally
                dc.MoveTo(rcTab.right - 1, rcTab.top + 2);
                dc.LineTo(rcTab.right - 1, rcTab.bottom - 2);
            }

            dc.SelectPen(penOld);
        }
    }

    void DrawItem_ImageAndText(DWORD /*dwStyle*/, LPNMCTCCUSTOMDRAW lpNMCustomDraw, int nIconVerticalCenter, RECT& rcTab, RECT& rcText)
    {
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);
        bool bHighlighted = (CDIS_MARKED == (lpNMCustomDraw->nmcd.uItemState & CDIS_MARKED));
        bool bSelected = (CDIS_SELECTED == (lpNMCustomDraw->nmcd.uItemState & CDIS_SELECTED));
        bool bHot = (CDIS_HOT == (lpNMCustomDraw->nmcd.uItemState & CDIS_HOT));
        int nItem = (int)lpNMCustomDraw->nmcd.dwItemSpec;
        TItem* pItem = this->GetItem(nItem);
        HFONT hOldFont = NULL;

        if (bSelected)
        {
            hOldFont = dc.SelectFont(lpNMCustomDraw->hFontSelected);
        }
        else
        {
            hOldFont = dc.SelectFont(lpNMCustomDraw->hFontInactive);
        }

        COLORREF crPrevious = 0;

        if (bHighlighted)
        {
            crPrevious = dc.SetTextColor(lpNMCustomDraw->clrHighlightText);
        }
        else if (bHot)
        {
            crPrevious = dc.SetTextColor(lpNMCustomDraw->clrHighlightHotTrack);
        }
        else if (bSelected)
        {
            crPrevious = dc.SetTextColor(lpNMCustomDraw->clrTextSelected);
        }
        else
        {
            crPrevious = dc.SetTextColor(lpNMCustomDraw->clrTextInactive);
        }

        //--------------------------------------------
        // This is how CDotNetTabCtrlImpl interprets padding, margin, etc.:
        //
        //  M - Margin
        //  P - Padding
        //  I - Image
        //  Text - Tab Text
        //
        // With image:
        //     __________________________
        //
        //    | M | I | P | Text | P | M |
        //     --------------------------
        //
        // Without image:
        //     ______________________
        //
        //    | M | P | Text | P | M |
        //     ----------------------
        //rcText.left += (bSelected ? m_settings.iSelMargin : m_settings.iMargin);
        rcText.left += m_settings.iMargin;
        rcText.right -= m_settings.iMargin;

        if (pItem->UsingImage() && !m_imageList.IsNull())
        {
            // Draw the image.
            IMAGEINFO ii = {0};
            int nImageIndex = pItem->GetImageIndex();
            m_imageList.GetImageInfo(nImageIndex, &ii);

            if ((ii.rcImage.right - ii.rcImage.left) < (rcTab.right - rcTab.left))
            {
                int nImageHalfHeight = (ii.rcImage.bottom - ii.rcImage.top) / 2;
                m_imageList.Draw(dc, nImageIndex, rcText.left, nIconVerticalCenter - nImageHalfHeight + m_nFontSizeTextTopOffset, ILD_NORMAL);
            }

            // Offset on the right of the image.
            rcText.left += (ii.rcImage.right - ii.rcImage.left);
        }

        if (rcText.left + m_nMinWidthToDisplayText < rcText.right)
        {
            ::InflateRect(&rcText, -m_settings.iPadding, 0);
            _CSTRING_NS::CString sText = pItem->GetText();
            dc.DrawText(sText, sText.GetLength(), &rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_PATH_ELLIPSIS);
        }

        dc.SetTextColor(crPrevious);
        dc.SelectFont(hOldFont);
    }

    void DrawCloseButton(LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);
        WTL::CPen penButtons;
        penButtons.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrTextInactive);
        WTL::CBrush brushArrow;
        brushArrow.CreateSolidBrush(lpNMCustomDraw->clrTextInactive);
        WTL::CPenHandle penOld = dc.SelectPen(penButtons);
        WTL::CBrushHandle brushOld = dc.SelectBrush(brushArrow);
        RECT rcX = m_rcCloseButton;

        if (ectcMouseDownL_CloseButton == (m_dwState & ectcMouseDown))
        {
            if (ectcMouseOver_CloseButton == (m_dwState & ectcMouseOver))
            {
                ::OffsetRect(&rcX, 1, 1);
            }
        }

        const int sp = 4;
        dc.MoveTo(rcX.left + sp + -1, rcX.top + sp);
        dc.LineTo(rcX.right - sp - 1, rcX.bottom - sp);
        dc.MoveTo(rcX.left + sp, rcX.top + sp);
        dc.LineTo(rcX.right - sp, rcX.bottom - sp);
        dc.MoveTo(rcX.left + sp - 1, rcX.bottom - sp - 1);
        dc.LineTo(rcX.right - sp - 1, rcX.top + sp - 1);
        dc.MoveTo(rcX.left + sp, rcX.bottom - sp - 1);
        dc.LineTo(rcX.right - sp, rcX.top + sp - 1);

        if (ectcMouseDownL_CloseButton == (m_dwState & ectcMouseDown))
        {
            if (ectcMouseOver_CloseButton == (m_dwState & ectcMouseOver))
            {
                dc.DrawEdge(&m_rcCloseButton, BDR_SUNKENOUTER, BF_RECT);
            }
        }
        else if (ectcHotTrack_CloseButton == (m_dwState & ectcHotTrack))
        {
            dc.DrawEdge(&m_rcCloseButton, BDR_RAISEDINNER, BF_RECT);
        }

        dc.SelectBrush(brushOld);
        dc.SelectPen(penOld);
    }

    void DrawScrollButtons(LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);
        WTL::CPen penButtons;
        penButtons.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrTextInactive);
        WTL::CBrush brushArrow;
        brushArrow.CreateSolidBrush(lpNMCustomDraw->clrTextInactive);
        WTL::CPenHandle penOld = dc.SelectPen(penButtons);
        WTL::CBrushHandle brushOld = dc.SelectBrush(brushArrow);
        RECT rcArrowRight = m_rcScrollRight;
        RECT rcArrowLeft = m_rcScrollLeft;

        if (ectcMouseDownL_ScrollRight == (m_dwState & ectcMouseDown))
        {
            if (ectcMouseOver_ScrollRight == (m_dwState & ectcMouseOver))
            {
                if (ectcOverflowRight == (m_dwState & ectcOverflowRight))
                {
                    ::OffsetRect(&rcArrowRight, 1, 1);
                }
            }
        }

        if (ectcMouseDownL_ScrollLeft == (m_dwState & ectcMouseDown))
        {
            if (ectcMouseOver_ScrollLeft == (m_dwState & ectcMouseOver))
            {
                if (ectcOverflowLeft == (m_dwState & ectcOverflowLeft))
                {
                    ::OffsetRect(&rcArrowLeft, 1, 1);
                }
            }
        }

        const int spRight = 5;
        const int spLeft = 6;
        POINT ptsArrowRight[] =
        {
            {rcArrowRight.left + spRight, rcArrowRight.top + spRight - 2},
            {rcArrowRight.left + spRight, rcArrowRight.bottom - spRight + 1},
            {rcArrowRight.right - spRight - 1, (rcArrowRight.bottom + m_rcScrollRight.top) / 2},
            {rcArrowRight.left + spRight, rcArrowRight.top + spRight - 2}
        };

        if (ectcOverflowRight != (m_dwState & ectcOverflowRight))
        {
            dc.Polyline(ptsArrowRight, 4);
        }
        else
        {
            dc.Polygon(ptsArrowRight, 4);

            if (ectcMouseDownL_ScrollRight == (m_dwState & ectcMouseDown))
            {
                if (ectcMouseOver_ScrollRight == (m_dwState & ectcMouseOver))
                {
                    dc.DrawEdge(&m_rcScrollRight, BDR_SUNKENOUTER, BF_RECT);
                }
            }
            else if (ectcHotTrack_ScrollRight == (m_dwState & ectcHotTrack))
            {
                dc.DrawEdge(&m_rcScrollRight, BDR_RAISEDINNER, BF_RECT);
            }
        }

        POINT ptsArrowLeft[] =
        {
            {rcArrowLeft.right - spLeft, rcArrowLeft.top + spLeft - 3},
            {rcArrowLeft.right - spLeft, rcArrowLeft.bottom - spLeft + 2},
            {rcArrowLeft.left + spLeft - 1, (rcArrowLeft.bottom + m_rcScrollLeft.top) / 2},
            {rcArrowLeft.right - spLeft, rcArrowLeft.top + spLeft - 3}
        };

        if (ectcOverflowLeft != (m_dwState & ectcOverflowLeft))
        {
            dc.Polyline(ptsArrowLeft, 4);
        }
        else
        {
            dc.Polygon(ptsArrowLeft, 4);

            if (ectcMouseDownL_ScrollLeft == (m_dwState & ectcMouseDown))
            {
                if (ectcMouseOver_ScrollLeft == (m_dwState & ectcMouseOver))
                {
                    dc.DrawEdge(&m_rcScrollLeft, BDR_SUNKENOUTER, BF_RECT);
                }
            }
            else if (ectcHotTrack_ScrollLeft == (m_dwState & ectcHotTrack))
            {
                dc.DrawEdge(&m_rcScrollLeft, BDR_RAISEDINNER, BF_RECT);
            }
        }

        dc.SelectBrush(brushOld);
        dc.SelectPen(penOld);
    }

// Overrides for painting from CCustomTabCtrl
public:

    void InitializeDrawStruct(LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        //DWORD dwStyle = this->GetStyle();
        lpNMCustomDraw->hFontInactive = m_font;
        lpNMCustomDraw->hFontSelected = m_fontSel;
        lpNMCustomDraw->hBrushBackground = m_hbrBackground;
        lpNMCustomDraw->clrTextSelected = ::GetSysColor(COLOR_BTNTEXT);
        lpNMCustomDraw->clrTextInactive = m_clrTextInactiveTab;
        lpNMCustomDraw->clrSelectedTab = m_clrSelectedTab;
        lpNMCustomDraw->clrBtnFace = ::GetSysColor(COLOR_BTNFACE);
        lpNMCustomDraw->clrBtnShadow = ::GetSysColor(COLOR_BTNSHADOW);
        lpNMCustomDraw->clrBtnHighlight = ::GetSysColor(COLOR_BTNHIGHLIGHT);
        lpNMCustomDraw->clrBtnText = ::GetSysColor(COLOR_BTNTEXT);
        lpNMCustomDraw->clrHighlight = ::GetSysColor(COLOR_HIGHLIGHT);
#if WINVER >= 0x0500
        lpNMCustomDraw->clrHighlightHotTrack = ::GetSysColor(COLOR_HOTLIGHT);
#else
        lpNMCustomDraw->clrHighlightHotTrack = ::GetSysColor(COLOR_HIGHLIGHT);
#endif
        lpNMCustomDraw->clrHighlightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
    }

    void DoPrePaint(RECT rcClient, LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        T* pT = static_cast<T*>(this);
        pT->DrawBackground(rcClient, lpNMCustomDraw);
    }

    void DoItemPaint(LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        T* pT = static_cast<T*>(this);
        bool bSelected = (CDIS_SELECTED == (lpNMCustomDraw->nmcd.uItemState & CDIS_SELECTED));
        // NOTE: lpNMCustomDraw->nmcd.rc is in logical coordinates
        RECT& rcItem = lpNMCustomDraw->nmcd.rc;
        DWORD dwStyle = pT->GetStyle();
        RECT rcTab = rcItem;
        RECT rcText = rcItem;
        int nIconVerticalCenter = 0;
        pT->DrawItem_InitBounds(dwStyle, rcItem, rcTab, rcText, nIconVerticalCenter);

        if (bSelected)
        {
            pT->DrawItem_TabSelected(dwStyle, lpNMCustomDraw, rcTab);
        }
        else
        {
            pT->DrawItem_TabInactive(dwStyle, lpNMCustomDraw, rcTab);
        }

        pT->DrawItem_ImageAndText(dwStyle, lpNMCustomDraw, nIconVerticalCenter, rcTab, rcText);
    }

    void DoPostPaint(RECT /*rcClient*/, LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        T* pT = static_cast<T*>(this);
        DWORD dwStyle = this->GetStyle();

        if (0 == (dwStyle & (CTCS_CLOSEBUTTON | CTCS_SCROLL)))
        {
            return;
        }

        // Close Button
        if (CTCS_CLOSEBUTTON == (dwStyle & CTCS_CLOSEBUTTON))
        {
            if ((m_iCurSel >= 0) && ((size_t)m_iCurSel < m_Items.GetCount()))
            {
                TItem* pItem = m_Items[m_iCurSel];
                ATLASSERT(pItem != NULL);

                if ((pItem != NULL) && pItem->CanClose())
                {
                    pT->DrawCloseButton(lpNMCustomDraw);
                }
            }
        }

        // Scroll Buttons
        if (CTCS_SCROLL == (dwStyle & CTCS_SCROLL))
        {
            pT->DrawScrollButtons(lpNMCustomDraw);
        }
    }

// Overrides from CCustomTabCtrl
public:

    void CalcSize_NonClient(LPRECT prcTabItemArea)
    {
        // account for "non-client" areas
        // TODO: For the short term, we will use this
        //  for the non-client areas on the left and right.
        //  The drawing code for the tabs already accounts
        //  for the "non-client" areas on the top and bottom, and
        //  would need to be updated if we account for it here.
        //  Tab item rect methods also would need to be
        //  updated to account for the non-client areas
        //  on top and bottom (and effected drawing code
        //  would need to be updated).
        DWORD dwStyle = this->GetStyle();

        if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
        {
            // TODO: Update to actually specify the
            //  non-client areas, and adjust all of the
            //  effected drawing code, as well as
            //  tab item rect related things
            //prcTabItemArea->top += 3;
        }
        else
        {
            prcTabItemArea->left += 2;
            prcTabItemArea->right -= 2;
            // TODO: Update to actually specify the top and bottom
            //  non-client areas, and adjust all of the
            //  effected drawing code, as well as
            //  tab item rect related things
            //prcTabItemArea->top += 1;
            //// We would have bottom as 3, but we want the
            //// selected tab to actually paint over highlight part
            //prcTabItemArea->bottom -= 2;
        }
    }

    void CalcSize_CloseButton(LPRECT prcTabItemArea)
    {
        //int nButtonSizeX = ::GetSystemMetrics(SM_CXSMSIZE);
        //int nButtonSizeY = ::GetSystemMetrics(SM_CYSMSIZE);
        // NOTE: After several tests, VS.Net does NOT depend on
        //  any system metric for the button size, so neither will we.
        int nButtonSizeX = 15;
        int nButtonSizeY = 15;

        if ((prcTabItemArea->right - prcTabItemArea->left) < nButtonSizeX)
        {
            ::SetRectEmpty(&m_rcCloseButton);
            return;
        }

        m_rcCloseButton = *prcTabItemArea;
        DWORD dwStyle = this->GetStyle();

        if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
        {
            m_rcCloseButton.top += 3;
            m_rcCloseButton.right -= 3;
        }
        else
        {
            m_rcCloseButton.top += 1;
            m_rcCloseButton.bottom -= 2;
            m_rcCloseButton.right -= 2;
        }

        m_rcCloseButton.top = (m_rcCloseButton.bottom + m_rcCloseButton.top - nButtonSizeY) / 2;
        m_rcCloseButton.bottom = m_rcCloseButton.top + nButtonSizeY;
        m_rcCloseButton.left = m_rcCloseButton.right - (nButtonSizeX);

        if (m_tooltip.IsWindow())
        {
            m_tooltip.SetToolRect(m_hWnd, (UINT)ectcToolTip_Close, &m_rcCloseButton);
        }

        // Adjust the tab area
        prcTabItemArea->right = m_rcCloseButton.left;
    }

    void CalcSize_ScrollButtons(LPRECT prcTabItemArea)
    {
        //int nButtonSizeX = ::GetSystemMetrics(SM_CXSMSIZE);
        //int nButtonSizeY = ::GetSystemMetrics(SM_CYSMSIZE);
        // NOTE: After several tests, VS.Net does NOT depend on
        //  any system metric for the button size, so neither will we.
        int nButtonSizeX = 15;
        int nButtonSizeY = 15;

        if ((prcTabItemArea->right - prcTabItemArea->left) < nButtonSizeX)
        {
            ::SetRectEmpty(&m_rcScrollRight);
            ::SetRectEmpty(&m_rcScrollLeft);
            return;
        }

        RECT rcScroll = *prcTabItemArea;
        DWORD dwStyle = this->GetStyle();

        if (CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
        {
            rcScroll.top += 3;

            if (0 == (dwStyle & CTCS_CLOSEBUTTON))
            {
                rcScroll.right -= 3;
            }
        }
        else
        {
            rcScroll.top += 1;
            rcScroll.bottom -= 2;

            if (0 == (dwStyle & CTCS_CLOSEBUTTON))
            {
                rcScroll.right -= 2;
            }
        }

        rcScroll.top = (rcScroll.bottom + rcScroll.top - nButtonSizeY) / 2;
        rcScroll.bottom = rcScroll.top + nButtonSizeY;
        m_rcScrollRight = rcScroll;
        m_rcScrollLeft = rcScroll;
        m_rcScrollRight.left = m_rcScrollRight.right - nButtonSizeX;
        m_rcScrollLeft.right = m_rcScrollRight.left;
        m_rcScrollLeft.left = m_rcScrollLeft.right - nButtonSizeX;

        if (m_tooltip.IsWindow())
        {
            m_tooltip.SetToolRect(m_hWnd, (UINT)ectcToolTip_ScrollRight, &m_rcScrollRight);
            m_tooltip.SetToolRect(m_hWnd, (UINT)ectcToolTip_ScrollLeft, &m_rcScrollLeft);
        }

        // Adjust the tab area
        prcTabItemArea->right = m_rcScrollLeft.left;
    }


    void UpdateLayout_Default(RECT rcTabItemArea)
    {
        long nMinInactiveWidth = 0x7FFFFFFF;
        long nMaxInactiveWidth = 0;
        //DWORD dwStyle = this->GetStyle();
        WTL::CClientDC dc(m_hWnd);
        //HFONT hOldFont = dc.SelectFont(lpNMCustomDraw->hFontInactive);
        HFONT hOldFont = dc.SelectFont(m_font);
        LONG nTabAreaWidth = (rcTabItemArea.right - rcTabItemArea.left);
        RECT rcItem = rcTabItemArea;
        // rcItem.top and rcItem.bottom aren't really going to change
        // Recalculate tab positions and widths
        // See DrawItem_ImageAndText for a discussion of how CDotNetTabCtrlImpl
        //  interprets margin, padding, etc.
        size_t nCount = m_Items.GetCount();
        int xpos = m_settings.iIndent;
        HFONT hRestoreNormalFont = NULL;

        for (size_t i = 0; i < nCount; ++i)
        {
            bool bSelected = ((int)i == m_iCurSel);

            if (bSelected)
            {
                //hRestoreNormalFont = dc.SelectFont(lpNMCustomDraw->hFontSelected);
                hRestoreNormalFont = dc.SelectFont(m_fontSel);
            }

            TItem* pItem = m_Items[i];
            ATLASSERT(pItem != NULL);
            rcItem.left = rcItem.right = xpos;
            //rcItem.right += ((bSelected ? m_settings.iSelMargin : m_settings.iMargin));
            rcItem.right += m_settings.iMargin;

            if (pItem->UsingImage() && !m_imageList.IsNull())
            {
                IMAGEINFO ii = {0};
                int nImageIndex = pItem->GetImageIndex();
                m_imageList.GetImageInfo(nImageIndex, &ii);
                rcItem.right += (ii.rcImage.right - ii.rcImage.left);
            }

            if (pItem->UsingText())
            {
                RECT rcText = {0};
                _CSTRING_NS::CString sText = pItem->GetText();
                dc.DrawText(sText, sText.GetLength(), &rcText, DT_SINGLELINE | DT_CALCRECT);
                rcItem.right += (rcText.right - rcText.left) + (m_settings.iPadding * 2);
            }

            rcItem.right += m_settings.iMargin;
            pItem->SetRect(rcItem);
            xpos += (rcItem.right - rcItem.left);

            if (hRestoreNormalFont != NULL)
            {
                dc.SelectFont(hRestoreNormalFont);
                hRestoreNormalFont = NULL;
            }

            if (!bSelected)
            {
                if ((rcItem.right - rcItem.left) < nMinInactiveWidth)
                {
                    nMinInactiveWidth = (rcItem.right - rcItem.left);
                }

                if ((rcItem.right - rcItem.left) > nMaxInactiveWidth)
                {
                    nMaxInactiveWidth = (rcItem.right - rcItem.left);
                }
            }
        }

        xpos += m_settings.iIndent;

        if (xpos > nTabAreaWidth && nCount > 0 && m_iCurSel >= 0)
        {
            // Our desired widths are more than the width of the client area.
            // We need to have some or all of the tabs give up some real estate
            // We'll try to let the selected tab have its fully desired width.
            // If it can't, we'll make all the tabs the same width.
            RECT rcSelected = m_Items[m_iCurSel]->GetRect();
            LONG nSelectedWidth = (rcSelected.right - rcSelected.left);
            long cxClientInactiveTabs = nTabAreaWidth - (m_settings.iIndent * 2) - nSelectedWidth;
            long cxDesiredInactiveTabs = xpos - (m_settings.iIndent * 2) - nSelectedWidth;
            double nRatioWithSelectionFullSize = 0.0;

            if (cxDesiredInactiveTabs != 0)
            {
                nRatioWithSelectionFullSize = (double)(cxClientInactiveTabs) / (double)(cxDesiredInactiveTabs);
            }

            long nInactiveSameSizeWidth = (m_nMinWidthToDisplayText + (m_settings.iMargin * 2) + (m_settings.iPadding));

            if (cxClientInactiveTabs > (nInactiveSameSizeWidth * (long)(nCount - 1)))
            {
                //  There should be enough room to display the entire contents of
                //  the selected tab plus something for the inactive tabs
                bool bMakeInactiveSameSize = ((nMinInactiveWidth * nRatioWithSelectionFullSize) < nInactiveSameSizeWidth);
                xpos = m_settings.iIndent;

                for (size_t i = 0; i < nCount; ++i)
                {
                    TItem* pItem = m_Items[i];
                    ATLASSERT(pItem != NULL);
                    RECT rcItemDesired = pItem->GetRect();
                    rcItem.left = rcItem.right = xpos;

                    if ((int)i == m_iCurSel)
                    {
                        rcItem.right += (rcItemDesired.right - rcItemDesired.left);
                    }
                    else
                    {
                        if (bMakeInactiveSameSize && (nCount != 1))
                        {
                            rcItem.right += (long)((cxClientInactiveTabs / (nCount - 1)) + 0.5);
                        }
                        else
                        {
                            rcItem.right += (long)(((rcItemDesired.right - rcItemDesired.left) * nRatioWithSelectionFullSize) + 0.5);
                        }
                    }

                    pItem->SetRect(rcItem);
                    xpos += (rcItem.right - rcItem.left);
                }
            }
            else
            {
                // We're down pretty small, so just make all the tabs the same width
                int cxItem = (nTabAreaWidth - (m_settings.iIndent * 2)) / (int)nCount;
                xpos = m_settings.iIndent;

                for (size_t i = 0; i < nCount; ++i)
                {
                    rcItem.left = rcItem.right = xpos;
                    rcItem.right += cxItem;
                    m_Items[i]->SetRect(rcItem);
                    xpos += (rcItem.right - rcItem.left);
                }
            }
        }

        dc.SelectFont(hOldFont);
    }

    void UpdateLayout_ScrollToFit(RECT rcTabItemArea)
    {
        //DWORD dwStyle = this->GetStyle();
        // When we scroll to fit, we ignore what's passed in for the
        // tab item area rect, and use the client rect instead
        RECT rcClient;
        this->GetClientRect(&rcClient);
        WTL::CClientDC dc(m_hWnd);
        //HFONT hOldFont = dc.SelectFont(lpNMCustomDraw->hFontInactive);
        HFONT hOldFont = dc.SelectFont(m_font);
        RECT rcItem = rcClient;
        // rcItem.top and rcItem.bottom aren't really going to change
        // Recalculate tab positions and widths
        // See DrawItem_ImageAndText for a discussion of how CDotNetTabCtrlImpl
        //  interprets margin, padding, etc.
        size_t nCount = m_Items.GetCount();
        int xpos = m_settings.iIndent;
        HFONT hRestoreNormalFont = NULL;

        for (size_t i = 0; i < nCount; ++i)
        {
            bool bSelected = ((int)i == m_iCurSel);

            if (bSelected)
            {
                //hRestoreNormalFont = dc.SelectFont(lpNMCustomDraw->hFontSelected);
                hRestoreNormalFont = dc.SelectFont(m_fontSel);
            }

            TItem* pItem = m_Items[i];
            ATLASSERT(pItem != NULL);
            rcItem.left = rcItem.right = xpos;
            //rcItem.right += ((bSelected ? m_settings.iSelMargin : m_settings.iMargin));
            rcItem.right += m_settings.iMargin;

            if (pItem->UsingImage() && !m_imageList.IsNull())
            {
                IMAGEINFO ii = {0};
                int nImageIndex = pItem->GetImageIndex();
                m_imageList.GetImageInfo(nImageIndex, &ii);
                rcItem.right += (ii.rcImage.right - ii.rcImage.left);
            }

            if (pItem->UsingText())
            {
                RECT rcText = {0};
                _CSTRING_NS::CString sText = pItem->GetText();
                dc.DrawText(sText, sText.GetLength(), &rcText, DT_SINGLELINE | DT_CALCRECT);
                rcItem.right += (rcText.right - rcText.left) + (m_settings.iPadding * 2);
            }

            rcItem.right += m_settings.iMargin;
            pItem->SetRect(rcItem);
            xpos += (rcItem.right - rcItem.left);

            if (hRestoreNormalFont != NULL)
            {
                dc.SelectFont(hRestoreNormalFont);
                hRestoreNormalFont = NULL;
            }
        }

        xpos += m_settings.iIndent;

        // If we've been scrolled to the left, and resize so
        // there's more client area to the right, adjust the
        // scroll offset accordingly.
        if ((xpos + m_iScrollOffset) < rcTabItemArea.right)
        {
            m_iScrollOffset = (rcTabItemArea.right - xpos);
        }

        dc.SelectFont(hOldFont);
    }

};

template <class TItem = CCustomTabItem>
class CDotNetTabCtrl :
    public CDotNetTabCtrlImpl<CDotNetTabCtrl<TItem>, TItem>
{
protected:
    typedef CDotNetTabCtrl thisClass;
    typedef CDotNetTabCtrlImpl<CDotNetTabCtrl<TItem>, TItem> baseClass;

// Constructors:
public:
    CDotNetTabCtrl()
    {
    }

public:

    DECLARE_WND_CLASS_EX(_T("WTL_DotNetTabCtrl"), CS_DBLCLKS, COLOR_WINDOW)

    //We have nothing special to add.
    //BEGIN_MSG_MAP(thisClass)
    //    CHAIN_MSG_MAP(baseClass)
    //END_MSG_MAP()
};

template<typename T, typename TItem = CCustomTabItem, class TBase = ATL::CWindow, class TWinTraits = CCustomTabCtrlWinTraits>
class CDotNetButtonTabCtrlImpl :
    public CDotNetTabCtrlImpl<T, TItem, TBase, TWinTraits>
{
protected:
    typedef CDotNetButtonTabCtrlImpl<T, TItem, TBase, TWinTraits> thisClass;
    typedef CDotNetTabCtrlImpl<T, TItem, TBase, TWinTraits> baseClass;

// Constructor
public:
    CDotNetButtonTabCtrlImpl()
    {
        // We can't use a member initialization list to initialize
        // members of our base class, so do it explictly by assignment here.
        m_clrTextInactiveTab = ::GetSysColor(COLOR_BTNTEXT);
        m_clrSelectedTab = ::GetSysColor(COLOR_WINDOW);
    }

// Message Handling
public:
    DECLARE_WND_CLASS_EX(_T("WTL_DotNetButtonTabCtrl"), CS_DBLCLKS, COLOR_WINDOW)

    BEGIN_MSG_MAP(thisClass)
    MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
    MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSettingChange)
    CHAIN_MSG_MAP(baseClass)
    END_MSG_MAP()

    LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        DWORD dwStyle = this->GetStyle();
        // Initialize/Reinitialize font
        // Visual Studio.Net seems to use the "icon" font for the tabs
        LOGFONT lfIcon = { 0 };
        ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIcon), &lfIcon, 0);
        bool bResetFont = true;

        if (!m_font.IsNull())
        {
            LOGFONT lf = {0};

            if (m_font.GetLogFont(&lf))
            {
                if (lstrcmpi(lf.lfFaceName, lfIcon.lfFaceName) == 0 &&
                        lf.lfHeight == lfIcon.lfHeight)
                {
                    bResetFont = false;
                }
            }
        }

        if (bResetFont)
        {
            if (!m_font.IsNull()) m_font.DeleteObject();

            if (!m_fontSel.IsNull()) m_fontSel.DeleteObject();

            HFONT font = m_font.CreateFontIndirect(&lfIcon);

            if (font == NULL)
            {
                m_font.Attach(AtlGetDefaultGuiFont());
            }

            if (CTCS_BOLDSELECTEDTAB == (dwStyle & CTCS_BOLDSELECTEDTAB))
            {
                lfIcon.lfWeight = FW_BOLD;
            }

            font = m_fontSel.CreateFontIndirect(&lfIcon);

            if (font == NULL)
            {
                m_fontSel.Attach(AtlGetDefaultGuiFont());
            }
        }

        // Background brush
        if (!m_hbrBackground.IsNull()) m_hbrBackground.DeleteObject();

        m_hbrBackground.CreateSysColorBrush(COLOR_BTNFACE);
        m_settings.iIndent = 5;
        m_settings.iPadding = 4;
        m_settings.iMargin = 3;
        m_settings.iSelMargin = 3;
        T* pT = static_cast<T*>(this);
        pT->UpdateLayout();
        pT->Invalidate();
        return 0;
    }

// Overrides for painting from CDotNetTabCtrlImpl
public:

    void DrawBackground(RECT /*rcClient*/, LPNMCTCCUSTOMDRAW lpNMCustomDraw)
    {
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);
        // Set up the text color and background mode
        dc.SetTextColor(lpNMCustomDraw->clrBtnText);
        dc.SetBkMode(TRANSPARENT);
        // Erase Background
        //  (do it here instead of a handler for WM_ERASEBKGND
        //   so that we can do flicker-free drawing with the help
        //   of COffscreenDrawRect that's in the base class)
        // Note: Because the "erase" part is very simple, and only coloring
        //  it with the background color, we can do a smarter erase.
        //  Instead of erasing the whole client area (which might be clipped),
        //  We'll just ask the HDC for the clip box.
        RECT rc = {0};
        //GetClientRect(&rc);
        dc.GetClipBox(&rc);
        HBRUSH hOldBrush = dc.SelectBrush(lpNMCustomDraw->hBrushBackground);
        dc.PatBlt(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
        dc.SelectBrush(hOldBrush);
    }

    void DrawItem_InitBounds(DWORD /*dwStyle*/, RECT /*rcItem*/, RECT& rcTab, RECT& /*rcText*/, int& nIconVerticalCenter)
    {
        rcTab.top += 3;
        rcTab.bottom -= 3;
        nIconVerticalCenter = (rcTab.bottom + rcTab.top) / 2;
    }

    void DrawItem_TabSelected(DWORD /*dwStyle*/, LPNMCTCCUSTOMDRAW lpNMCustomDraw, RECT& rcTab)
    {
        // Tab is selected, so paint as select
        bool bHighlighted = (CDIS_MARKED == (lpNMCustomDraw->nmcd.uItemState & CDIS_MARKED));
        WTL::CDCHandle dc(lpNMCustomDraw->nmcd.hdc);
        WTL::CPen penOutline;
        WTL::CBrush brushSelected;

        if (bHighlighted)
        {
            penOutline.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrBtnHighlight);
            brushSelected.CreateSolidBrush(lpNMCustomDraw->clrHighlight);
        }
        else
        {
            penOutline.CreatePen(PS_SOLID, 1, lpNMCustomDraw->clrHighlight);
            brushSelected.CreateSolidBrush(lpNMCustomDraw->clrSelectedTab);
        }

        HPEN hOldPen = dc.SelectPen(penOutline);
        HBRUSH hOldBrush = dc.SelectBrush(brushSelected);
        dc.Rectangle(&rcTab);
        dc.SelectPen(hOldPen);
        dc.SelectBrush(hOldBrush);
    }

};

template <class TItem = CCustomTabItem>
class CDotNetButtonTabCtrl :
    public CDotNetButtonTabCtrlImpl<CDotNetButtonTabCtrl<TItem>, TItem>
{
protected:
    typedef CDotNetButtonTabCtrl<TItem> thisClass;
    typedef CDotNetButtonTabCtrlImpl<CDotNetButtonTabCtrl<TItem>, TItem> baseClass;

// Constructors:
public:
    CDotNetButtonTabCtrl()
    {
    }

public:

    DECLARE_WND_CLASS_EX(_T("WTL_DotNetButtonTabCtrl"), CS_DBLCLKS, COLOR_WINDOW)

    //We have nothing special to add.
    //BEGIN_MSG_MAP(thisClass)
    //    CHAIN_MSG_MAP(baseClass)
    //END_MSG_MAP()
};

#endif // __DOTNET_TABCTRL_H__

