// CXTPSearchOptionsCtrl : implementation file
//
// This file is a part of the XTREME CONTROLS 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/XTPColorManager.h"
#include "Common/XTPDrawHelpers.h"

#include "../Defines.h"
#include "../Util/XTPGlobal.h"
#include "../Resize/XTPResizeRect.h"
#include "../Resize/XTPResizePoint.h"
#include "XTPSearchOptionsCtrl.h"

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

/////////////////////////////////////////////////////////////////////////////
// CXTPSearchOptionsCtrl

CXTPSearchOptionsCtrl::CXTPSearchOptionsCtrl()
{
	m_strExpandLabel = _T("");
	m_strContractLabel = _T("");
	m_iMinSize = ::GetSystemMetrics(SM_CYMENU);
	m_iMaxSize = 0;
	m_bExpanded = true;
	m_bPreSubclassInit = true;
}

CXTPSearchOptionsCtrl::~CXTPSearchOptionsCtrl()
{
}

IMPLEMENT_DYNAMIC(CXTPSearchOptionsCtrl, CStatic)

BEGIN_MESSAGE_MAP(CXTPSearchOptionsCtrl, CStatic)
	//{{AFX_MSG_MAP(CXTPSearchOptionsCtrl)
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_SETCURSOR()
	ON_WM_CREATE()
	ON_WM_WINDOWPOSCHANGED()
	ON_WM_ENABLE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXTPSearchOptionsCtrl message handlers

void CXTPSearchOptionsCtrl::OnEnable(BOOL bEnable)
{
	CStatic::OnEnable(bEnable);

	// enable/disable each of our windows in the hide controls array.
	int iIndex;
	for (iIndex = 0; iIndex < m_arHideCtrls.GetSize(); ++iIndex)
	{
		CWnd* pWndCtrl = (CWnd*)m_arHideCtrls.GetAt(iIndex);
		if (::IsWindow(pWndCtrl->GetSafeHwnd()))
		{
			pWndCtrl->EnableWindow(bEnable);
		}
	}

	// enable/disable each of the controls in our move controls array.
	for (iIndex = 0; iIndex < m_arMoveCtrls.GetSize(); ++iIndex)
	{
		CWnd* pWndCtrl = (CWnd*)m_arMoveCtrls.GetAt(iIndex);
		if (::IsWindow(pWndCtrl->GetSafeHwnd()))
		{
			pWndCtrl->EnableWindow(bEnable);
		}
	}
}

void CXTPSearchOptionsCtrl::AddControl(CWnd* pWndCtrl)
{
	ASSERT_VALID(pWndCtrl); // must be valid.
	m_arHideCtrls.Add(pWndCtrl);
}

void CXTPSearchOptionsCtrl::MoveControl(CWnd* pWndCtrl)
{
	ASSERT_VALID(pWndCtrl); // must be valid.
	m_arMoveCtrls.Add(pWndCtrl);
}

void CXTPSearchOptionsCtrl::SetLabelText(LPCTSTR lpszExpand, LPCTSTR lpszContract)
{
	m_strExpandLabel = lpszExpand;
	m_strContractLabel = lpszContract;
}

void CXTPSearchOptionsCtrl::Expand()
{
	// set our expanded flag to true.
	m_bExpanded = true;

	CWnd* pOwner = GetOwner();
	ASSERT_VALID(pOwner);

	// notify owner of expanding, this is sent prior to our
	// adjusting any controls to allow the parent to prepare
	// itself for resizing.

	if (pOwner != NULL)
	{
		pOwner->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(),
			SON_XTP_ITEMEXPANDING), (LPARAM)m_hWnd);
	}

	// expand this window.
	CRect rect;
	GetWindowRect(&rect);
	GetParent()->ScreenToClient(&rect);
	rect.bottom = rect.top + m_iMaxSize;
	MoveWindow(&rect);

	// show each of our windows in the hide controls array.
	int iIndex;
	for (iIndex = 0; iIndex < m_arHideCtrls.GetSize(); ++iIndex)
	{
		CWnd* pWndCtrl = (CWnd*)m_arHideCtrls.GetAt(iIndex);
		ASSERT_VALID(pWndCtrl); // must be valid.
		pWndCtrl->ShowWindow(SW_SHOW);
	}

	// move each of the controls in our move controls array.
	for (iIndex = 0; iIndex < m_arMoveCtrls.GetSize(); ++iIndex)
	{
		CWnd* pWndCtrl = (CWnd*)m_arMoveCtrls.GetAt(iIndex);
		ASSERT_VALID(pWndCtrl); // must be valid.

		CWnd* pParentWnd = pWndCtrl->GetParent();
		ASSERT_VALID(pParentWnd);

		CRect rc;
		pWndCtrl->GetWindowRect(&rc);
		pParentWnd->ScreenToClient(&rc);

		int iOffset = m_iMaxSize-m_iMinSize;
		rc.top += iOffset;
		rc.bottom += iOffset;

		// move the control and repaint it.
		pWndCtrl->MoveWindow(&rc);
		pWndCtrl->InvalidateRect(NULL);
	}

	// notify owner that expand has completed.

	if (pOwner != NULL)
	{
		pOwner->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(),
			SON_XTP_ITEMEXPAND), (LPARAM)m_hWnd);
	}
}

void CXTPSearchOptionsCtrl::Contract()
{
	// set our expanded flag to false.
	m_bExpanded = false;

	CWnd* pOwner = GetOwner();
	ASSERT_VALID(pOwner);

	// notify owner of contracting, this is sent prior to our
	// adjusting any controls to allow the parent to prepare
	// itself for resizing.

	if (pOwner != NULL)
	{
		pOwner->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(),
			SON_XTP_ITEMCONTRACTING), (LPARAM)m_hWnd);
	}

	// contract this window.
	CRect rect;
	GetWindowRect(&rect);

	if (m_iMaxSize == 0)
		m_iMaxSize = rect.Height();

	GetParent()->ScreenToClient(&rect);
	rect.bottom = rect.top + m_iMinSize;
	MoveWindow(&rect);

	// hide each of our windows in the hide controls array.
	int iIndex;
	for (iIndex = 0; iIndex < m_arHideCtrls.GetSize(); ++iIndex)
	{
		CWnd* pWndCtrl = (CWnd*)m_arHideCtrls.GetAt(iIndex);
		ASSERT_VALID(pWndCtrl); // must be valid.
		pWndCtrl->ShowWindow(SW_HIDE);
	}

	// move each of the controls in our move controls array.
	for (iIndex = 0; iIndex < m_arMoveCtrls.GetSize(); ++iIndex)
	{
		CWnd* pWndCtrl = (CWnd*)m_arMoveCtrls.GetAt(iIndex);
		ASSERT_VALID(pWndCtrl); // must be valid.

		CWnd* pParentWnd = pWndCtrl->GetParent();
		ASSERT_VALID(pParentWnd);

		CRect rc;
		pWndCtrl->GetWindowRect(&rc);
		pParentWnd->ScreenToClient(&rc);

		int iOffset = m_iMaxSize-m_iMinSize;
		rc.top -= iOffset;
		rc.bottom -= iOffset;

		// move the control and repaint it.
		pWndCtrl->MoveWindow(&rc);
		pWndCtrl->InvalidateRect(NULL);
	}

	// notify owner that contracting has completed.

	if (pOwner != NULL)
	{
		pOwner->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(),
			SON_XTP_ITEMCONTRACT), (LPARAM)m_hWnd);
	}
}

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

void CXTPSearchOptionsCtrl::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	CRect rcClient;
	GetClientRect(&rcClient);

	// exclude the windows in our "hide list" from paint routines.
	int iIndex;
	for (iIndex = 0; iIndex < m_arHideCtrls.GetSize(); ++iIndex)
	{
		CWnd* pWndCtrl = (CWnd*)m_arHideCtrls.GetAt(iIndex);
		ASSERT_VALID(pWndCtrl); // must be valid.

		CRect rcHide;
		pWndCtrl->GetWindowRect(&rcHide);
		ScreenToClient(&rcHide);

		dc.ExcludeClipRect(&rcHide);
	}

	// paint off screen.
	CXTPBufferDC memDC(dc);

	HBRUSH hBrush = (HBRUSH)GetParent()->SendMessage(WM_CTLCOLORSTATIC, (WPARAM)memDC.GetSafeHdc(), (LPARAM)GetSafeHwnd());
	if (hBrush)
	{
		::FillRect(memDC.GetSafeHdc(), rcClient, hBrush);
	}
	else
	{
		memDC.FillSolidRect(rcClient, GetXtremeColor(COLOR_WINDOW));
	}


	BOOL bIsEnabled = IsWindowEnabled();

	memDC.SetBkMode(TRANSPARENT);

	CFont* pOldFont = 0;

	// construct the font's attributes
	if (m_bExpanded)
	{
		pOldFont = memDC.SelectObject(&XTPAuxData().font);
		memDC.SetTextColor(bIsEnabled ? GetXtremeColor(COLOR_HIGHLIGHTTEXT) :
			GetXtremeColor(COLOR_GRAYTEXT));

		// if we are expanded, draw a border and fill caption.
		memDC.Draw3dRect(rcClient, GetXtremeColor(COLOR_HIGHLIGHT),
			GetXtremeColor(COLOR_HIGHLIGHT));

		CRect rcCaption(rcClient);
		rcCaption.bottom = rcCaption.top + m_iMinSize;
		rcCaption.top += 1;
		rcCaption.bottom -= 1;
		memDC.FillSolidRect(rcCaption, GetXtremeColor(COLOR_HIGHLIGHT));
	}
	else
	{
		pOldFont = memDC.SelectObject(&XTPAuxData().fontULine);
		memDC.SetTextColor(bIsEnabled ? GetXtremeColor(COLOR_HIGHLIGHT) :
			GetXtremeColor(COLOR_GRAYTEXT));
	}

	// get the size of the label's text.
	CSize size = memDC.GetTextExtent(m_bExpanded ? m_strExpandLabel : m_strContractLabel);

	// construct the "hot" area used for mouse activation.
	GetWindowRect(&m_rcLabel);
	m_rcLabel.bottom = m_rcLabel.top + m_iMinSize;

	if (m_bExpanded)
		m_rcLabel.left += 3;
	else
		m_rcLabel.right = m_rcLabel.left + size.cx;

	// paint the label.
	ScreenToClient(&m_rcLabel);
	memDC.DrawText(m_bExpanded ? m_strExpandLabel : m_strContractLabel,
		&m_rcLabel, DT_SINGLELINE | DT_LEFT | DT_VCENTER);

	memDC.SelectObject(pOldFont);

}

void CXTPSearchOptionsCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
	if (m_rcLabel.PtInRect(point))
	{
		if (m_bExpanded)
			Contract();
		else
			Expand();

		Invalidate();
	}

	CStatic::OnLButtonDown(nFlags, point);
}

BOOL CXTPSearchOptionsCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	// get the cursor position in client coordinates.
	CPoint point;
	::GetCursorPos(&point);
	ScreenToClient(&point);

	// if the cursor is over the label set the hand cursor.
	if (m_rcLabel.PtInRect(point))
	{
		::SetCursor(XTPAuxData().hcurHand);
		return TRUE;
	}

	return CStatic::OnSetCursor(pWnd, nHitTest, message);
}

bool CXTPSearchOptionsCtrl::Init()
{
	if (::IsWindow(m_hWnd))
	{
		if (m_iMaxSize == 0)
		{
			CRect rect;
			GetWindowRect(&rect);
			m_iMaxSize = rect.Height();
		}

		// update display.
		RedrawWindow();
		return true;
	}

	return false;
}

void CXTPSearchOptionsCtrl::PreSubclassWindow()
{
	CStatic::PreSubclassWindow();

	if (m_bPreSubclassInit)
	{
		// Initialize the control.
		Init();
	}
}

int CXTPSearchOptionsCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CStatic::OnCreate(lpCreateStruct) == -1)
		return -1;

	// Initialize the control.
	Init();

	return 0;
}

BOOL CXTPSearchOptionsCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
	if (!CStatic::PreCreateWindow(cs))
		return FALSE;

	// When creating controls dynamically Init() must
	// be called from OnCreate() and not from
	// PreSubclassWindow().

	m_bPreSubclassInit = false;

	return TRUE;
}

void CXTPSearchOptionsCtrl::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos)
{
	CStatic::OnWindowPosChanged(lpwndpos);

	// Update the control whenever the window size or position has changed.
	Invalidate();
	UpdateWindow();
}


/////////////////////////////////////////////////////////////////////////////

CXTPSearchOptionsView::CResizeWnd::CResizeWnd(CWnd* pWndParent, HWND hWndChild, const XTP_RESIZEPOINT& ptTopLeft, const XTP_RESIZEPOINT& ptTopRight)
: m_hWndChild(hWndChild)
, m_pWndParent(pWndParent)
, m_rcWindow(0, 0, 0, 0)
, m_rcSizing(CXTPResizeRect(ptTopLeft.x, ptTopLeft.y, ptTopRight.x, ptTopRight.y))
{
	ASSERT(m_hWndChild != NULL);
	ASSERT_VALID(m_pWndParent);

	::GetWindowRect(m_hWndChild, &m_rcWindow);
	m_pWndParent->ScreenToClient(&m_rcWindow);

	// if the control is a group box, then make it transparent
	if (IsGroupBox())
	{
		CWnd::ModifyStyleEx(m_hWndChild, 0, WS_EX_TRANSPARENT,
			SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
	}
}

CXTPSearchOptionsView::CResizeWnd::~CResizeWnd()
{
}

bool CXTPSearchOptionsView::CResizeWnd::IsGroupBox()
{
	if (!::IsWindow(m_hWndChild))
		return false;

	TCHAR szClass[8];
	::GetClassName(m_hWndChild, szClass, 8);

	if (_tcsicmp(szClass, _T("Button")) == 0)
	{
		DWORD dwStyle = ::GetWindowLong(m_hWndChild, GWL_STYLE);
		if ((dwStyle & (BS_GROUPBOX | WS_TABSTOP)) == BS_GROUPBOX)
		{
			return true;
		}
	}

	return false;
}

bool CXTPSearchOptionsView::CResizeWnd::Resize(HDWP& hDWP, float dx)
{
	if (!::IsWindow(m_hWndChild))
		return false;

	if (dx > 0)
	{
		CXTPResizeRect rrcWindow = m_rcWindow;
		CXTPResizeRect rrcItem = m_rcWindow;

		rrcItem.left += dx * m_rcSizing.left;
		rrcItem.right += dx * m_rcSizing.right;

		if (rrcItem != rrcWindow)
		{
			// get the size of the dialog item in client coordinates.
			CRect rcOld;
			::GetWindowRect(m_hWndChild, &rcOld);
			m_pWndParent->ScreenToClient(&rcOld);

			int x = (int) rrcItem.left;
			int y = (int) rcOld.top;
			int cx = (int) rrcItem.Width();
			int cy = (int) rcOld.Height();

			// Set positioning flags
			UINT uFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER;

			// if the x-y coordinates have not changed, there is no reason
			// to move the dialog item.
			CRect rcNew = rrcItem;
			if (rcNew.TopLeft() == rcOld.TopLeft())
				uFlags |= SWP_NOMOVE;

			// if the cx-cy size has not changed, there is no reason to
			// size the dialog item.  If size has changed, don't
			// copy bits of the client area (i.e. have them invalidated/redrawn)
			if (rcNew.Size() == rcOld.Size())
				uFlags |= SWP_NOSIZE;
			else
				uFlags |= SWP_NOCOPYBITS;

			::DeferWindowPos(hDWP, m_hWndChild, 0, x, y,
				cx, cy, uFlags);

			if (hDWP == NULL)
			{
				TRACE(_T("DeferWindowPos failed for ID %i\n"), ::GetDlgCtrlID(m_hWndChild));
				return false;
			}
		}
	}

	return true;
}

/////////////////////////////////////////////////////////////////////////////
// CXTPSearchOptionsView

IMPLEMENT_DYNAMIC(CXTPSearchOptionsView, CFormView)

CXTPSearchOptionsView::CXTPSearchOptionsView(LPCTSTR lpszTemplateName)
: CFormView(lpszTemplateName)
, m_rcWindow(0, 0, 0, 0)
{
	m_hBrush = NULL;
	m_clrBrush = (COLORREF)-1;
}

CXTPSearchOptionsView::CXTPSearchOptionsView(UINT nIDTemplate)
: CFormView(nIDTemplate)
, m_rcWindow(0, 0, 0, 0)
{
	m_hBrush = NULL;
	m_clrBrush = (COLORREF)-1;
}

CXTPSearchOptionsView::~CXTPSearchOptionsView()
{
	while (!m_arResizeWnd.IsEmpty())
	{
		CResizeWnd* pResizeWnd = m_arResizeWnd.RemoveHead();
		SAFE_DELETE(pResizeWnd);
	}
	if (m_hBrush != NULL)
		::DeleteObject(m_hBrush);
}

BEGIN_MESSAGE_MAP(CXTPSearchOptionsView, CFormView)
	//{{AFX_MSG_MAP(CXTPSearchOptionsView)
	ON_WM_CTLCOLOR()
	ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXTPSearchOptionsView message handlers

COLORREF CXTPSearchOptionsView::GetBackColor() const
{
	return GetXtremeColor(COLOR_WINDOW);
}

HBRUSH CXTPSearchOptionsView::OnCtlColor(CDC* pDC, CWnd* /*pWnd*/, UINT /*nCtlColor*/)
{

	COLORREF clr = GetBackColor();

	if (m_hBrush && clr != m_clrBrush)
	{
		DeleteObject(m_hBrush);
		m_hBrush = NULL;
	}

	if (!m_hBrush)
	{
		m_hBrush = ::CreateSolidBrush(clr);
		m_clrBrush = clr;
	}

	pDC->SetBkColor(m_clrBrush);
	pDC->SetTextColor(GetXtremeColor(COLOR_WINDOWTEXT));

	return m_hBrush;
}

LRESULT CXTPSearchOptionsView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	LRESULT lRet = CFormView::WindowProc(message, wParam, lParam);

	if (message == WM_INITDIALOG)
	{
		OnInitDialog();
	}

	return lRet;
}

BOOL CXTPSearchOptionsView::OnInitDialog()
{
	GetClientRect(&m_rcWindow);
	return TRUE;
}

void CXTPSearchOptionsView::SetResize(int nID, const XTP_RESIZEPOINT& ptTopLeft, const XTP_RESIZEPOINT& ptTopRight)
{
	CResizeWnd* pResizeWnd = 0;

	// search for an item with the given id
	POSITION pos;
	for (pos = m_arResizeWnd.GetHeadPosition(); pos; m_arResizeWnd.GetNext(pos))
	{
		HWND hWndChild = m_arResizeWnd.GetAt(pos)->m_hWndChild;
		if (hWndChild && ::IsWindow(hWndChild) && (::GetDlgCtrlID(hWndChild) == nID))
		{
			pResizeWnd = m_arResizeWnd.GetAt(pos);
			break;
		}
	}

	if (pResizeWnd == 0)
	{
		HWND hWndChild = ::GetDlgItem(m_hWnd, nID);

		if (hWndChild != NULL)
		{
			pResizeWnd = new CResizeWnd(this, hWndChild, ptTopLeft, ptTopRight);
			m_arResizeWnd.AddHead(pResizeWnd);
		}
	}
	else
	{
		pResizeWnd->m_rcSizing += CXTPResizeRect(ptTopLeft.x, ptTopLeft.y, ptTopRight.x, ptTopRight.y);
	}
}

void CXTPSearchOptionsView::OnSize(UINT nType, int cx, int cy)
{
	CFormView::OnSize(nType, cx, cy);

	CRect rcWindow;
	GetClientRect(rcWindow);

	float iOffset = (float)(rcWindow.Width() - m_rcWindow.Width());

	HDWP hDWP = ::BeginDeferWindowPos((int)m_arResizeWnd.GetCount());

	for (POSITION pos = m_arResizeWnd.GetHeadPosition(); pos; m_arResizeWnd.GetNext(pos))
	{
		CResizeWnd* pResizeWnd = m_arResizeWnd.GetAt(pos);
		if (pResizeWnd != NULL)
		{
			pResizeWnd->Resize(hDWP, iOffset);
		}
	}

	::EndDeferWindowPos(hDWP);
}

void CXTPSearchOptionsView::AdjustScrollSizes(CXTPSearchOptionsCtrl* pSOCtrl, bool bAdd)
{
	// get the scroll sizes for the form view.
	int nMapMode;
	CSize sizeTotal, sizePage, sizeLine;
	GetDeviceScrollSizes(nMapMode, sizeTotal, sizePage, sizeLine);

	// adjust the scroll sizes to accomindate for expansion.
	if (bAdd)
	{
		sizeTotal.cy += (pSOCtrl->GetOffsetSize());
	}
	else
	{
		sizeTotal.cy -= (pSOCtrl->GetOffsetSize());
	}

	SetScrollSizes(nMapMode, sizeTotal);

	// make sure the window and all siblings are updated.
	RedrawWindow(NULL, NULL,
		RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ALLCHILDREN);
}