// XTPTabControl.cpp : implementation file
//
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "Common/XTPToolTipContext.h"
#include "Common/XTPDrawHelpers.h"
#include "Common/XTPImageManager.h"
#include "Common/XTPColorManager.h"

#include "XTPTabManager.h"
#include "XTPTabControl.h"
#include "XTPTabPaintManager.h"

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


//////////////////////////////////////////////////////////////////////////
// CTabManagerDropTarget

class CXTPTabControl::CTabControlDropTarget : public COleDropTarget
{
public:
	CTabControlDropTarget()
	{
		m_ptDragLastPoint = CPoint(-1, -1);
		m_dwDragLastTick = 0;
	}

	void OnDragLeave(CWnd* /*pWnd*/)
	{
		m_dwDragLastTick = 0;
		m_ptDragLastPoint = CPoint(-1, -1);
	}

	virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* /*pDataObject*/, DWORD /*dwKeyState*/, CPoint point)
	{
		CXTPTabControl* pControl = (CXTPTabControl*)pWnd;
		ASSERT_VALID(pControl);

		if (!pControl->GetPaintManager()->m_bSelectOnDragOver)
			return DROPEFFECT_NONE;

		if (m_dwDragLastTick != (DWORD)-1 && pControl->GetPaintManager()->m_bSelectOnDragOver == 2)
		{
			DWORD dwTick = GetTickCount();

			if (point != m_ptDragLastPoint)
			{
				m_dwDragLastTick = dwTick;
				m_ptDragLastPoint = point;
			}

			if (dwTick - m_dwDragLastTick > CXTPTabPaintManager::m_nSelectOnDragOverDelay)
			{
				m_dwDragLastTick = (DWORD)-1;
			}
		}
		else
		{
			CXTPTabManagerItem* pItem = pControl->HitTest(point);

			if (pItem)
			{
				pControl->SetSelectedItem(pItem);
			}
			else
			{
				m_dwDragLastTick = 0;
			}
		}

		return DROPEFFECT_NONE;
	}

protected:
	DWORD m_dwDragHoverMode;
	DWORD m_dwDragLastTick;
	CPoint m_ptDragLastPoint;
};

/////////////////////////////////////////////////////////////////////////////
// CXTPTabControl

CXTPTabControl::CXTPTabControl()
{
	m_pPaintManager = new CXTPTabPaintManager();
	m_pPaintManager->DisableLunaColors(TRUE);
	m_pPaintManager->m_bDrawTextNoPrefix = FALSE;

	m_hwndClient = 0;
	m_bAllowReorder = FALSE;
	m_nLockRedraw = 0;

	m_pDropTarget = new CTabControlDropTarget();
	m_pImageManager = new CXTPImageManager();
	m_pToolTipContext = new CXTPToolTipContext;

	CXTPDrawHelpers::RegisterWndClass(0, _T("XTPTabControl"), CS_DBLCLKS);
}

CXTPTabControl::~CXTPTabControl()
{
	CMDTARGET_RELEASE(m_pPaintManager);
	CMDTARGET_RELEASE(m_pImageManager);
	CMDTARGET_RELEASE(m_pToolTipContext);

	delete m_pDropTarget;
}

BOOL CXTPTabControl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
	if (!CWnd::Create(AfxRegisterWndClass(CS_DBLCLKS, AfxGetApp()->LoadStandardCursor(IDC_ARROW)), NULL, dwStyle, rect, pParentWnd, nID))
		return FALSE;

	m_pDropTarget->Register(this);

	return TRUE;
}

CXTPTabManagerItem* CXTPTabControl::InsertItem(int nItem, LPCTSTR lpszItem, HWND hwndChild /*= NULL*/, int nImage /*= -1*/, CXTPTabManagerItem* pAddItem /* = NULL */)
{
	CXTPTabManagerItem* pItem = AddItem(nItem, pAddItem);
	pItem->SetCaption(lpszItem);
	pItem->SetHandle(hwndChild);
	pItem->SetImageIndex(nImage);

	if (hwndChild)
		::ShowWindow(hwndChild, SW_HIDE);

	if (nItem == 0 && m_pSelected == NULL)
	{
		SetCurSel(0);
	}

	return pItem;
}

void CXTPTabControl::RedrawControl(LPCRECT lpRect, BOOL /*bAnimate*/)
{
	if (!GetSafeHwnd())
		return;

	if (m_nLockRedraw > 0)
		return;

	InvalidateRect(lpRect, FALSE);
}

void CXTPTabControl::SetLockRedraw(BOOL bLockRedraw)
{
	m_nLockRedraw += bLockRedraw ? +1 : -1;
	ASSERT(m_nLockRedraw >= 0);

	if (m_nLockRedraw == 0)
		Reposition();
}

void CXTPTabControl::Reposition()
{
	if (!GetSafeHwnd())
		return;

	if (m_nLockRedraw > 0)
		return;

	CXTPClientRect rc(this);
	CClientDC dc(this);

	GetPaintManager()->RepositionTabControl(this, &dc, rc);

	if (m_hwndClient && ::IsWindow(m_hwndClient))
	{
		GetPaintManager()->AdjustClientRect(this, rc);
		::MoveWindow(m_hwndClient, rc.left, rc.top, rc.Width(), rc.Height(), TRUE);
	}

	Invalidate(FALSE);
}

void CXTPTabControl::OnItemClick(CXTPTabManagerItem* pItem)
{
	CXTPTabManager::OnItemClick(pItem);

	if (m_hwndClient)
	{
		if (::GetFocus() != m_hWnd && !::IsChild(m_hwndClient, ::GetFocus()))
		{
			HWND hWnd = ::GetNextDlgTabItem(m_hwndClient, NULL, FALSE);
			::SetFocus(hWnd ? hWnd : m_hwndClient);
		}
	}

}

void CXTPTabControl::SetSelectedItem(CXTPTabManagerItem* pItem)
{
	if (m_pSelected == pItem)
		return;

	NMHDR nmhdr;
	nmhdr.hwndFrom = GetSafeHwnd();
	nmhdr.idFrom = GetDlgCtrlID();
	nmhdr.code = TCN_SELCHANGING;

	CWnd* pOwner = GetOwner();
	if (pOwner && IsWindow(pOwner->m_hWnd))
	{
		if (pOwner->SendMessage(WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr) == TRUE)
			return;
	}

	if (m_hwndClient)
		::ShowWindow(m_hwndClient, SW_HIDE);

	if (pItem)
	{
		m_hwndClient = pItem->GetHandle();
	}
	else
	{
		m_hwndClient = 0;
	}

	if (m_hwndClient)
	{
		::ShowWindow(m_hwndClient, SW_SHOW);
	}

	CXTPTabManager::SetSelectedItem(pItem);


	nmhdr.code = TCN_SELCHANGE;

	if (pOwner && IsWindow(pOwner->m_hWnd))
	{
		pOwner->SendMessage(WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
	}
}

void CXTPTabControl::PreSubclassWindow()
{
	CWnd::PreSubclassWindow();

	Reposition();
}

void CXTPTabControl::SetImageList(CImageList* pImageList)
{
	if (pImageList)
	{
		m_pImageManager->SetIcons(*pImageList, 0, 0, 0, xtpImageNormal);
	}
	Reposition();
}

void CXTPTabControl::SetImageManager(CXTPImageManager* pImageManager)
{
	if (pImageManager)
	{
		m_pImageManager->InternalRelease();

		m_pImageManager = pImageManager;
	}
	Reposition();
}


BEGIN_MESSAGE_MAP(CXTPTabControl, CWnd)
	//{{AFX_MSG_MAP(CXTPTabControl)
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_LBUTTONDOWN()
	ON_WM_SYSCOLORCHANGE()
	ON_WM_MOUSEMOVE()
	ON_MESSAGE_VOID(WM_MOUSELEAVE, OnMouseLeave)
	ON_MESSAGE(WM_PRINTCLIENT, OnPrintClient)
	ON_WM_KEYDOWN()
	ON_WM_GETDLGCODE()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CXTPTabControl message handlers

BOOL CXTPTabControl::OnEraseBkgnd(CDC* /*pDC*/)
{
	return TRUE;
}

BOOL CXTPTabControl::DrawParentBackground(CDC* pDC, CRect rc)
{
	HBRUSH hBrush = (HBRUSH)GetParent()->SendMessage(WM_CTLCOLORSTATIC,
		(WPARAM)pDC->GetSafeHdc(), (LPARAM)m_hWnd);
	if (hBrush)
	{
		::FillRect(pDC->GetSafeHdc(), rc, hBrush);
		return TRUE;
	}

	return FALSE;
}

void CXTPTabControl::OnPaint()
{
	CPaintDC dcPaint(this);
	CXTPBufferDC dc(dcPaint);

	GetPaintManager()->DrawTabControl(this, &dc, m_rcControl);
}

LRESULT CXTPTabControl::OnPrintClient(WPARAM wParam, LPARAM /*lParam*/)
{
	CXTPClientRect rc(this);

	CDC* pDC = CDC::FromHandle((HDC)wParam);
	if (pDC) GetPaintManager()->DrawTabControl(this, pDC, rc);

	return 1;
}

void CXTPTabControl::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);

	Reposition();
}


void CXTPTabControl::OnLButtonDown(UINT /*nFlags*/, CPoint point)
{
	if ((GetStyle() & WS_TABSTOP) && m_pSelected && (m_pSelected == HitTest(point)))
	{
		SetFocus();
	}

	PerformClick(m_hWnd, point);
}

void CXTPTabControl::OnNavigateButtonClick(CXTPTabManagerNavigateButton* pButton)
{
	CXTPTabManager::OnNavigateButtonClick(pButton->GetID());

	CXTPTabManagerItem* pItem = pButton->GetItem() ? pButton->GetItem() : m_pSelected;

	if (pItem && (pButton->GetID() == xtpTabNavigateButtonClose) && pItem->IsClosable())
	{
		DeleteItem(pItem->GetIndex());
	}
}


void CXTPTabControl::OnSysColorChange()
{
	CWnd::OnSysColorChange();

	RefreshXtremeColors();
	GetPaintManager()->RefreshMetrics();

	Reposition();

}

INT_PTR CXTPTabControl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
	return PerformToolHitTest(m_hWnd, point, pTI);
}

void CXTPTabControl::OnMouseMove(UINT nFlags, CPoint point)
{
	PerformMouseMove(m_hWnd, point);

	CWnd::OnMouseMove(nFlags, point);
}

void CXTPTabControl::OnMouseLeave()
{
	PerformMouseMove(m_hWnd, CPoint(-1, -1));
}

BOOL CXTPTabControl::DrawIcon(CDC* pDC, CPoint pt, CXTPTabManagerItem* pItem, BOOL bDraw, CSize& szIcon) const
{
	if (!pItem)
		return GetPaintManager()->m_bShowIcons;

	if (pItem->GetImageIndex() == -1 || GetPaintManager()->m_bShowIcons == FALSE)
		return FALSE;

	CXTPImageManagerIcon* pImage = m_pImageManager->GetImage(pItem->GetImageIndex(), szIcon.cx);

	if (!pImage)
		return FALSE;

	if (!bDraw)
	{
		return TRUE;
	}

	pItem->DrawImage(pDC, CRect(pt, szIcon), pImage);

	return TRUE;
}

void CXTPTabControl::SetPaintManager(CXTPTabPaintManager* pPaintManager)
{
	delete m_pPaintManager;
	m_pPaintManager = pPaintManager;
	m_pPaintManager->RefreshMetrics();
	Reposition();
}

BOOL CXTPTabControl::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	m_pToolTipContext->FilterToolTipMessage(this, message, wParam, lParam);

	return CWnd::OnWndMsg(message, wParam, lParam, pResult);
}

void CXTPTabControl::EnableToolTips(XTPTabToolTipBehaviour behaviour /*= xtpTabToolTipAlways*/)
{
	m_pPaintManager->EnableToolTips(behaviour);
}


void CXTPTabControl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	PerformKeyDown(m_hWnd, nChar);

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

UINT CXTPTabControl::OnGetDlgCode()
{
	return ::GetFocus() == m_hWnd ? DLGC_WANTARROWS : CWnd::OnGetDlgCode();
}

void CXTPTabControl::OnSetFocus(CWnd* pOldWnd)
{
	CWnd::OnSetFocus(pOldWnd);
	InvalidateRect(m_rcHeaderRect, FALSE);
}

void CXTPTabControl::OnKillFocus(CWnd* pNewWnd)
{
	CWnd::OnKillFocus(pNewWnd);
	InvalidateRect(m_rcHeaderRect, FALSE);
}

#ifndef WM_QUERYUISTATE
#define WM_QUERYUISTATE                 0x0129
#endif

#ifndef WM_CHANGEUISTATE
#define WM_CHANGEUISTATE                0x0127
#endif

#ifndef UISF_HIDEFOCUS
#define UISF_HIDEFOCUS                  0x1
#endif

#ifndef UIS_CLEAR
#define UIS_CLEAR                       2
#endif

void CXTPTabControl::SetFocusedItem(CXTPTabManagerItem* pItem)
{
	CXTPTabManager::SetFocusedItem(pItem);

	if ((::SendMessage(::GetParent(m_hWnd), WM_QUERYUISTATE, 0, 0) & UISF_HIDEFOCUS) != 0)
	{
		::SendMessage(::GetParent(m_hWnd), WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);

	}
}

BOOL CXTPTabControl::HeaderHasFocus() const
{
	return (GetStyle() & WS_TABSTOP) && (::GetFocus() == m_hWnd)
		&& ((::SendMessage(::GetParent(m_hWnd), WM_QUERYUISTATE, 0, 0) & UISF_HIDEFOCUS) == 0);
}

void CXTPTabControl::AdjustRect(BOOL bLarger, LPRECT lpRect) const
{
	if (!lpRect)
		return;

	CRect rc(lpRect);
	GetPaintManager()->AdjustClientRect((CXTPTabControl*)this, rc);

	if (bLarger)
	{
		lpRect->top -= rc.top - lpRect->top;
		lpRect->left -= rc.left - lpRect->left;
		lpRect->bottom -= rc.bottom - lpRect->bottom;
		lpRect->right -= rc.right - lpRect->right;
	}
	else
	{
		*lpRect = rc;
	}
}