#include "stdafx.h"
#include "TabCtrlEx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CTabCtrlEx::CTabCtrlEx()
{
m_CrossDrawer.Init();
_Init();
}
CTabCtrlEx::~CTabCtrlEx()
{
}
BEGIN_MESSAGE_MAP(CTabCtrlEx, CTabCtrl)
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
END_MESSAGE_MAP()
void CTabCtrlEx::OnPaint()
{
Default();
CClientDC dc(this);
DrawDefaultTabItem(&dc);
}
void CTabCtrlEx::OnMouseMove(UINT nFlags, CPoint point)
{
if( !m_bTracking )
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = GetSafeHwnd();
tme.dwFlags = TME_LEAVE;
tme.dwHoverTime = 0;
m_bTracking = _TrackMouseEvent(&tme);
}
_DoOnMouseMove(nFlags, point);
CTabCtrl::OnMouseMove(nFlags, point);
}
void CTabCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
if( 1 )
{
TCHITTESTINFO hti; hti.flags = 0;
hti.pt = point;
int ixHot = HitTest(&hti);
if( ixHot >= 0 )
{
CRect rcCross;
CSize sizeBitmap;
m_CrossDrawer.GetBitmapSize(sizeBitmap);
rcCross = _GetCrossRectInItem(ixHot, sizeBitmap);
if( rcCross.PtInRect(point) )
{
SetCapture();
m_bMousePressed = TRUE;
m_iSelTabIndex = m_iCurTabIndex = ixHot;
m_rcCurCrossArea = m_rcSelCrossArea = rcCross;
InvalidateRect(&m_rcCurCrossArea);
}
}
}
CTabCtrl::OnLButtonDown(nFlags, point);
}
LRESULT CTabCtrlEx::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
m_bHover = FALSE;
m_bTracking = FALSE;
TRACE("OnMouseLeave\n");
CPoint point;
if( GetCursorPos(&point) )
{
ScreenToClient(&point);
_DoOnMouseMove(0, point);
InvalidateRect(&m_rcCurCrossArea);
}
return 0;
}
LRESULT CTabCtrlEx::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
#ifndef GET_X_LPARAM
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#endif
#ifndef GET_Y_LPARAM
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
#endif
return 0;
}
void CTabCtrlEx::OnLButtonUp(UINT nFlags, CPoint point)
{
CTabCtrl::OnLButtonUp(nFlags, point);
if( m_bMousePressed )
{
TRACE("CTabCtrlEx::OnLButtonUp() \n");
m_bMousePressed = FALSE;
ReleaseCapture();
int iSelTabIndex = m_iSelTabIndex;
m_rcSelCrossArea.SetRectEmpty();
m_iSelTabIndex = -1;
InvalidateRect(&m_rcSelCrossArea);
if( iSelTabIndex == _ValidTabItem(point) )
{
FireDeleteTabItemNotify(iSelTabIndex);
}
}
m_bMousePressed = FALSE;
}
BOOL CTabCtrlEx::_Init()
{
m_iExpandWith = 0;
m_iExpandTailSpace = 0;
m_iCurTabIndex = -1;
m_rcCurCrossArea.SetRectEmpty();
m_bHover = FALSE;
m_bTracking = FALSE;
m_bMousePressed = FALSE;
m_iSelTabIndex = -1;
m_rcSelCrossArea.SetRectEmpty();
return TRUE;
}
CString CTabCtrlEx::_RepeatChar(CString str, int count)
{
CString strR;
for( int i = 0; i < count; i++ )
strR += str;
return strR;
}
BOOL CTabCtrlEx::_CalcExpand(CDC* pDC)
{
CSize s(0, 50);
int iExpand = 0;
CSize sizeExtent;
CString strTestText = _T(" ");
sizeExtent = pDC->GetOutputTextExtent(strTestText);
CSize size1 = pDC->GetTextExtent(strTestText);
CSize sizeBitmap;
m_CrossDrawer.GetBitmapSize(sizeBitmap);
iExpand = sizeBitmap.cx / sizeExtent.cx + 1;
if( iExpand == m_iExpandTailSpace ) return FALSE;
m_iExpandWith = iExpand * sizeExtent.cx;
m_iExpandTailSpace = iExpand;
return TRUE;
}
void CTabCtrlEx::_RefromatTabTitles()
{
CString strSpace = _RepeatChar(_T(" "), m_iExpandTailSpace);
CString strText;
TCITEM tci;
int iCount = GetItemCount();
for( int i = 0; i < iCount; i++ )
{
strText = m_astrOriginalTabTitles[i] + strSpace;
tci.mask = TCIF_TEXT;
tci.pszText = (LPTSTR)(LPCTSTR)strText;
CTabCtrl::SetItem(i, &tci);
}
}
BOOL CTabCtrlEx::InsertItem(int nItem, LPCTSTR lpszItem)
{
return CTabCtrlEx::InsertItem(nItem, lpszItem, -1);
}
BOOL CTabCtrlEx::InsertItem(int nItem, LPCTSTR lpszItem, int nImage)
{
int iIndex;
CString strText = lpszItem;
strText += _RepeatChar(_T(" "), m_iExpandTailSpace);
iIndex = CTabCtrl::InsertItem(nItem, (LPCTSTR)strText, nImage);
if( iIndex >= 0 )
{
CString strItem = lpszItem;
m_astrOriginalTabTitles.InsertAt(iIndex, lpszItem);
}
return iIndex;
}
HWND CTabCtrlEx::_GetAdjustCtrlHwnd()
{
HWND hWnd = NULL;
hWnd = ::GetWindow(GetSafeHwnd(), GW_CHILD);
while( hWnd )
{
if( 1 == ::GetDlgCtrlID(hWnd) )
break;
::GetWindow(hWnd, GW_HWNDNEXT);
}
return hWnd;
}
void CTabCtrlEx::DrawDefaultTabItem(CDC* pDC)
{
if( _CalcExpand(pDC) )
_RefromatTabTitles();
CRect rcAdjust;
HWND hAdjustWnd = _GetAdjustCtrlHwnd();
if( hAdjustWnd )
{
::GetWindowRect(hAdjustWnd, &rcAdjust);
ScreenToClient(&rcAdjust);
}
CRect rcTabCtrl, rcItem, rcCross;
CSize sizeBitmap;
int nTab = GetItemCount();
if( !nTab ) return;
m_CrossDrawer.GetBitmapSize(sizeBitmap);
GetClientRect(&rcTabCtrl);
int iStatus;
CPoint ptCrossCenter;
TCHITTESTINFO hti; hti.flags = 0;
for( int ixTab = 0; ixTab < nTab; ixTab++ )
{
if( !GetItemRect(ixTab, &rcItem) )
continue;
if( rcItem.right < rcTabCtrl.left || (hAdjustWnd && ::IsWindowVisible(hAdjustWnd) && (rcItem.right >= rcAdjust.left)) )
continue;
iStatus = _GetCrossButtonStatus(ixTab);
_DrawCrossButton(pDC, rcItem, (ixTab == GetCurSel()), iStatus);
}
}
void CTabCtrlEx::_DrawCrossButton(CDC* pDC, int iItem)
{
CRect rcItem, rcCross;
CSize sizeBitmap;
int iStatus;
if( iItem >= 0 )
{
GetItemRect(iItem, &rcItem);
iStatus = _GetCrossButtonStatus(iItem);
_DrawCrossButton(pDC, rcItem, (iItem == GetCurSel()), iStatus);
}
}
void CTabCtrlEx::_DrawCrossButton(CDC* pDC, const CRect& rcItem, BOOL bCur, int iCrossStatus)
{
CRect rcCross;
CSize sizeBitmap;
m_CrossDrawer.GetBitmapSize(iCrossStatus, sizeBitmap);
rcCross = _GetCrossRectInItem(rcItem, sizeBitmap, bCur);
m_CrossDrawer.Draw(pDC, rcCross, iCrossStatus);
}
inline int CTabCtrlEx::_GetCrossButtonStatus(int iItem)
{
if( m_bMousePressed )
{
if( iItem == m_iSelTabIndex )
return CCrossDrawer::ePressed;
}
else
{
if( iItem == m_iCurTabIndex )
return CCrossDrawer::eHot;
}
return CCrossDrawer::eNormal;
}
CRect CTabCtrlEx::_GetCrossRectInItem(int iIndex, const CSize& sizeBitmap)
{
CRect rcItem, rcCross;
rcCross.SetRectEmpty();
if( !GetItemRect(iIndex, &rcItem) )
return rcCross;
return _GetCrossRectInItem(rcItem, sizeBitmap, (iIndex == GetCurSel()));
}
CRect CTabCtrlEx::_GetCrossRectInItem(const CRect& rcItem, const CSize& sizeBitmap, BOOL bCur)
{
CRect rcCross;
CSize sizeCross;
rcCross.SetRectEmpty();
sizeCross.cx = __min((rcItem.Height() - 2), sizeBitmap.cx);
sizeCross.cy = __min((rcItem.Height() - 2), sizeBitmap.cy);
rcCross.top = rcItem.top + (rcItem.Height() + 2 - sizeCross.cy) / 2;
rcCross.right = rcItem.right - 4;
rcCross.bottom = rcCross.top + sizeCross.cy;
rcCross.left = rcCross.right - sizeCross.cx;
if( bCur )
rcCross.OffsetRect(1, -1);
return rcCross;
}
int CTabCtrlEx::_ValidTabItem(CPoint& point)
{
TCHITTESTINFO hti; hti.flags = 0;
hti.pt = point;
int ixHot = HitTest(&hti);
if( ixHot >= 0 )
{
CRect rcCross;
CSize sizeBitmap;
m_CrossDrawer.GetBitmapSize(sizeBitmap);
rcCross = _GetCrossRectInItem(ixHot, sizeBitmap);
if( rcCross.PtInRect(point) )
return ixHot;
}
return -1;
}
LRESULT CTabCtrlEx::_DoOnMouseMove(UINT nFlags, CPoint point)
{
TRACE("_DoOnMouseMove()\n");
if( !m_rcCurCrossArea.IsRectEmpty() )
{
if( !m_rcCurCrossArea.PtInRect(point) )
{
m_iCurTabIndex = -1;
if( !m_bMousePressed )
InvalidateRect(&m_rcCurCrossArea);
m_rcCurCrossArea.SetRectEmpty();
}
}
else
{
TCHITTESTINFO hti; hti.flags = 0;
hti.pt = point;
int ixHot = HitTest(&hti);
if( ixHot >= 0 )
{
CRect rcCross;
CSize sizeBitmap;
m_CrossDrawer.GetBitmapSize(sizeBitmap);
rcCross = _GetCrossRectInItem(ixHot, sizeBitmap);
if( rcCross.PtInRect(point) )
{
m_rcCurCrossArea = rcCross;
m_iCurTabIndex = ixHot;
if( !m_bMousePressed )
InvalidateRect(&m_rcCurCrossArea);
}
}
}
return 0;
}
void CTabCtrlEx::FireDeleteTabItemNotify(int iItem)
{
UINT uId = GetWindowLong(GetSafeHwnd(), GWL_ID);
TCNMDELETEITEM nmdr;
memset(&nmdr, 0, sizeof(nmdr));
nmdr.hdr.code = TCN_DELETEITEM;
nmdr.hdr.hwndFrom = GetSafeHwnd();
nmdr.hdr.idFrom = uId;
nmdr.iDeleteItem = iItem;
CWnd *pParentWnd = GetParent();
if( pParentWnd )
{
if( pParentWnd->SendMessage(WM_NOTIFY, (WPARAM)uId, (LPARAM)&nmdr) )
{
DeleteItem(iItem);
if( iItem >= GetItemCount() )
iItem = GetItemCount() - 1;
if( iItem >= 0 )
{
SetCurSel(iItem);
NMHDR hdr;
hdr.hwndFrom = GetSafeHwnd();
hdr.code = TCN_SELCHANGE;
hdr.idFrom = uId;
pParentWnd->SendMessage(WM_NOTIFY, (WPARAM)uId, (LPARAM)&hdr);
}
}
}
}
BOOL CTabCtrlEx::DeleteItem(int iItem)
{
BOOL bRet = CTabCtrl::DeleteItem(iItem);
if( bRet )
{
m_astrOriginalTabTitles.RemoveAt(iItem);
HWND hWnd = _GetAdjustCtrlHwnd();
if( hWnd && ::IsWindowVisible(hWnd) )
{
UINT uID = ::GetWindowLong(hWnd, GWL_ID);
int iMax = ::SendMessage(hWnd, UDM_GETRANGE, (WPARAM)0, (LPARAM)0);
int iPos = ::SendMessage(hWnd, UDM_GETPOS, (WPARAM)0, (LPARAM)0);
NMUPDOWN hdr;
hdr.hdr.code = UDN_DELTAPOS;
hdr.hdr.hwndFrom = hWnd;
hdr.hdr.idFrom =
hdr.iPos = iPos;
hdr.iDelta = 1;
SendMessage(WM_NOTIFY, (WPARAM)uID, (LPARAM)&hdr);
SendMessage(WM_HSCROLL, (WPARAM)MAKEWPARAM(SB_THUMBPOSITION, iPos), (LPARAM)hWnd);
SendMessage(WM_HSCROLL, (WPARAM)MAKEWPARAM(SB_ENDSCROLL, iPos), (LPARAM)hWnd);
}
}
return bRet;
}
BOOL CTabCtrlEx::DeleteAllItems()
{
BOOL bRet = CTabCtrl::DeleteAllItems();
if( bRet )
{
m_astrOriginalTabTitles.RemoveAll();
}
return bRet;
}