// XTPMarkupKeyboardNavigation.cpp: implementation of the CXTPMarkupKeyboardNavigation class.
//
// 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 "XTPMarkupObject.h"
#include "XTPMarkupInputElement.h"
#include "XTPMarkupUIElement.h"
#include "XTPMarkupFrameworkElement.h"
#include "Text/XTPMarkupInline.h"
#include "XTPMarkupKeyboardNavigation.h"

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


CXTPMarkupDependencyProperty* CXTPMarkupKeyboardNavigation::m_pIsTabStopProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupKeyboardNavigation::m_pTabNavigationProperty = NULL;
CXTPMarkupDependencyProperty* CXTPMarkupKeyboardNavigation::m_pTabIndexProperty = NULL;

IMPLEMENT_MARKUPCLASS(L"KeyboardNavigation", CXTPMarkupKeyboardNavigation, CXTPMarkupObject)

void CXTPMarkupKeyboardNavigation::RegisterMarkupClass()
{
	m_pIsTabStopProperty = CXTPMarkupDependencyProperty::RegisterAttached(L"IsTabStop", MARKUP_TYPE(CXTPMarkupBool), MARKUP_TYPE(CXTPMarkupKeyboardNavigation),
		new CXTPMarkupPropertyMetadata(CXTPMarkupBool::CreateTrueValue()));

	m_pTabNavigationProperty = CXTPMarkupDependencyProperty::RegisterAttached(L"TabNavigation", MARKUP_TYPE(CXTPMarkupEnum), MARKUP_TYPE(CXTPMarkupKeyboardNavigation),
		new CXTPMarkupPropertyMetadata(NULL, &CXTPMarkupKeyboardNavigation::ConvertKeyboardNavigationMode));

	m_pTabIndexProperty = CXTPMarkupDependencyProperty::RegisterAttached(L"TabIndex", MARKUP_TYPE(CXTPMarkupInt), MARKUP_TYPE(CXTPMarkupKeyboardNavigation),
		new CXTPMarkupPropertyMetadata(new CXTPMarkupInt(INT_MAX)));
}

CXTPMarkupKeyboardNavigation::CXTPMarkupKeyboardNavigation(CXTPMarkupContext* pContext)
{
	m_pFocused = NULL;
	m_pLastFocused = NULL;

	m_pMarkupContext = pContext;
}

CXTPMarkupKeyboardNavigation::~CXTPMarkupKeyboardNavigation()
{
	MARKUP_RELEASE(m_pFocused);
	MARKUP_RELEASE(m_pLastFocused);
}

XTPMarkupKeyboardNavigationMode CXTPMarkupKeyboardNavigation::GetTabNavigation(CXTPMarkupObject* pElement)
{
	CXTPMarkupEnum* pValue = MARKUP_STATICCAST(CXTPMarkupEnum, pElement->GetValue(m_pTabNavigationProperty));
	return pValue ? (XTPMarkupKeyboardNavigationMode)(int)*pValue : xtpMarkupKeyboardNavigationContinue;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::ConvertKeyboardNavigationMode(CXTPMarkupBuilder* /*pBuilder*/, CXTPMarkupObject* pObject)
{
	if (IsStringObject(pObject))
	{
		LPCWSTR lpszValue = *((CXTPMarkupString*)pObject);
		int nLength = ((CXTPMarkupString*)pObject)->GetLength();

		if (nLength == 8 && _wcsicmp(lpszValue, L"Continue") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupKeyboardNavigationContinue);
		if (nLength == 4 && _wcsicmp(lpszValue, L"Once") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupKeyboardNavigationOnce);
		if (nLength == 5 && _wcsicmp(lpszValue, L"Cycle") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupKeyboardNavigationCycle);
		if (nLength == 4 && _wcsicmp(lpszValue, L"None") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupKeyboardNavigationNone);
		if (nLength == 9 && _wcsicmp(lpszValue, L"Contained") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupKeyboardNavigationContained);
		if (nLength == 5 && _wcsicmp(lpszValue, L"Local") == 0) return CXTPMarkupEnum::CreateValue(xtpMarkupKeyboardNavigationLocal);
	}

	return NULL;
}


BOOL CXTPMarkupKeyboardNavigation::OnWndMsg(UINT /*message*/, WPARAM /*wParam*/, LPARAM /*lParam*/, LRESULT* /*pResult*/)
{
	// NotImplmeneted
	return FALSE;

#if 0
	if (message == WM_SETFOCUS)
	{
		if (m_pLastFocused && !m_pFocused)
		{
			Focus(m_pLastFocused);
			MARKUP_RELEASE(m_pLastFocused);
		}
	}

	if (message == WM_KILLFOCUS)
	{
		if (m_pFocused)
		{
			m_pLastFocused = m_pFocused;
			MARKUP_ADDREF(m_pLastFocused);

			Focus(NULL);
		}
	}

	if (message == WM_KEYDOWN)
	{
		ProcessInput((UINT)wParam);
	}

	return FALSE;
#endif
}

BOOL CXTPMarkupKeyboardNavigation::IsInNavigationTree(CXTPMarkupObject* pVisual)
{
	CXTPMarkupUIElement* pElement = MARKUP_DYNAMICCAST(CXTPMarkupUIElement, pVisual);
	if ((pElement != NULL) && (pElement->IsVisible()))
	{
		return TRUE;
	}

	return FALSE;
}


CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetParent(CXTPMarkupObject* e)
{
	if (!e)
		return NULL;

	if (e->IsKindOf(MARKUP_TYPE(CXTPMarkupVisual)))
	{
		CXTPMarkupVisual* reference = ((CXTPMarkupVisual*)e)->GetVisualParent();
		while (reference != NULL)
		{
			if (IsInNavigationTree(reference))
			{
				return reference;
			}

			reference = reference->GetVisualParent();
		}
	}
	else if (e->IsKindOf(MARKUP_TYPE(CXTPMarkupFrameworkContentElement)))
	{
		return (CXTPMarkupObject*)(((CXTPMarkupFrameworkContentElement*)e)->GetParent());
	}

	return NULL;
}

XTPMarkupKeyboardNavigationMode CXTPMarkupKeyboardNavigation::GetKeyNavigationMode(CXTPMarkupObject* pElement)
{
	CXTPMarkupEnum* pValue = MARKUP_STATICCAST(CXTPMarkupEnum, pElement->GetValue(m_pNavigationProperty));
	return pValue ? (XTPMarkupKeyboardNavigationMode)(int)*pValue : xtpMarkupKeyboardNavigationContinue;
}

BOOL CXTPMarkupKeyboardNavigation::IsGroup(CXTPMarkupObject* e)
{
	return (GetKeyNavigationMode(e) != xtpMarkupKeyboardNavigationContinue);
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetGroupParent(CXTPMarkupObject* e, BOOL bIncludeCurrent)
{
	CXTPMarkupObject* obj2 = e;
	if (!bIncludeCurrent)
	{
		obj2 = e;
		e = GetParent(e);
		if (e == NULL)
		{
			return obj2;
		}
	}
	while (e != NULL)
	{
		if (IsGroup(e))
		{
			return e;
		}
		obj2 = e;
		e = GetParent(e);
	}
	return obj2;
}

BOOL CXTPMarkupKeyboardNavigation::IsTabStopOrGroup(CXTPMarkupObject* e)
{
	return IsTabStop(e) || IsGroup(e);
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::FocusedElement(CXTPMarkupObject* /*e*/)
{
	return NULL;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetTreeFirstChild(CXTPMarkupObject* e)
{
	CXTPMarkupObject* obj2 = FocusedElement(e);
	if (obj2 != NULL)
	{
		return obj2;
	}

	CXTPMarkupUIElement* o = MARKUP_DYNAMICCAST(CXTPMarkupUIElement, e);
	if (o != NULL && o->IsVisible())
	{
		CXTPMarkupVisual* reference = o;

		if (reference != NULL)
		{
			int childrenCount = reference->GetVisualChildrenCount();
			for (int i = 0; i < childrenCount; i++)
			{
				CXTPMarkupVisual* child = reference->GetVisualChild(i);
				if (IsInNavigationTree(child))
				{
					return child;
				}

				CXTPMarkupObject* firstChild = GetTreeFirstChild(child);
				if (firstChild != NULL)
				{
					return firstChild;
				}
			}
		}
	}
	return NULL;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetTreeNextSibling(CXTPMarkupObject* e)
{
	CXTPMarkupObject* parent = GetParent(e);

	{
		CXTPMarkupVisual* reference = MARKUP_DYNAMICCAST(CXTPMarkupVisual, parent);
		CXTPMarkupObject* obj4 = MARKUP_DYNAMICCAST(CXTPMarkupVisual, e);

		if ((reference != NULL) && obj4)
		{
			int childrenCount = reference->GetVisualChildrenCount();
			int childIndex = 0;
			while (childIndex < childrenCount)
			{
				if (reference->GetVisualChild(childIndex) == obj4)
				{
					break;
				}
				childIndex++;
			}
			childIndex++;
			while (childIndex < childrenCount)
			{
				CXTPMarkupObject* child = reference->GetVisualChild(childIndex);

				if (IsInNavigationTree(child))
				{
					return child;
				}
				childIndex++;
			}
		}
	}
	return NULL;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetNextInTree(CXTPMarkupObject* e, CXTPMarkupObject* container)
{
	CXTPMarkupObject* firstChild = NULL;
	if ((e == container) || !IsGroup(e))
	{
		firstChild = GetTreeFirstChild(e);
	}
	if ((firstChild != NULL) || (e == container))
	{
		return firstChild;
	}
	CXTPMarkupObject* parent = e;
	do
	{
		CXTPMarkupObject* nextSibling = GetTreeNextSibling(parent);
		if (nextSibling != NULL)
		{
			return nextSibling;
		}
		parent = GetParent(parent);
	}
	while ((parent != NULL) && (parent != container));
	return NULL;
}

int CXTPMarkupKeyboardNavigation::GetTabIndexHelper(CXTPMarkupObject* d)
{
	CXTPMarkupInt* pValue = MARKUP_STATICCAST(CXTPMarkupInt, d->GetValue(m_pTabIndexProperty));
	return pValue ? (int)*pValue : INT_MAX;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetFirstTabInGroup(CXTPMarkupObject* container)
{
	CXTPMarkupObject* obj2 = NULL;
	int num = INT_MIN;
	CXTPMarkupObject* e = container;
	while ((e = GetNextInTree(e, container)) != NULL)
	{
		if (IsTabStopOrGroup(e))
		{
			int tabIndexHelper = GetTabIndexHelper(e);
			if ((tabIndexHelper < num) || (obj2 == NULL))
			{
				num = tabIndexHelper;
				obj2 = e;
			}
		}
	}
	return obj2;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetNextTabWithSameIndex(CXTPMarkupObject* e, CXTPMarkupObject* container)
{
	int tabIndexHelper = GetTabIndexHelper(e);
	CXTPMarkupObject* obj2 = e;
	while ((obj2 = GetNextInTree(obj2, container)) != NULL)
	{
		if (IsTabStopOrGroup(obj2) && (GetTabIndexHelper(obj2) == tabIndexHelper))
		{
			return obj2;
		}
	}
	return NULL;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetNextTabWithNextIndex(CXTPMarkupObject* e, CXTPMarkupObject* container, XTPMarkupKeyboardNavigationMode tabbingType)
{
	CXTPMarkupObject* obj2 = NULL;
	CXTPMarkupObject* obj3 = NULL;
	int num = INT_MIN;
	int num2 = INT_MIN;
	int tabIndexHelper = GetTabIndexHelper(e);
	CXTPMarkupObject* obj4 = container;
	while ((obj4 = GetNextInTree(obj4, container)) != NULL)
	{
		if (IsTabStopOrGroup(obj4))
		{
			int num4 = GetTabIndexHelper(obj4);
			if ((num4 > tabIndexHelper) && ((num4 < num2) || (obj2 == NULL)))
			{
				num2 = num4;
				obj2 = obj4;
			}
			if ((num4 < num) || (obj3 == NULL))
			{
				num = num4;
				obj3 = obj4;
			}
		}
	}
	if ((tabbingType == xtpMarkupKeyboardNavigationCycle) && (obj2 == NULL))
	{
		obj2 = obj3;
	}
	return obj2;
}


CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetNextTabInGroup(CXTPMarkupObject* e, CXTPMarkupObject* container, XTPMarkupKeyboardNavigationMode tabbingType)
{
	if (tabbingType == xtpMarkupKeyboardNavigationNone)
	{
		return NULL;
	}
	if ((e == NULL) || (e == container))
	{
		return GetFirstTabInGroup(container);
	}
	if (tabbingType == xtpMarkupKeyboardNavigationOnce)
	{
		return NULL;
	}
	CXTPMarkupObject* nextTabWithSameIndex = GetNextTabWithSameIndex(e, container);
	if (nextTabWithSameIndex != NULL)
	{
		return nextTabWithSameIndex;
	}
	return GetNextTabWithNextIndex(e, container, tabbingType);
}

AFX_INLINE BOOL GetBoolValue(CXTPMarkupObject* e, CXTPMarkupDependencyProperty* p)
{
	CXTPMarkupBool* pValue = MARKUP_STATICCAST(CXTPMarkupBool, e->GetValue(p));
	return pValue ? (BOOL)*pValue : FALSE;
}

BOOL CXTPMarkupKeyboardNavigation::IsTabStop(CXTPMarkupObject* e)
{
	if (e->IsKindOf(MARKUP_TYPE(CXTPMarkupFrameworkElement)))
	{
		CXTPMarkupFrameworkElement* pElement = (CXTPMarkupFrameworkElement*)e;
		return pElement->IsFocusable() && GetBoolValue(pElement, m_pIsTabStopProperty) && pElement->IsEnabled() && pElement->IsVisible();
	}
	else if (e->IsKindOf(MARKUP_TYPE(CXTPMarkupFrameworkContentElement)))
	{
		return FALSE;
	}

	return FALSE;
}


CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetTabOnceActiveElement(CXTPMarkupObject* /*d*/)
{
	return NULL;
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetActiveElement(CXTPMarkupObject* d)
{
	return GetTabOnceActiveElement(d);
}

CXTPMarkupObject* CXTPMarkupKeyboardNavigation::GetNextTab(CXTPMarkupObject* e, CXTPMarkupObject* pContainer, BOOL goDownOnly)
{
	XTPMarkupKeyboardNavigationMode keyNavigationMode = GetKeyNavigationMode(pContainer);
	if (e == NULL)
	{
		if (IsTabStop(pContainer))
		{
			return pContainer;
		}
		CXTPMarkupObject* pActiveElement = GetActiveElement(pContainer);
		if (pActiveElement)
		{
			return GetNextTab(NULL, pActiveElement, TRUE);
		}
	}
	else if (((keyNavigationMode == xtpMarkupKeyboardNavigationOnce) || (keyNavigationMode == xtpMarkupKeyboardNavigationNone)) && (pContainer != e))
	{
		if (goDownOnly)
		{
			return NULL;
		}
		CXTPMarkupObject* groupParent = GetGroupParent(pContainer);
		return GetNextTab(pContainer, groupParent, goDownOnly);
	}
	CXTPMarkupObject* obj4 = NULL;
	CXTPMarkupObject* obj5 = e;
	XTPMarkupKeyboardNavigationMode tabbingType = keyNavigationMode;
	while ((obj5 = GetNextTabInGroup(obj5, pContainer, tabbingType)) != NULL)
	{
		if (obj4 == obj5)
		{
			break;
		}
		if (obj4 == NULL)
		{
			obj4 = obj5;
		}
		CXTPMarkupObject* obj6 = GetNextTab(NULL, obj5, TRUE);
		if (obj6 != NULL)
		{
			return obj6;
		}
		if (tabbingType == xtpMarkupKeyboardNavigationOnce)
		{
			tabbingType = xtpMarkupKeyboardNavigationContained;
		}
	}
	if ((!goDownOnly && (tabbingType != xtpMarkupKeyboardNavigationContained)) && (GetParent(pContainer) != NULL))
	{
		return GetNextTab(pContainer, GetGroupParent(pContainer), FALSE);
	}
	return NULL;
}

void CXTPMarkupKeyboardNavigation::Navigate(CXTPMarkupInputElement* pCurrentElement, XTPMarkupFocusNavigationDirection direction)
{
	CXTPMarkupObject* pNextInDirection = NULL;
	switch (direction)
	{
	case xtpMarkupFocusNavigationDirectionNext:
		m_pNavigationProperty = m_pTabNavigationProperty;
		pNextInDirection = GetNextTab(pCurrentElement, GetGroupParent(pCurrentElement, TRUE), FALSE);
		break;

   /*case xtpMarkupFocusNavigationDirectionPrevious:
		m_pNavigationProperty = m_pTabNavigationProperty;
		pNextInDirection = GetPrevTab(pCurrentElement, NULL, FALSE);
		break;*/
	}

	if (pNextInDirection != NULL)
	{
		if (pNextInDirection->IsKindOf(MARKUP_TYPE(CXTPMarkupInputElement)))
		{
			((CXTPMarkupInputElement*)pNextInDirection)->Focus();
		}
	}
}

void CXTPMarkupKeyboardNavigation::Navigate(CXTPMarkupInputElement* pSourceElement, UINT nChar)
{
	if (nChar == VK_TAB)
	{
		Navigate(pSourceElement, (::GetKeyState(VK_SHIFT) & 0x8000) == 0 ? xtpMarkupFocusNavigationDirectionNext : xtpMarkupFocusNavigationDirectionPrevious);
	}
}

void CXTPMarkupKeyboardNavigation::ProcessInput(UINT nChar)
{
	if (!m_pFocused)
		return;

	if (nChar == VK_TAB)
	{
		Navigate(m_pFocused, nChar);
	}
}

void CXTPMarkupKeyboardNavigation::Focus(CXTPMarkupInputElement* /*pInputElement*/)
{
	// NotImplmeneted
	return;

#if 0
	if (!m_pMarkupContext->m_hContextWnd)
		return;

	if (m_pFocused == pInputElement)
		return;

	if (m_pFocused)
	{
		m_pFocused->SetValue(CXTPMarkupInputElement::m_pIsKeyboardFocusedProperty, NULL);

		MARKUP_RELEASE(m_pFocused);
	}

	m_pFocused = pInputElement;

	if (m_pFocused)
	{
		m_pFocused->SetValue(CXTPMarkupInputElement::m_pIsKeyboardFocusedProperty, CXTPMarkupBool::CreateTrueValue());

		MARKUP_ADDREF(m_pFocused);

		::SetFocus(m_pMarkupContext->m_hContextWnd);
	}
#endif
}