// XTPSyntaxEditAutoCompleteWnd.cpp: implementation of the CXTPSyntaxEditAutoCompleteWnd 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 SYNTAX EDIT 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"

// common includes
#include "Common/XTPDrawHelpers.h"
#include "Common/XTPSmartPtrInternalT.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPColorManager.h"

// syntax editor includes
#include "XTPSyntaxEditDefines.h"
#include "XTPSyntaxEditStruct.h"
#include "XTPSyntaxEditAutoCompleteWnd.h"
#include "XTPSyntaxEditCtrl.h"
#include "XTPSyntaxEditDrawTextProcessor.h"

#include <search.h>

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

#define XTP_SYNTAXEDIT_AUTOCOMPLETE_HEIGHT_MAX  160
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

BEGIN_MESSAGE_MAP(CXTPSyntaxEditAutoCompleteWnd, CWnd)
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_MOUSEWHEEL()
	ON_WM_KEYDOWN()
	ON_WM_CHAR()
	ON_WM_VSCROLL()
	ON_WM_SETCURSOR()
	ON_WM_PAINT()
END_MESSAGE_MAP()

CXTPSyntaxEditAutoCompleteWnd::CXTPSyntaxEditAutoCompleteWnd()
{
	RegisterWindowClass();

	m_strSearch = _T("");
	m_pParentWnd = NULL;
	m_nLineHeight = 0;
	m_nLines = 0;
	m_nWndHeight = 0;
	m_nWndWidth = 160;
	m_nHighLightLine = -1;
	m_nFirstDisplayedStr = 0;
	m_nBordersHeight = 0;
	m_bActive = FALSE;
	m_bFixedBottom = FALSE;

	m_strDelims = _T("");
	VERIFY( m_ilACGlyphs.Create(XTP_IDB_EDIT_GLYPHS, 16, 16, RGB(255, 255, 255)) );
}

CXTPSyntaxEditAutoCompleteWnd::~CXTPSyntaxEditAutoCompleteWnd()
{
	RemoveAll();
	DestroyWindow();
}

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

	if (!(::GetClassInfo(hInstance, XTP_EDIT_CLASSNAME_AUTOCOMPLETEWND, &wndcls)))
	{
		// otherwise we need to register a new class
		wndcls.style = CS_SAVEBITS | CS_DBLCLKS;
		wndcls.lpfnWndProc = ::DefWindowProc;
		wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
		wndcls.hInstance = hInstance;
		wndcls.hIcon = NULL;
		wndcls.hCursor = ::LoadCursor(NULL, IDC_ARROW);
		wndcls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
		wndcls.lpszMenuName = NULL;
		wndcls.lpszClassName = XTP_EDIT_CLASSNAME_AUTOCOMPLETEWND;

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

	return TRUE;
}

BOOL CXTPSyntaxEditAutoCompleteWnd::Create(CWnd* pParentWnd)
{
	if (!CWnd::CreateEx(WS_EX_TOOLWINDOW, XTP_EDIT_CLASSNAME_AUTOCOMPLETEWND, NULL,
		WS_CHILD | WS_DLGFRAME | WS_VSCROLL | WS_DISABLED, CRect(0, 0, 160, 160),
		GetDesktopWindow(), NULL))
	{
		TRACE0("Failed to create Auto Complete View.\n");
		return FALSE;
	}

	CXTPClientRect rcClient(this);
	CXTPWindowRect rcWin(this);
	m_nBordersHeight = (rcWin.Height()-rcClient.Height());
	m_pParentWnd = DYNAMIC_DOWNCAST(CXTPSyntaxEditCtrl, pParentWnd);

	return TRUE;
}

void CXTPSyntaxEditAutoCompleteWnd::Show(CPoint pt, CString strSearch)
{
	if (Filter(strSearch) < 1)
		return;

	m_nFirstDisplayedStr = 0;
	int nSearch = Search(strSearch);
	m_nHighLightLine = nSearch;

//<<>>Forum Proposal
	m_bHighLight = (m_nHighLightLine >= 0);
//<<>>
	ScrollTo(nSearch);

	RefreshMetrics();

	AdjusLayout();

	CRect rcWnd(pt.x - 21, pt.y, pt.x - 21 + m_nWndWidth, pt.y + m_nWndHeight);

	if (GetParent())
		GetParent()->ClientToScreen(&rcWnd);

	_AdjustWndRect(rcWnd);

	if (GetParent())
		GetParent()->ScreenToClient(&rcWnd);

	//SetWindowPos(NULL, pt.x - 21, pt.y, m_nWndWidth, m_nWndHeight, SWP_SHOWWINDOW);
	SetWindowPos(NULL, rcWnd.left, rcWnd.top, rcWnd.Width(), rcWnd.Height(), SWP_SHOWWINDOW);

	BOOL bVScrollEnabled = AdjusLayout(rcWnd.Height());

	ShowScrollBar(SB_VERT, bVScrollEnabled);

	RedrawWindow();

	EnableWindow(TRUE);
	SetFocus();

	m_bActive = TRUE;
	int nSearchLen = strSearch.GetLength();
	m_bFilteredMode = nSearchLen > 0;
	m_nStartReplacePos = m_pParentWnd->GetCurCol() - nSearchLen;
	m_nEndReplacePos = m_nStartReplacePos + nSearchLen;
	m_strSearch = strSearch;
}

void CXTPSyntaxEditAutoCompleteWnd::_AdjustWndRect(CRect& rc)
{
	CRect rcWorkArea = XTPMultiMonitor()->GetWorkArea(); //from cursor point
	//CRect rcWorkArea = XTPMultiMonitor()->GetWorkArea(this);
	m_bFixedBottom = rc.bottom > rcWorkArea.bottom;

	if (m_bFixedBottom)
	{
		int nNewTop = rc.top - m_nWndHeight -
			m_pParentWnd->GetDrawTextProcessor().GetRowHeight();
		rc.top = max(rcWorkArea.top, nNewTop);
		rc.bottom = min(rcWorkArea.bottom, rc.top + m_nWndHeight);

		m_bFixedBottom = TRUE;
	}

	if (rc.right > rcWorkArea.right)
	{
		int nOffsetX = rc.right - rcWorkArea.right;
		rc.left = max(rcWorkArea.left, rc.left - nOffsetX);
		rc.right = rcWorkArea.right;
	}

	if (rc.left < rcWorkArea.left)
	{
		int nOffsetX = rcWorkArea.left - rc.left;
		rc.right = rc.right + nOffsetX;
		rc.left = rcWorkArea.left;
	}
}

void CXTPSyntaxEditAutoCompleteWnd::SetWndWidth(int nWidth)
{
	m_nWndWidth = nWidth;

	if (m_hWnd)
	{
		CXTPWindowRect rcWnd(this);
		if (GetParent())
			GetParent()->ScreenToClient(&rcWnd);
		rcWnd.right = rcWnd.left + m_nWndWidth;

		MoveWindow(&rcWnd);

		AdjusLayout(rcWnd.Height());

		RedrawWindow();
	}
}

void CXTPSyntaxEditAutoCompleteWnd::UpdateFilteredList()
{
	int nIdx = Search(m_strSearch);
	if (nIdx >=0)
	{
		Filter(m_strSearch);

		m_nFirstDisplayedStr = 0;
		nIdx = Search(m_strSearch);

		m_nHighLightLine = nIdx;

		AdjusLayout();

		ScrollTo(nIdx);

		CXTPWindowRect rcWnd(this);
		CRect rcWorkArea = XTPMultiMonitor()->GetWorkArea(this);

		if (m_bFixedBottom)
			rcWnd.top = rcWnd.bottom - m_nWndHeight;
		else
			rcWnd.bottom = min(rcWorkArea.bottom, rcWnd.top + m_nWndHeight);

		if (GetParent())
			GetParent()->ScreenToClient(&rcWnd);

		MoveWindow(&rcWnd);

		AdjusLayout(rcWnd.Height());

		RedrawWindow();
	}
}

void CXTPSyntaxEditAutoCompleteWnd::OnLButtonDown(UINT nFlags, CPoint point)
{

	CWnd::OnLButtonDown(nFlags, point);

	int nHitLine = HitTest(point);
	if (nHitLine > -1)
	{   m_bHighLight = TRUE;
		m_nHighLightLine = m_nFirstDisplayedStr + nHitLine;
		RedrawWindow();
	}

}

void CXTPSyntaxEditAutoCompleteWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
	CWnd::OnRButtonDown(nFlags, point);
	int nHitLine = HitTest(point);
	if (nHitLine > -1)
	{
		m_nHighLightLine = m_nFirstDisplayedStr + nHitLine;
		RedrawWindow();
	}
}

void CXTPSyntaxEditAutoCompleteWnd::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	CWnd::OnLButtonDblClk(nFlags, point);
	m_bHighLight = TRUE;
	ReturnSelected(FALSE);
	Hide();
}

void CXTPSyntaxEditAutoCompleteWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	int nDataMax = (int)m_arrACDataFiltered.GetSize();
	int nPage = m_nLines - 1;
	int nLastDisp = m_nFirstDisplayedStr + nPage;
	m_bHighLight = TRUE;
	switch (nChar)
	{
	case VK_UP:
		m_nHighLightLine -= nRepCnt;

		if (m_nHighLightLine >= 0)
		{
			if (m_nFirstDisplayedStr > m_nHighLightLine && ScrollTo(m_nHighLightLine))
			{
				SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
			}

			RedrawWindow();
		}
		else
		{
			m_nHighLightLine = 0;
		}

		break;
	case VK_DOWN:
		m_nHighLightLine += nRepCnt;
		if (m_nHighLightLine < nDataMax)
		{
			int nL = m_nLines - 1;
			nLastDisp = m_nFirstDisplayedStr + nL;
			if (nLastDisp < m_nHighLightLine)
			{
				if (ScrollTo(m_nHighLightLine - nL))
				{
					SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
				}
			}

			RedrawWindow();
		}
		else
		{
			m_nHighLightLine = nDataMax - 1;
		}
		break;
	case VK_PRIOR:
		if (m_nFirstDisplayedStr < m_nHighLightLine)
		{
			m_nHighLightLine = m_nFirstDisplayedStr;
		}
		else
		{
			if (m_nHighLightLine > 0)
				m_nHighLightLine = max(0, m_nHighLightLine - m_nLines + 1);
			else
				m_nHighLightLine -= m_nLines + 1;
		}

		if (m_nHighLightLine >= 0)
		{
			if (m_nFirstDisplayedStr > m_nHighLightLine && ScrollTo(m_nHighLightLine))
			{
					SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
			}

			RedrawWindow();
		}
		else
		{
			m_nHighLightLine = 0;
		}
		break;
	case VK_NEXT:

		if (m_nFirstDisplayedStr + nPage > m_nHighLightLine)
		{
			m_nHighLightLine = m_nFirstDisplayedStr + nPage;
		}
		else
		{
			if (m_nHighLightLine < nDataMax)
				m_nHighLightLine = min(nDataMax - 1, m_nHighLightLine + nPage);
			else
				m_nHighLightLine += nPage;
		}

		if (m_nHighLightLine < nDataMax)
		{
			if (nLastDisp < m_nHighLightLine)
			{
				if (ScrollTo(m_nHighLightLine - nPage))
				{
					SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
				}
			}

			RedrawWindow();
		}
		else
		{
			m_nHighLightLine = nDataMax - 1;
		}

		break;
	case VK_END:
		if (m_nHighLightLine != nDataMax - 1 && ScrollTo(nDataMax - m_nLines))
		{
			SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
			m_nHighLightLine = nDataMax - 1;
			RedrawWindow();
		}
		break;
	case VK_HOME:
		if (m_nHighLightLine != 0 && ScrollTo(0))
		{
			SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
			m_nHighLightLine = 0;
			RedrawWindow();
		}
		break;
	case VK_BACK:

		m_nEndReplacePos--;
		if (m_strSearch.GetLength() > 0)
			m_strSearch = m_strSearch.Left(m_strSearch.GetLength() - 1);

		if (m_nStartReplacePos >= m_nEndReplacePos)
		{
			Hide();
			m_pParentWnd->SendMessage(WM_LBUTTONUP, 0, 0);
		}
		else
		{
			UpdateFilteredList();
		}
		break;
	case VK_LEFT :
	case VK_RIGHT :
	case VK_MENU :
	case VK_CONTROL:
	case VK_ESCAPE:
		Hide();
		break;

	case VK_RETURN:
	case VK_SPACE:
		ReturnSelected(FALSE);
		Hide();
		break;

	}

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

void CXTPSyntaxEditAutoCompleteWnd::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (IsCloseTag((TCHAR)nChar))
	{
		ReturnSelected(FALSE);
		Hide();
	}

	if (isalnum((TCHAR)nChar) || (TCHAR)nChar == _T('_')) //<<>>
	{
		m_nEndReplacePos++;
		m_strSearch += (TCHAR)nChar;

		UpdateFilteredList();

		int nFound = Search(m_strSearch);
		if (nFound >= 0)
		{
			m_nHighLightLine = nFound;
			m_bHighLight = TRUE;
			if (m_nFirstDisplayedStr > m_nHighLightLine)
			{
				if (ScrollTo(m_nHighLightLine))
					SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
			}
			if (m_nFirstDisplayedStr + m_nLines <= m_nHighLightLine)
			{
				if (ScrollTo(m_nHighLightLine - m_nLines + 1))
					SetScrollPos(SB_VERT, m_nFirstDisplayedStr, TRUE);
			}
			RedrawWindow();
			CString sTxt = m_arrACDataFiltered.GetAt(m_nHighLightLine)->m_strText;
			if (m_bFilteredMode && sTxt.CompareNoCase(m_strSearch) == 0)
			{
				int  nCnt = (int) m_arrACDataFiltered.GetSize();
				if (nCnt == 1)
				{
					ReturnSelected(TRUE);
					Hide();
				}
			}
		}
		else if (m_bFilteredMode)
		{
			Hide();
		}
		else
		{
			m_bHighLight = FALSE;
			RedrawWindow();
		}
	}

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

void CXTPSyntaxEditAutoCompleteWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	UNREFERENCED_PARAMETER(nPos);
	UNREFERENCED_PARAMETER(pScrollBar);

	SCROLLINFO info;
	GetScrollInfo(SB_VERT, &info);

	switch (nSBCode) {
	case SB_LINEDOWN:
		if (ScrollTo(m_nFirstDisplayedStr + 1))
		{
			SetScrollPos(SB_VERT, m_nFirstDisplayedStr);
			RedrawWindow();
		}

		break;
	case SB_LINEUP:
		if (ScrollTo(m_nFirstDisplayedStr - 1))
		{
			SetScrollPos(SB_VERT, m_nFirstDisplayedStr);
			RedrawWindow();
		}

		break;  case SB_PAGEDOWN:
		if (ScrollTo(m_nFirstDisplayedStr + m_nLines - 1))
		{
			SetScrollPos(SB_VERT, m_nFirstDisplayedStr);
			RedrawWindow();
		}
		break;
	case SB_PAGEUP:
		if (ScrollTo(m_nFirstDisplayedStr - m_nLines + 1))
		{
			SetScrollPos(SB_VERT, m_nFirstDisplayedStr);
			RedrawWindow();
		}
		break;
	case SB_TOP:
		SetScrollPos(SB_VERT, info.nMin);
		RedrawWindow();
		break;
	case SB_BOTTOM:
		SetScrollPos(SB_VERT, info.nMax);
		RedrawWindow();
		break;
	case SB_THUMBTRACK:
		{
			SCROLLINFO si;
			ZeroMemory(&si, sizeof(SCROLLINFO));
			si.cbSize = sizeof(SCROLLINFO);
			si.fMask = SIF_TRACKPOS;

			if (GetScrollInfo(SB_VERT, &si))
			{
				if (si.nTrackPos != m_nFirstDisplayedStr && ScrollTo(si.nTrackPos))
				{
					SetScrollPos(SB_VERT, si.nTrackPos);
					RedrawWindow();
				}

			}
		}
		break;
	case SB_ENDSCROLL:
		return;
	default:
		break;
	}
}

BOOL CXTPSyntaxEditAutoCompleteWnd::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	int nCount = labs(zDelta) / WHEEL_DELTA;
	for (int i = 0; i < nCount; i++)
	{
		SendMessage(WM_VSCROLL, (WPARAM)(zDelta > 0 ? SB_LINEUP : SB_LINEDOWN));
	}

	return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}

BOOL CXTPSyntaxEditAutoCompleteWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	UNREFERENCED_PARAMETER(message);
	UNREFERENCED_PARAMETER(nHitTest);
	UNREFERENCED_PARAMETER(pWnd);
	::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
	return TRUE;
}

BOOL CXTPSyntaxEditAutoCompleteWnd::AdjusLayout(int nHeightMax)
{

	CWindowDC dc(NULL);
	CXTPFontDC fontDC(&dc, &m_fontBasic);

	int nDataLines = (int)m_arrACDataFiltered.GetSize();
	m_nLineHeight = dc.GetTextExtent(_T("A"), 1).cy + 6;

	if (nHeightMax <= 0)
		nHeightMax = XTP_SYNTAXEDIT_AUTOCOMPLETE_HEIGHT_MAX;

	if (nDataLines * m_nLineHeight < nHeightMax)
	{
		m_nLines = nDataLines;
	}
	else
	{
		m_nLines = (int) nHeightMax / m_nLineHeight;
	}

	BOOL bVScrollEnabled = nDataLines > m_nLines;

	ShowScrollBar(SB_VERT, bVScrollEnabled);
	EnableScrollBarCtrl(SB_VERT, bVScrollEnabled);

	//--------------------------------------------------
	m_nWndHeight = m_nLines * m_nLineHeight + m_nBordersHeight;

	CXTPClientRect rcClient(this);

	CRect rc(0, 0, rcClient.Width(), m_nLineHeight);
	m_arrGrid.RemoveAll();

	for (int nLine = 0; nLine < m_nLines; nLine++)
	{
		m_arrGrid.Add(rc);
		rc.OffsetRect(0, m_nLineHeight);
	}

	//--------------------------------------------------
	if (bVScrollEnabled)
	{
		SCROLLINFO scrollInfo;

		scrollInfo.cbSize = sizeof(SCROLLINFO);
		scrollInfo.fMask = SIF_ALL;
		scrollInfo.nMin = 0;
		scrollInfo.nMax = max(0, nDataLines-1);
		scrollInfo.nPage = m_nLines;
		scrollInfo.nPos = m_nFirstDisplayedStr;

		SetScrollInfo(SB_VERT, &scrollInfo, FALSE);
	}

	return bVScrollEnabled;
}

void CXTPSyntaxEditAutoCompleteWnd::OnPaint()
{
	CPaintDC dc(this);
	CXTPFontDC fontDC(&dc, &m_fontBasic);

	CRect rc;
	CRect rcText;
	CString str;

	int nData = 0;
	int nHighLightedLine = m_nHighLightLine - m_nFirstDisplayedStr;

	for (int nLine = 0; nLine < m_nLines; nLine++)
	{
		rc.CopyRect(m_arrGrid.GetAt(nLine));

		int nIcon = min(m_arrACDataFiltered.GetAt(nData)->m_nIcon, m_ilACGlyphs.GetImageCount() - 1);
		m_ilACGlyphs.Draw(&dc, nIcon, CPoint(rc.left, rc.top), ILD_NORMAL);
		rc.DeflateRect(19, 0, 0, 0);

		if (nLine == nHighLightedLine && m_bHighLight)
		{
			dc.FillSolidRect(rc.left, rc.top, rc.Width(), rc.Height(), m_clrHighLight);
			dc.SetTextColor(m_clrHighLightText);
		}
		else
		{
			dc.SetTextColor(m_clrWindowText);
			dc.FillSolidRect(rc.left, rc.top, rc.Width(), rc.Height(), m_clrWindow);
		}

		rcText.CopyRect(rc);
		rcText.DeflateRect(2, 3, 2, 3);
		nData = m_nFirstDisplayedStr + nLine;

		dc.TextOut(rcText.left, rcText.top, m_arrACDataFiltered.GetAt(nData)->m_strText);

		if (nLine == nHighLightedLine)
		{
			dc.DrawFocusRect(rc);
		}
	}

}

void CXTPSyntaxEditAutoCompleteWnd::RefreshMetrics()
{
	m_fontBasic.CreateStockObject(DEFAULT_GUI_FONT);
	m_clrHighLight = GetSysColor(COLOR_HIGHLIGHT);
	m_clrWindow = GetSysColor(COLOR_WINDOW);
	m_clrWindowText = GetSysColor(COLOR_WINDOWTEXT);
	m_clrHighLightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
}

int CXTPSyntaxEditAutoCompleteWnd::HitTest(CPoint ptTest) const
{
	int nHitLine = -1;
	for (int nLine = 0; nLine < m_nLines; nLine++)
	{
		if (m_arrGrid.GetAt(nLine).PtInRect(ptTest))
		{
			nHitLine = nLine;
		}
	}

	return nHitLine;
}

void CXTPSyntaxEditAutoCompleteWnd::SetList(CXTPSyntaxEditACDataArray& parrData)
{
	RemoveAll();
	m_arrACData.Copy(parrData);
	Sort();
}

void CXTPSyntaxEditAutoCompleteWnd::SetCloseTags(CString strCloseTags)
{
	m_strCloseTags = strCloseTags;
}

BOOL CXTPSyntaxEditAutoCompleteWnd::IsCloseTag(CString strToTest)
{
	BOOL bTagFound = FALSE;
	if (strToTest == _T("~"))
		return bTagFound;

	m_strTmpCloseTag += strToTest;
	int nTmpLen = m_strTmpCloseTag.GetLength();
	int nFound = -1;
	if ((nFound = m_strCloseTags.Find(m_strTmpCloseTag)) >=0)
	{
		if (nFound+nTmpLen == m_strCloseTags.GetLength())
		{
			bTagFound = TRUE;
		}
		else
		{
			TCHAR ch = m_strCloseTags.GetAt(nFound + nTmpLen);
			if (ch == _T('~'))
			{
				bTagFound = TRUE;
			}
		}
	}
	else
	{
		m_strTmpCloseTag = _T("");
	}

	return bTagFound;
}


void CXTPSyntaxEditAutoCompleteWnd::SetOpenTags(CString strOpenTags)
{
	m_strOpenTags = strOpenTags;
}

BOOL CXTPSyntaxEditAutoCompleteWnd::IsOpenTag(CString strToTest)
{

	BOOL bTagFound = FALSE;
	if (strToTest == _T("~"))
		return bTagFound;

	m_strTmpOpenTag += strToTest;
	int nTmpLen = m_strTmpOpenTag.GetLength();
	int nFound = -1;
	if ((nFound = m_strOpenTags.Find(m_strTmpOpenTag)) >=0 )
	{
		if (nFound + nTmpLen == m_strOpenTags.GetLength())
		{
			bTagFound = TRUE;
		}
		else
		{
			TCHAR ch = m_strOpenTags.GetAt(nFound + nTmpLen);
			if (ch == _T('~'))
			{
				bTagFound = TRUE;
			}
		}

	}
	else
	{
		m_strTmpOpenTag = _T("");
	}

	return bTagFound;
}

BOOL CXTPSyntaxEditAutoCompleteWnd::ScrollTo(int nNewLine)
{
	BOOL bScrollRequestCompleted = FALSE;

	if (nNewLine + m_nLines <= m_arrACDataFiltered.GetSize() && nNewLine >= 0)
	{
		bScrollRequestCompleted = TRUE;
		m_nFirstDisplayedStr = nNewLine;
	}

	return bScrollRequestCompleted;
}

void CXTPSyntaxEditAutoCompleteWnd::ReturnSelected(BOOL bAdjust)
{
	if (m_nHighLightLine < 0 || !m_bHighLight)
		return;

	int nCurrentRow = m_pParentWnd->GetCurrentDocumentRow();

	CString strRet = m_arrACDataFiltered.GetAt(m_nHighLightLine)->m_strText;

	if (m_strTmpCloseTag.GetLength() > 0)
	{
		strRet += m_strTmpCloseTag;
		m_strTmpCloseTag = _T("");
	}
	else if (bAdjust)
	{
		strRet = strRet.Left(strRet.GetLength() - 1);
		m_nEndReplacePos = max(m_nStartReplacePos, m_nEndReplacePos - 1);
	}

	m_pParentWnd->Select(nCurrentRow, m_nStartReplacePos, nCurrentRow, m_nEndReplacePos, FALSE);
	m_pParentWnd->ReplaceSel(strRet);

	m_pParentWnd->SetCurCaretPos(m_pParentWnd->GetCurrentDocumentRow(), m_pParentWnd->m_nDispCol);
	m_pParentWnd->SetDocModified();

}

void CXTPSyntaxEditAutoCompleteWnd::Hide()
{
	if (!m_bActive)
		return;

	m_bActive = FALSE;
	EnableWindow(FALSE);
	ShowWindow(SW_HIDE);
	m_bFilteredMode = FALSE;
	m_pParentWnd->PostMessage(WM_LBUTTONUP, 0, 0);
	RedrawWindow();
}

int CXTPSyntaxEditAutoCompleteWnd::CompareACData(const XTP_EDIT_ACDATA** p1, const XTP_EDIT_ACDATA** p2)
{
	const XTP_EDIT_ACDATA* pData1 = *p1;
	const XTP_EDIT_ACDATA* pData2 = *p2;
	return _tcsicmp((LPCTSTR)pData1->m_strText, (LPCTSTR)pData2->m_strText);
}

void CXTPSyntaxEditAutoCompleteWnd::Sort()
{
	int nCount = (int)m_arrACData.GetSize();
	if (nCount > 1)
	{
		typedef int (_cdecl *GENERICCOMPAREFUNC)(const void *, const void*);
		qsort(m_arrACData.GetData(), nCount, sizeof(XTP_EDIT_ACDATA*), (GENERICCOMPAREFUNC)CompareACData);
	}
}

int CXTPSyntaxEditAutoCompleteWnd::CompareACDataToSearch(const XTP_EDIT_ACDATA** ppKey, const XTP_EDIT_ACDATA** ppElem)
{
	int nKeyLength =  (*ppKey)->m_strText.GetLength();
	return (*ppElem)->m_strText.Left(nKeyLength).CompareNoCase((*ppKey)->m_strText);
}

int CXTPSyntaxEditAutoCompleteWnd::Search(CString strSearch)
{
	int nFirstOccurrence = -1;
	if (strSearch.GetLength() == 0)
		return 0;

	XTP_EDIT_ACDATA* key = new XTP_EDIT_ACDATA(0, strSearch);

	XTP_EDIT_ACDATA** pFirst = &m_arrACDataFiltered[0];
	UINT nNum = (UINT)m_arrACDataFiltered.GetSize();
	typedef int (_cdecl *GENERICCOMPAREFUNC)(const void *, const void*);

	XTP_EDIT_ACDATA** pRez = (XTP_EDIT_ACDATA**)_lfind(&key,
		pFirst, &nNum, sizeof(XTP_EDIT_ACDATA*), (GENERICCOMPAREFUNC)CompareACDataToSearch);

	delete key;

	if (pRez)
	{
		nFirstOccurrence = (LONG)((LONG_PTR)pRez - (LONG_PTR)pFirst)/sizeof(XTP_EDIT_ACDATA*);
	}

	return nFirstOccurrence;
}

int CXTPSyntaxEditAutoCompleteWnd::Filter(CString strSearch)
{
	int nFound = 0;
	m_arrACDataFiltered.RemoveAll();
	if (strSearch.GetLength() == 0)
	{
		m_arrACDataFiltered.Copy(m_arrACData);
		return (int)m_arrACDataFiltered.GetSize();
	}



	if (!m_arrACData.GetSize())
	{
		return 0;
	}

	XTP_EDIT_ACDATA* key = new XTP_EDIT_ACDATA(0, strSearch);
	XTP_EDIT_ACDATA** pFirst = &m_arrACData[0];
	XTP_EDIT_ACDATA** pNext = NULL;
	UINT nNum = (UINT)m_arrACData.GetSize();
	typedef int (_cdecl *GENERICCOMPAREFUNC)(const void *, const void*);
	while (nNum > 0)
	{
		pNext = (XTP_EDIT_ACDATA**)_lfind(&key,
			pFirst, &nNum, sizeof(XTP_EDIT_ACDATA*), (GENERICCOMPAREFUNC)CompareACDataToSearch);

		if (pNext)
		{
			nNum -= (LONG) ((LONG_PTR)pNext - (LONG_PTR)pFirst)/sizeof(XTP_EDIT_ACDATA*);
			pFirst = pNext;
			nNum --;
			pFirst++;
			m_arrACDataFiltered.Add(*pNext);
			nFound++;
		}
		else
		{
			nNum --;
			pFirst++;
		}
	}

	delete key;

	return nFound;
}

void CXTPSyntaxEditAutoCompleteWnd::RemoveAll()
{
	int nDataLines = (int)m_arrACData.GetSize();
	m_arrACDataFiltered.RemoveAll();
	XTP_EDIT_ACDATA* pACData = NULL;
	for (int nDataLine = 0; nDataLine < nDataLines; nDataLine++)
	{
		pACData = m_arrACData.GetAt(nDataLine); ASSERT(pACData);
		delete pACData;
	}
	m_arrACData.RemoveAll();
}