// XTPDatePickerControl.cpp: implementation of the CXTPDatePickerControl class.
//
// This file is a part of the XTREME CALENDAR 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 "Resource.h"

#include "Common/XTPDrawHelpers.h"
#include "Common/XTPImageManager.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPResourceImage.h"
#include "Common/XTPColorManager.h"
#include "Common/XTPNotifyConnection.h"

#include "XTPCalendarDefines.h"
#include "XTPCalendarMsgNotifier.h"
#include "XTPCalendarUtils.h"
#include "XTPDatePickerItemMonth.h"
#include "XTPDatePickerPaintManager.h"
#include "XTPDatePickerDaysCollection.h"
#include "XTPDatePickerList.h"
#include "XTPDatePickerItemDay.h"
#include "XTPDatePickerControl.h"
#include "XTPDatePickerNotifications.h"

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

#pragma warning(disable: 4571) // warning C4571: catch(...) blocks compiled with /EHs do not catch or re-throw Structured Exceptions

// Custom class name for the Date Picker control window
const TCHAR XTP_DATEPICKERCTRL_CLASSNAME[] = _T("XTPDatePicker");

//////////////////////////////////////////////////////////////////////////
// CXTPDatePickerButtons

int CXTPDatePickerButtons::GetVisibleButtonCount() const
{
	int nCount = 0;
	for (int i = 0; i < (int)GetSize(); i++)
	{
		if (GetAt(i)->m_bVisible) nCount++;
	}
	return nCount;
}
CXTPDatePickerButton* CXTPDatePickerButtons::Find(int nID) const
{
	for (int i = 0; i < (int)GetSize(); i++)
	{
		if (GetAt(i)->m_nID == nID)
			return GetAt(i);
	}
	return NULL;
}
CXTPDatePickerButton* CXTPDatePickerButtons::HitTest(CPoint point) const
{
	for (int i = 0; i < (int)GetSize(); i++)
	{
		if (GetAt(i)->m_bVisible && GetAt(i)->m_rcButton.PtInRect(point))
			return GetAt(i);
	}
	return NULL;

}

//////////////////////////////////////////////////////////////////////////
// XTP_DAYITEM_METRICS

XTP_DAYITEM_METRICS::XTP_DAYITEM_METRICS()
{

	clrForeground = 0;
	clrBackground = 0;
}

CFont* XTP_DAYITEM_METRICS::GetFont()
{
	return &m_fntText;
}

void XTP_DAYITEM_METRICS::SetFont(CFont* pFont)
{
	ASSERT_VALID(pFont);
	if (!pFont)
		return;
	// set new font
	LOGFONT lf;
	pFont->GetLogFont(&lf);
	m_fntText.DeleteObject();
	m_fntText.CreateFontIndirect(&lf);
}


CXTPDatePickerButton::CXTPDatePickerButton()
{
	m_rcButton.SetRectEmpty();
	m_bVisible = TRUE;
	m_bPressed = FALSE;
	m_bHighlight = FALSE;
	m_nID = 0;
}

CString CXTPDatePickerButton::GetCaption() const
{
	if (m_strCaption.IsEmpty())
		return CXTPCalendarUtils::LoadString(m_nID);

	return m_strCaption;
}

void CXTPDatePickerButton::SetCaption(LPCTSTR pcszCaption)
{
	m_strCaption = pcszCaption;
}

//////////////////////////////////////////////////////////////////////////////
// CXTPDatePickerControl

CXTPDatePickerControl::CXTPDatePickerControl()
{
	m_pConnection = new CXTPNotifyConnection();
	m_pSink = new CXTPNotifySink();

	RegisterWindowClass();

	m_pfnCallback = NULL;
	m_pCallbackParam = NULL;
	m_nLockUpdateCount = 0;

	m_lcidActiveLocale = CXTPCalendarUtils::GetActiveLCID();

	m_pPaintManager = NULL;
	SetTheme(NULL);// set default paint manager
	//SetTheme(new CXTPDatePickerThemeOffice2007());

	m_bAutoSize = TRUE;
	m_bRestrictMode = FALSE;
	m_mouseMode = mouseNothing;
	m_nTimerID = 0;
	m_bIsModal = FALSE;
	m_nMaxSelectionDays = XTP_SELECTION_INFINITE;
	m_bSelectWeek = FALSE;
	m_bRightToLeft = FALSE;

	m_bShowWeekNumbers = FALSE;

	m_nRows = 1;
	m_nColumns = 1;
	m_nDesiredRows = 1;
	m_nDesiredColumns = 1;

	m_nFirstDayOfWeek = 2;
	m_nFirstWeekOfYearDays = 1;
	m_nMonthDelta = 0;
	m_bHighlightToday = TRUE;
	m_bChanged = TRUE;
	m_dtToday = COleDateTime::GetCurrentTime();
	m_dtFirstMonth.SetDate(m_dtToday.GetYear(), m_dtToday.GetMonth(), 1);
	m_dtMinRange.SetStatus(COleDateTime::null);
	m_dtMaxRange.SetStatus(COleDateTime::null);

	m_rcControl.SetRectEmpty();
	m_rcGrid.SetRectEmpty();
	m_bShowNonMonthDays = TRUE;
	m_borderStyle = xtpDatePickerBorder3D;

	m_pButtonCaptured = NULL;

	AddButton(XTP_IDS_DATEPICKER_TODAY);
	AddButton(XTP_IDS_DATEPICKER_NONE);

	m_pListControl = NULL;

	m_pSelectedDays = new CXTPDatePickerDaysCollection(this);

	m_arMonthNames = new CString[12];
	m_arDayOfWeekNames = new CString[7];

	m_bAllowNoncontinuousSelection = TRUE;
	m_bMultiSelectionMode = FALSE;

	m_bDeleteOnFinalRelease = FALSE;
	m_bYearsTriangle = FALSE;

	InitNames();

	ClearFocus();

	Populate();

	//-----------------------------------------------------------------------
	XTPCalendarMsgNotifier()->Advise(this, WM_TIMECHANGE);


	if (XTPResourceImages() && XTPResourceImages()->GetConnection())
	{
		m_pSink->Advise(XTPResourceImages()->GetConnection(), XTP_NC_COMMON_RESOURCEIMAGES_CHANGED,
			CreateNotfySinkClassDelegate(this, &CXTPDatePickerControl::OnEventResourceImagesChanged));
	}
}

CXTPDatePickerControl::~CXTPDatePickerControl()
{
	XTPCalendarMsgNotifier()->Unadvise(this);

	ClearMonths();

	CMDTARGET_RELEASE(m_pPaintManager);

	if (m_pListControl)
	{
		m_pListControl->DestroyWindow();
		delete m_pListControl;
	}

	CMDTARGET_RELEASE(m_pSelectedDays);

	if (m_nTimerID != 0 && m_hWnd)
		KillTimer(m_nTimerID);

	DestroyWindow();

	for (int i = 0; i < GetButtonCount(); i++)
		delete m_arrButtons[i];

	delete[] m_arDayOfWeekNames;
	delete[] m_arMonthNames;

	CMDTARGET_RELEASE(m_pConnection);

	m_pSink->Delete();
}

void CXTPDatePickerControl::OnDestroy()
{
	if (m_bIsModal)
	{
		if (ContinueModal()) // skip if EndModalLoop has already called
		{
			EndModalLoop(IDABORT);
		}
	}
	CWnd::OnDestroy();
}

void CXTPDatePickerControl::OnEventResourceImagesChanged(XTP_NOTIFY_CODE Event, WPARAM /*wParam*/, LPARAM /*lParam*/)
{
	ASSERT(Event == XTP_NC_COMMON_RESOURCEIMAGES_CHANGED);

	if (Event == XTP_NC_COMMON_RESOURCEIMAGES_CHANGED && GetPaintManager()->GetPaintTheme() == xtpCalendarThemeResource)
	{
		GetPaintManager()->RefreshMetrics();

		_RedrawControl(FALSE);
	}
}


void CXTPDatePickerControl::AddButton(UINT nID)
{
	CXTPDatePickerButton* pButton = new CXTPDatePickerButton();

	pButton->m_nID = nID;

	m_arrButtons.Add(pButton);
}

BOOL CXTPDatePickerControl::IsTodayButtonVisible()
{
	CXTPDatePickerButton* pButton = m_arrButtons.Find(XTP_IDS_DATEPICKER_TODAY);
	return pButton ? pButton->m_bVisible : FALSE;
}

BOOL CXTPDatePickerControl::IsNoneButtonVisible()
{
	CXTPDatePickerButton* pButton = m_arrButtons.Find(XTP_IDS_DATEPICKER_NONE);
	return pButton ? pButton->m_bVisible : FALSE;
}

void CXTPDatePickerControl::SetTheme(CXTPDatePickerPaintManager* pPaintManager)
{
	// set default
	if (!pPaintManager)
		pPaintManager = new CXTPDatePickerPaintManager();

	CMDTARGET_RELEASE(m_pPaintManager);

	m_pPaintManager = pPaintManager;

	if (m_pPaintManager)
	{
		m_pPaintManager->RefreshMetrics();
	}

	_RedrawControl(FALSE);
}

void CXTPDatePickerControl::RedrawControl()
{
	_RedrawControl(TRUE);
}

void CXTPDatePickerControl::_RedrawControl(BOOL bUpdateNow)
{
	m_bChanged = TRUE;

	if (GetSafeHwnd() && (m_nLockUpdateCount == 0))
	{
		Invalidate(FALSE);
		if (bUpdateNow)
		{
			UpdateWindow();
		}
	}
}

BEGIN_MESSAGE_MAP(CXTPDatePickerControl, CWnd)
	//{{AFX_MSG_MAP(CXTPDatePickerControl)
	ON_WM_CREATE()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_MESSAGE(WM_PRINTCLIENT, OnPrintClient)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_CAPTURECHANGED()
	ON_WM_CANCELMODE()
	ON_WM_MOUSEMOVE()
	ON_WM_TIMER()
	ON_WM_TIMECHANGE()
	ON_WM_SIZE()
	ON_WM_SYSCOLORCHANGE()
	ON_WM_KEYDOWN()
	ON_WM_SETCURSOR()

	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	ON_WM_GETDLGCODE()
	ON_WM_DESTROY()
	ON_WM_ENABLE()
	//}}AFX_MSG_MAP
	ON_MESSAGE_VOID(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CXTPDatePickerControl message handlers

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

int CXTPDatePickerControl::OnCreate(LPCREATESTRUCT lp)
{
	int nRet = CWnd::OnCreate(lp);

	CXTPClientRect rc(this);
	AdjustLayout(rc);

	if (m_bRightToLeft)
	{
		SetLayoutRTL(TRUE);
	}

	return nRet;
}

void CXTPDatePickerControl::PreSubclassWindow()
{
	CXTPClientRect rc(this);
	AdjustLayout(rc);
}

BOOL CXTPDatePickerControl::GoModal(const RECT& rect, CWnd* pParentWnd)
{
	if (!pParentWnd)
		pParentWnd = AfxGetMainWnd();

	CRect rcScreen(rect);

	if (!::IsWindow(m_hWnd) && !CreateEx(WS_EX_TOOLWINDOW, XTP_DATEPICKERCTRL_CLASSNAME, NULL, pParentWnd ? WS_CHILD : WS_POPUP, rcScreen, pParentWnd, 0))
		return FALSE;

	// Enable this window
	EnableWindow(TRUE);

	HWND hwndFocusWnd = ::SetFocus(m_hWnd);

	if (pParentWnd)
	{
		SetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT, 0);
		ModifyStyle(WS_CHILD, WS_POPUP);
		SetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)pParentWnd->m_hWnd);
	}

	SetWindowPos(&CWnd::wndTopMost, rcScreen.left, rcScreen.top, rcScreen.Width(), rcScreen.Height(), SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOOWNERZORDER);

	SetCapture();

	SendNotification(XTP_NC_DATEPICKERBEFOREGOMODAL);
	SendMessageToParent(XTP_NC_DATEPICKERBEFOREGOMODAL);

	m_bIsModal = TRUE;

	m_nFlags |= WF_CONTINUEMODAL;
	int nResult = m_nModalResult;
	if (ContinueModal())
	{
		// enter modal loop
		DWORD dwFlags = MLF_SHOWONIDLE;
		if (GetStyle() & DS_NOIDLEMSG)
			dwFlags |= MLF_NOIDLEMSG;
		nResult = RunModalLoop(dwFlags);
	}

	ReleaseCapture();
	DestroyWindow();

	m_bIsModal = FALSE;

	if (hwndFocusWnd && ::IsWindow(hwndFocusWnd) && ::IsWindowVisible(hwndFocusWnd))
		::SetFocus(hwndFocusWnd);

	return (nResult == IDOK);
}

CSize CXTPDatePickerControl::SetGridDimentions(CRect rcClient)
{
	CWindowDC dc(GetOwner());
	CSize szMonth(m_pPaintManager->CalcMonthRect(&dc));

	szMonth.cy += 2;

	m_nRows = max(1, rcClient.Height() / szMonth.cy);
	m_nColumns = max(1, rcClient.Width() / szMonth.cx);

	if (m_dtMaxRange.GetStatus() == COleDateTime::valid)
	{
		int nMaxRangeMonths = (m_dtMaxRange.GetYear() * 12 + m_dtMaxRange.GetMonth()) -
			(m_dtFirstMonth.GetYear() * 12 + m_dtFirstMonth.GetMonth()) + 1;

		if (m_dtMinRange.GetStatus() == COleDateTime::valid)
		{
			int nTotalMaxRangeMonths = (m_dtMaxRange.GetYear() * 12 + m_dtMaxRange.GetMonth()) -
				(m_dtMinRange.GetYear() * 12 + m_dtMinRange.GetMonth()) + 1;

			if (nTotalMaxRangeMonths == 12 && m_nColumns >= 6 && m_nRows >= 2)
			{
				m_nColumns = 6;
				m_nRows = 2;
			}

			if (nTotalMaxRangeMonths == 12 && m_nColumns == 5 && m_nRows > 2)
			{
				m_nColumns = 4;
				m_nRows = 3;
			}

			while (m_nRows * m_nColumns > nTotalMaxRangeMonths)
			{
				if (m_nRows > m_nColumns)
				{
					if (m_nRows > 1)
						m_nRows--;
					else
						break;
				}
				else
				{
					if (m_nColumns > 1)
						m_nColumns--;
					else
						break;
				}
				//original logic
				//if (m_nRows > 1)
				//  m_nRows--;
				//else if (m_nColumns > 1)
				//  m_nColumns--;
				//else
				//  break;

				//opposite logic
				//if (m_nColumns > 1)
				//  m_nColumns--;
				//else if (m_nRows > 1)
				//  m_nRows--;
				//else
				//  break;
			}
		}

		if (m_nRows * m_nColumns > nMaxRangeMonths)
		{
			ShiftDate(m_dtFirstMonth, nMaxRangeMonths - m_nRows * m_nColumns);
		}

		if (m_dtMinRange.GetStatus() == COleDateTime::valid &&
			m_dtFirstMonth < m_dtMinRange)
		{
			m_dtFirstMonth = m_dtMinRange;
			while (m_nRows * m_nColumns > nMaxRangeMonths)
			{
				if (m_nRows > m_nColumns)
				{
					if (m_nRows > 1)
						m_nRows--;
					else
						break;
				}
				else
				{
					if (m_nColumns > 1)
						m_nColumns--;
					else
						break;
				}

				//original logic
				//if (m_nRows > 1)
				//  m_nRows--;
				//else if (m_nColumns > 1)
				//  m_nColumns--;
				//else
				//  break;
			}
		}
	}
	return szMonth;
}

void CXTPDatePickerControl::AdjustLayout(CRect rcClient)
{
	if (!GetSafeHwnd())
		return;

	if (m_lcidActiveLocale != CXTPCalendarUtils::GetActiveLCID())
	{
		m_lcidActiveLocale = CXTPCalendarUtils::GetActiveLCID();

		InitNames();
	}

	m_dtToday = COleDateTime::GetCurrentTime();

	m_rcControl = rcClient;

	CSize szMonth(0, 0);
	CRect rcMonth(0, 0, 0, 0);

	//get button size
	CSize szButton(CalcButtonSize());

	m_rcGrid.CopyRect(&m_rcControl);

	m_pPaintManager->DrawBorder(0, this, m_rcGrid, FALSE);

	if (m_arrButtons.GetVisibleButtonCount() > 0)
		m_rcGrid.bottom -= szButton.cy;

	if (!m_bAutoSize)
	{
		szMonth.cx = m_rcGrid.Width() / m_nColumns;
		szMonth.cy = m_rcGrid.Height() / m_nRows;
	}
	else
	{
		szMonth = SetGridDimentions(m_rcGrid);

		if (m_bRestrictMode)
		{
			m_nRows = min(m_nDesiredRows, m_nRows);
			m_nColumns = min(m_nDesiredColumns, m_nColumns);
		}

		CSize szGrid (szMonth.cx * m_nColumns, szMonth.cy * m_nRows);

		int nXOffset = max(0, (m_rcGrid.left + m_rcGrid.right - szGrid.cx) / 2);
		int nYOffset = max(0, (m_rcGrid.top + m_rcGrid.bottom - szGrid.cy) / 2);

		m_rcGrid = CRect(CPoint(nXOffset, nYOffset), szGrid);

		ClearMonths();
		CreateMonthArray();

	} // else auto end

	CalcButtonBandRect();
	int nIndex = 0;

	for (int nRow = 0; nRow < m_nRows; nRow++)
	{
		for (int nCol = 0; nCol < m_nColumns; nCol++)
		{
			rcMonth = CRect(CPoint(m_rcGrid.left + nCol * szMonth.cx, m_rcGrid.top + nRow * szMonth.cy), szMonth);
			rcMonth.DeflateRect(1, 1);

			// get next month item
			CXTPDatePickerItemMonth* pMonth = m_arrMonths.GetAt(nIndex);
			nIndex++;
			// adjust internal month layout
			pMonth->AdjustLayout(rcMonth, !m_bAutoSize);
		}
	}

	m_bChanged = TRUE;
}

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

	CXTPClientRect rc(this);

	// Check cached bitmap
	if (!m_bChanged && m_bmpCache.GetSafeHandle() != 0)
	{
		CXTPCompatibleDC memDC(&dc, &m_bmpCache);
		dc.BitBlt(0, 0, rc.right, rc.bottom, &memDC, 0, 0, SRCCOPY);
	}
	else
	{
		CDC memDC;
		memDC.CreateCompatibleDC(&dc);

		m_bmpCache.DeleteObject();
		m_bmpCache.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height());

		CBitmap* pOldBitmap = memDC.SelectObject(&m_bmpCache);

		OnDraw(&memDC);

		if (!IsWindowEnabled())
		{
			XTPImageManager()->DisableBitmap(memDC, rc, XTP_CALENDAR_DISABLED_COLOR_LIGHT, XTP_CALENDAR_DISABLED_COLOR_DARK);
		}

		dc.BitBlt(0, 0, rc.right, rc.bottom, &memDC, 0, 0, SRCCOPY);

		memDC.SelectObject(pOldBitmap);

		// update flag
		m_bChanged = FALSE;
	}
}

LRESULT CXTPDatePickerControl::OnPrintClient(WPARAM wParam, LPARAM /*lParam*/)
{
	CDC* pDC = CDC::FromHandle((HDC)wParam);
	if (pDC)
	{
		OnDraw(pDC);
	}

	return TRUE;
}

void CXTPDatePickerControl::OnDraw(CDC* pDC)
{
	CXTPClientRect rcClient(this);

	// draw background
	m_pPaintManager->DrawBackground(pDC, rcClient);

	// draw all month items in the collection
	int nMonthCount = (int)m_arrMonths.GetSize();
	for (int nIndex = 0; nIndex < nMonthCount; nIndex++)
	{
		// get next month item
		CXTPDatePickerItemMonth* pMonth = m_arrMonths.GetAt(nIndex);
		// draw it
		pMonth->Draw(pDC);
	}

	// draw today/none buttons
	DrawButtons(pDC);

	// draw border
	m_pPaintManager->DrawBorder(pDC, this, rcClient, TRUE);
}

UINT CXTPDatePickerControl::OnGetDlgCode()
{
	return DLGC_WANTARROWS /*| DLGC_WANTTAB | DLGC_WANTALLKEYS*/;
}

void CXTPDatePickerControl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
#ifdef XTP_DATEPICKER_SITENOTIFY_KEY
	if (!XTP_DATEPICKER_SITENOTIFY_KEY(this, TRUE, nChar))
		return;
#endif
	if (nChar == 0)
		return;

	if (nChar == VK_RETURN || nChar == VK_ESCAPE)
	{
		if (m_bIsModal)
		{
			if (ContinueModal()) // skip if EndModalLoop has already called
			{
				EndModalLoop(nChar == VK_RETURN ? IDOK : IDCANCEL);
			}
			return;
		}
	}
	else if (nChar == VK_LEFT || nChar == VK_RIGHT || nChar == VK_UP ||
			nChar == VK_DOWN || nChar == VK_PRIOR || nChar == VK_NEXT ||
			nChar == VK_HOME || nChar == VK_END ||
			nChar == VK_SPACE)
	{
		CXTPSelectionHelper _selector(this);

		BOOL bIsShift = (GetKeyState(VK_SHIFT) < 0);
		BOOL bIsCtrl = (GetKeyState(VK_CONTROL) < 0) || IsMultiSelectionMode();
		XTPDrawHelpers()->KeyToLayout(this, nChar);

		if (nChar == VK_RIGHT)
		{
			_selector.MoveFocus(stepDay, dirNext, bIsShift, bIsCtrl);
		}
		else if (nChar == VK_DOWN)
		{
			_selector.MoveFocus(stepWeek, dirNext, bIsShift, bIsCtrl);
		}
		else if (nChar == VK_NEXT && !bIsCtrl)
		{
			_selector.MoveFocus(stepMonth, dirNext, FALSE, FALSE);
		}
		else if (nChar == VK_NEXT && bIsCtrl)
		{
			_selector.MoveFocus(stepYear, dirNext, FALSE, FALSE);
		}
		//*****
		else if (nChar == VK_LEFT)
		{
			_selector.MoveFocus(stepDay, dirPrev, bIsShift, bIsCtrl);
		}
		else if (nChar == VK_UP)
		{
			_selector.MoveFocus(stepWeek, dirPrev, bIsShift, bIsCtrl);
		}
		else if (nChar == VK_PRIOR && !bIsCtrl)
		{
			_selector.MoveFocus(stepMonth, dirPrev, FALSE, FALSE);
		}
		else if (nChar == VK_PRIOR && bIsCtrl)
		{
			_selector.MoveFocus(stepYear, dirPrev, FALSE, FALSE);
		}
		//***
		else if (nChar == VK_HOME && !bIsCtrl)
		{
			_selector.MoveFocus(stepWeekBE, dirPrev, FALSE, FALSE);
		}
		else if (nChar == VK_HOME && bIsCtrl)
		{
			_selector.MoveFocus(stepMonthBE, dirPrev, FALSE, FALSE);
		}
		//***
		else if (nChar == VK_END && !bIsCtrl)
		{
			_selector.MoveFocus(stepWeekBE, dirNext, FALSE, FALSE);
		}
		else if (nChar == VK_END && bIsCtrl)
		{
			_selector.MoveFocus(stepMonthBE, dirNext, FALSE, FALSE);
		}
		//***
		else if (nChar == VK_SPACE && bIsCtrl)
		{
			_selector.SelUnselFocus();
		}

		//***
		_selector._TmpSaveFocus();
		m_nLockUpdateCount++;

		// fire selection changed
		SendNotification(XTP_NC_DATEPICKERSELECTIONCHANGED);
		SendMessageToParent(XTP_NC_DATEPICKER_SELECTION_CHANGED);
		//***
		_selector._TmpRestoreFocus();
		m_nLockUpdateCount--;

		//-------------------
		EnsureVisibleFocus();

		//--------------
		RedrawControl();
	}

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

void CXTPDatePickerControl::OnLButtonDown(UINT nFlags, CPoint point)
{
	if (m_bIsModal && !m_rcControl.PtInRect(point))
	{
		if (ContinueModal()) // skip if EndModalLoop has already called
		{
			EndModalLoop(IDCANCEL);
		}
		return;
	}
	if (!m_bIsModal)
	{
		SetCapture();
	}
	SetFocus();

	CXTPDatePickerItemMonth* pMonth = HitTest(point);
	if (pMonth)
	{
		pMonth->OnLButtonDown(nFlags, point);
	}

	// process buttons
	m_pButtonCaptured = m_arrButtons.HitTest(point);

	ProcessButtons(point);

	CWnd::OnLButtonDown(nFlags, point);
}

void CXTPDatePickerControl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	CXTPDatePickerItemMonth* pMonth = HitTest(point);
	if (pMonth)
	{
		pMonth->OnLButtonDown(nFlags, point);
	}

	m_pButtonCaptured = m_arrButtons.HitTest(point);
	ProcessButtons(point);

	CWnd::OnLButtonDblClk(nFlags, point);
}

void CXTPDatePickerControl::OnLButtonUp(UINT nFlags, CPoint point)
{
	// kill timer
	if (m_nTimerID != 0)
	{
		KillTimer(m_nTimerID);
		m_nTimerID = 0;
	}

	// logic message processing
	if (m_mouseMode == mouseTrackingHeaderList)
	{
		// reset mouse mode
		m_mouseMode = mouseNothing;

		if (m_pListControl)
		{
			int nDelta = m_pListControl->GetMonthInterval();
			// destroy list control
			m_pListControl->DestroyWindow();
			delete m_pListControl;
			m_pListControl = NULL;
			// scroll on selected months count
			if (nDelta < 0)
				ScrollLeft(-nDelta);
			else if (nDelta > 0)
				ScrollRight(nDelta);
		}
	}
	else
	{
		// forward message to appropriate month
		CXTPDatePickerItemMonth* pMonth = HitTest(point);
		if (pMonth)
		{
			pMonth->OnLButtonUp(nFlags, point);
		}

		// Update selection
		BOOL bSelecting = m_mouseMode == mouseSelecting;
		BOOL bDeselecting = m_mouseMode == mouseDeselecting;
		if (bSelecting || bDeselecting)
		{
			int nDay = (m_dtLastClicked - m_dtFirstClicked).GetDays();
			for (; abs(nDay) >= 0; nDay += nDay > 0 ? -1 : 1)
			{
				COleDateTime dtSelect(m_dtFirstClicked);
				dtSelect += nDay;
				if (bDeselecting)
				{
					m_pSelectedDays->Remove(dtSelect);
				}
				else if (bSelecting && IsSelected(dtSelect))
				{
					m_pSelectedDays->Add(dtSelect);
				}

				if (nDay == 0)
					break;
			}

			// reset mouse mode
			m_mouseMode = mouseNothing;

			// fire selection changed
			SendNotification(XTP_NC_DATEPICKERSELECTIONCHANGED);
			SendMessageToParent(XTP_NC_DATEPICKER_SELECTION_CHANGED);
			_EndModalIfNeed();
		}
	}

	//release resources
	if (!m_bIsModal)
		ReleaseCapture();


	// process buttons
	m_pButtonCaptured = NULL;
	ProcessButtons(point);


	// reset mouse mode
	m_mouseMode = mouseNothing;

	CWnd::OnLButtonUp(nFlags, point);
}

void CXTPDatePickerControl::OnCaptureChanged(CWnd* pWnd)
{
	// kill timer
	if (m_nTimerID != 0)
	{
		KillTimer(m_nTimerID);
		m_nTimerID = 0;
	}

	// logic message processing
	if (m_mouseMode == mouseTrackingHeaderList)
	{
		// reset mouse mode
		m_mouseMode = mouseNothing;

		if (m_pListControl)
		{
			if (m_pListControl->GetSafeHwnd())
				m_pListControl->DestroyWindow();
			delete m_pListControl;
			m_pListControl = NULL;
		}
	}
	m_pButtonCaptured = NULL;

	if (m_bIsModal)
	{
		if (ContinueModal()) // skip if EndModalLoop has already called
		{
			EndModalLoop(IDOK);
		}
	}

	CWnd::OnCaptureChanged(pWnd);
}

void CXTPDatePickerControl::OnCancelMode()
{
	CWnd::OnCancelMode();
}

void CXTPDatePickerControl::OnMouseLeave()
{
	TRACKMOUSEEVENT tre;
	tre.dwFlags = TME_CANCEL;
	tre.hwndTrack = this->m_hWnd;
	tre.dwHoverTime = HOVER_DEFAULT;
	tre.cbSize = sizeof(tre);

	_TrackMouseEvent(&tre);

	ProcessButtons(CPoint(-1, -1));
}


void CXTPDatePickerControl::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_mouseMode == mouseTrackingHeaderList)
	{
		if (m_pListControl)
		{
			m_pListControl->OnMouseMove(nFlags, point);
		}
	}
	else
	{
		// forward message to appropriate month
		CXTPDatePickerItemMonth* pMonth = HitTest(point);
		if (pMonth)
			pMonth->OnMouseMove(nFlags, point);
	}

	ProcessButtons(point);


	// standard processing
	CWnd::OnMouseMove(nFlags, point);

	TRACKMOUSEEVENT tre;
	tre.dwFlags = TME_LEAVE;
	tre.hwndTrack = this->m_hWnd;
	tre.dwHoverTime = HOVER_DEFAULT;
	tre.cbSize = sizeof(tre);

	_TrackMouseEvent(&tre);

	// fire mouse move
	DWORD dwPoint = MAKELONG(point.x, point.y);
	SendNotification(XTP_NC_DATEPICKERMOUSEMOVE, dwPoint);
}

BOOL CXTPDatePickerControl::RegisterWindowClass(HINSTANCE hInstance /*= NULL*/)
{
	WNDCLASS wndcls;
	if (hInstance == NULL) hInstance = AfxGetInstanceHandle();

	if (!(::GetClassInfo(hInstance, XTP_DATEPICKERCTRL_CLASSNAME, &wndcls)))
	{
		// otherwise we need to register a new class
		wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.lpfnWndProc = ::DefWindowProc;
		wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
		wndcls.hInstance = hInstance;
		wndcls.hIcon = NULL;
		wndcls.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
		wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
		wndcls.lpszMenuName = NULL;
		wndcls.lpszClassName = XTP_DATEPICKERCTRL_CLASSNAME;

		if (!AfxRegisterClass(&wndcls))
		{
			AfxThrowResourceException();
			return FALSE;
		}
	}

	return TRUE;
}

BOOL CXTPDatePickerControl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
{
	if (!CWnd::Create(XTP_DATEPICKERCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID, pContext))
		return FALSE;

	return TRUE;
}

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

	CXTPClientRect rc(this);

	CRect rcClient(0, 0, 0, 0);
	GetClientRect(&rcClient);

	AdjustLayout(rc);
}

void CXTPDatePickerControl::ClearMonths()
{
	// cleanup old month array
	int nOldMonthCount = (int)m_arrMonths.GetSize();
	for (int nMonth = 0; nMonth < nOldMonthCount; nMonth++)
	{
		CXTPDatePickerItemMonth* pMonth = m_arrMonths.GetAt(nMonth);
		pMonth->InternalRelease();
	}
	m_arrMonths.RemoveAll();
}

void CXTPDatePickerControl::CreateMonthArray()
{
	CString s; s = m_dtFirstMonth.Format();
	COleDateTime dtNextMonth(m_dtFirstMonth);
	for (int nRow = 0; nRow < m_nRows; nRow++)
	{
		for (int nCol = 0; nCol < m_nColumns; nCol++)
		{
			//int nIndex = nRow * m_nRows + nCol;
			// create next month item
			CXTPDatePickerItemMonth* pMonth = new CXTPDatePickerItemMonth(this, dtNextMonth, nRow, nCol);

			// add it to the array
			m_arrMonths.Add(pMonth);

			// go to the next month
			if (dtNextMonth.GetMonth() < 12)
				dtNextMonth.SetDate(dtNextMonth.GetYear(), dtNextMonth.GetMonth() + 1, 1);
			else
				dtNextMonth.SetDate(dtNextMonth.GetYear() + 1, 1, 1);
		}
	}

	// set first month and last month defaults
	int nMonthCount = (int)m_arrMonths.GetSize();
	if (nMonthCount > 0)
	{
		CXTPDatePickerItemMonth* pFirstMonth = m_arrMonths.GetAt(0);
		CXTPDatePickerItemMonth* pLastMonth = m_arrMonths.GetAt(nMonthCount - 1);
		CXTPDatePickerItemMonth* pLastMonthTopRow = m_arrMonths.GetAt(m_nColumns - 1);

		// by default set show previous days for first month and show following days for last month
		pFirstMonth->SetShowDaysBefore(m_bShowNonMonthDays);
		pLastMonth->SetShowDaysAfter(m_bShowNonMonthDays);


		// set triangles showing
		BOOL bScrollLeft = TRUE;
		if (m_dtMinRange.GetStatus() == COleDateTime::valid
			&& pFirstMonth->GetMonth() <= m_dtMinRange)
		{
			bScrollLeft = FALSE;
		}

		pFirstMonth->SetShowScrolling(bScrollLeft, pFirstMonth->GetShowRightScroll());

		BOOL bScrollRight = TRUE;
		if (m_dtMaxRange.GetStatus() == COleDateTime::valid
			&& (DATE)pLastMonth->GetMonth() + 30.0 >= (DATE)m_dtMaxRange)
		{
			bScrollRight = FALSE;
		}

		pLastMonthTopRow->SetShowScrolling(pLastMonthTopRow->GetShowLeftScroll(), bScrollRight);
	}
}

void CXTPDatePickerControl::Populate()
{
	// cleanup old month array
	ClearMonths();

	// add all month items in the grid to the array
	// and set first month and last month defaults
	CreateMonthArray();

	// redraw control image
	if (!m_rcControl.IsRectEmpty())
		AdjustLayout(m_rcControl);
	_RedrawControl(FALSE);
///////////////////////////////
	ClearFocus();
//  m_pSelectedDays->Clear();
///////////////////////////////
	SendNotification(XTP_NC_DATEPICKERMONTHCHANGED);
	SendMessageToParent(XTP_NC_DATEPICKERMONTHCHANGED);
}

void CXTPDatePickerControl::GetDayMetrics(COleDateTime& dtDay, XTP_DAYITEM_METRICS* pDayMetics)
{
	WPARAM wprmDay = (XTP_DATE_VALUE)(DATE)dtDay;
	SendNotification(XTP_NC_DATEPICKERGETDAYMETRICS, wprmDay, (LPARAM)pDayMetics);

	if (m_pfnCallback)
	{
		try
		{
			m_pfnCallback(this, dtDay, pDayMetics, m_pCallbackParam);
		}
		catch(...)
		{
			ASSERT(FALSE);
		}
	}
}

void CXTPDatePickerControl::SetGridSize(int nRows, int nCols, BOOL bRestrictMode)
{
	m_nDesiredRows = nRows;
	m_nDesiredColumns = nCols;

	m_nRows = nRows;
	m_nColumns = nCols;

	m_bRestrictMode = bRestrictMode;

	Populate();
}

void CXTPDatePickerControl::SetHighlightToday(BOOL bValue)
{
	m_bHighlightToday = bValue;
	_RedrawControl(FALSE);
}

void CXTPDatePickerControl::SetShowWeekNumbers(BOOL bValue)
{
	m_bShowWeekNumbers = bValue;
	AdjustLayout(m_rcControl);
	_RedrawControl(FALSE);
}

CSize CXTPDatePickerControl::CalcButtonSize() const
{
	// get button size
	CWindowDC dc(GetDesktopWindow());
	CXTPFontDC fnt(&dc, m_pPaintManager->GetButtonFont());

	CSize szButton(0, 0);

	for (int i = 0; i < GetButtonCount(); i++)
	{
		CSize sz = dc.GetTextExtent(GetButton(i)->GetCaption());

		szButton.cx = max(szButton.cx, sz.cx + 12);
		szButton.cy = max(szButton.cy, sz.cy + 6);
	}

	return szButton;
}

void CXTPDatePickerControl::SetButtonRect()
{
	CSize szButton(CalcButtonSize());

	int nGap = 10;

	int nButtonLen = szButton.cx + nGap;
	int nVisibleCount = m_arrButtons.GetVisibleButtonCount();

	int nLeft = m_rcGrid.CenterPoint().x - (nButtonLen * nVisibleCount - nGap)/2;

	for (int i = 0; i < GetButtonCount(); i++)
	{
		CXTPDatePickerButton* pButton = GetButton(i);
		if (!pButton->m_bVisible) continue;

		pButton->m_rcButton = CRect(CPoint(nLeft, m_rcGrid.bottom + 1), szButton);
		nLeft += nButtonLen;
	}
}

void CXTPDatePickerControl::CalcButtonBandRect()
{
	SetButtonRect();
}

void CXTPDatePickerControl::DrawButtons(CDC* pDC)
{
	for (int i = 0; i < GetButtonCount(); i++)
	{
		CXTPDatePickerButton* pButton = GetButton(i);
		if (pButton->m_bVisible)
			m_pPaintManager->DrawButton(pDC, pButton->m_rcButton, pButton->GetCaption(), pButton->m_bPressed, pButton->m_bHighlight);
	}
}

CXTPDatePickerItemMonth* CXTPDatePickerControl::HitTest(CPoint ptMouse) const
{
	// enumerate all month items in the collection
	int nMonthCount = (int)m_arrMonths.GetSize();
	for (int nIndex = 0; nIndex < nMonthCount; nIndex++)
	{
		CXTPDatePickerItemMonth* pMonth = m_arrMonths.GetAt(nIndex);
		if (pMonth && pMonth->m_rcMonth.PtInRect(ptMouse))
			return pMonth;
	}
	return NULL;
}

void CXTPDatePickerControl::ScrollLeft(int nMonthCount)
{
	int nYear = m_dtFirstMonth.GetYear();
	int nMonth = m_dtFirstMonth.GetMonth();
	if (nYear < 100 || (nYear == 100 && nMonth <= 2))
		return;
	int nYearNew = nYear - nMonthCount / 12;
	int nMonthNew = nMonth - nMonthCount % 12;
	if (nMonthNew < 1)
	{
		nMonthNew += 12;
		nYearNew--;
	}
	ASSERT(nMonthNew >= 1 && nMonthNew <= 12);

	if (m_dtMinRange.GetStatus() == COleDateTime::valid)
	{
		if (nYearNew < m_dtMinRange.GetYear())
		{
			nYearNew = m_dtMinRange.GetYear();
			nMonthNew = m_dtMinRange.GetMonth();
		}

		if (m_dtMinRange.GetYear() == nYearNew)
		{
			if (nMonthNew < m_dtMinRange.GetMonth())
				nMonthNew = m_dtMinRange.GetMonth();
		}
	}

	m_dtFirstMonth.SetDate(nYearNew, nMonthNew, 1);

	Populate();
}

void CXTPDatePickerControl::ScrollRight(int nMonthCount)
{
	int nYear = m_dtFirstMonth.GetYear();
	int nMonth = m_dtFirstMonth.GetMonth();

	int nYearNew = nYear + nMonthCount / 12;
	int nMonthNew = nMonth + nMonthCount % 12;
	if (nMonthNew > 12)
	{
		nMonthNew -= 12;
		nYearNew++;
	}

	if (m_dtMaxRange.GetStatus() == COleDateTime::valid)
	{
		int nLeftYear = m_dtMaxRange.GetYear();
		int nMaxMonth = m_dtMaxRange.GetMonth();
		int nLeftMonth = nMaxMonth - m_nRows * m_nColumns + 1;
		if (nLeftMonth < 1)
		{
			nLeftYear -= (-nLeftMonth) / 12 + 1;
			nLeftMonth = -(((-nLeftMonth) % 12) - 12);
		}

		if (nYearNew > nLeftYear)
		{
			nYearNew = nLeftYear;
			nMonthNew = nLeftMonth;
		}

		if (nLeftYear == nYearNew)
		{
			if (nMonthNew > nLeftMonth)
				nMonthNew = nLeftMonth;
		}
	}

	m_dtFirstMonth.SetDate(nYearNew, nMonthNew, 1);

	ScrollLeft(0); // adjust also scrolling left before calling Populate();
}


void CXTPDatePickerControl::OnTimeChange()
{
	CWnd::OnTimeChange();
	m_dtToday = CXTPCalendarUtils::GetCurrentTime();
	_RedrawControl(FALSE);
}

void CXTPDatePickerControl::OnTimer(UINT_PTR nIDEvent)
{
	if (nIDEvent == XTP_DATEPICKER_TIMERID)
	{
		if (m_mouseMode == mouseScrollingLeft)
			ScrollLeft(GetMonthDelta());
		else if (m_mouseMode == mouseScrollingRight)
			ScrollRight(GetMonthDelta());

		if ((m_mouseMode == mouseTrackingHeaderList) &&
			(m_pListControl != NULL))
		{
			m_pListControl->OnTimer(nIDEvent);
		}
	}

	CWnd::OnTimer(nIDEvent);
}

void CXTPDatePickerControl::ShowListHeader(CRect rcHeader, COleDateTime dtMonth)
{
	// make sure that the list is not already created
	ASSERT(!m_pListControl);
	if (m_pListControl)
	{
		m_pListControl->DestroyWindow();
		delete m_pListControl;
		m_pListControl = NULL;
	}

	if (!m_bIsModal)
		SetCapture();

	// create list
	m_pListControl = new CXTPDatePickerList(this, dtMonth);
	if (!m_pListControl)
		throw (new CMemoryException());

	// create control
	m_pListControl->Create(rcHeader);

	m_nTimerID = (UINT)SetTimer(XTP_DATEPICKER_TIMERID, 2 * XTP_DATEPICKER_TIMER_INTERVAL / 3, NULL);

	m_mouseMode = mouseTrackingHeaderList;
}

void CXTPDatePickerControl::Select(const COleDateTime& dtDay)
{
	ClearFocus();
	CString s = dtDay.Format();
	if (m_nMaxSelectionDays == XTP_SELECTION_INFINITE ||
		m_nMaxSelectionDays > m_pSelectedDays->GetSelectedDaysCount())
	{
		m_pSelectedDays->Add(dtDay);
	}
}

void CXTPDatePickerControl::Deselect(const COleDateTime& dtDay)
{
	ClearFocus();
	m_pSelectedDays->Remove(dtDay);
}

BOOL CXTPDatePickerControl::IsSelected(const COleDateTime& dtDay) const
{
	BOOL bSelected = m_pSelectedDays->Contains(dtDay);
	//CString s = dtDay.Format();

	if (m_mouseMode == mouseSelecting ||
		m_mouseMode == mouseDeselecting)
	{
		BOOL bBetween = FALSE;
		if (m_dtFirstClicked <= m_dtLastClicked &&
			m_dtFirstClicked <= dtDay && m_dtLastClicked >= dtDay)
		{
			bBetween = TRUE;
		}
		if (m_dtFirstClicked >= m_dtLastClicked &&
			m_dtFirstClicked >= dtDay && m_dtLastClicked <= dtDay)
		{
			bBetween = TRUE;
		}
		if (bBetween && m_mouseMode == mouseSelecting)
		{
			COleDateTimeSpan spDays = dtDay - m_dtFirstClicked;
			int nDays = abs(spDays.GetDays());
			if (m_nMaxSelectionDays == XTP_SELECTION_INFINITE ||
				m_pSelectedDays->GetSelectedDaysCount() + nDays < m_nMaxSelectionDays)
			{
				bSelected = TRUE;
			}
		}
		if (bBetween && m_mouseMode == mouseDeselecting)
			bSelected = FALSE;
	}

	return bSelected;
}

BOOL CXTPDatePickerControl::IsFocused(const COleDateTime& dtDay) const
{
	if (m_dtFocused.GetStatus() != COleDateTime::valid)
	{
		return FALSE;
	}

	return CXTPCalendarUtils::IsEqual(CXTPCalendarUtils::ResetTime(m_dtFocused),
		CXTPCalendarUtils::ResetTime(dtDay));
}

void CXTPDatePickerControl::ClearFocus()
{
	CXTPSelectionHelper _selector(this);
	_selector.RemoveFocus();
}

void CXTPDatePickerControl::OnButtonClick(UINT nID)
{
	XTP_NC_DATEPICKER_BUTTON nm;
	nm.nID = nID;

	SendNotification(XTP_NC_DATEPICKERBUTTONCLICKED, nID);
	SendMessageToParent(XTP_NC_DATEPICKER_BUTTON_CLICK, (NMHDR*)&nm);

	switch (nID)
	{
		case XTP_IDS_DATEPICKER_TODAY:
		{
			// behavior: change selection to today
			ClearFocus();
			m_pSelectedDays->Clear();
			COleDateTime dt;
			GetToday(dt);
			EnsureVisible(dt);
			Select(dt);
			SendNotification(XTP_NC_DATEPICKERSELECTIONCHANGED);
			SendMessageToParent(XTP_NC_DATEPICKER_SELECTION_CHANGED);
			_EndModalIfNeed();
		}
		break;

		case XTP_IDS_DATEPICKER_NONE:
		{
			// behavior: change selection
			ClearFocus();
			m_pSelectedDays->Clear();
			SendNotification(XTP_NC_DATEPICKERSELECTIONCHANGED);
			SendMessageToParent(XTP_NC_DATEPICKER_SELECTION_CHANGED);
			_EndModalIfNeed();
		}
		break;
	}
}

void CXTPDatePickerControl::ProcessButtons(CPoint point)
{
	for (int i = 0; i < GetButtonCount(); i++)
	{
		CXTPDatePickerButton* pButton = GetButton(i);
		BOOL bHighlight = pButton->m_rcButton.PtInRect(point);

		if (pButton == m_pButtonCaptured)
		{
			if (bHighlight != pButton->m_bPressed)
			{
				pButton->m_bPressed = bHighlight;
				_RedrawControl(FALSE);
			}
		}
		else if (pButton->m_bPressed)
		{
			pButton->m_bPressed = FALSE;
			if (bHighlight)
				OnButtonClick(pButton->m_nID);
			_RedrawControl(FALSE);
		}

		bHighlight = pButton->m_bHighlight;

		pButton->m_bHighlight = ((m_pButtonCaptured == pButton) ||
			(!m_pButtonCaptured && pButton->m_rcButton.PtInRect(point)));

		if (pButton->m_bHighlight != bHighlight)
		{
			_RedrawControl(FALSE);
		}
	}
}


LRESULT CXTPDatePickerControl::SendMessageToParent(int nMessage, NMHDR* pNMHDR)
{
	if (!IsWindow(m_hWnd))
		return 0;

	NMHDR nmhdr;
	if (pNMHDR == NULL)
		pNMHDR = &nmhdr;

	pNMHDR->hwndFrom = GetSafeHwnd();
	pNMHDR->idFrom = GetDlgCtrlID();
	pNMHDR->code = nMessage;

	CWnd *pOwner = GetOwner();
	LRESULT res = 0;
	if (pOwner && IsWindow(pOwner->m_hWnd))
		res = pOwner->SendMessage(WM_NOTIFY, pNMHDR->idFrom, (LPARAM)pNMHDR);

	return res;
}

void CXTPDatePickerControl::_EndModalIfNeed()
{
	BOOL bSingleDaySel = (GetKeyState(VK_CONTROL) < 0) || IsMultiSelectionMode();
	if (m_bIsModal && !bSingleDaySel)
	{
		if (ContinueModal()) // skip if EndModalLoop has already called
			EndModalLoop(IDOK);
	}
}

void CXTPDatePickerControl::SetButtonsVisible(BOOL bShowToday, BOOL bShowNone)
{
	m_arrButtons.Find(XTP_IDS_DATEPICKER_TODAY)->m_bVisible = bShowToday;
	m_arrButtons.Find(XTP_IDS_DATEPICKER_NONE)->m_bVisible = bShowNone;

	if (!m_rcControl.IsRectEmpty())
	{
		AdjustLayout(m_rcControl);
		_RedrawControl(FALSE);
	}
}

void CXTPDatePickerControl::EnsureVisible(const COleDateTime& dtDate)
{
	int nYear = dtDate.GetYear();
	int nMonth = dtDate.GetMonth();
	// enumerate all month items in the collection
	int nMonthCount = (int)m_arrMonths.GetSize();
	for (int nIndex = 0; nIndex < nMonthCount; nIndex++)
	{
		CXTPDatePickerItemMonth* pMonth = m_arrMonths.GetAt(nIndex);
		if (pMonth)
		{
			COleDateTime dtm = pMonth->GetMonth();
			int iM = dtm.GetMonth();
			int iY = dtm.GetYear();
			if (iM == nMonth &&  iY == nYear)
				return;
		}
	}
	m_dtFirstMonth.SetDate(nYear, nMonth, 1);
	Populate();
}

AFX_INLINE int GetEqualLeftSymbols(const CString& str1, const CString& str2)
{
	int nCount = min(str1.GetLength(), str2.GetLength());

	for (int i = 0; i < nCount; i++)
	{
		if (str1.GetAt(i) != str2.GetAt(i))
			return i;
	}
	return nCount;
}

#ifndef LOCALE_SYEARMONTH
#define LOCALE_SYEARMONTH 0x00001006
#endif

void CXTPDatePickerControl::InitNames()
{
	// initialize month names
	for (int nMonth = 0; nMonth < 12; nMonth++)
	{
		CString strMonth = CXTPCalendarUtils::GetLocaleString(LOCALE_SMONTHNAME1 + nMonth, 255) ;

		m_arMonthNames[nMonth] = strMonth;
	}

	int nEqualLeftSymbols = 255, nDay;

	// initialize day names
	for (nDay = 0; nDay < 7; nDay++)
	{
		CString strDayName = CXTPCalendarUtils::GetLocaleString(LOCALE_SABBREVDAYNAME1 + nDay, 255);

		int nOleDayOfWeek = (nDay + 1) % 7;
		m_arDayOfWeekNames[nOleDayOfWeek] = strDayName;

		if (nDay > 0 && nEqualLeftSymbols > 0)
		{
			nEqualLeftSymbols = min(nEqualLeftSymbols, GetEqualLeftSymbols(strDayName, m_arDayOfWeekNames[1]));
		}
	}

	// If first symbols equal, remove them.
	if (nEqualLeftSymbols > 0 && !m_bRightToLeft)
	{
		for (nDay = 0; nDay < 7; nDay++)
			m_arDayOfWeekNames[nDay].Delete(0, nEqualLeftSymbols);
	}
//RTL case fix
	if (m_bRightToLeft)
	{
		for (nDay = 0; nDay < 7; nDay++)
		{
			if (m_arDayOfWeekNames[nDay].Find(_T(" ")) > -1)
				m_arDayOfWeekNames[nDay] = m_arDayOfWeekNames[nDay].Right(1);
			else
				m_arDayOfWeekNames[nDay] = m_arDayOfWeekNames[nDay].Left(1);

			//if (nDay == 6) //Hebrew
			//  m_arDayOfWeekNames[nDay] = m_arDayOfWeekNames[nDay].Left(1);
			//else
			//  m_arDayOfWeekNames[nDay] = m_arDayOfWeekNames[nDay].Right(1);
		}
	}


	m_strYearMonthFormat.Empty();

	if (XTPSystemVersion()->IsWin2KOrGreater())
	{
		m_strYearMonthFormat = CXTPCalendarUtils::GetLocaleString(LOCALE_SYEARMONTH, 256);
	}
}

void CXTPDatePickerControl::SetShowNonMonthDays(BOOL bShow)
{
	if (bShow != m_bShowNonMonthDays)
	{
		m_bShowNonMonthDays = bShow;
		// set first month and last month defaults
		int nMonthCount = (int)m_arrMonths.GetSize();
		if (nMonthCount > 0)
		{
			CXTPDatePickerItemMonth* pFirstMonth = m_arrMonths.GetAt(0);
			CXTPDatePickerItemMonth* pLastMonth = m_arrMonths.GetAt(nMonthCount - 1);
			// by default set show previous days for first month and show following days for last month
			pFirstMonth->SetShowDaysBefore(bShow);
			pLastMonth->SetShowDaysAfter(bShow);
		}
		_RedrawControl(FALSE);
	}
}

void CXTPDatePickerControl::AllowNoncontinuousSelection(BOOL bAllow /*= TRUE*/)
{
	m_bAllowNoncontinuousSelection = bAllow;
	if (!m_bAllowNoncontinuousSelection && m_pSelectedDays->GetSelectedBlocksCount() > 1)
	{
		m_pSelectedDays->Clear();
		_RedrawControl(FALSE);
	}
}


void CXTPDatePickerControl::SetMaxSelCount(int nMax)
{
	m_nMaxSelectionDays = nMax;
	int nCurrentSelectedDays = m_pSelectedDays->GetSelectedDaysCount();

	// clear extra days
	if (nCurrentSelectedDays > m_nMaxSelectionDays)
	{
		m_pSelectedDays->Clear();
		_RedrawControl(FALSE);
	}
}

BOOL CXTPDatePickerControl::GetSelRange(COleDateTime& refMinRange, COleDateTime& refMaxRange) const
{
	return m_pSelectedDays->GetMinMaxRange(refMinRange, refMaxRange);
}

BOOL CXTPDatePickerControl::SetSelRange(const COleDateTime& dtMinRange, const COleDateTime& dtMaxRange)
{
	ClearFocus();
	m_pSelectedDays->SelectRange(dtMinRange, dtMaxRange);
	m_dtFirstClicked = dtMinRange;
	m_dtLastClicked = dtMaxRange;

	return TRUE;
}

BOOL CXTPDatePickerControl::SizeMinReq(BOOL bRepaint /* = TRUE */)
{
	CRect rect;
	BOOL bRetVal = FALSE;
	if (GetMinReqRect(rect))
	{
		DWORD dwFlags = SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOMOVE | SWP_NOACTIVATE;
		if (!bRepaint)
			dwFlags |= SWP_NOREDRAW;
		SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), dwFlags);
		bRetVal = TRUE;
	}

	return bRetVal;
}

BOOL CXTPDatePickerControl::GetMinReqRect(RECT* pRect) const
{
	return GetMinReqRect(pRect, 1, 1);
}


BOOL CXTPDatePickerControl::GetMinReqRect(RECT* pRect, int nRows, int nCols) const
{
	CWindowDC dc(GetDesktopWindow());
	CSize szMonth(m_pPaintManager->CalcMonthRect(&dc));

	pRect->left = pRect->top = 0;
	pRect->right = szMonth.cx * nCols;
	pRect->bottom = szMonth.cy * nRows;

	CRect rcBorder(0, 0, 0, 0);
	m_pPaintManager->DrawBorder(0, this, rcBorder, FALSE);

	pRect->right += rcBorder.left - rcBorder.right;
	pRect->bottom += rcBorder.top - rcBorder.bottom;

	if (m_arrButtons.GetVisibleButtonCount() > 0)
	{
		CSize szButton(CalcButtonSize());
		pRect->bottom += szButton.cy;
	}

	return TRUE;
}

BOOL CXTPDatePickerControl::GetCurSel(COleDateTime& refDateTime) const
{
	return m_pSelectedDays->GetMinMaxRange(refDateTime, refDateTime);
}

BOOL CXTPDatePickerControl::SetCurSel(const COleDateTime& refDateTime)
{
	return SetSelRange(refDateTime, refDateTime);
}

void CXTPDatePickerControl::SetToday(const COleDateTime& refDateTime)
{
	m_dtToday = refDateTime;
	_RedrawControl(FALSE);
}

DWORD CXTPDatePickerControl::GetRange(COleDateTime* pMinRange, COleDateTime* pMaxRange) const
{
	DWORD dwRes = 0;
	if (pMinRange && m_dtMinRange.GetStatus() == COleDateTime::valid)
	{
		*pMinRange = m_dtMinRange;
		dwRes |= GDTR_MIN;
	}
	if (pMaxRange && m_dtMaxRange.GetStatus() == COleDateTime::valid)
	{
		*pMaxRange = m_dtMaxRange;
		dwRes |= GDTR_MAX;
	}
	return dwRes;
}

void CXTPDatePickerControl::SetDesiredVisibleAndFullRange(
	COleDateTime FirstVisibleDay, COleDateTime LastVisibleDay,
	COleDateTime FirstDay, COleDateTime LastDay)
{
	//GetMonthRange(FirstVisibleDay, LastVisibleDay, GMR_VISIBLE);//GMR_DAYSTATE or GMR_VISIBLE
	SetRange(&FirstVisibleDay, &LastVisibleDay);

	m_dtMinRange = FirstDay;
	m_dtMaxRange = LastDay;
}

BOOL CXTPDatePickerControl::SetRange(const COleDateTime* pMinRange, const COleDateTime* pMaxRange)
{
	if (pMinRange)
	{
		m_dtMinRange = *pMinRange;
		ScrollLeft(0);
	}
	if (pMaxRange)
	{
		m_dtMaxRange = *pMaxRange;
		ScrollRight(0);
	}
	return TRUE;
}

int CXTPDatePickerControl::GetMonthRange(COleDateTime& refMinRange, COleDateTime& refMaxRange, DWORD dwFlags) const
{
	refMinRange = m_dtFirstMonth;
	refMaxRange = m_dtFirstMonth;
	int nMonthCount = m_nRows * m_nColumns;
	ShiftDate(refMaxRange, nMonthCount);

	//if (GMR_DAYSTATE == dwFlags)
		// do nothing

	if (GMR_VISIBLE == dwFlags)
	{
		nMonthCount = (int)m_arrMonths.GetSize();
		if (nMonthCount > 0)
		{
			CXTPDatePickerItemMonth* pLastMonth = m_arrMonths.GetAt(nMonthCount - 1);
			if (pLastMonth)
			{
				CXTPDatePickerItemDay* pLastDay = pLastMonth->m_arrDays.GetAt(XTP_MAX_WEEKS * XTP_WEEK_DAYS - 1);
				if (pLastDay->GetDate().GetMonth() != refMaxRange.GetMonth())
				{
					ShiftDate(refMaxRange, 1);
					nMonthCount++;
				}
			}

			CXTPDatePickerItemMonth* pFirstMonth = m_arrMonths.GetAt(0);
			if (pFirstMonth)
			{
				CXTPDatePickerItemDay* pFirstDay = pFirstMonth->m_arrDays.GetAt(0);
				if (pFirstDay->GetDate().GetMonth() != refMinRange.GetMonth())
				{
					ShiftDate(refMinRange, -1);
					nMonthCount++;
				}
			}
		}
	}
	return nMonthCount;
}

BOOL CXTPDatePickerControl::ShiftDate(COleDateTime &refDate, int nMonthCount)
{
	int nYearNew = refDate.GetYear();
	int nMonthNew = refDate.GetMonth();
	int nInc = abs(nMonthCount) / nMonthCount;
	for (int nItem = 0; nItem < abs(nMonthCount); nItem++)
	{
		nMonthNew += nInc;
		if (nMonthNew < 1)
		{
			nMonthNew = 12;
			nYearNew--;
		}
		if (nMonthNew > 12)
		{
			nMonthNew = 1;
			nYearNew++;
		}
	}
	return 0 == refDate.SetDate(nYearNew, nMonthNew, 1);
}

void XTPGetAsSystemTime(const COleDateTime& dateTime, SYSTEMTIME& st)
{
#if _MSC_VER < 1200
	UNREFERENCED_PARAMETER(dateTime);
	UNREFERENCED_PARAMETER(st);
	ASSERT(FALSE);
#else
	dateTime.GetAsSystemTime(st);
#endif
}

DWORD CXTPDatePickerControl::HitTest(PMCHITTESTINFO pMCHitTest) const
{
	// check structure
	if (!pMCHitTest)
		return 0;

	ASSERT(sizeof(MCHITTESTINFO) == pMCHitTest->cbSize);

	// default result
	pMCHitTest->uHit = MCHT_NOWHERE;

	// start checking
	CPoint ptHit(pMCHitTest->pt);

	CXTPDatePickerItemMonth* pHitMonth = HitTest(ptHit);
	if (pHitMonth)
	{
		CXTPDatePickerItemDay* pHitDay = pHitMonth->HitTest(ptHit);
		if (pHitDay)
		{
			// MCHT_CALENDARDATE
			pMCHitTest->uHit = MCHT_CALENDARDATE;
			XTPGetAsSystemTime(pHitDay->GetDate(), pMCHitTest->st);

			// MCHT_CALENDARDATEPREV
			if (pHitMonth->GetShowDaysBefore() &&
				pHitMonth->GetMonth().GetMonth() < pHitDay->GetDate().GetMonth())
			{
				pMCHitTest->uHit = MCHT_CALENDARDATEPREV;
			}
			else
			// MCHT_CALENDARDATENEXT
			if (pHitMonth->GetShowDaysAfter() &&
				pHitMonth->GetMonth().GetMonth() > pHitDay->GetDate().GetMonth())
			{
				pMCHitTest->uHit = MCHT_CALENDARDATENEXT;
			}
		}
		else
		{
			XTPGetAsSystemTime(pHitMonth->GetMonth(), pMCHitTest->st);

			// MCHT_CALENDARDAY
			if (pHitMonth->m_rcDaysOfWeek.PtInRect(ptHit))
			{
				pMCHitTest->uHit = MCHT_CALENDARDAY;
				// The SYSTEMTIME structure at lpMCHitTest>st is set to the corresponding date in the top row.
				CPoint ptTopRow(ptHit.x, pHitMonth->m_rcDaysOfWeek.bottom + 1);
				CXTPDatePickerItemDay* pDay = pHitMonth->HitTest(ptTopRow);
				if (pDay)
				{
					XTPGetAsSystemTime(pDay->GetDate(), pMCHitTest->st);
				}
			}
			else
			// MCHT_CALENDARWEEKNUM
			if (pHitMonth->m_rcWeekNumbers.PtInRect(ptHit))
			{
				pMCHitTest->uHit = MCHT_CALENDARWEEKNUM;
				// The SYSTEMTIME structure at lpMCHitTest>st is set to the corresponding date in the leftmost column
				CPoint ptLeftRow(pHitMonth->m_rcWeekNumbers.right + 1, ptHit.y);
				CXTPDatePickerItemDay* pDay = pHitMonth->HitTest(ptLeftRow);
				if (pDay)
				{
					XTPGetAsSystemTime(pDay->GetDate(), pMCHitTest->st);
				}
			}
			else
			// MCHT_TITLEMONTH
			if (pHitMonth->m_rcHeader.PtInRect(ptHit))
			{
				pMCHitTest->uHit = MCHT_TITLEMONTH;
			}
			else
			// MCHT_TITLEBTNNEXT
			if (pHitMonth->GetShowRightScroll() &&
				pHitMonth->m_rcRightScroll.PtInRect(ptHit))
			{
				pMCHitTest->uHit = MCHT_TITLEBTNNEXT;
			}
			else
			// MCHT_TITLEBTNPREV
			if (pHitMonth->GetShowLeftScroll() &&
				pHitMonth->m_rcLeftScroll.PtInRect(ptHit))
			{
				pMCHitTest->uHit = MCHT_TITLEBTNPREV;
			}
		}
	}
	else
	{
		// MCHT_CALENDARBK
		pMCHitTest->uHit = MCHT_CALENDARBK;
	}

	return pMCHitTest->uHit;
}

void CXTPDatePickerControl::SetBorderStyle(XTPDatePickerBorderStyle borderStyle)
{
	m_borderStyle = borderStyle;
	AdjustLayout(m_rcControl);
	_RedrawControl(FALSE);
}

void CXTPDatePickerControl::SetAutoSize(BOOL bAuto)
{
	m_bAutoSize = bAuto;
	AdjustLayout(m_rcControl);
	_RedrawControl(FALSE);
}

void CXTPDatePickerControl::OnSysColorChange()
{
	m_pPaintManager->RefreshMetrics();
	AdjustLayout(m_rcControl);
	_RedrawControl(FALSE);
}

BOOL CXTPDatePickerControl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	if (nHitTest == HTCLIENT)
	{
		CPoint point;
		::GetCursorPos(&point);
		ScreenToClient(&point);
		CXTPDatePickerItemMonth* pMonth = HitTest(point);
		if (pMonth)
		{
			BOOL bRet = pMonth->OnSetCursor(point);
			if (bRet)
				return bRet;
		}
	}
	return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

void CXTPDatePickerControl::OnSetFocus(CWnd* pOldWnd)
{
	CWnd::OnSetFocus(pOldWnd);

	RedrawControl();

#ifdef XTP_DATEPICKER_SITENOTIFY_ONFOCUS
	XTP_DATEPICKER_SITENOTIFY_ONFOCUS(this, this, TRUE)
#endif
}

void CXTPDatePickerControl::OnKillFocus (CWnd* pNewWnd)
{
	CWnd::OnKillFocus(pNewWnd);

	RedrawControl();

#ifdef XTP_DATEPICKER_SITENOTIFY_ONFOCUS
	XTP_DATEPICKER_SITENOTIFY_ONFOCUS(this, this, FALSE)
#endif
}

void CXTPDatePickerControl::OnFinalRelease()
{
	CWnd::OnFinalRelease();

	if (m_bDeleteOnFinalRelease)
	{
		CCmdTarget::OnFinalRelease();
	}
}

void CXTPDatePickerControl::OnEnable(BOOL bEnable)
{
	UNREFERENCED_PARAMETER(bEnable);

	_RedrawControl(FALSE);
}

BOOL CXTPDatePickerControl::GetVisibleRange(COleDateTime& refFirstVisibleDay,
											COleDateTime& refLastVisibleDay) const
{
	int nCount = (int)m_arrMonths.GetSize();
	if (!nCount || !m_arrMonths[0] || !m_arrMonths[nCount-1])
	{
		return FALSE;
	}
	if (!m_arrMonths[0]->GetDay(0))
	{
		ASSERT(FALSE);
		return FALSE;
	}
	refFirstVisibleDay = m_arrMonths[0]->GetDay(0)->GetDate();

	if (!m_arrMonths[0]->GetShowDaysBefore())
	{
		int nFirstMnDCount = m_arrMonths[0]->GetDayCount();
		for (int i = 1; i < nFirstMnDCount; i++)
		{
			if (refFirstVisibleDay.GetMonth() == m_arrMonths[0]->GetMonth().GetMonth())
			{
				break;
			}
			refFirstVisibleDay = m_arrMonths[0]->GetDay(i)->GetDate();
		}
		ASSERT(refFirstVisibleDay.GetMonth() == m_arrMonths[0]->GetMonth().GetMonth());
	}

	int nLastMnDCount = m_arrMonths[nCount-1]->GetDayCount();
	if (!nLastMnDCount || !m_arrMonths[nCount-1]->GetDay(nLastMnDCount-1))
	{
		ASSERT(FALSE);
		return FALSE;
	}
	refLastVisibleDay = m_arrMonths[nCount-1]->GetDay(nLastMnDCount-1)->GetDate();

	if (!m_arrMonths[nCount-1]->GetShowDaysAfter())
	{
		for (int i = nLastMnDCount-2; i > 0 ; i--)
		{
			if (refLastVisibleDay.GetMonth() == m_arrMonths[nCount-1]->GetMonth().GetMonth())
			{
				break;
			}
			refLastVisibleDay = m_arrMonths[nCount-1]->GetDay(i)->GetDate();
		}
		ASSERT(refLastVisibleDay.GetMonth() == m_arrMonths[nCount-1]->GetMonth().GetMonth());
	}

	return TRUE;
}

void CXTPDatePickerControl::EnsureVisibleSelection()
{
	COleDateTime dtFirstSelDay, dtLastSelDay;
	COleDateTime dtFirstVisibleDay, dtLastVisibleDay;

	BOOL bRes1 = GetVisibleRange(dtFirstVisibleDay, dtLastVisibleDay);
	BOOL bRes2 = GetSelRange(dtFirstSelDay, dtLastSelDay);
	if (bRes1 && bRes2)
	{
		if (dtFirstSelDay > dtLastVisibleDay)
		{
			int nMonths = CXTPCalendarUtils::GetDiff_Months(dtFirstSelDay, dtLastVisibleDay);
			nMonths += CXTPCalendarUtils::GetDiff_Months(dtLastSelDay, dtFirstSelDay);
			nMonths = max(nMonths, 1);
			ScrollRight(nMonths);
		}
		else if (dtLastSelDay > dtLastVisibleDay)
		{
			int nMonths = CXTPCalendarUtils::GetDiff_Months(dtLastSelDay, dtLastVisibleDay);
			nMonths = max(nMonths, 1);
			ScrollRight(nMonths);
		}

		//---------------------------
		bRes1 = GetVisibleRange(dtFirstVisibleDay, dtLastVisibleDay);
		bRes2 = GetSelRange(dtFirstSelDay, dtLastSelDay);
		if (bRes1 && bRes2)
		{
			if (dtFirstSelDay < dtFirstVisibleDay || dtLastSelDay > dtLastVisibleDay)
			{
				EnsureVisible(dtLastSelDay);
				EnsureVisible(dtFirstSelDay);
			}
		}
	}
}

void CXTPDatePickerControl::SetLayoutRTL(BOOL bRightToLeft)
{
	if (!XTPSystemVersion()->IsLayoutRTLSupported())
		return;

	m_bRightToLeft = bRightToLeft;

	if (!m_hWnd)
		return;

	ModifyStyleEx(bRightToLeft ? 0 : WS_EX_LAYOUTRTL, !bRightToLeft ? 0 : WS_EX_LAYOUTRTL);
	InitNames(); //<<>>
	RedrawControl();
}



void CXTPDatePickerControl::EnsureVisibleFocus()
{
	if (m_dtFocused.GetStatus() != COleDateTime::valid)
	{
		return;
	}
	COleDateTime dtFirstVisibleDay, dtLastVisibleDay;

	BOOL bRes1 = GetVisibleRange(dtFirstVisibleDay, dtLastVisibleDay);
	if (bRes1)
	{
		if (m_dtFocused > dtLastVisibleDay)
		{
			int nMonths = CXTPCalendarUtils::GetDiff_Months(m_dtFocused, dtLastVisibleDay);
			nMonths = max(nMonths, 1);
			ScrollRight(nMonths);
			m_dtFocused.SetStatus(COleDateTime::valid);
		}

		//---------------------------
		bRes1 = GetVisibleRange(dtFirstVisibleDay, dtLastVisibleDay);
		if (bRes1)
		{
			if (m_dtFocused < dtFirstVisibleDay || m_dtFocused > dtLastVisibleDay)
			{
				EnsureVisible(m_dtFocused);
			}
		}
	}
}

XTPCalendarTheme CXTPDatePickerControl::GetPaintTheme() const
{
	if (m_pPaintManager)
		return m_pPaintManager->GetPaintTheme();

	return xtpCalendarThemeUnknown;
}

void CXTPDatePickerControl::SetPaintTheme(XTPCalendarTheme ePaintTheme)
{
	if (ePaintTheme == xtpCalendarThemeResource)
	{
		SetTheme(new CXTPDatePickerThemeOffice2007());
	}
	else
	{
		SetTheme(new CXTPDatePickerPaintManager());
	}

	if (m_pPaintManager)
	{
		m_pPaintManager->SetPaintTheme(ePaintTheme);
	}
}

/////////////////////////////////////////////////////////////////////////////
//class CXTPSelectionHelper
CXTPDatePickerControl::CXTPSelectionHelper::CXTPSelectionHelper(CXTPDatePickerControl* pControl)
{
	ASSERT(pControl);
	m_pDP = pControl;
}

void CXTPDatePickerControl::CXTPSelectionHelper::InitFocusIfNeed()
{
	if (m_pDP->m_dtFocused.GetStatus() != COleDateTime::valid)
	{
		COleDateTime dtSel0, dtSel1;
		BOOL bSelExists = m_pDP->GetSelectedDays()->GetMinMaxRange(dtSel0, dtSel1);
		if (!bSelExists)
		{
			dtSel0.SetDate(m_pDP->m_dtFirstMonth.GetYear(), m_pDP->m_dtFirstMonth.GetMonth(), 1);
		}
		m_pDP->m_dtFocused = dtSel0;

		if (m_pDP->m_dtFirstClicked.GetStatus() == COleDateTime::valid &&
			m_pDP->m_dtFirstClicked >= dtSel0 && m_pDP->m_dtFirstClicked <= dtSel1)
		{
			m_pDP->m_dtFSelBase = m_pDP->m_dtFirstClicked;
		}
		else
		{
			m_pDP->m_dtFSelBase = dtSel0;
		}
	}
}

void CXTPDatePickerControl::CXTPSelectionHelper::RemoveFocus()
{
	m_pDP->m_dtFocused.SetStatus(COleDateTime::null);
	m_pDP->m_dtFSelBase.SetStatus(COleDateTime::null);
}


void CXTPDatePickerControl::CXTPSelectionHelper::_TmpSaveFocus()
{
	m_dtFocusedTmp = m_pDP->m_dtFocused;
	m_dtFSelBaseTmp = m_pDP->m_dtFSelBase;
}

void CXTPDatePickerControl::CXTPSelectionHelper::_TmpRestoreFocus()
{
	m_pDP->m_dtFocused = m_dtFocusedTmp;
	m_pDP->m_dtFSelBase = m_dtFSelBaseTmp;
}

void CXTPDatePickerControl::CXTPSelectionHelper::MoveFocus(int eStep, int eDirection,
									BOOL bContinuouse, BOOL bSaveSel)
{
	ASSERT(eDirection == 1 || eDirection == -1);
	if (bContinuouse)
	{
		bSaveSel = FALSE;
	}

	InitFocusIfNeed();

	_MoveFocus(eStep, eDirection);

	if (bContinuouse)
	{
		COleDateTime dtSel0 = m_pDP->m_dtFocused;
		COleDateTime dtSel1 = m_pDP->m_dtFSelBase;

		BOOL bBackSel = dtSel0 > dtSel1;

		if (bBackSel)
		{
			dtSel1 = m_pDP->m_dtFocused;
			dtSel0 = m_pDP->m_dtFSelBase;
		}

		int nDays = (int)dtSel1 - (int)dtSel0;
		int nMaxSel = m_pDP->GetMaxSelCount();
		if (nMaxSel == XTP_SELECTION_INFINITE)
		{
			nMaxSel = INT_MAX;
		}

		if (nDays > nMaxSel)
		{
			if (bBackSel)
			{
				dtSel1 = (double)dtSel0 + nMaxSel;
			}
			else
			{
				dtSel0 = (double)dtSel1 - nMaxSel;
			}
		}

		m_pDP->GetSelectedDays()->Clear();
		m_pDP->GetSelectedDays()->SelectRange(dtSel0, dtSel1);
	}
	else
	{
		//int nSelDays = m_pDP->GetSelectedDays()->GetSelectedDaysCount();

		if (!bSaveSel)
		{
			m_pDP->GetSelectedDays()->Clear();
		}

		if (!bSaveSel /*|| nSelDays == 0*/)
		{
			m_pDP->GetSelectedDays()->Add(m_pDP->m_dtFocused);
		}

		m_pDP->m_dtFSelBase = m_pDP->m_dtFocused;
	}
}

void CXTPDatePickerControl::CXTPSelectionHelper::SelUnselFocus()
{
	InitFocusIfNeed();

	BOOL bSelected = m_pDP->GetSelectedDays()->Contains(m_pDP->m_dtFocused);

	if (bSelected)
	{
		m_pDP->GetSelectedDays()->Remove(m_pDP->m_dtFocused);
	}
	else
	{
		int nSelDays = m_pDP->GetSelectedDays()->GetSelectedDaysCount();
		int nMaxSel = m_pDP->GetMaxSelCount();
		if (nMaxSel == XTP_SELECTION_INFINITE)
		{
			nMaxSel = INT_MAX;
		}

		if (nSelDays < nMaxSel)
		{
			m_pDP->GetSelectedDays()->Add(m_pDP->m_dtFocused);
		}
	}
}

void CXTPDatePickerControl::CXTPSelectionHelper::_MoveFocus(int eStep, int eDirection)
{
	ASSERT(eDirection == 1 || eDirection == -1);

	InitFocusIfNeed();

	COleDateTime dtNew = m_pDP->m_dtFocused;

	if (eStep == stepDay || eStep == stepWeek)
	{
		dtNew = (double)dtNew + eDirection * (eStep == stepDay ? 1 : 7);
	}
	else if (eStep == stepMonth || eStep == stepYear)
	{
		int nMonths = eDirection * (eStep == stepMonth ? 1 : 12);
		CXTPCalendarUtils::ShiftDate_Month(dtNew, nMonths, dtNew.GetDay());
	}
	else if (eStep == stepWeekBE)
	{
		int nFWD = m_pDP->GetFirstDayOfWeek();

		int nShift = -1 * ((dtNew.GetDayOfWeek() - nFWD + 7) % 7);

		if (eDirection == dirNext)
		{
			nShift += 6;
		}

		dtNew = (double)dtNew + nShift;
	}
	else if (eStep == stepMonthBE)
	{
		CXTPCalendarUtils::UpdateMonthDay(dtNew, eDirection == dirPrev ? 1 : 31);
	}
	else
	{
		ASSERT(FALSE);
	}

	m_pDP->m_dtFocused = dtNew;
}

void CXTPDatePickerControl::SendNotification(XTP_NOTIFY_CODE EventCode, WPARAM wParam, LPARAM lParam)
{
	m_pConnection->SendEvent(EventCode, wParam, lParam);
}