// XTPDockingPaneKeyboardHook.cpp : implementation of the CXTPDockingPaneKeyboardHook class.
//
// This file is a part of the XTREME DOCKINGPANE 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/XTPResourceManager.h"
#include "Common/XTPDrawHelpers.h"
#include "Common/Resource.h"
#include "Common/XTPColorManager.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPImageManager.h"

#include "Resource.h"

#include "TabManager/XTPTabManager.h"
#include "TabManager/XTPTabPaintManager.h"

#include "XTPDockingPaneDefines.h"
#include "XTPDockingPaneBase.h"
#include "XTPDockingPaneKeyboardHook.h"
#include "XTPDockingPane.h"
#include "XTPDockingPanePaintManager.h"
#include "XTPDockingPaneManager.h"
#include "XTPDockingPaneMiniWnd.h"


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


/////////////////////////////////////////////////////////////////////////////
// CXTPDockingPaneWindowSelect
IMPLEMENT_DYNCREATE(CXTPDockingPaneWindowSelect, CMiniFrameWnd)

CXTPDockingPaneWindowSelect::CXTPDockingPaneWindowSelect()
{
	LOGFONT lfIcon;
	VERIFY(CXTPDrawHelpers::GetIconLogFont(&lfIcon));

	lfIcon.lfWeight = FW_NORMAL;
	m_fnt.CreateFontIndirect(&lfIcon);

	lfIcon.lfWeight = FW_BOLD;
	m_fntBold.CreateFontIndirect(&lfIcon);

	XTPResourceManager()->LoadString(&m_strActiveTools, XTP_IDS_DOCKINGPANE_SELECTWINDOW_ACTIVETOOLS);
	XTPResourceManager()->LoadString(&m_strActiveFiles, XTP_IDS_DOCKINGPANE_SELECTWINDOW_ACTIVEFILES);

	m_pSelected = NULL;
	m_pManager = NULL;
	m_bActivatePanes = FALSE;

	m_nPaneCount = 0;
	m_nFirstFile = 0;

	m_hHandCursor = AfxGetApp()->LoadStandardCursor(MAKEINTRESOURCE(32649));

	if (m_hHandCursor == 0)
		m_hHandCursor = XTPResourceManager()->LoadCursor(XTP_IDC_HAND);

	m_hArrowCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
}

CXTPDockingPaneWindowSelect::~CXTPDockingPaneWindowSelect()
{
	for (int i = 0; i < (int)m_arrItems.GetSize(); i++)
	{
		delete m_arrItems[i];
	}
	m_arrItems.RemoveAll();
}

int CXTPDockingPaneWindowSelect::CalcItemHeight(CDC* pDC)
{
	CXTPFontDC font(pDC, &m_fnt);
	CSize szFont = pDC->GetTextExtent(_T(" "), 1);

	CSize szIcon = m_pManager->GetPaintManager()->GetTabPaintManager()->m_szIcon;

	return max(szIcon.cy + 4, szFont.cy + 4);
}

CXTPDockingPaneWindowSelect::CItem* CXTPDockingPaneWindowSelect::HitTest(CPoint point) const
{
	for (int i = 0; i < (int)m_arrItems.GetSize(); i++)
	{
		CItem* pItem = m_arrItems[i];
		if (pItem->rc.PtInRect(point))
		{
			return pItem;
		}
	}
	return 0;
}

CString CXTPDockingPaneWindowSelect::GetItemCaption(CItem* pItem) const
{
	if (pItem->type == itemPane)
	{
		return pItem->pPane->GetTitle();
	}
	else
	{
		CString strCaption;
		CFrameWnd* pChild = DYNAMIC_DOWNCAST(CFrameWnd, CWnd::FromHandle(pItem->hWndFrame));
		if (pChild && pChild->GetActiveDocument())
		{
			strCaption = pChild->GetActiveDocument()->GetTitle();
		}
		else
		{
			CXTPDrawHelpers::GetWindowCaption(pItem->hWndFrame, strCaption);
		}
		return strCaption;
	}
}


CString CXTPDockingPaneWindowSelect::GetItemDescription(CItem* pItem) const
{
	if (pItem->type == itemPane)
		return _T("");

	CFrameWnd* pChild = DYNAMIC_DOWNCAST(CFrameWnd, CWnd::FromHandle(pItem->hWndFrame));

	if (!pChild)
		return _T("");

	CDocument* pDocument = pChild->GetActiveDocument();
	if (!pDocument)
		return _T("");

	CString strTypeName;
	if (pDocument->GetDocTemplate() != NULL) pDocument->GetDocTemplate()->GetDocString(strTypeName, CDocTemplate::regFileTypeName);

	return strTypeName;
}

CString CXTPDockingPaneWindowSelect::GetItemPath(CItem* pItem) const
{
	if (pItem->type == itemPane)
		return _T("");

	CFrameWnd* pChild = DYNAMIC_DOWNCAST(CFrameWnd, CWnd::FromHandle(pItem->hWndFrame));

	if (!pChild)
		return _T("");

	CDocument* pDocument = pChild->GetActiveDocument();
	if (!pDocument)
		return  _T("");

	return pDocument->GetPathName();
}


HWND CXTPDockingPaneWindowSelect::GetMDIClient() const
{
	CMDIFrameWnd* pFrame = DYNAMIC_DOWNCAST(CMDIFrameWnd, m_pManager->GetSite());
	HWND hWndClient = pFrame ? pFrame->m_hWndMDIClient : NULL;
	return hWndClient;
}

BOOL CXTPDockingPaneWindowSelect::Reposition()
{
	CClientDC dc(this);
	int nItemHeight = CalcItemHeight(&dc);
	int nItemWidth = 163;

	int y = nItemHeight + 13;
	int x = 9;
	int nRow = 0;
	int nColumn = 0;

	m_pSelected = 0;
	int nTotalRow = 0;
	int nFirstColumn = 0;

	CXTPDockingPaneInfoList& paneList = m_pManager->GetPaneList();
	POSITION pos = paneList.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPane* pPane = paneList.GetNext(pos);
		if ((pPane->GetEnabled() & xtpPaneEnableClient) == 0)
			continue;

		if (nColumn == 0 && m_pManager->IsClientHidden() && nRow > paneList.GetCount() / 2)
		{
			CColumn colNext;
			colNext.nFirst = 0;
			colNext.nLast = (int)m_arrItems.GetSize() - 1;
			m_arrColumns.Add(colNext);

			nTotalRow = max(nRow, nTotalRow);
			nFirstColumn = (int)m_arrItems.GetSize();
			nRow = 0;
			nColumn++;
			y = nItemHeight + 13;
			x += nItemWidth + 12;
		}

		CItem* pItem = new CItem;
		pItem->pPane = pPane;
		pItem->type = itemPane;

		pItem->rc = CRect(x, y, x + nItemWidth, y + nItemHeight);
		y += nItemHeight + 1;

		pItem->nIndex = (int)m_arrItems.Add(pItem);
		pItem->nColumn = nColumn;
		pItem->nRow = nRow;

		if (m_pManager->GetActivePane() == pPane)
			m_pSelected = pItem;

		nRow++;
	}
	m_nPaneCount = (int)m_arrItems.GetSize();

	if (m_bActivatePanes && m_nPaneCount > 0)
	{
		if (m_pSelected == 0)
		{
			m_pSelected = m_arrItems[GetKeyState(VK_SHIFT) >= 0 ? 0 : nRow - 1];
		}
		else
		{
			if (GetKeyState(VK_SHIFT) >= 0)
			{
				m_pSelected = m_arrItems[m_pSelected->nIndex >= nRow - 1 ? 0 : m_pSelected->nIndex + 1];
			}
			else
			{
				m_pSelected = m_arrItems[m_pSelected->nIndex > 0 ? m_pSelected->nIndex - 1 : nRow - 1];
			}
		}
	}


	nTotalRow = max(nRow, nTotalRow);

	if (m_nPaneCount > 0)
	{
		CColumn col;
		col.nFirst = nFirstColumn;
		col.nLast = (int)m_arrItems.GetSize() - 1;
		m_arrColumns.Add(col);
	}
	else
	{
		m_bActivatePanes = FALSE;
	}

	m_nFirstFile = (int)m_arrItems.GetSize();
	nFirstColumn = m_nFirstFile;

	nRow = 0;
	if (m_nPaneCount > 0 && !m_pManager->IsClientHidden())
	{
		nColumn++;
		x += nItemWidth + 12;
		y = nItemHeight + 13;
	}

	HWND hWndClient = GetMDIClient();
	HWND hWndActive = hWndClient ? (HWND)::SendMessage(hWndClient, WM_MDIGETACTIVE, 0, 0) : 0;

	if (m_pManager->IsClientHidden())
	{

	}
	else if (hWndActive)
	{
		HWND hWndFrame = hWndActive;
		while (hWndFrame)
		{
			DWORD dwStyle = GetWindowLong(hWndFrame, GWL_STYLE);
			if (((dwStyle & WS_VISIBLE) != 0) && ((dwStyle & WS_DISABLED) == 0))
			{
				if (nRow > 14)
				{
					CColumn colNext;
					colNext.nFirst = nFirstColumn;
					colNext.nLast = (int)m_arrItems.GetSize() - 1;
					m_arrColumns.Add(colNext);

					nTotalRow = max(nRow, nTotalRow);
					nFirstColumn = (int)m_arrItems.GetSize();
					nRow = 0;
					nColumn++;
					y = nItemHeight + 13;
					x += nItemWidth + 12;
				}
				CItem* pItem = new CItem;
				pItem->hWndFrame = hWndFrame;
				pItem->type = itemMDIFrame;

				pItem->rc = CRect(x, y, x + nItemWidth, y + nItemHeight);
				y += nItemHeight + 1;

				pItem->nIndex = (int)m_arrItems.Add(pItem);
				pItem->nColumn = nColumn;
				pItem->nRow = nRow;

				if (hWndActive == hWndFrame && !m_bActivatePanes)
					m_pSelected = pItem;

				nRow++;
			}

			hWndFrame = ::GetWindow(hWndFrame, GW_HWNDNEXT);
		}
	}
	else if (!hWndClient)
	{
		CItem* pItem = new CItem;
		pItem->hWndFrame = m_pManager->GetSite()->GetSafeHwnd();
		pItem->type = itemSDIFrame;

		pItem->rc = CRect(x, y, x + nItemWidth, y + nItemHeight);

		pItem->nIndex = (int)m_arrItems.Add(pItem);
		pItem->nColumn = nColumn;
		pItem->nRow = nRow;

		if (m_pSelected == NULL)
			m_pSelected = pItem;

		m_nFirstFile++;
	}

	if (nFirstColumn < m_arrItems.GetSize())
	{
		CColumn colNext;
		colNext.nFirst = nFirstColumn;
		colNext.nLast = (int)m_arrItems.GetSize() - 1;
		m_arrColumns.Add(colNext);
	}

	if (m_nFirstFile < m_arrItems.GetSize() - 1 && !m_bActivatePanes)
	{
		if (GetKeyState(VK_SHIFT) >= 0)
			m_pSelected = m_arrItems[m_nFirstFile + 1];
		else
			m_pSelected = m_arrItems[ m_arrItems.GetSize() - 1];
	}

	nTotalRow = max(nRow, nTotalRow);
	int nTotalColumns = max(2, (int)m_arrColumns.GetSize());

	CSize sz(4 + (nItemWidth + 12) * nTotalColumns,
		nItemHeight + 13 + nTotalRow * (nItemHeight + 1) + 110);

	CXTPWindowRect rcWindow(m_pManager->GetSite());
	CPoint ptCenter = rcWindow.CenterPoint();

	CRect rect(CPoint(ptCenter.x - sz.cx / 2, ptCenter.y - sz.cy /2), sz);
	SetWindowPos(&CWnd::wndTopMost, rect.left, rect.top, rect.Width(), rect.Height(),
		SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOOWNERZORDER);

	return TRUE;
}


BEGIN_MESSAGE_MAP(CXTPDockingPaneWindowSelect, CMiniFrameWnd)
	//{{AFX_MSG_MAP(CXTPDockingPaneWindowSelect)
	ON_WM_KILLFOCUS()
	ON_WM_CAPTURECHANGED()
	ON_WM_PAINT()
	ON_WM_KEYDOWN()
	ON_WM_SYSKEYDOWN()
	ON_WM_KEYUP()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_CHAR()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

#define CS_DROPSHADOW       0x00020000

/////////////////////////////////////////////////////////////////////////////
// CXTPDockingPaneWindowSelect message handlers

BOOL CXTPDockingPaneWindowSelect::DoModal()
{
	CWnd* pSite = m_pManager->GetSite();
	HWND hWndParent = pSite->GetSafeHwnd();
	HWND hwndFocus = ::GetFocus();

	BOOL bLayoutRTL = m_pManager->IsLayoutRTL();
	UINT nClassStyle = bLayoutRTL ? 0 : CS_SAVEBITS | CS_OWNDC;

	if (XTPSystemVersion()->IsWinXPOrGreater()) // Windows XP only
	{
		nClassStyle |= CS_DROPSHADOW;
	}

	if (!CreateEx(WS_EX_TOOLWINDOW | (bLayoutRTL ? WS_EX_LAYOUTRTL : 0),
		AfxRegisterWndClass(nClassStyle, m_hArrowCursor), 0,
		WS_POPUP | MFS_SYNCACTIVE, CRect(0, 0, 0, 0), pSite, 0))
		return FALSE;

	if (!Reposition())
	{
		DestroyWindow();
		return FALSE;
	}

	SetFocus();
	SetCapture();

	BOOL bEnableParent = FALSE;
	if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
	{
		CWnd::ModifyStyle(hWndParent, 0, WS_DISABLED, 0);
		bEnableParent = TRUE;
	}

	m_nFlags |= WF_CONTINUEMODAL;

	if (m_nFlags & WF_CONTINUEMODAL)
	{
		// enter modal loop
		DWORD dwFlags = MLF_SHOWONIDLE;

		VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
	}

	// hide the window before enabling the parent, etc.
	if (m_hWnd != NULL)
		SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
		SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);

	if (bEnableParent)
		CWnd::ModifyStyle(hWndParent, WS_DISABLED, 0, 0);
	if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
		::SetActiveWindow(hWndParent);

	if (m_nModalResult == IDOK && m_pSelected)
	{
		if (m_pSelected->type == itemPane)
		{
			m_pManager->ShowPane(m_pSelected->pPane);
		}
		else if (m_pSelected->type == itemMDIFrame)
		{
			HWND hWndClient = GetMDIClient();
			::SendMessage(hWndClient, WM_MDIACTIVATE, (WPARAM)m_pSelected->hWndFrame, 0);

			CWnd* pWnd = CWnd::FromHandle(m_pSelected->hWndFrame);
			CWnd* pFocus = GetFocus();

			BOOL bHasFocus = pFocus->GetSafeHwnd() &&
				(pFocus == pWnd || pWnd->IsChild(pFocus) || (pFocus->GetOwner()->GetSafeHwnd() && pWnd->IsChild(pFocus->GetOwner())));

			if (!bHasFocus)
				pWnd->SetFocus();
		}
	}
	else
	{
		::SetFocus(hwndFocus);
	}

	DestroyWindow();
	return TRUE;
}

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

	if (m_nFlags & WF_CONTINUEMODAL) EndModalLoop(IDCANCEL);
}

void CXTPDockingPaneWindowSelect::OnCaptureChanged(CWnd *pWnd)
{
	if (m_nFlags & WF_CONTINUEMODAL) EndModalLoop(IDCANCEL);

	CWnd::OnCaptureChanged(pWnd);
}

void CXTPDockingPaneWindowSelect::OnPaint()
{
	CPaintDC dcPaint(this); // device context for painting
	CXTPClientRect rc(this);

	CXTPBufferDC dc(dcPaint);

	dc.FillSolidRect(rc, GetSysColor(COLOR_3DFACE));

	dc.Draw3dRect(rc, GetXtremeColor(XPCOLOR_HIGHLIGHT_BORDER), GetXtremeColor(XPCOLOR_HIGHLIGHT_BORDER));

	int nItemHeight = CalcItemHeight(&dc);

	CXTPFontDC font(&dc, &m_fntBold);
	dc.SetBkMode(TRANSPARENT);
	dc.SetTextColor(GetXtremeColor(COLOR_BTNTEXT));
	if (m_nPaneCount > 0)
	{
		CRect rcActiveTools(9, 0, 9 + 163, nItemHeight + 12);
		dc.DrawText(m_strActiveTools, rcActiveTools, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);

		if (!m_pManager->IsClientHidden())
		{
			CRect rcActiveFiles( 9 + 13 + 163, 0, 9 + 13 + 2 * 163, nItemHeight + 12);
			dc.DrawText(m_strActiveFiles, rcActiveFiles, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);
		}
	}
	else
	{
		CRect rcActiveFiles( 9, 0, 9 + 2 * 163, nItemHeight + 12);
		dc.DrawText(m_strActiveFiles, rcActiveFiles, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);
	}

	font.SetFont(&m_fnt);

	CSize szIcon = m_pManager->GetPaintManager()->GetTabPaintManager()->m_szIcon;

	for (int i = 0; i < (int)m_arrItems.GetSize(); i++)
	{
		CItem* pItem = m_arrItems[i];

		if (m_pSelected == pItem)
		{
			dc.FillSolidRect(pItem->rc, GetXtremeColor(XPCOLOR_HIGHLIGHT));
			dc.Draw3dRect(pItem->rc, GetXtremeColor(XPCOLOR_HIGHLIGHT_BORDER), GetXtremeColor(XPCOLOR_HIGHLIGHT_BORDER));

		}

		if (pItem->type == itemPane)
		{
			CPoint ptIcon(pItem->rc.left + 2, pItem->rc.CenterPoint().y - szIcon.cy/2);

			CXTPImageManagerIcon* pImage = pItem->pPane->GetIcon(szIcon.cx);
			if (pImage) pImage->Draw(&dc, ptIcon, szIcon);
		}
		else
		{
			CPoint ptIcon(pItem->rc.left + 2, pItem->rc.CenterPoint().y - szIcon.cy/2);

			HICON hIcon = (HICON)::SendMessage(pItem->hWndFrame, WM_GETICON, ICON_SMALL, 0);
			if (hIcon == NULL) hIcon = (HICON)::SendMessage(pItem->hWndFrame, WM_GETICON, ICON_BIG, 0);
			if (hIcon == NULL) hIcon = (HICON)(ULONG_PTR)::GetClassLongPtr(pItem->hWndFrame, GCLP_HICONSM);

			if (hIcon)
			{
				if (GetExStyle() & WS_EX_LAYOUTRTL)
					::DrawIconEx(dc, ptIcon.x + szIcon.cx, ptIcon.y, hIcon, -szIcon.cx, szIcon.cy, 0, 0, DI_NORMAL);
				else
					::DrawIconEx(dc, ptIcon.x, ptIcon.y, hIcon, szIcon.cx, szIcon.cy, 0, 0, DI_NORMAL);
			}
		}

		CRect rcText(pItem->rc);
		rcText.left += 5 + szIcon.cx;
		dc.DrawText(GetItemCaption(pItem), rcText, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX);
	}

	CRect rcInfoPane(11, rc.bottom - 100, rc.right - 11, rc.bottom - 10);
	dc.Draw3dRect(rcInfoPane, GetXtremeColor(XPCOLOR_HIGHLIGHT_BORDER), GetXtremeColor(XPCOLOR_HIGHLIGHT_BORDER));

	if (!m_pSelected)
		return;

	rcInfoPane.DeflateRect(15, 13, 15, 13);

	font.SetFont(&m_fntBold);

	CRect rcCaption(rcInfoPane.left, rcInfoPane.top, rcInfoPane.right, rcInfoPane.top + rcInfoPane.Height() / 3);
	dc.DrawText(GetItemCaption(m_pSelected), rcCaption, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX);

	font.SetFont(&m_fnt);

	CRect rcTypeName(rcInfoPane.left, rcCaption.bottom, rcInfoPane.right, rcCaption.bottom + rcInfoPane.Height() / 3);
	dc.DrawText(GetItemDescription(m_pSelected), rcTypeName, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX);

	CRect rcPathName(rcInfoPane.left, rcTypeName.bottom, rcInfoPane.right, rcInfoPane.bottom);
	dc.DrawText(GetItemPath(m_pSelected), rcPathName, DT_SINGLELINE | DT_VCENTER | DT_PATH_ELLIPSIS | DT_NOPREFIX);
}

void CXTPDockingPaneWindowSelect::Select(int nItem)
{
	Select(nItem >= 0 && nItem < m_arrItems.GetSize() ? m_arrItems[nItem] : NULL);
}


void CXTPDockingPaneWindowSelect::Select(CItem* pItem)
{
	if (m_pSelected != pItem)
	{
		m_pSelected = pItem;
		Invalidate(FALSE);
	}
}

void CXTPDockingPaneWindowSelect::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	CMiniFrameWnd::OnChar(nChar, nRepCnt, nFlags);
}

void CXTPDockingPaneWindowSelect::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	OnKeyDown(nChar, nRepCnt, nFlags);
}

void CXTPDockingPaneWindowSelect::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/)
{
	XTPDrawHelpers()->KeyToLayout(this, nChar);

	if (nChar == VK_TAB)
	{
		if (GetKeyState(VK_SHIFT) >= 0)
		{
			int nSelected = m_pSelected ? m_pSelected->nIndex + 1: 0;
			if (nSelected >= m_arrItems.GetSize())
				nSelected = m_nFirstFile == m_arrItems.GetSize() ? 0 : m_nFirstFile;
			else if (nSelected == m_nFirstFile)
				nSelected = 0;
			Select(nSelected);

		}
		else
		{
			int nSelected = m_pSelected ? m_pSelected->nIndex - 1: (int)m_arrItems.GetSize() - 1;
			if (nSelected < 0)
				nSelected = m_nFirstFile == 0 ? (int)m_arrItems.GetSize() - 1 : m_nFirstFile - 1;
			else if (nSelected == m_nFirstFile - 1)
				nSelected = (int)m_arrItems.GetSize() - 1;
			Select(nSelected);
		}
	}
	else if (nChar == VK_LEFT)
	{
		if (m_arrColumns.GetSize() > 1 && m_pSelected)
		{
			int nColumn = m_pSelected->nColumn;
			int nRow = m_pSelected->nRow;
			CColumn& col = m_arrColumns[nColumn > 0 ? nColumn - 1 : m_arrColumns.GetSize() - 1];
			int nItem = col.nFirst + nRow > col.nLast ? col.nLast : col.nFirst + nRow;
			Select(nItem);
		}

	}
	else if (nChar == VK_RIGHT)
	{
		if (m_arrColumns.GetSize() > 1 && m_pSelected)
		{
			int nColumn = m_pSelected->nColumn;
			int nRow = m_pSelected->nRow;
			CColumn& col = m_arrColumns[nColumn < m_arrColumns.GetSize() - 1 ? nColumn + 1 : 0];
			int nItem = col.nFirst + nRow > col.nLast ? col.nLast : col.nFirst + nRow;
			Select(nItem);

		}
	}
	else if (nChar == VK_DOWN)
	{
		int nSelected = m_pSelected ? m_pSelected->nIndex + 1 : 0;
		Select(nSelected < m_arrItems.GetSize() ? nSelected : 0);
	}
	else if (nChar == VK_UP)
	{
		int nSelected = m_pSelected ? m_pSelected->nIndex - 1 : -1;
		Select(nSelected >= 0 ? nSelected : (int)m_arrItems.GetSize() - 1);
	}
	else if (nChar == VK_SHIFT)
	{

	}
	else
	{
		EndModalLoop(IDOK);
	}
}

void CXTPDockingPaneWindowSelect::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (::GetKeyState(VK_CONTROL) >= 0)
	{
		EndModalLoop(IDOK);
	}

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

void CXTPDockingPaneWindowSelect::OnLButtonDown(UINT nFlags, CPoint point)
{
	CXTPClientRect rc(this);
	if (!rc.PtInRect(point))
		EndModalLoop(IDCANCEL);

	CItem* pItem = HitTest(point);
	if (pItem)
	{
		Select(pItem);
	}

	CWnd::OnLButtonDown(nFlags, point);
}

void CXTPDockingPaneWindowSelect::OnLButtonUp(UINT nFlags, CPoint point)
{
	CItem* pItem = HitTest(point);
	if (pItem)
	{
		m_pSelected = pItem;
		EndModalLoop(IDOK);
	}

	CWnd::OnLButtonUp(nFlags, point);
}

void CXTPDockingPaneWindowSelect::OnMouseMove(UINT /*nFlags*/, CPoint point)
{
	CItem* pItem = HitTest(point);
	::SetCursor(pItem ? m_hHandCursor : m_hArrowCursor);
}

void CXTPDockingPaneWindowSelect::PostNcDestroy()
{

}

//////////////////////////////////////////////////////////////////////////
// CXTPDockingPaneKeyboardHook

CThreadLocal<CXTPDockingPaneKeyboardHook> CXTPDockingPaneKeyboardHook::_xtpKeyboardThreadState;



CXTPDockingPaneKeyboardHook::CXTPDockingPaneKeyboardHook()
{
	m_pWindowSelect = 0;

	m_hHookKeyboard = 0;

#ifdef _AFXDLL
	m_pModuleState = 0;
#endif
}

CXTPDockingPaneKeyboardHook::~CXTPDockingPaneKeyboardHook()
{
	ASSERT(m_mapSites.IsEmpty());
	ASSERT(m_hHookKeyboard == 0);
}

CXTPDockingPaneManager* CXTPDockingPaneKeyboardHook::FindFocusedManager()
{
	HWND hWnd = GetFocus();
	while (hWnd)
	{
		CXTPDockingPaneManager* pManager = Lookup(hWnd);
		if (pManager)
		{
			if (!pManager->GetSite()->IsWindowEnabled())
				return FALSE;

			return pManager;
		}

		LONG dwStyle = ::GetWindowLong(hWnd, GWL_STYLE);

		if (dwStyle & WS_CHILD)
		{
			hWnd = ::GetParent(hWnd);
		}
		else if (::GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
		{
			CXTPDockingPaneMiniWnd* pMinWnd = DYNAMIC_DOWNCAST(CXTPDockingPaneMiniWnd,
				CWnd::FromHandle(hWnd));

			if (pMinWnd != NULL)
				return pMinWnd->GetDockingPaneManager();

			return NULL;
		}
		else return NULL;
	}
	return NULL;
}

LRESULT CALLBACK CXTPDockingPaneKeyboardHook::KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
	CXTPDockingPaneKeyboardHook* pKeyboardManager = GetThreadState();

	if (code != HC_ACTION)
		return CallNextHookEx(pKeyboardManager->m_hHookKeyboard, code, wParam, lParam);

	if ((!(HIWORD(lParam) & KF_UP)))
	{
		if (wParam == VK_TAB && GetKeyState(VK_CONTROL) < 0 && !(HIWORD(lParam) & KF_ALTDOWN))
		{
			SAFE_MANAGE_STATE(pKeyboardManager->m_pModuleState);

			CXTPDockingPaneWindowSelect*& pWindowSelect = pKeyboardManager->m_pWindowSelect;

			if (pWindowSelect)
			{
				pWindowSelect->OnKeyDown(VK_TAB, 0, 0);
				return TRUE;
			}

			CXTPDockingPaneManager* pManager = pKeyboardManager->FindFocusedManager();
			if (pManager && (pManager->IsKeyboardNavigateEnabled() & xtpPaneKeyboardUseCtrlTab))
			{
				pWindowSelect = (CXTPDockingPaneWindowSelect*)pManager->GetKeyboardWindowSelectClass()->CreateObject();
				pWindowSelect->m_pManager = pManager;
				pWindowSelect->m_bActivatePanes = FALSE;

				BOOL bResult = pWindowSelect->DoModal();

				delete pWindowSelect;
				pWindowSelect = 0;

				if (bResult)
					return TRUE;
			}
		}

		if ((HIWORD(lParam) & KF_ALTDOWN) && (wParam == VK_F7) && GetKeyState(VK_CONTROL) >= 0)
		{
			SAFE_MANAGE_STATE(pKeyboardManager->m_pModuleState);

			CXTPDockingPaneWindowSelect*& pWindowSelect = pKeyboardManager->m_pWindowSelect;

			if (pWindowSelect)
			{
				pWindowSelect->OnKeyDown(VK_TAB, 0, 0);
				return TRUE;
			}

			CXTPDockingPaneManager* pManager = pKeyboardManager->FindFocusedManager();
			if (pManager && (pManager->IsKeyboardNavigateEnabled() & xtpPaneKeyboardUseAltF7))
			{
				pWindowSelect = (CXTPDockingPaneWindowSelect*)pManager->GetKeyboardWindowSelectClass()->CreateObject();
				pWindowSelect->m_pManager = pManager;
				pWindowSelect->m_bActivatePanes = TRUE;

				BOOL bResult = pWindowSelect->DoModal();

				delete pWindowSelect;
				pWindowSelect = 0;

				if (bResult)
					return TRUE;
			}
		}

		if ((HIWORD(lParam) & KF_ALTDOWN) && (wParam == VK_F6) && GetKeyState(VK_CONTROL) >= 0) // Alt + F6
		{
			SAFE_MANAGE_STATE(pKeyboardManager->m_pModuleState);

			CXTPDockingPaneManager* pManager = pKeyboardManager->FindFocusedManager();
			if (pManager && (pManager->IsKeyboardNavigateEnabled() & xtpPaneKeyboardUseAltF6))
			{
				pManager->PostMessage(WM_SYSCOMMAND,
					(UINT)(GetKeyState(VK_SHIFT) >= 0 ? SC_NEXTWINDOW : SC_PREVWINDOW), 0);
				return TRUE;
			}
		}

		if ((HIWORD(lParam) & KF_ALTDOWN) && ((wParam == 0xBD || wParam == VK_SUBTRACT)) &&
			GetKeyState(VK_CONTROL) >= 0 && GetKeyState(VK_SHIFT) >= 0) //  Alt + '-'
		{
			SAFE_MANAGE_STATE(pKeyboardManager->m_pModuleState);

			CXTPDockingPaneManager* pManager = pKeyboardManager->FindFocusedManager();
			if (pManager && (pManager->IsKeyboardNavigateEnabled() & xtpPaneKeyboardUseAltMinus))
			{
				CXTPDockingPane* pPane = pManager->GetActivePane();
				if (pPane)
				{
					if (!pPane->IsClosed() && !pPane->IsHidden())
						pManager->PostMessage(WM_SYSCOMMAND, SC_KEYMENU, MAKELONG(TEXT('-'), 0));
					return TRUE;
				}
			}
		}
	}

	return CallNextHookEx(pKeyboardManager->m_hHookKeyboard, code, wParam, lParam);
}

CXTPDockingPaneManager* CXTPDockingPaneKeyboardHook::Lookup(HWND hSite) const
{
	CXTPDockingPaneManager* pManager = NULL;
	if (m_mapSites.Lookup(hSite, pManager))
		return pManager;

	return NULL;
}

void CXTPDockingPaneKeyboardHook::SetupKeyboardHook(CXTPDockingPaneManager* pManager, BOOL bSetup)
{
	if (!pManager->GetSite())
		return;

	if (bSetup)
	{
		if (Lookup(pManager->GetSite()->GetSafeHwnd()))
			return;

		if (m_hHookKeyboard == 0)
		{
			m_hHookKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, AfxGetInstanceHandle(), GetCurrentThreadId());
		}


#ifdef _AFXDLL
		m_pModuleState = AfxGetModuleState();
#endif

		m_mapSites.SetAt(pManager->GetSite()->GetSafeHwnd(), pManager);
	}
	else
	{
		if (Lookup(pManager->GetSite()->GetSafeHwnd()))
		{
			m_mapSites.RemoveKey(pManager->GetSite()->GetSafeHwnd());
		}

		if (m_hHookKeyboard && m_mapSites.IsEmpty())
		{
			UnhookWindowsHookEx(m_hHookKeyboard);
			m_hHookKeyboard = 0;
		}
	}
}