Click here to Skip to main content
15,884,625 members
Articles / Desktop Programming / MFC

CUltraPadWnd - A Simple Syntax Coloring Control Based on the MFC CWnd Class

Rate me:
Please Sign up or sign in to vote.
4.89/5 (31 votes)
16 Aug 2008CPOL3 min read 85.1K   3K   92  
An article on a simple syntax coloring control
// UltraPadWnd.cpp : implementation file
//

#include "stdafx.h"
#include "UltraPad.h"
#include "UltraPadWnd.h"


// CUltraPadWnd
IMPLEMENT_DYNAMIC(CUltraPadWnd, CWnd)

CUltraPadWnd::CUltraPadWnd()
{
	// Init members
	m_iSize = 2;
	m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
	m_lpszText[0] = '\r';
	m_lpszText[1] = '\n';
	m_Font.CreatePointFont(100, _T("Courier New"), NULL);
	m_iLineStartSelection = m_iLineEndSelection = -1;
	m_iCharStartSelection = m_iCharEndSelection = -1;
	m_iNumberLines = 1;
	m_lpLineInfo = (LPLINEINFO)malloc(m_iNumberLines*sizeof(LINEINFO));
	m_lpLineInfo[m_iNumberLines-1].dwOffset = 0;
	m_lpLineInfo[m_iNumberLines-1].nLen = 0;
	m_cKeywordColor = RGB(0,0,0);
	m_lpKeywordInfo = NULL;
	m_iNumberKeywords = 0;
	m_bKeywordsCaseSensitive = NULL;
	m_cConstantColor = RGB(0,0,0);
	m_lpConstantInfo = NULL;
	m_iNumberConstants = 0;
	m_bConstantsCaseSensitive = NULL;
	m_cCommentColor = RGB(0,0,0);
	m_cTextColor = RGB(0,0,0);
	m_SyntaxColoring = SCT_NONE;
	m_iCurrentLine = 0;
	m_iCurrentChar = 0;
	m_iTABs = 4;
	m_bComment = FALSE;
	m_bText = FALSE;
	m_bSelecting = FALSE;
	m_bCopying = FALSE;
	m_cBgColor = RGB(255,255,255);
	m_lpfnLineDrawProc = NULL;
}

CUltraPadWnd::~CUltraPadWnd()
{
	// Destroy caret
	DestroyCaret();

	// Clear data
	if (m_lpszText != NULL)
		free(m_lpszText);
	if (m_lpLineInfo != NULL)
	{
		free(m_lpLineInfo);
		m_lpLineInfo = NULL;
	}
	for (int i=0; i<m_iNumberKeywords; i++)
		delete m_lpKeywordInfo[i].lpszTagName;
	free(m_lpKeywordInfo);
	m_lpKeywordInfo = NULL;
	for (int i=0; i<m_iNumberConstants; i++)
		delete m_lpConstantInfo[i].lpszTagName;
	free(m_lpConstantInfo);
	m_lpConstantInfo = NULL;

	// Clear members
	if (m_MemDC.m_hDC != NULL)
	{
		m_MemDC.SelectObject(m_pOldMemBitmap);
		m_MemDC.SelectObject(m_pOldFont);
		m_MemDC.DeleteDC();
		m_MemBitmap.DeleteObject();
		m_Font.DeleteObject();
	}
}


BEGIN_MESSAGE_MAP(CUltraPadWnd, CWnd)
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_VSCROLL()
	ON_WM_HSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_SETFOCUS()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_CHAR()
	ON_WM_SETCURSOR()
	ON_WM_MOUSEWHEEL()
END_MESSAGE_MAP()



// CUltraPadWnd message handlers
BOOL CUltraPadWnd::Create(DWORD dwExStyle, DWORD dwStyle, RECT& rect, CWnd* pParentWnd, UINT nID)
{
	// Create window
	return CWnd::CreateEx(dwExStyle, NULL, NULL, dwStyle, rect, pParentWnd, nID, NULL);
}

void CUltraPadWnd::DrawContent()
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Get client rectangle
		RECT rcClient;
		GetClientRect(&rcClient);

		// Get scroll info
		int iVerticalOffset = GetScrollPos(SB_VERT);
		int iHorizontalOffset = GetScrollPos(SB_HORZ);

		// Get visible lines range
		POINT ptTop = {0, rcClient.top+iVerticalOffset};
		POINT ptBottom = {0, (rcClient.bottom-rcClient.top)+iVerticalOffset};
		int iLineStart = LineFromPosition(ptTop);
		int iLineEnd = LineFromPosition(ptBottom);

		// Process invisible lines
		m_bComment = FALSE;
		m_bText = FALSE;
		int iTmpOffset = 0;
		int iTmpOldOffset = iTmpOffset;
		int iTmpCurrentLine = 0;
		LPTSTR lpszTmpNextLine = NULL;
		while ((lpszTmpNextLine=GetLine(iTmpOffset)) != NULL)
		{
			// Process only invisible lines
			if (iTmpCurrentLine < iLineStart)
			{
				// Process line
				int iLen = iTmpOffset - iTmpOldOffset;
				ProcessLine(lpszTmpNextLine, m_lpLineInfo[iTmpCurrentLine].nLen);
			}
			iTmpOldOffset = iTmpOffset;
			iTmpCurrentLine++;

			// Exit
			if (iTmpCurrentLine >= iLineStart)
				break;
		}

		// Draw lines
		RECT rcLine = {-iHorizontalOffset, 0, rcClient.right, m_szTextSize.cy};
		int iOffset = m_lpLineInfo[iLineStart].dwOffset;
		int iOldOffset = iOffset;
		int iCurrentLine = iLineStart;
		LPTSTR lpszNextLine = NULL;
		while ((lpszNextLine=GetLine(iOffset)) != NULL)
		{
			// Draw only visible lines
			if ((iCurrentLine >= iLineStart) && (iCurrentLine <= iLineEnd))
			{
				// Get visible chars range
				POINT ptLeft = {iHorizontalOffset, 0};
				POINT ptRight = {rcLine.right+iHorizontalOffset, 0};
				int iCharStart = CharFromPosition(iCurrentLine, ptLeft);
				int iCharEnd = CharFromPosition(iCurrentLine, ptRight);

				// Calculate text length
				int iLen = iOffset - iOldOffset;

				// Check for valid selection
				if ((m_iLineStartSelection != m_iLineEndSelection) || (m_iCharStartSelection != m_iCharEndSelection))
				{
					// Format selection
					int iLineStartSelection = min(m_iLineStartSelection, m_iLineEndSelection);
					int iLineEndSelection = max(m_iLineStartSelection, m_iLineEndSelection);
					int iCharStartSelection = m_iCharStartSelection;
					int iCharEndSelection = m_iCharEndSelection;

					// Check for selected text
					if ((iCurrentLine >= iLineStartSelection) && (iCurrentLine <= iLineEndSelection))
					{
						// Calculate selected chars
						int iSelectionStart;
						if (iCurrentLine == iLineStartSelection)
						{
							if (m_iLineStartSelection < m_iLineEndSelection)
								iSelectionStart = max(iCharStartSelection, iCharStart);
							else if (m_iLineStartSelection > m_iLineEndSelection)
								iSelectionStart = max(iCharEndSelection, iCharStart);
							else
								iSelectionStart = max(min(m_iCharStartSelection, m_iCharEndSelection), iCharStart);
						}
						else if (iCurrentLine == iLineEndSelection)
						{
							if (m_iLineStartSelection < m_iLineEndSelection)
								iSelectionStart = iCharStart;
							else if (m_iLineStartSelection > m_iLineEndSelection)
								iSelectionStart = iCharStart;
							else
								iSelectionStart = max(min(m_iCharStartSelection, m_iCharEndSelection), iCharStart);
						}
						else
							iSelectionStart = iCharStart;
						int iSelectionEnd;
						if (iCurrentLine == iLineStartSelection)
						{
							if (m_iLineStartSelection < m_iLineEndSelection)
								iSelectionEnd = iCharEnd;
							else if (m_iLineStartSelection > m_iLineEndSelection)
								iSelectionEnd = iCharEnd;
							else
								iSelectionEnd = min(max(m_iCharStartSelection, m_iCharEndSelection), iCharEnd);
						}
						else if (iCurrentLine == iLineEndSelection)
						{
							if (m_iLineStartSelection < m_iLineEndSelection)
								iSelectionEnd = min(iCharEndSelection, iCharEnd);
							else if (m_iLineStartSelection > m_iLineEndSelection)
								iSelectionEnd = min(iCharStartSelection, iCharEnd);
							else
								iSelectionEnd = min(max(m_iCharStartSelection, m_iCharEndSelection), iCharEnd);
						}
						else
							iSelectionEnd = iCharEnd;
						int iSelected = iSelectionEnd - iSelectionStart;
						if (iSelected > 0)
						{
							// Draw selected text
							if (m_lpfnLineDrawProc == NULL)
								DrawLine(lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, iSelectionStart, iSelectionEnd);
							else
								m_lpfnLineDrawProc(m_MemDC.m_hDC, lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, iSelectionStart, iSelectionEnd);
						}
						else
						{
							// Draw normal text
							if (m_lpfnLineDrawProc == NULL)
								DrawLine(lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, -1, -1);
							else
								m_lpfnLineDrawProc(m_MemDC.m_hDC, lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, -1, -1);
						}
					}
					else
					{
						// Draw normal text
						if (m_lpfnLineDrawProc == NULL)
							DrawLine(lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, -1, -1);
						else
							m_lpfnLineDrawProc(m_MemDC.m_hDC, lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, -1, -1);
					}
				}
				else
				{
					// Draw normal text
					if (m_lpfnLineDrawProc == NULL)
						DrawLine(lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, -1, -1);
					else
						m_lpfnLineDrawProc(m_MemDC.m_hDC, lpszNextLine, m_lpLineInfo[iCurrentLine].nLen, rcLine, -1, -1);
				}

				// Update text rectangle
				OffsetRect(&rcLine, 0, m_szTextSize.cy);
			}
			iOldOffset = iOffset;
			iCurrentLine++;

			// Exit
			if (iCurrentLine > iLineEnd)
				break;
		}
	}
}

void CUltraPadWnd::DrawLine(LPTSTR lpszText, int iLen, RECT rect, int iSelectionStart, int iSelectionEnd)
{
	// Draw single line of text
	int iStartTAB=-1, iEndTAB, iTABs;
	int iStart=0, i=0;
	int iEnd=-1, iCount;
	POINT pt = {rect.left, rect.top};
	BOOL bComment = m_bComment;
	BOOL bText = m_bText;
	while (i <= iLen)
	{
		// Check for special char
		if ((IsSpecial(lpszText[i])) || (i == iLen))
		{
			// Get word end position
			iEnd = i - 1;
			iCount = iEnd - iStart + 1;

			// Check for comment
			BOOL bTurnCommentOff = FALSE;
			if ((!bComment) && (!m_bComment))
			{
				switch (m_SyntaxColoring)
				{
					case SCT_C_ANSI:
					case SCT_C_PLUS_PLUS:
					case SCT_JSCRIPT:
						{
							if (_tcsncmp(lpszText+iStart, _T("//"), 2) == 0)
								bComment = TRUE;
							else if (_tcsncmp(lpszText+iStart, _T("/*"), 2) == 0)
							{
								m_bComment = TRUE;
								bComment = TRUE;
							}
						}
						break;

					case SCT_HTML:
						{
							if (_tcsncmp(lpszText+iStart, _T("<!--"), 4) == 0)
							{
								m_bComment = TRUE;
								bComment = TRUE;
							}
						}
						break;

					case SCT_SQL:
						{
							if (_tcsncmp(lpszText+iStart, _T("--"), 2) == 0)
								bComment = TRUE;
							else if (_tcsncmp(lpszText+iStart, _T("/*"), 2) == 0)
							{
								m_bComment = TRUE;
								bComment = TRUE;
							}
						}
						break;
				}
			}
			else if (m_bComment)
			{
				switch (m_SyntaxColoring)
				{
					case SCT_C_ANSI:
					case SCT_C_PLUS_PLUS:
					case SCT_JSCRIPT:
					case SCT_SQL:
						{
							if (_tcsncmp(lpszText+iStart, _T("*/"), 2) == 0)
							{
								m_bComment = FALSE;
								bTurnCommentOff = TRUE;
							}
						}
						break;

					case SCT_HTML:
						{
							if (_tcsncmp(lpszText+iStart, _T("-->"), 3) == 0)
							{
								m_bComment = FALSE;
								bTurnCommentOff = TRUE;
							}
						}
						break;
				}
			}

			// Check for comment
			if (!bComment)
			{
				// Draw normal word
				m_MemDC.TextOut(pt.x, pt.y, lpszText+iStart, iCount);

				// Check for keyword
				int iKeywordIndex = -1;
				int iKeywordOffset = -1;
				if (IsKeyword(lpszText+iStart, iCount, iKeywordIndex, iKeywordOffset))
				{
					// Draw keyword
					COLORREF cOldTextColor = m_MemDC.SetTextColor(m_cKeywordColor);
					m_MemDC.TextOut(pt.x+iKeywordOffset*m_szTextSize.cx, pt.y, lpszText+iStart+iKeywordOffset, m_lpKeywordInfo[iKeywordIndex].nLen-1);
					m_MemDC.SetTextColor(cOldTextColor);
				}

				// Check for constant
				int iConstantIndex = -1;
				int iConstantOffset = -1;
				if (IsConstant(lpszText+iStart, iCount, iConstantIndex, iConstantOffset))
				{
					// Draw constant
					COLORREF cOldTextColor = m_MemDC.SetTextColor(m_cConstantColor);
					m_MemDC.TextOut(pt.x+iConstantOffset*m_szTextSize.cx, pt.y, m_lpConstantInfo[iConstantIndex].lpszTagName, m_lpConstantInfo[iConstantIndex].nLen-1);
					m_MemDC.SetTextColor(cOldTextColor);
				}

				// Check for text
				int iTextStart = -1;
				int iTextEnd = -1;
				if (IsText(lpszText+iStart, iCount, iTextStart, iTextEnd) || (bText))
				{
					// Update text offsets
					if (!bText)
					{
						if (iTextEnd == -1)
						{
							iTextEnd = iCount - 1;
							bText = TRUE;
						}
					}
					else
					{
						if (iTextEnd == -1)
						{
							iTextEnd = iTextStart;
							if (iTextEnd == -1)
								iTextEnd = iCount - 1;
							else
								bText = FALSE;
							iTextStart = 0;
						}
					}

					// Draw text
					COLORREF cOldTextColor = m_MemDC.SetTextColor(m_cTextColor);
					m_MemDC.TextOut(pt.x+iTextStart*m_szTextSize.cx, pt.y, lpszText+iStart+iTextStart, iTextEnd-iTextStart+1);
					m_MemDC.SetTextColor(cOldTextColor);
					m_bText = bText;
				}
			}
			else
			{
				// Draw comment
				COLORREF cOldTextColor = m_MemDC.SetTextColor(m_cCommentColor);
				m_MemDC.TextOut(pt.x, pt.y, lpszText+iStart, iCount);
				m_MemDC.SetTextColor(cOldTextColor);
				if (bTurnCommentOff == TRUE)
					bComment = FALSE;
			}

			// Check for valid selection
			int iWordStartSelection = max(iStart, iSelectionStart);
			int iWordEndSelection = min(iEnd+1, iSelectionEnd);
			int iNumberSelected = iWordEndSelection - iWordStartSelection;
			int iSelectionOffset = (iWordStartSelection-iStart) * m_szTextSize.cx;
			if (iNumberSelected > 0)
			{
				// Draw selection
				COLORREF cOldTextColor = m_MemDC.SetTextColor(RGB(255,255,255));
				COLORREF cOldBkColor = m_MemDC.SetBkColor(RGB(0,0,0));
				m_MemDC.TextOut(pt.x+iSelectionOffset, pt.y, lpszText+iWordStartSelection, iNumberSelected);
				m_MemDC.SetBkColor(cOldBkColor);
				m_MemDC.SetTextColor(cOldTextColor);
			}

			// Update offset
			pt.x += (iCount * m_szTextSize.cx);

			// Check for SPACE char
			if (lpszText[i] == ' ')
			{
				// Check for valid selection
				if ((i >= iSelectionStart) && (i < iSelectionEnd))
				{
					// Draw selection
					COLORREF cOldTextColor = m_MemDC.SetTextColor(RGB(255,255,255));
					COLORREF cOldBkColor = m_MemDC.SetBkColor(RGB(0,0,0));
					m_MemDC.TextOut(pt.x, pt.y, _T(" "), 1);
					m_MemDC.SetBkColor(cOldBkColor);
					m_MemDC.SetTextColor(cOldTextColor);
				}

				// Update offset
				pt.x += m_szTextSize.cx;
				iStart = i + 1;
			}
			// Check for TAB char
			else if (lpszText[i] == '\t')
			{
				// Update TABs
				if (iStartTAB == -1)
				{
					iTABs = m_iTABs;
					iStartTAB = i;
				}
				else
				{
					iEndTAB = i;
					iTABs = m_iTABs - ((iEndTAB - iStartTAB) % m_iTABs);
					if (((iEndTAB - iStartTAB) % m_iTABs) != 0)
						iTABs++;
					if ((iEndTAB - iStartTAB) == 1)
						iTABs = m_iTABs;
					iStartTAB = iEndTAB;
				}

				// Check for valid selection
				if ((i >= iSelectionStart) && (i < iSelectionEnd))
				{
					// Draw selection
					COLORREF cOldTextColor = m_MemDC.SetTextColor(RGB(255,255,255));
					COLORREF cOldBkColor = m_MemDC.SetBkColor(RGB(0,0,0));
					m_MemDC.TextOut(pt.x, pt.y, _T("        "), iTABs);
					m_MemDC.SetBkColor(cOldBkColor);
					m_MemDC.SetTextColor(cOldTextColor);
				}

				// Update offset
				pt.x += (iTABs * m_szTextSize.cx);
				iStart = i + 1;
			}
			else
				break;
		}
		i++;
	}
}

void CUltraPadWnd::ProcessLine(LPTSTR lpszText, int iLen)
{
	// Process single line of text
	int iStartTAB=-1, iEndTAB, iTABs;
	int iStart=0, i=0;
	int iEnd=-1, iCount;
	BOOL bComment = m_bComment;
	BOOL bText = m_bText;
	while (i <= iLen)
	{
		// Check for special char
		if ((IsSpecial(lpszText[i])) || (i == iLen))
		{
			// Get word end position
			iEnd = i - 1;
			iCount = iEnd - iStart + 1;

			// Check for comment
			BOOL bTurnCommentOff = FALSE;
			if ((!bComment) && (!m_bComment))
			{
				switch (m_SyntaxColoring)
				{
					case SCT_C_ANSI:
					case SCT_C_PLUS_PLUS:
					case SCT_JSCRIPT:
						{
							if (_tcsncmp(lpszText+iStart, _T("//"), 2) == 0)
								bComment = TRUE;
							else if (_tcsncmp(lpszText+iStart, _T("/*"), 2) == 0)
							{
								m_bComment = TRUE;
								bComment = TRUE;
							}
						}
						break;

					case SCT_HTML:
						{
							if (_tcsncmp(lpszText+iStart, _T("<!--"), 4) == 0)
							{
								m_bComment = TRUE;
								bComment = TRUE;
							}
						}
						break;

					case SCT_SQL:
						{
							if (_tcsncmp(lpszText+iStart, _T("--"), 2) == 0)
								bComment = TRUE;
							else if (_tcsncmp(lpszText+iStart, _T("/*"), 2) == 0)
							{
								m_bComment = TRUE;
								bComment = TRUE;
							}
						}
						break;
				}
			}
			else if (m_bComment)
			{
				switch (m_SyntaxColoring)
				{
					case SCT_C_ANSI:
					case SCT_C_PLUS_PLUS:
					case SCT_JSCRIPT:
					case SCT_SQL:
						{
							if (_tcsncmp(lpszText+iStart, _T("*/"), 2) == 0)
							{
								m_bComment = FALSE;
								bTurnCommentOff = TRUE;
							}
						}
						break;

					case SCT_HTML:
						{
							if (_tcsncmp(lpszText+iStart, _T("-->"), 3) == 0)
							{
								m_bComment = FALSE;
								bTurnCommentOff = TRUE;
							}
						}
						break;
				}
			}

			// Check for comment
			if (!bComment)
			{
				// Check for text
				int iTextStart = -1;
				int iTextEnd = -1;
				if (IsText(lpszText+iStart, iCount, iTextStart, iTextEnd) || (bText))
				{
					// Update text offsets
					if (!bText)
					{
						if (iTextEnd == -1)
						{
							iTextEnd = iCount - 1;
							bText = TRUE;
						}
					}
					else
					{
						if (iTextEnd == -1)
						{
							iTextEnd = iTextStart;
							if (iTextEnd == -1)
								iTextEnd = iCount - 1;
							else
								bText = FALSE;
							iTextStart = 0;
						}
					}
					m_bText = bText;
				}
			}
			else
			{
				// Turn-off comment
				if (bTurnCommentOff == TRUE)
					bComment = FALSE;
			}

			// Check for SPACE char
			if (lpszText[i] == ' ')
			{
				// Update offset
				iStart = i + 1;
			}
			// Check for TAB char
			else if (lpszText[i] == '\t')
			{
				// Update TABs
				if (iStartTAB == -1)
				{
					iTABs = m_iTABs;
					iStartTAB = i;
				}
				else
				{
					iEndTAB = i;
					iTABs = m_iTABs - ((iEndTAB - iStartTAB) % m_iTABs);
					if (((iEndTAB - iStartTAB) % m_iTABs) != 0)
						iTABs++;
					if ((iEndTAB - iStartTAB) == 1)
						iTABs = m_iTABs;
					iStartTAB = iEndTAB;
				}

				// Update offset
				iStart = i + 1;
			}
			else
				break;
		}
		i++;
	}
}

void CUltraPadWnd::UpdateControl()
{
	// Clear drawing flag
	SetRedraw(FALSE);

	// Clear line info
	if (m_lpLineInfo != NULL)
	{
		free(m_lpLineInfo);
		m_lpLineInfo = NULL;
	}

	// Get client rectangle
	RECT rcClient;
	GetClientRect(&rcClient);

	// Get line size
	m_szTextSize = m_MemDC.GetTextExtent(_T("A"), 1);

	// Calculate content size
	int iWidth = 0;
	int iHeight = 0;
	int iOffset = 0;
	int iOldOffset = iOffset;
	m_iNumberLines = 0;
	LPTSTR lpszNextLine = NULL;
	while ((lpszNextLine=GetLine(iOffset)) != NULL)
	{
		// Update line info
		m_iNumberLines++;
		if (m_lpLineInfo == NULL)
			m_lpLineInfo = (LPLINEINFO)malloc(m_iNumberLines*sizeof(LINEINFO));
		else
			m_lpLineInfo = (LPLINEINFO)realloc(m_lpLineInfo, m_iNumberLines*sizeof(LINEINFO));
		m_lpLineInfo[m_iNumberLines-1].dwOffset = iOldOffset;

		// Calculate line length
		int iLen = iOffset - iOldOffset;
		iOldOffset = iOffset;
		if ((lpszNextLine[iLen-2] == '\r') && (lpszNextLine[iLen-1] == '\n'))
			m_lpLineInfo[m_iNumberLines-1].nLen = iLen - 2;
		else if (lpszNextLine[iLen-1] == '\n')
			m_lpLineInfo[m_iNumberLines-1].nLen = iLen - 1;
		else
			m_lpLineInfo[m_iNumberLines-1].nLen = iLen;

		// Calculate single line size
		SIZE szLine = GetLineSize(lpszNextLine, m_lpLineInfo[m_iNumberLines-1].nLen);
		if (iWidth < szLine.cx)
			iWidth = szLine.cx;
		iHeight += szLine.cy;
	}

	// Get scrollbar sizes
	int iHorizontalOffset = GetSystemMetrics(SM_CYHSCROLL);
	if (iHeight <= (rcClient.bottom - rcClient.top))
		iHorizontalOffset = 0;
	int iVerticalOffset = GetSystemMetrics(SM_CXVSCROLL);
	if (iWidth <= (rcClient.right - rcClient.left))
		iVerticalOffset = 0;

	// Check content width
	if (iWidth >= (rcClient.right - rcClient.left))
	{
		// Show horizontal scrollbar
		SCROLLINFO si;
		memset(&si, 0, sizeof(SCROLLINFO));
		si.cbSize = sizeof(SCROLLINFO);
		si.fMask = SIF_ALL;
		si.nMax = iWidth;
		si.nPage = ((rcClient.right - rcClient.left - iVerticalOffset) / m_szTextSize.cx + 1) * m_szTextSize.cx;
		si.nPos = GetScrollPos(SB_HORZ);
		SetScrollInfo(SB_HORZ, &si, TRUE);
		ShowScrollBar(SB_HORZ, TRUE);
	}
	else
	{
		// Update horizontal scrollbar
		SCROLLINFO si;
		memset(&si, 0, sizeof(SCROLLINFO));
		si.cbSize = sizeof(SCROLLINFO);
		si.fMask = SIF_ALL;
		si.nMax = iWidth;
		si.nPage = ((rcClient.right - rcClient.left - iVerticalOffset) / m_szTextSize.cx + 1) * m_szTextSize.cx;
		SetScrollInfo(SB_HORZ, &si, TRUE);
		SetScrollPos(SB_HORZ, 0, TRUE);
		ShowScrollBar(SB_HORZ, FALSE);
	}

	// Check content height
	if (iHeight >= (rcClient.bottom - rcClient.top))
	{
		// Show vertical scrollbar
		SCROLLINFO si;
		memset(&si, 0, sizeof(SCROLLINFO));
		si.cbSize = sizeof(SCROLLINFO);
		si.fMask = SIF_ALL;
		si.nMax = iHeight;
		si.nPage = ((rcClient.bottom - rcClient.top - iHorizontalOffset) / m_szTextSize.cy) * m_szTextSize.cy;
		si.nPos = GetScrollPos(SB_VERT);
		SetScrollInfo(SB_VERT, &si, TRUE);
		ShowScrollBar(SB_VERT, TRUE);
	}
	else
	{
		// Update vertical scrollbar
		SCROLLINFO si;
		memset(&si, 0, sizeof(SCROLLINFO));
		si.cbSize = sizeof(SCROLLINFO);
		si.fMask = SIF_ALL;
		si.nMax = iHeight;
		si.nPage = ((rcClient.bottom - rcClient.top - iHorizontalOffset) / m_szTextSize.cy) * m_szTextSize.cy;
		SetScrollInfo(SB_VERT, &si, TRUE);
		SetScrollPos(SB_VERT, 0, TRUE);
		ShowScrollBar(SB_VERT, FALSE);
	}

	// Set drawing flag
	SetRedraw(TRUE);
}

void CUltraPadWnd::DeleteText()
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Check for valid selection
		if ((m_iLineStartSelection != m_iLineEndSelection) || (m_iCharStartSelection != m_iCharEndSelection))
		{
			// Get selection range
			int iStartChar, iEndChar;
			int iStartLine, iEndLine;
			if (m_iLineStartSelection < m_iLineEndSelection)
			{
				iStartChar = m_iCharStartSelection;
				iEndChar = m_iCharEndSelection;
				iStartLine = m_iLineStartSelection;
				iEndLine = m_iLineEndSelection;
			}
			else if (m_iLineStartSelection > m_iLineEndSelection)
			{
				iStartChar = m_iCharEndSelection;
				iEndChar = m_iCharStartSelection;
				iStartLine = m_iLineEndSelection;
				iEndLine = m_iLineStartSelection;
			}
			else
			{
				iStartChar = min(m_iCharStartSelection, m_iCharEndSelection);
				iEndChar = max(m_iCharStartSelection, m_iCharEndSelection);
				iStartLine = m_iLineStartSelection;
				iEndLine = m_iLineEndSelection;
			}
			int iStartOffset = GetCharOffset(iStartLine, iStartChar);
			if (m_lpszText[iStartOffset] == '\r')
				iStartOffset += 2;
			int iEndOffset = GetCharOffset(iEndLine, iEndChar);
			int iCount = iEndOffset - iStartOffset;
			if (m_lpszText != NULL)
			{
				int iOldSize = m_iSize;
				LPTSTR lpszTemp = new _TCHAR[iOldSize];
				memcpy(lpszTemp, m_lpszText, iOldSize*sizeof(_TCHAR));
				m_iSize -= iCount;
				free(m_lpszText);
				m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
				if (m_lpszText != NULL)
				{
					memcpy(m_lpszText, lpszTemp, iStartOffset*sizeof(_TCHAR));
					memcpy(m_lpszText+iStartOffset, lpszTemp+iEndOffset, (iOldSize-iEndOffset)*sizeof(_TCHAR));
				}
				m_iCurrentChar = iStartChar;
				m_iCurrentLine = iStartLine;

				// Clear selection
				m_iLineStartSelection = -1;
				m_iLineEndSelection = -1;
				m_iCharStartSelection = -1;
				m_iCharEndSelection = -1;
			}
		}
	}
}

SIZE CUltraPadWnd::GetLineSize(LPTSTR lpszText, int iLen)
{
	SIZE szResult = {0, m_szTextSize.cy};

	// Get line width
	BOOL bDone = FALSE;
	int iStartTAB=-1;
	int iEndTAB, iTABs;
	for (int i=0; i<iLen; i++)
	{
		// Check for TAB char
		if (lpszText[i] == '\t')
		{
			if (iStartTAB == -1)
			{
				iTABs = m_iTABs;
				iStartTAB = i;
			}
			else
			{
				iEndTAB = i;
				iTABs = m_iTABs - ((iEndTAB - iStartTAB) % m_iTABs);
				if (((iEndTAB - iStartTAB) % m_iTABs) != 0)
					iTABs++;
				if ((iEndTAB - iStartTAB) == 1)
					iTABs = m_iTABs;
				iStartTAB = iEndTAB;
			}
			szResult.cx += (iTABs * m_szTextSize.cx);
		}
		else
			szResult.cx += m_szTextSize.cx;
	}

	return szResult;
}

BOOL CUltraPadWnd::Load(LPTSTR lpszFilePath)
{
	BOOL bResult = FALSE;

	// Try to open the file on disk
	CFile file;
	if (file.Open(lpszFilePath, CFile::modeRead, NULL))
	{
		// Clear old data
		if (m_lpszText != NULL)
			free(m_lpszText);

		// Clear selection
		m_iLineStartSelection = -1;
		m_iLineEndSelection = -1;
		m_iCharStartSelection = -1;
		m_iCharEndSelection = -1;

		// Read data from file
		DWORD dwSize = (DWORD)file.GetLength();
		LPBYTE lpData = (LPBYTE)malloc(dwSize*sizeof(BYTE));
		file.Read(lpData, dwSize);
		file.Close();
		bResult = TRUE;

		// Check for file type
		WORD wFlag = 0;
		memcpy(&wFlag, lpData, 2);
		DWORD dwFlag = 0;
		memcpy(&dwFlag, lpData, 3);
		int iOffset;
		if (wFlag == 0xFEFF)
		{
			// Copy UNICODE data
			iOffset = 1;
			m_iSize = (dwSize-2) / sizeof(_TCHAR);
			m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
			_tcscpy(m_lpszText, (LPTSTR)lpData+iOffset);
		}
		else if (dwFlag == 0xBFBBEF)
		{
			// Convert from UTF-8 to UNICODE data
			iOffset = 3;
			m_iSize = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpData+iOffset, dwSize, NULL, 0);
			m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
			MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)lpData+iOffset, dwSize, m_lpszText, m_iSize);
		}
		else
		{
			// Convert from ANSI to UNICODE data
			iOffset = 0;
			m_iSize = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpData+iOffset, dwSize, NULL, 0);
			m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
			MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpData+iOffset, dwSize, m_lpszText, m_iSize);
		}
		free(lpData);

		// Update text
		m_iSize += 2;
		m_lpszText = (LPTSTR)realloc(m_lpszText, m_iSize*sizeof(_TCHAR));
		m_lpszText[m_iSize-2] = '\r';
		m_lpszText[m_iSize-1] = '\n';

		// Update control
		UpdateControl();

		// Update screen
		Invalidate(FALSE);
		UpdateWindow();
	}

	return bResult;
}

BOOL CUltraPadWnd::Save(LPTSTR lpszFilePath)
{
	BOOL bResult = FALSE;

	// Try to open the file on disk
	CFile file;
	if (file.Open(lpszFilePath, CFile::modeCreate | CFile::modeWrite, NULL))
	{
		// Write data to file
		file.Write(m_lpszText, m_iSize*sizeof(_TCHAR));
		file.Close();
		bResult = TRUE;
	}

	return bResult;
}

void CUltraPadWnd::AddText(LPTSTR lpszText, int iLen)
{
	// Check for valid data
	if ((m_lpszText != NULL) && (lpszText != NULL))
	{
		// Add text
		int iOldSize = m_iSize;
		int iCurrentOffset = GetCharOffset(m_iCurrentLine, m_iCurrentChar);
		LPTSTR lpszTemp = new _TCHAR[iOldSize];
		memcpy(lpszTemp, m_lpszText, iOldSize*sizeof(_TCHAR));
		m_iSize += iLen;
		free(m_lpszText);
		m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
		memcpy(m_lpszText, lpszTemp, iCurrentOffset*sizeof(_TCHAR));
		memcpy(m_lpszText+iCurrentOffset, lpszText, iLen*sizeof(_TCHAR));
		memcpy(m_lpszText+iCurrentOffset+iLen, lpszTemp+iCurrentOffset, (iOldSize-iCurrentOffset)*sizeof(_TCHAR));
		delete lpszTemp;
	}
}

void CUltraPadWnd::DeleteText(int iStartLine, int iStartChar, int iEndLine, int iEndChar)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Format selection
		m_iLineStartSelection = max(0, iStartLine);
		m_iCharStartSelection = max(0, iStartChar);
		m_iLineEndSelection = min(m_iNumberLines-1, iEndLine);
		if (m_iLineEndSelection == -1)
			m_iLineEndSelection = m_iNumberLines - 1;
		m_iCharEndSelection = min(m_lpLineInfo[m_iLineEndSelection].nLen, iEndChar);
		if (m_iCharEndSelection == -1)
			m_iCharEndSelection = m_lpLineInfo[m_iLineEndSelection].nLen;

		// Delete selected text
		OnChar(VK_BACK, 0, 0);
	}
}

LPTSTR CUltraPadWnd::GetTextBuffer()
{
	// Get text buffer
	return m_lpszText;
}

int CUltraPadWnd::GetBufferLen()
{
	// Get text len
	return m_iSize;
}

void CUltraPadWnd::GetTextFromLine(int iLineIndex, LPTSTR lpszText, int iLen)
{
	// Check for valid data
	int iLine = max(0, min(m_iNumberLines-1, iLineIndex));
	if (lpszText != NULL)
	{
		// Get line text
		int iOffset = m_lpLineInfo[iLine].dwOffset;
		int iOldOffset = iOffset;
		LPTSTR lpszNextLine = NULL;
		if ((lpszNextLine=GetLine(iOffset)) != NULL)
		{
			// Calculate line length
			int iLen = iOffset - iOldOffset;
			if ((lpszNextLine[iLen-2] == '\r') && (lpszNextLine[iLen-1] == '\n'))
				iLen = iLen - 2;
			else if (lpszNextLine[iLen-1] == '\n')
				iLen = iLen - 1;

			// Copy line
			_tcsncpy(lpszText, lpszNextLine, iLen);
			lpszText[iLen] = '\0';
		}
	}
}

int CUltraPadWnd::LineFromPosition(POINT pt)
{
	int iResult = -1;

	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Get line index
		iResult = min(pt.y/m_szTextSize.cy, m_iNumberLines-1);
	}

	return iResult;
}

int CUltraPadWnd::CharFromPosition(int iLineIndex, POINT pt)
{
	int iResult = -1;

	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Get char index
		int iOffset = m_lpLineInfo[iLineIndex].dwOffset;
		int iOldOffset = iOffset;
		int iCurrentLine = iLineIndex;
		LPTSTR lpszNextLine = NULL;
		while ((lpszNextLine=GetLine(iOffset)) != NULL)
		{
			// Calculate line length
			int iLen = iOffset - iOldOffset;
			iOldOffset = iOffset;

			// Check for valid line
			if (iCurrentLine == iLineIndex)
			{
				// Get char index
				BOOL bDone = FALSE;
				int iStartTAB=-1;
				int iEndTAB, iTABs;
				int iCharOffset = 0;
				for (int i=0; i<=m_lpLineInfo[iLineIndex].nLen; i++)
				{
					// Check for TAB char
					if (lpszNextLine[i] == '\t')
					{
						if (iStartTAB == -1)
						{
							iTABs = m_iTABs;
							iStartTAB = i;
						}
						else
						{
							iEndTAB = i;
							iTABs = m_iTABs - ((iEndTAB - iStartTAB) % m_iTABs);
							if (((iEndTAB - iStartTAB) % m_iTABs) != 0)
								iTABs++;
							if ((iEndTAB - iStartTAB) == 1)
								iTABs = m_iTABs;
							iStartTAB = iEndTAB;
						}
						iCharOffset += (iTABs * m_szTextSize.cx);
					}
					else
						iCharOffset += m_szTextSize.cx;
					if (iCharOffset >= pt.x)
					{
						bDone = TRUE;
						iResult = i;
						break;
					}
				}
				if (!bDone)
					iResult = m_lpLineInfo[iLineIndex].nLen;
			}
			iCurrentLine++;
			break;
		}
	}

	return iResult;
}

POINT CUltraPadWnd::PositionFromChar(int iLineIndex, int iCharIndex)
{
	POINT ptResult = {0, 0};

	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Get char position
		int iOffset = m_lpLineInfo[iLineIndex].dwOffset;
		int iOldOffset = iOffset;
		int iCurrentLine = iLineIndex;
		LPTSTR lpszNextLine = NULL;
		while ((lpszNextLine=GetLine(iOffset)) != NULL)
		{
			// Calculate line length
			int iLen = iOffset - iOldOffset;
			iOldOffset = iOffset;

			// Check for valid line
			if (iCurrentLine == iLineIndex)
			{
				// Get char index
				BOOL bDone = FALSE;
				int iStartTAB=-1;
				int iEndTAB, iTABs;
				int iCharOffset = 0;
				for (int i=0; i<=m_lpLineInfo[iLineIndex].nLen; i++)
				{
					// Check for char found
					if (i == iCharIndex)
					{
						bDone = TRUE;
						ptResult.y = iLineIndex * m_szTextSize.cy;
						ptResult.x = iCharOffset;
						break;
					}
					else
					{
						// Check for TAB char
						if (lpszNextLine[i] == '\t')
						{
							if (iStartTAB == -1)
							{
								iTABs = m_iTABs;
								iStartTAB = i;
							}
							else
							{
								iEndTAB = i;
								iTABs = m_iTABs - ((iEndTAB - iStartTAB) % m_iTABs);
								if (((iEndTAB - iStartTAB) % m_iTABs) != 0)
									iTABs++;
								if ((iEndTAB - iStartTAB) == 1)
									iTABs = m_iTABs;
								iStartTAB = iEndTAB;
							}
							iCharOffset += (iTABs * m_szTextSize.cx);
						}
						else
							iCharOffset += m_szTextSize.cx;
					}
				}
				if (!bDone)
				{
					ptResult.y = iLineIndex * m_szTextSize.cy;
					ptResult.x = iCharOffset - m_szTextSize.cx;
				}
			}
			iCurrentLine++;
			break;
		}
	}

	return ptResult;
}

int CUltraPadWnd::GetCharOffset(int iLineIndex, int iCharIndex)
{
	int iResult = 0;

	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Get char offset
		iResult = m_lpLineInfo[iLineIndex].dwOffset + iCharIndex;
	}

	return iResult;
}

void CUltraPadWnd::UpdateCaret()
{
	// Ensure caret visible
	EnsureVisible(m_iCurrentLine, m_iCurrentChar);

	// Get scroll info
	int iVerticalOffset = GetScrollPos(SB_VERT);
	int iHorizontalOffset = GetScrollPos(SB_HORZ);

	// Set char position
	POINT pt = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
	pt.x -= iHorizontalOffset;
	pt.y -= iVerticalOffset;
	SetCaretPos(pt);
}

void CUltraPadWnd::EnsureVisible(int iLineIndex, int iCharIndex)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Check for valid text size
		if (m_iSize == 2)
		{
			// Update screen
			Invalidate(FALSE);
			UpdateWindow();
			return;
		}

		// Clear drawing flag
		SetRedraw(FALSE);

		// Get client rectangle
		RECT rcClient;
		GetClientRect(&rcClient);

		// Get scroll info
		int iVerticalOffset = GetScrollPos(SB_VERT);
		int iHorizontalOffset = GetScrollPos(SB_HORZ);

		// Get visible lines range
		POINT ptTop = {0, rcClient.top+iVerticalOffset};
		POINT ptBottom = {0, (rcClient.bottom-rcClient.top)+iVerticalOffset};
		int iLineStart = LineFromPosition(ptTop);
		int iLineEnd = LineFromPosition(ptBottom);
		RECT rcLine = {-iHorizontalOffset, 0, rcClient.right, m_szTextSize.cy};
		POINT ptLeft = {iHorizontalOffset, 0};
		POINT ptRight = {rcLine.right+iHorizontalOffset, 0};
		int iCharStart = CharFromPosition(iLineIndex, ptLeft);
		int iCharEnd = CharFromPosition(iLineIndex, ptRight);

		// Get current caret position
		POINT ptCurrent = PositionFromChar(iLineIndex, iCharIndex);

		// Ensure caret is visible
		if (iLineIndex >= iLineEnd)
		{
			// Scroll down
			int iNumberScrolls = iLineIndex - iLineEnd;
			for (int i=0; i<=iNumberScrolls; i++)
				OnVScroll(SB_LINEDOWN, 0, NULL);
		}
		else if (iLineIndex < iLineStart)
		{
			// Scroll up
			int iNumberScrolls = iLineStart - iLineIndex;
			for (int i=0; i<iNumberScrolls; i++)
				OnVScroll(SB_LINEUP, 0, NULL);
		}
		if ((iCharIndex > iCharEnd) && (iLineIndex <= m_iNumberLines-1))
		{
			// Scroll right
			POINT ptEnd = PositionFromChar(iLineIndex, iCharEnd);
			int iNumberScrolls = (ptCurrent.x - ptEnd.x) / m_szTextSize.cx;
			for (int i=0; i<iNumberScrolls; i++)
				OnHScroll(SB_LINERIGHT, 0, NULL);
		}
		else if (iCharIndex <= iCharStart)
		{
			// Scroll left
			int iNumberScrolls = (iHorizontalOffset - ptCurrent.x) / m_szTextSize.cx;
			for (int i=0; i<=iNumberScrolls; i++)
				OnHScroll(SB_LINELEFT, 0, NULL);
		}

		// Set drawing flag
		SetRedraw(TRUE);
	}

	// Update screen
	Invalidate(FALSE);
	UpdateWindow();
}

LPTSTR CUltraPadWnd::GetLine(int& iOffset)
{
	LPTSTR lpszResult = NULL;

	// Check for valid offset
	if (iOffset < m_iSize)
	{
		// Get next line
		lpszResult = m_lpszText + iOffset;
		LPTSTR lpszNextLine = _tcsstr(lpszResult, _T("\r\n"));
		if (lpszNextLine != NULL)
		{
			// Update offset
			iOffset = (int)(lpszNextLine - m_lpszText + 2);
		}
		else
		{
			// Update offset
			iOffset = m_iSize;
		}
	}

	return lpszResult;
}

void CUltraPadWnd::SelectWord(POINT pt)
{
	// Get scroll info
	int iVerticalOffset = GetScrollPos(SB_VERT);
	int iHorizontalOffset = GetScrollPos(SB_HORZ);

	// Get char
	int iSelectedLine = LineFromPosition(pt);
	int iSelectedChar = CharFromPosition(iSelectedLine, pt);

	// Check for word double-clicked
	int iOffset = m_lpLineInfo[iSelectedLine].dwOffset;
	int iOldOffset = iOffset;
	int iCurrentLine = iSelectedLine;
	LPTSTR lpszNextLine = NULL;
	while ((lpszNextLine=GetLine(iOffset)) != NULL)
	{
		// Calculate line length
		int iLen = iOffset - iOldOffset;
		iOldOffset = iOffset;

		// Check for valid line
		if (iCurrentLine == iSelectedLine)
		{
			// Check for word selected
			if (!IsSpecial(lpszNextLine[iSelectedChar]))
			{
				// Select word
				int iStart = iSelectedChar;
				while ((IsLetter(lpszNextLine[iStart])) && (iStart > 0))
					iStart--;
				if ((iStart >= 0) && !IsLetter(lpszNextLine[iStart]))
					iStart++;
				int iEnd = iSelectedChar;
				while ((IsLetter(lpszNextLine[iEnd])) && (iEnd < iLen))
					iEnd++;

				// Set selection
				m_iLineStartSelection = iSelectedLine;
				m_iLineEndSelection = iSelectedLine;
				m_iCharStartSelection = iStart;
				m_iCharEndSelection = iEnd;

				// Set caret position
				m_iCurrentLine = iSelectedLine;
				m_iCurrentChar = iEnd;
				POINT pt = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
				pt.x -= iHorizontalOffset;
				pt.y -= iVerticalOffset;
				SetCaretPos(pt);
			}
			else
			{
				// Clear selection
				m_iLineStartSelection = -1;
				m_iLineEndSelection = -1;
				m_iCharStartSelection = -1;
				m_iCharEndSelection = -1;
			}
			break;
		}
		iCurrentLine++;
	}
}

BOOL CUltraPadWnd::IsSpecial(_TCHAR tch)
{
	BOOL bResult = FALSE;

	// Check for special char (SPACE, TAB, CR, LF)
	if ((tch == ' ') || (tch == '\t') || (tch == '\r') || (tch == '\n'))
	{
		// Exit
		bResult = TRUE;
	}

	return bResult;
}

BOOL CUltraPadWnd::IsLetter(_TCHAR tch)
{
	BOOL bResult = FALSE;

	// Check for letter ('A' to 'Z', 'a' to 'z', '0' to '9')
	if (((tch >= 'A') && (tch <= 'Z')) || ((tch >= 'a') && (tch <= 'z')) || ((tch >= '0') && (tch <= '9')) || (tch == '_') || (tch == '#'))
	{
		// Exit
		bResult = TRUE;
	}

	return bResult;
}

BOOL CUltraPadWnd::IsKeyword(LPTSTR lpszText, int iLen, int& iKeywordIndex, int& iKeywordOffset)
{
	BOOL bResult = FALSE;

	// Check for valid length
	if (iLen == 0)
		return FALSE;

	// Format text
	LPTSTR lpszKeywordText = new _TCHAR[iLen+1];
	_tcsncpy(lpszKeywordText, lpszText, iLen);
	lpszKeywordText[iLen] = '\0';
	if (!m_bKeywordsCaseSensitive)
		_tcsupr(lpszKeywordText);

	// Check for keyword
	for (int i=0; i<m_iNumberKeywords; i++)
	{
		// Check for keyword
		LPTSTR lpszSearch = _tcsstr(lpszKeywordText, m_lpKeywordInfo[i].lpszTagName);
		if (lpszSearch != NULL)
		{
			// Check for valid keyword
			int iFirst = (int)(lpszSearch - lpszKeywordText - 1);
			int iLast = iFirst + m_lpKeywordInfo[i].nLen;
			bResult = TRUE;
			if (iFirst >= 0)
			{
				if (m_SyntaxColoring != SCT_HTML)
				{
					if (((lpszKeywordText[iFirst] >= 'A') && (lpszKeywordText[iFirst] <= 'Z')) || 
						((lpszKeywordText[iFirst] >= 'a') && (lpszKeywordText[iFirst] <= 'z')))
						bResult = FALSE;
				}

			}
			if (bResult)
			{
				if (((lpszKeywordText[iLast] >= 'A') && (lpszKeywordText[iLast] <= 'Z')) || 
					((lpszKeywordText[iLast] >= 'a') && (lpszKeywordText[iLast] <= 'z')))
					bResult = FALSE;
				else
				{
					// Exit
					iKeywordIndex = i;
					iKeywordOffset = iFirst + 1;
					break;
				}
			}
		}
	}
	delete lpszKeywordText;

	return bResult;
}

BOOL CUltraPadWnd::IsConstant(LPTSTR lpszText, int iLen, int& iConstantIndex, int& iConstantOffset)
{
	BOOL bResult = FALSE;

	// Format text
	LPTSTR lpszConstantText = new _TCHAR[iLen+1];
	_tcsncpy(lpszConstantText, lpszText, iLen);
	lpszConstantText[iLen] = '\0';
	if (!m_bConstantsCaseSensitive)
		_tcsupr(lpszConstantText);

	// Check for constant
	for (int i=0; i<m_iNumberConstants; i++)
	{
		// Check for constant
		LPTSTR lpszSearch = _tcsstr(lpszConstantText, m_lpConstantInfo[i].lpszTagName);
		if (lpszSearch != NULL)
		{
			// Check for valid constant
			int iFirst = (int)(lpszSearch - lpszConstantText - 1);
			int iLast = iFirst + m_lpConstantInfo[i].nLen;
			bResult = TRUE;
			if (iFirst >= 0)
			{
				if (((lpszConstantText[iFirst] >= 'A') && (lpszConstantText[iFirst] <= 'Z')) || 
					((lpszConstantText[iFirst] >= 'a') && (lpszConstantText[iFirst] <= 'z')))
					bResult = FALSE;

			}
			if (bResult)
			{
				if (((lpszConstantText[iLast] >= 'A') && (lpszConstantText[iLast] <= 'Z')) || 
					((lpszConstantText[iLast] >= 'a') && (lpszConstantText[iLast] <= 'z')))
					bResult = FALSE;
				else
				{
					// Exit
					iConstantIndex = i;
					iConstantOffset = iFirst + 1;
					break;
				}
			}
		}
	}
	delete lpszConstantText;

	return bResult;
}

BOOL CUltraPadWnd::IsText(LPTSTR lpszText, int iLen, int& iTextStart, int& iTextEnd)
{
	BOOL bResult = FALSE;

	// Check for text
	iTextStart = -1;
	iTextEnd = -1;
	for (int i=0; i<iLen; i++)
	{
		// Check for text start index
		if ((iTextStart == -1) && ((lpszText[i] == '\"') || (lpszText[i] == '\'')))
		{
			iTextStart = i;
			bResult = TRUE;
		}
		else
		{
			// Check for text end index
			if ((iTextStart != -1) && ((lpszText[i] == '\"') || (lpszText[i] == '\'')))
				iTextEnd = i;
		}
	}

	return bResult;
}

void CUltraPadWnd::ShowAutoComplete()
{
	// Get scroll info
	int iVerticalOffset = GetScrollPos(SB_VERT);
	int iHorizontalOffset = GetScrollPos(SB_HORZ);

	// Create listbox
	POINT pt = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
	SelectWord(pt);
	pt.x -= iHorizontalOffset;
	pt.y -= iVerticalOffset;
	RECT rect = {pt.x, pt.y+m_szTextSize.cy, pt.x+100, pt.y+m_szTextSize.cy+200};
	m_ListBox.Create(WS_CHILD | WS_VISIBLE | WS_HSCROLL | LBS_STANDARD | LBS_WANTKEYBOARDINPUT, rect, this, 0x10001);
	m_ListBox.SetFont(&m_Font, TRUE);

	// Check for keyword or constant
	int iEndOffset = GetCharOffset(m_iCurrentLine, m_iCurrentChar);
	if (!IsLetter(m_lpszText[iEndOffset]))
		iEndOffset--;
	int iStartOffset = iEndOffset;
	while ((iStartOffset > 0) && (IsLetter(m_lpszText[iStartOffset])))
		iStartOffset--;
	if (iStartOffset > 0)
		iStartOffset++;
	int iLen = iEndOffset - iStartOffset + 1;
	if (iLen > 0)
	{
		LPTSTR lpszSearch = new _TCHAR[iLen+1];
		_tcsncpy(lpszSearch, m_lpszText+iStartOffset, iLen);
		lpszSearch[iLen] = '\0';
		for (int i=0; i<m_iNumberKeywords; i++)
		{
			// Check for valid keyword
			if (_tcsncmp(m_lpKeywordInfo[i].lpszTagName, lpszSearch, iLen) == 0)
			{
				// Add keyword
				m_ListBox.AddString(m_lpKeywordInfo[i].lpszTagName);
			}
		}
		for (int j=0; j<m_iNumberConstants; j++)
		{
			// Check for valid constant
			if (_tcsncmp(m_lpConstantInfo[j].lpszTagName, lpszSearch, iLen) == 0)
			{
				// Add constant
				m_ListBox.AddString(m_lpConstantInfo[j].lpszTagName);
			}
		}
		delete lpszSearch;
	}

	// Show listbox
	if (m_ListBox.GetCount() > 0)
	{
		m_ListBox.SetFocus();
		m_ListBox.SetCurSel(0);
	}
	else
		m_ListBox.DestroyWindow();
}

void CUltraPadWnd::SetKeywordColor(COLORREF cKeywordColor)
{
	// Set keyword color
	m_cKeywordColor = cKeywordColor;
}

COLORREF CUltraPadWnd::GetKeywordColor()
{
	// Get keyword color
	return m_cKeywordColor;
}

void CUltraPadWnd::SetKeywords(LPTSTR lpszKeywords, int iLen, BOOL bCaseSensitive)
{
	// Clear keywords
	for (int i=0; i<m_iNumberKeywords; i++)
		delete m_lpKeywordInfo[i].lpszTagName;
	free(m_lpKeywordInfo);
	m_lpKeywordInfo = NULL;

	// Set case sensitive flag
	m_bKeywordsCaseSensitive = bCaseSensitive;

	// Extract keywords
	m_iNumberKeywords = 0;
	int iOffset = 0;
	int iOldOffset = iOffset;
	int iCount;
	LPTSTR lpszNextKeyword = NULL;
	while ((lpszNextKeyword=_tcsstr(lpszKeywords+iOffset, _T(";"))) != NULL)
	{
		// Extract keyword
		iCount = (int)(lpszNextKeyword - (lpszKeywords+iOffset));
		iOffset = (int)(lpszNextKeyword - lpszKeywords);
		m_iNumberKeywords++;
		if (m_lpKeywordInfo == NULL)
			m_lpKeywordInfo = (LPTAGINFO)malloc(m_iNumberKeywords*sizeof(TAGINFO));
		else
			m_lpKeywordInfo = (LPTAGINFO)realloc(m_lpKeywordInfo, m_iNumberKeywords*sizeof(TAGINFO));
		m_lpKeywordInfo[m_iNumberKeywords-1].nLen = iCount + 1;
		m_lpKeywordInfo[m_iNumberKeywords-1].lpszTagName = new _TCHAR[m_lpKeywordInfo[m_iNumberKeywords-1].nLen];
		_tcsncpy(m_lpKeywordInfo[m_iNumberKeywords-1].lpszTagName, lpszKeywords+iOldOffset, m_lpKeywordInfo[m_iNumberKeywords-1].nLen-1);
		m_lpKeywordInfo[m_iNumberKeywords-1].lpszTagName[m_lpKeywordInfo[m_iNumberKeywords-1].nLen-1] = '\0';
		if (!m_bKeywordsCaseSensitive)
			_tcsupr(m_lpKeywordInfo[m_iNumberKeywords-1].lpszTagName);
		iOffset++;
		iOldOffset = iOffset;
	}
}

void CUltraPadWnd::SetConstantColor(COLORREF cConstantColor)
{
	// Set constant color
	m_cConstantColor = cConstantColor;
}

COLORREF CUltraPadWnd::GetConstantColor()
{
	// Get constant color
	return m_cConstantColor;
}

void CUltraPadWnd::SetConstants(LPTSTR lpszConstants, int iLen, BOOL bCaseSensitive)
{
	// Clear constants
	for (int i=0; i<m_iNumberConstants; i++)
		delete m_lpConstantInfo[i].lpszTagName;
	free(m_lpConstantInfo);
	m_lpConstantInfo = NULL;

	// Set case sensitive flag
	m_bConstantsCaseSensitive = bCaseSensitive;

	// Extract constants
	m_iNumberConstants = 0;
	int iOffset = 0;
	int iOldOffset = iOffset;
	int iCount;
	LPTSTR lpszNextConstant = NULL;
	while ((lpszNextConstant=_tcsstr(lpszConstants+iOffset, _T(";"))) != NULL)
	{
		// Extract constant
		iCount = (int)(lpszNextConstant - (lpszConstants+iOffset));
		iOffset = (int)(lpszNextConstant - lpszConstants);
		m_iNumberConstants++;
		if (m_lpConstantInfo == NULL)
			m_lpConstantInfo = (LPTAGINFO)malloc(m_iNumberConstants*sizeof(TAGINFO));
		else
			m_lpConstantInfo = (LPTAGINFO)realloc(m_lpConstantInfo, m_iNumberConstants*sizeof(TAGINFO));
		m_lpConstantInfo[m_iNumberConstants-1].nLen = iCount + 1;
		m_lpConstantInfo[m_iNumberConstants-1].lpszTagName = new _TCHAR[m_lpConstantInfo[m_iNumberConstants-1].nLen];
		_tcsncpy(m_lpConstantInfo[m_iNumberConstants-1].lpszTagName, lpszConstants+iOldOffset, m_lpConstantInfo[m_iNumberConstants-1].nLen-1);
		m_lpConstantInfo[m_iNumberConstants-1].lpszTagName[m_lpConstantInfo[m_iNumberConstants-1].nLen-1] = '\0';
		if (!m_bConstantsCaseSensitive)
			_tcsupr(m_lpConstantInfo[m_iNumberConstants-1].lpszTagName);
		iOffset++;
		iOldOffset = iOffset;
	}
}

void CUltraPadWnd::SetCommentColor(COLORREF cCommentColor)
{
	// Set comment color
	m_cCommentColor = cCommentColor;
}

COLORREF CUltraPadWnd::GetCommentColor()
{
	// Get comment color
	return m_cCommentColor;
}

void CUltraPadWnd::SetTextColor(COLORREF cTextColor)
{
	// Set text color
	m_cTextColor = cTextColor;
}

COLORREF CUltraPadWnd::GetTextColor()
{
	// Get text color
	return m_cTextColor;
}

void CUltraPadWnd::SetSyntaxColoring(SYNTAX_COLORING_TYPE syntaxColoring)
{
	// Set syntax coloring
	m_SyntaxColoring = syntaxColoring;

	// Update syntax coloring
	switch (m_SyntaxColoring)
	{
		case SCT_C_ANSI:
			{
				// Set ANSI-C syntax coloring
				CString strKeywords = _T("auto;__asm;__based;break;case;__cdecl;char;const;continue;__declspec;default;");
				strKeywords += _T("dllexport;dllimport;do;double;__except;else;enum;extern;__fastcall;__finally;float;for;");
				strKeywords += _T("goto;__inline;__int8;__int16;__int32;__int64;if;int;__leave;long;naked;register;return;");
				strKeywords += _T("__stdcall;short;signed;sizeof;static;struct;switch;__try;thread;typedef;union;unsigned;");
				strKeywords += _T("void;volatile;while;#define;#elif;#else;#endif;#error;#if;#ifdef;#ifndef;#import;#include;");
				strKeywords += _T("#line;#pragma;#undef;#using;once;pack;");
				SetKeywordColor(RGB(0,0,255));
				SetKeywords((LPTSTR)((LPCTSTR)strKeywords), strKeywords.GetLength(), TRUE);
				SetCommentColor(RGB(0,128,0));
				SetTextColor(RGB(163,21,21));
			}
			break;

		case SCT_C_PLUS_PLUS:
			{
				// Set C++ syntax coloring
				CString strKeywords = _T("__abstract;__alignof;__asm;__assume;__based;__box;__cdecl;__declspec;__delegate;");
				strKeywords += _T("__event;__except;__fastcall;__finally;__forceinline;__gc;__hook;__identifier;__if_exists;");
				strKeywords += _T("__if_not_exists;__inline;__int8;__int16;__int32;__int64;__interface;__leave;__m64;__m128;");
				strKeywords += _T("__m128d;__m128i;__multiple_inheritance;__nogc;__noop;__pin;__property;__raise;__sealed;");
				strKeywords += _T("__single_inheritance;__stdcall;__super;__try_cast;__try;__except;__finally;__unhook;__uuidof;");
				strKeywords += _T("__value;__virtual_inheritance;__w64;bool;break;case;catch;char;class;const;const_cast;continue;");
				strKeywords += _T("default;delete;deprecated;dllexport;dllimport;do;double;dynamic_cast;else;enum;explicit;extern;");
				strKeywords += _T("false;float;for;friend;goto;if;inline;int;long;mutable;naked;namespace;new;noinline;noreturn;");
				strKeywords += _T("nothrow;novtable;operator;private;property;protected;public;register;reinterpret_cast;return;");
				strKeywords += _T("selectany;short;signed;sizeof;static;static_cast;struct;switch;template;this;thread;throw;true;");
				strKeywords += _T("try;typedef;typeid;typename;union;unsigned;using;uuid;virtual;void;volatile;__wchar_t;wchar_t;while;");
				strKeywords += _T("#define;#elif;#else;#endif;#error;#if;#ifdef;#ifndef;#import;#include;");
				strKeywords += _T("#line;#pragma;#undef;#using;auto_rename;auto_search;embedded_idl;exclude;once;pack;");
				SetKeywordColor(RGB(0,0,255));
				SetKeywords((LPTSTR)((LPCTSTR)strKeywords), strKeywords.GetLength(), TRUE);
				CString strConstants = _T("TRUE;FALSE;NULL;");
				SetConstantColor(RGB(128,128,128));
				SetConstants((LPTSTR)((LPCTSTR)strConstants), strConstants.GetLength(), TRUE);
				SetCommentColor(RGB(0,128,0));
				SetTextColor(RGB(163,21,21));
			}
			break;

		case SCT_HTML:
			{
				// Set HTML syntax coloring
				CString strKeywords = _T("<a>;</a>;<a;<acronym>;</acronym>;<acronym;<address>;</address>;<address;<applet>;");
				strKeywords += _T("</applet>;<applet;<area>;</area>;<area;<b>;</b>;<b;<base>;</base>;<base;<baseFont>;</baseFont>;");
				strKeywords += _T("<baseFont;<bdo>;</bdo>;<bdo;<bgSound>;</bgSound>;<bgSound;<big>;</big>;<big;<blockQuote>;");
				strKeywords += _T("</blockQuote>;<blockQuote;<body>;</body>;<body;<br>;</br>;<br;<button>;</button>;<button;");
				strKeywords += _T("<caption>;</caption>;<caption;<center>;</center>;<center;<cite>;</cite>;<cite;<code>;</code>;<code;");
				strKeywords += _T("<col>;</col>;<col;<colGroup>;</colGroup>;<colGroup;<comment>;</comment>;<comment;<custom>;</custom>;<custom;");
				strKeywords += _T("<dd>;</dd>;<dd;<del>;</del>;<del;<dfn>;</dfn>;<dfn;<dir>;</dir>;<dir;<div>;</div>;<div;<dl>;</dl>;<dl;");
				strKeywords += _T("<dt>;</dt>;<dt;<em>;</em>;<em;<embed>;</embed>;<embed;<fieldSet>;</fieldSet>;<fieldSet;<font>;</font>;<font;");
				strKeywords += _T("<form>;</form>;<form;<frame>;</frame>;<frame;<frameSet>;</frameSet>;<frameSet;<head>;</head>;<head;");
				strKeywords += _T("<hn>;</hn>;<hn;<hr>;</hr>;<hr;<html>;</html>;<html;<i>;</i>;<i;<iframe>;</iframe>;<iframe;<img>;</img>;<img;");
				strKeywords += _T("<input>;</input>;<input;<ins>;</ins>;<ins;<isIndex>;</isIndex>;<isIndex;<kbd>;</kbd>;<kbd;<label>;</label>;<label;");
				strKeywords += _T("<legend>;</legend>;<legend;<li>;</li>;<li;<link>;</link>;<link;<listing>;</listing>;<listing;<map>;</map>;<map;");
				strKeywords += _T("<marquee>;</marquee>;<marquee;<menu>;</menu>;<menu;<meta>;</meta>;<meta;<noBR>;</noBR>;<noBR;");
				strKeywords += _T("<noFrames>;</noFrames>;<noFrames;<noScript>;</noScript>;<noScript;<object>;</object>;<object;<ol>;</ol>;<ol;");
				strKeywords += _T("<optGroup>;</optGroup>;<optGroup;<option>;</option>;<option;<p>;</p>;<p;<param>;</param>;<param;");
				strKeywords += _T("<plainText>;</plainText>;<plainText;<pre>;</pre>;<pre;<q>;</q>;<q;<rt>;</rt>;<rt;<ruby>;</ruby>;<ruby;");
				strKeywords += _T("<s>;</s>;<s;<samp>;</samp>;<samp;<script>;</script>;<script;<select>;</select>;<select;<small>;</small>;<small;");
				strKeywords += _T("<span>;</span>;<span;<strike>;</strike>;<strike;<strong>;</strong>;<strong;<style>;</style>;<style;");
				strKeywords += _T("<sub>;</sub>;<sub;<sup>;</sup>;<sup;<table>;</table>;<table;<tBody>;</tBody>;<tBody;<td>;</td>;<td;");
				strKeywords += _T("<textArea>;</textArea>;<textArea;<tFoot>;</tFoot>;<tFoot;<th>;</th>;<th;<tHead>;</tHead>;<tHead;");
				strKeywords += _T("<title>;</title>;<title;<tr>;</tr>;<tr;<tt>;</tt>;<tt;<u>;</u>;<u;<ul>;</ul>;<ul;<var>;</var>;<var;");
				strKeywords += _T("<wbr>;</wbr>;<wbr;<xml>;</xml>;<xml;<xmp>;</xmp>;<xmp;<h1>;</h1>;<h1;<h2>;</h2>;<h2;<h3>;</h3>;<h3;");
				strKeywords += _T("<h4>;</h4>;<h4;<h5>;</h5>;<h5;<h6></h6>;<h6;<;/>;>;");
				SetKeywordColor(RGB(0,0,255));
				SetKeywords((LPTSTR)((LPCTSTR)strKeywords), strKeywords.GetLength(), FALSE);
				SetCommentColor(RGB(0,128,0));
				SetTextColor(RGB(163,21,21));
			}
			break;

		case SCT_JSCRIPT:
			{
				// Set JScript syntax coloring
				CString strKeywords = _T("break;catch;@cc_on;continue;debugger;do;else;@elif;@else;@end;for;function;in;@if;if;");
				strKeywords += _T("return;switch;@set;this;throw;try;var;while;with;");
				SetKeywordColor(RGB(0,0,255));
				SetKeywords((LPTSTR)((LPCTSTR)strKeywords), strKeywords.GetLength(), FALSE);
				CString strConstants = _T("true;false;null;");
				SetConstantColor(RGB(128,128,128));
				SetConstants((LPTSTR)((LPCTSTR)strConstants), strConstants.GetLength(), FALSE);
				SetCommentColor(RGB(0,128,0));
				SetTextColor(RGB(163,21,21));
			}
			break;

		case SCT_SQL:
			{
				// Set SQL syntax coloring
				CString strKeywords = _T("@@IDENTITY;ENCRYPTION;ORDER;ADD;END;ALL;ERRLVL;OVER;ALTER;ESCAPE;PERCENT;");
				strKeywords += _T("AND;EXCEPT;PLAN;ANY;EXEC;PRECISION;AS;EXECUTE;PRIMARY;EXISTS;PRINT;AUTHORIZATION;");
				strKeywords += _T("EXIT;PROC;EXPRESSION;PROCEDURE;BACKUP;FETCH;PUBLIC;BEGIN;FILE;RAISERROR;BETWEEN;");
				strKeywords += _T("FILLFACTOR;READ;ASC;BREAK;FOR;READTEXT;BROWSE;FOREIGN;RECONFIGURE;BULK;FREETEXT;");
				strKeywords += _T("REFERENCES;BY;FREETEXTTABLE;REPLICATION;CASCADE;FROM;RESTORE;CASE;RESTRICT;CHECK;");
				strKeywords += _T("FUNCTION;RETURN;CHECKPOINT;GOTO;REVOKE;CLOSE;GRANT;CLUSTERED;GROUP;ROLLBACK;");
				strKeywords += _T("COALESCE;HAVING;ROWCOUNT;COLLATE;HOLDLOCK;ROWGUIDCOL;COLUMN;IDENTITY;RULE;COMMIT;IDENTITY_INSERT;");
				strKeywords += _T("SAVE;COMPUTE;IDENTITYCOL;SCHEMA;CONSTRAINT;IF;SELECT;CONTAINS;IN;SESSION_USER;CONTAINSTABLE;");
				strKeywords += _T("INDEX;SET;CONTINUE;SETUSER;INSERT;SHUTDOWN;INTERSECT;SOME;CREATE;INTO;");
				strKeywords += _T("STATISTICS;IS;CURRENT;JOIN;SYSTEM_USER;CURRENT_DATE;KEY;TABLE;CURRENT_TIME;KILL;");
				strKeywords += _T("TEXTSIZE;CURRENT_TIMESTAMP;THEN;CURRENT_USER;LIKE;TO;CURSOR;LINENO;TOP;DATABASE;LOAD;");
				strKeywords += _T("TRAN;DATABASEPASSWORD;TRANSACTION;DATEADD;TRIGGER;DATEDIFF;NATIONAL;TRUNCATE;DATENAME;");
				strKeywords += _T("NOCHECK;TSEQUAL;DATEPART;NONCLUSTERED;UNION;DBCC;NOT;UNIQUE;DEALLOCATE;UPDATE;DECLARE;NULLIF;");
				strKeywords += _T("UPDATETEXT;DEFAULT;OF;USE;DELETE;OFF;USER;DENY;OFFSETS;VALUES;DESC;ON;VARYING;DISK;OPEN;VIEW;");
				strKeywords += _T("DISTINCT;OPENDATASOURCE;WAITFOR;DISTRIBUTED;OPENQUERY;WHEN;DOUBLE;OPENROWSET;WHERE;DROP;OPENXML;");
				strKeywords += _T("WHILE;DUMP;OPTION;WITH;ELSE;OR;WRITETEXT;");
				SetKeywordColor(RGB(0,0,255));
				SetKeywords((LPTSTR)((LPCTSTR)strKeywords), strKeywords.GetLength(), FALSE);
				CString strConstants = _T("INNER;OUTER;CROSS;FULL;LEFT;RIGHT;TRUE;FALSE;NULL;COLUMNS;INDEXES;KEY_COLUMN_USAGE;DECIMAL;");
				strConstants += _T("PROVIDER_TYPES;TABLES;TABLE_CONSTRAINTS;BIGINT;INTEGER;SMALLINT;TINYINT;BIT;NUMERIC;MONEY;CHAR;VARCHAR;");
				strConstants += _T("FLOAT;REAL;DATETIME;NCHAR;NVARCHAR;NTEXT;BINARY;VARBINARY;IMAGE;UNIQUEIDENTIFIER;IDENTITY;ROWGUIDCOL;");
				strConstants += _T("AVG;MAX;MIN;SUM;COUNT;DATEADD;DATEDIFF;DATENAME;DATEPART;GETDATE;ABS;ACOS;ASIN;ATAN;ATN2;CEILING;COS;");
				strConstants += _T("COT;DEGREES;EXP;FLOOR;LOG;LOG10;PI;POWER;RADIANS;RAND;ROUND;SIGN;SIN;SQRT;TAN;NCHAR;CHARINDEX;LEN;");
				strConstants += _T("LOWER;LTRIM;PATINDEX;REPLACE;REPLICATE;RTRIM;SPACE;STR;STUFF;SUBSTRING;UNICODE;UPPER;CONVERT;");
				SetConstantColor(RGB(128,128,128));
				SetConstants((LPTSTR)((LPCTSTR)strConstants), strConstants.GetLength(), FALSE);
				SetCommentColor(RGB(0,128,0));
				SetTextColor(RGB(163,21,21));
			}
			break;
	}

	// Update screen
	Invalidate(FALSE);
	UpdateWindow();
}

SYNTAX_COLORING_TYPE CUltraPadWnd::GetSyntaxColoring()
{
	// Get syntax coloring
	return m_SyntaxColoring;
}

void CUltraPadWnd::SetCurrentLine(int index)
{
	// Set current line
	m_iCurrentLine = max(0, min(m_iNumberLines-1, index));
}

int CUltraPadWnd::GetCurrentLine()
{
	// Get current line
	return m_iCurrentLine;
}

void CUltraPadWnd::SetCurrentChar(int index)
{
	// Set current char
	m_iCurrentLine = max(0, min(m_lpLineInfo[m_iCurrentLine].nLen, index));
}

int CUltraPadWnd::GetCurrentChar()
{
	// Get current char
	return m_iCurrentChar;
}

void CUltraPadWnd::SetTABs(int iTABs)
{
	// Set TABs
	m_iTABs = max(1, min(8, iTABs));
}

int CUltraPadWnd::GetTABs()
{
	// Get TABs
	return m_iTABs;
}

void CUltraPadWnd::EnsureVisible()
{
	// Ensure caret is visible
	UpdateCaret();
}

void CUltraPadWnd::SetSelection(int iStartLine, int iStartChar, int iEndLine, int iEndChar)
{
	// Set selection
	m_iLineStartSelection = max(0, iStartLine);
	m_iCharStartSelection = max(0, iStartChar);
	m_iLineEndSelection = min(m_iNumberLines-1, iEndLine);
	if (m_iLineEndSelection == -1)
		m_iLineEndSelection = m_iNumberLines - 1;
	m_iCharEndSelection = min(m_lpLineInfo[m_iLineEndSelection].nLen, iEndChar);
	if (m_iCharEndSelection == -1)
		m_iCharEndSelection = m_lpLineInfo[m_iLineEndSelection].nLen;
}

void CUltraPadWnd::GetSelection(int& iStartLine, int& iStartChar, int& iEndLine, int& iEndChar)
{
	// Get selection
	iStartLine = m_iLineStartSelection;
	iStartChar = m_iCharStartSelection;
	iEndLine = m_iLineEndSelection;
	iEndChar = m_iCharEndSelection;
}

int CUltraPadWnd::GetNumberLines()
{
	// Get number lines
	return m_iNumberLines;
}

void CUltraPadWnd::SetBackgroundColor(COLORREF cBgColor)
{
	// Set background color
	m_cBgColor = cBgColor;
}

COLORREF CUltraPadWnd::GetBackgroundColor()
{
	// Get background color
	return m_cBgColor;
}

void CUltraPadWnd::SetLineDrawProc(LPLINEDRAWPROC lpfnLineDrawProc)
{
	// Set LineDrawProc
	m_lpfnLineDrawProc = lpfnLineDrawProc;
}

void CUltraPadWnd::DefaultLineDrawProc(LPTSTR lpszText, int iLen, RECT rect, int iSelectionStart, int iSelectionEnd)
{
	// Draw line by default
	DrawLine(lpszText, iLen, rect, iSelectionStart, iSelectionEnd);
}

BOOL CUltraPadWnd::OnEraseBkgnd(CDC* pDC)
{
	return FALSE;
}

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

	// Get client rectangle
	RECT rcClient;
	GetClientRect(&rcClient);

	// Clear members
	if (m_MemDC.m_hDC != NULL)
	{
		m_MemDC.SelectObject(m_pOldMemBitmap);
		m_MemDC.SelectObject(m_pOldFont);
		m_MemDC.DeleteDC();
		m_MemBitmap.DeleteObject();
	}

	// Create memory DC and bitmap
	CDC* pDC = GetDC();
	m_MemDC.CreateCompatibleDC(pDC);
	m_MemBitmap.CreateCompatibleBitmap(pDC, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
	m_pOldMemBitmap = m_MemDC.SelectObject(&m_MemBitmap);
	m_pOldFont = m_MemDC.SelectObject(&m_Font);
	ReleaseDC(pDC);

	// Create caret
	DestroyCaret();
	CSize sz = m_MemDC.GetTextExtent(_T("A"), 1);
	::CreateCaret(m_hWnd, NULL, 2, sz.cy);
	CPoint pt = GetCaretPos();
	SetCaretPos(pt);
	ShowCaret();
}

void CUltraPadWnd::OnPaint()
{
	CPaintDC dc(this);

	// Clear background
	RECT rcClient;
	GetClientRect(&rcClient);
	m_MemDC.FillSolidRect(&rcClient, m_cBgColor);

	// Draw content
	DrawContent();

	// Draw on screen DC
	dc.BitBlt(0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, &m_MemDC, 0, 0, SRCCOPY);
}

void CUltraPadWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// Check for listbox visible
	if (m_ListBox.m_hWnd != NULL)
	{
		// Destroy listbox
		m_ListBox.DestroyWindow();
	}

	// Get scroll info
	SCROLLINFO si;
	memset(&si, 0, sizeof(SCROLLINFO));
	si.cbSize = sizeof(SCROLLINFO);
	si.fMask = SIF_ALL;
	GetScrollInfo(SB_VERT, &si);

	// Update vertical scrolling
	switch (nSBCode)
	{
		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
			{
				// Update scroll position
				si.nPos = (si.nTrackPos / m_szTextSize.cy) * m_szTextSize.cy;
			}
			break;

		case SB_LINEDOWN:
		case SB_PAGEDOWN:
			{
				// Update scroll position
				si.nPos += m_szTextSize.cy;
				if (si.nPos > (int)(si.nMax-si.nPage))
					si.nPos -= m_szTextSize.cy;
			}
			break;

		case SB_LINEUP:
		case SB_PAGEUP:
			{
				// Update scroll position
				si.nPos -= m_szTextSize.cy;
				if (si.nPos < si.nMin)
					si.nPos = si.nMin;
			}
			break;
	}

	// Set scroll info
	SetScrollInfo(SB_VERT, &si, TRUE);

	// Get scroll info
	int iVerticalOffset = GetScrollPos(SB_VERT);
	int iHorizontalOffset = GetScrollPos(SB_HORZ);

	// Get char position
	POINT pt = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
	pt.x -= iHorizontalOffset;
	pt.y -= iVerticalOffset;
	SetCaretPos(pt);

	// Update screen
	Invalidate(FALSE);
	UpdateWindow();

	CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CUltraPadWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// Check for listbox visible
	if (m_ListBox.m_hWnd != NULL)
	{
		// Destroy listbox
		m_ListBox.DestroyWindow();
	}

	// Get scroll info
	SCROLLINFO si;
	memset(&si, 0, sizeof(SCROLLINFO));
	si.cbSize = sizeof(SCROLLINFO);
	si.fMask = SIF_ALL;
	GetScrollInfo(SB_HORZ, &si);

	// Update vertical scrolling
	switch (nSBCode)
	{
		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
			{
				// Update scroll position
				si.nPos = (si.nTrackPos / m_szTextSize.cx) * m_szTextSize.cx;
			}
			break;

		case SB_LINERIGHT:
		case SB_PAGERIGHT:
			{
				// Update scroll position
				si.nPos += m_szTextSize.cx;
				if (si.nPos > (int)(si.nMax-si.nPage))
					si.nPos -= m_szTextSize.cx;
			}
			break;

		case SB_LINELEFT:
		case SB_PAGELEFT:
			{
				// Update scroll position
				si.nPos -= m_szTextSize.cx;
				if (si.nPos < si.nMin)
					si.nPos = si.nMin;
			}
			break;
	}

	// Set scroll info
	SetScrollInfo(SB_HORZ, &si, TRUE);

	// Get scroll info
	int iVerticalOffset = GetScrollPos(SB_VERT);
	int iHorizontalOffset = GetScrollPos(SB_HORZ);

	// Get char position
	POINT pt = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
	pt.x -= iHorizontalOffset;
	pt.y -= iVerticalOffset;
	SetCaretPos(pt);

	// Update screen
	Invalidate(FALSE);
	UpdateWindow();

	CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}

BOOL CUltraPadWnd::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// Check for listbox visible
	if (m_ListBox.m_hWnd != NULL)
	{
		// Destroy listbox
		m_ListBox.DestroyWindow();
	}

	// Process mouse-wheel message
	if (zDelta < 0)
	{
		// Scroll down
		OnVScroll(SB_LINEDOWN, 0, 0);
	}
	else
	{
		// Scroll up
		OnVScroll(SB_LINEUP, 0, 0);
	}

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

void CUltraPadWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Check for listbox visible
		if (m_ListBox.m_hWnd != NULL)
		{
			// Destroy listbox
			m_ListBox.DestroyWindow();
		}

		// Capture mouse
		SetCapture();

		// Get scroll info
		int iVerticalOffset = GetScrollPos(SB_VERT);
		int iHorizontalOffset = GetScrollPos(SB_HORZ);

		// Get mouse position
		POINT pt = {point.x+iHorizontalOffset, point.y+iVerticalOffset};

		// Check for valid selection
		if (!m_bSelecting)
		{
			// Set selection start position
			m_iLineStartSelection = LineFromPosition(pt);
			m_iLineEndSelection = m_iLineStartSelection;
			m_iCharStartSelection = CharFromPosition(m_iLineStartSelection, pt);
			m_iCharEndSelection = m_iCharStartSelection;

			// Set caret position
			m_iCurrentLine = m_iLineStartSelection;
			m_iCurrentChar = m_iCharStartSelection;
			POINT ptCaret = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
			ptCaret.x -= iHorizontalOffset;
			ptCaret.y -= iVerticalOffset;
			SetCaretPos(ptCaret);
		}
		else
		{
			// Set selection end position
			m_iLineEndSelection = LineFromPosition(pt);
			m_iCharEndSelection = CharFromPosition(m_iLineEndSelection, pt);

			// Set caret position
			m_iCurrentLine = m_iLineEndSelection;
			m_iCurrentChar = m_iCharEndSelection;
			POINT ptCaret = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
			ptCaret.x -= iHorizontalOffset;
			ptCaret.y -= iVerticalOffset;
			SetCaretPos(ptCaret);
		}

		// Update screen
		Invalidate(FALSE);
		UpdateWindow();
	}

	CWnd::OnLButtonDown(nFlags, point);
}

void CUltraPadWnd::OnMouseMove(UINT nFlags, CPoint point)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Check for mouse dragging
		if ((nFlags & MK_LBUTTON) && (m_iLineStartSelection != -1) && (m_iCharStartSelection != -1))
		{
			// Get scroll info
			int iVerticalOffset = GetScrollPos(SB_VERT);
			int iHorizontalOffset = GetScrollPos(SB_HORZ);

			// Get mouse position
			POINT pt = {max(0, point.x+iHorizontalOffset), max(0, point.y+iVerticalOffset)};

			// Set selection end position
			m_iLineEndSelection = LineFromPosition(pt);
			m_iCharEndSelection = CharFromPosition(m_iLineEndSelection, pt);

			// Set caret position
			m_iCurrentLine = m_iLineEndSelection;
			m_iCurrentChar = m_iCharEndSelection;
			POINT ptCaret = PositionFromChar(m_iCurrentLine, m_iCurrentChar);
			ptCaret.x -= iHorizontalOffset;
			ptCaret.y -= iVerticalOffset;
			SetCaretPos(ptCaret);

			// Update screen
			Invalidate(FALSE);
			UpdateWindow();
		}
	}

	CWnd::OnMouseMove(nFlags, point);
}

void CUltraPadWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Release mouse
		ReleaseCapture();

		// Check for invalid selection
		if ((m_iLineStartSelection == m_iLineEndSelection) && (m_iCharStartSelection == m_iCharEndSelection))
		{
			// Clear selection
			m_iLineStartSelection = -1;
			m_iLineEndSelection = -1;
			m_iCharStartSelection = -1;
			m_iCharEndSelection = -1;
		}

		// Update screen
		Invalidate(FALSE);
		UpdateWindow();
	}

	CWnd::OnLButtonUp(nFlags, point);
}

void CUltraPadWnd::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Get scroll info
		int iVerticalOffset = GetScrollPos(SB_VERT);
		int iHorizontalOffset = GetScrollPos(SB_HORZ);

		// Get mouse position
		POINT pt = {point.x+iHorizontalOffset, point.y+iVerticalOffset};
		
		// Select word
		SelectWord(pt);

		// Update screen
		Invalidate(FALSE);
		UpdateWindow();
	}

	CWnd::OnLButtonDblClk(nFlags, point);
}

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

	// Create caret
	DestroyCaret();
	CSize sz = m_MemDC.GetTextExtent(_T("A"), 1);
	::CreateCaret(m_hWnd, NULL, 2, sz.cy);
	CPoint pt = GetCaretPos();
	SetCaretPos(pt);
	ShowCaret();
}

void CUltraPadWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Process keyboard input
		BOOL bUpdate = FALSE;
		switch (nChar)
		{
			case VK_LEFT:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);

						// Update mode
						bUpdate = TRUE;
					}
					else
					{
						// Move caret left
						if (m_iCurrentChar > 0)
							m_iCurrentChar--;
						else if (m_iCurrentLine > 0)
						{
							m_iCurrentLine--;
							m_iCurrentChar = m_lpLineInfo[m_iCurrentLine].nLen;
						}

						// Check for valid selection
						if (m_bSelecting)
						{
							// Set selection end position
							m_iLineEndSelection = m_iCurrentLine;
							m_iCharEndSelection = m_iCurrentChar;
						}
					}
				}
				break;

			case VK_RIGHT:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);

						// Update mode
						bUpdate = TRUE;
					}
					else
					{
						// Move caret right
						if (m_iCurrentChar < m_lpLineInfo[m_iCurrentLine].nLen)
							m_iCurrentChar++;
						else if (m_iCurrentLine < m_iNumberLines-1)
						{
							m_iCurrentLine++;
							m_iCurrentChar = 0;
						}

						// Check for valid selection
						if (m_bSelecting)
						{
							// Set selection end position
							m_iLineEndSelection = m_iCurrentLine;
							m_iCharEndSelection = m_iCurrentChar;
						}
					}
				}
				break;

			case VK_UP:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);

						// Update mode
						bUpdate = TRUE;
					}
					else
					{
						// Move caret up
						if (m_iCurrentLine > 0)
						{
							m_iCurrentLine--;
							if (m_iCurrentChar > m_lpLineInfo[m_iCurrentLine].nLen)
								m_iCurrentChar = m_lpLineInfo[m_iCurrentLine].nLen;
						}
						else
							m_iCurrentChar = 0;

						// Check for valid selection
						if (m_bSelecting)
						{
							// Set selection end position
							m_iLineEndSelection = m_iCurrentLine;
							m_iCharEndSelection = m_iCurrentChar;
						}
					}
				}
				break;

			case VK_DOWN:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);

						// Update mode
						bUpdate = TRUE;
					}
					else
					{
						// Move caret down
						if (m_iCurrentLine < m_iNumberLines-1)
						{
							m_iCurrentLine++;
							if (m_iCurrentChar > m_lpLineInfo[m_iCurrentLine].nLen)
								m_iCurrentChar = m_lpLineInfo[m_iCurrentLine].nLen;
						}
						else
							m_iCurrentChar = m_lpLineInfo[m_iCurrentLine].nLen;

						// Check for valid selection
						if (m_bSelecting)
						{
							// Set selection end position
							m_iLineEndSelection = m_iCurrentLine;
							m_iCharEndSelection = m_iCurrentChar;
						}
					}
				}
				break;

			case VK_HOME:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Move caret leftmost
						m_iCurrentChar = 0;

						// Check for valid selection
						if (m_bSelecting)
						{
							// Set selection end position
							m_iLineEndSelection = m_iCurrentLine;
							m_iCharEndSelection = m_iCurrentChar;
						}
					}
				}
				break;

			case VK_END:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Move caret rightmost
						m_iCurrentChar = m_lpLineInfo[m_iCurrentLine].nLen;

						// Check for valid selection
						if (m_bSelecting)
						{
							// Set selection end position
							m_iLineEndSelection = m_iCurrentLine;
							m_iCharEndSelection = m_iCurrentChar;
						}
					}
				}
				break;

			case VK_SHIFT:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Check for valid selection
						if (!m_bSelecting)
						{
							// Selection mode
							m_bSelecting = TRUE;

							// Set selection start and end position
							m_iLineStartSelection = m_iCurrentLine;
							m_iCharStartSelection = m_iCurrentChar;
							m_iLineEndSelection = m_iLineStartSelection;
							m_iCharEndSelection = m_iCharStartSelection;
						}
					}
				}
				break;

			case VK_CONTROL:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Copying mode
						m_bCopying = TRUE;
					}
				}
				break;

			case VK_DELETE:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Check for valid selection
						if ((m_iLineStartSelection != m_iLineEndSelection) || (m_iCharStartSelection != m_iCharEndSelection))
						{
							// Delete selected text
							DeleteText();
						}
						else
						{
							// Delete next char
							int iCurrentOffset = GetCharOffset(m_iCurrentLine, m_iCurrentChar);
							if ((m_lpszText != NULL) && (iCurrentOffset < m_iSize-2))
							{
								int iOldSize = m_iSize;
								LPTSTR lpszTemp = new _TCHAR[iOldSize];
								memcpy(lpszTemp, m_lpszText, iOldSize*sizeof(_TCHAR));
								if (m_lpszText[iCurrentOffset+1] == '\n')
								{
									m_iSize -= 2;
									free(m_lpszText);
									m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
									if (m_lpszText != NULL)
									{
										memcpy(m_lpszText, lpszTemp, iCurrentOffset*sizeof(_TCHAR));
										if (iCurrentOffset <= m_iSize)
											memcpy(m_lpszText+iCurrentOffset, lpszTemp+iCurrentOffset+2, (iOldSize-iCurrentOffset+2)*sizeof(_TCHAR));
									}
									m_iCurrentChar = m_lpLineInfo[m_iCurrentLine].nLen;
								}
								else
								{
									m_iSize--;
									free(m_lpszText);
									m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
									if (m_lpszText != NULL)
									{
										memcpy(m_lpszText, lpszTemp, iCurrentOffset*sizeof(_TCHAR));
										if (iCurrentOffset <= m_iSize)
											memcpy(m_lpszText+iCurrentOffset, lpszTemp+iCurrentOffset+1, (iOldSize-iCurrentOffset+1)*sizeof(_TCHAR));
									}
								}
								delete lpszTemp;
							}
						}

						// Update control
						UpdateControl();

						// Update mode
						bUpdate = TRUE;
					}
				}
				break;

			case VK_BACK:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Update mode
						bUpdate = TRUE;
					}
				}
				break;

			default:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Check for copying flag
						if (!m_bCopying)
						{
							// Check for valid selection
							if ((m_iLineStartSelection != m_iLineEndSelection) || (m_iCharStartSelection != m_iCharEndSelection))
							{
								// Delete selected text
								DeleteText();
							}
						}
					}
				}
				break;
		}

		// Check for valid selection
		if ((!m_bSelecting) && (!bUpdate) && (!m_bCopying))
		{
			// Clear selection
			m_iLineStartSelection = -1;
			m_iLineEndSelection = -1;
			m_iCharStartSelection = -1;
			m_iCharEndSelection = -1;
		}

		// Update caret
		UpdateCaret();
	}

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

void CUltraPadWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// Check for valid data
	if (m_lpszText != NULL)
	{
		// Process keyboard input
		switch (nChar)
		{
			case VK_SHIFT:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Selection mode
						m_bSelecting = FALSE;
					}
				}
				break;

			case VK_CONTROL:
				{
					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Send message to listbox
						m_ListBox.SendMessage(WM_KEYDOWN, (WPARAM)nChar, 0);
					}
					else
					{
						// Copying mode
						m_bCopying = FALSE;
					}
				}
				break;
		}
	}

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

void CUltraPadWnd::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// Check for keyboard input
	switch (nChar)
	{
		case VK_RETURN:
			{
				// Check for listbox visible
				if (m_ListBox.m_hWnd != NULL)
				{
					// Get selected keyword
					CString strKeyword;
					m_ListBox.GetText(m_ListBox.GetCurSel(), strKeyword);

					// Destroy listbox
					m_ListBox.DestroyWindow();

					// Delete selected text
					DeleteText();

					// Add text
					AddText((LPTSTR)((LPCTSTR)strKeyword), strKeyword.GetLength());

					// Update caret position
					m_iCurrentChar += strKeyword.GetLength();
					UpdateCaret();
				}
				else
				{
					// Add new line
					int iOldSize = m_iSize;
					int iCurrentOffset = GetCharOffset(m_iCurrentLine, m_iCurrentChar);
					LPTSTR lpszTemp = new _TCHAR[iOldSize];
					memcpy(lpszTemp, m_lpszText, iOldSize*sizeof(_TCHAR));
					m_iSize += 2;
					free(m_lpszText);
					m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
					memcpy(m_lpszText, lpszTemp, iCurrentOffset*sizeof(_TCHAR));
					m_lpszText[iCurrentOffset] = '\r';
					m_lpszText[iCurrentOffset+1] = '\n';
					memcpy(m_lpszText+iCurrentOffset+2, lpszTemp+iCurrentOffset, (iOldSize-iCurrentOffset)*sizeof(_TCHAR));
					delete lpszTemp;
					m_iCurrentChar = 0;
					m_iCurrentLine++;
				}
			}
			break;

		case VK_BACK:
			{
				// Check for valid selection
				if ((m_iLineStartSelection != m_iLineEndSelection) || (m_iCharStartSelection != m_iCharEndSelection))
				{
					// Delete selected text
					DeleteText();
				}
				else
				{
					// Delete previous char
					int iCurrentOffset = GetCharOffset(m_iCurrentLine, m_iCurrentChar);
					if ((m_lpszText != NULL) && (iCurrentOffset > 0))
					{
						int iOldSize = m_iSize;
						LPTSTR lpszTemp = new _TCHAR[iOldSize];
						memcpy(lpszTemp, m_lpszText, iOldSize*sizeof(_TCHAR));
						if (m_lpszText[iCurrentOffset-1] == '\n')
						{
							m_iSize -= 2;
							free(m_lpszText);
							m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
							if (m_lpszText != NULL)
							{
								memcpy(m_lpszText, lpszTemp, (iCurrentOffset-2)*sizeof(_TCHAR));
								if (iCurrentOffset <= m_iSize)
									memcpy(m_lpszText+iCurrentOffset-2, lpszTemp+iCurrentOffset, (iOldSize-iCurrentOffset)*sizeof(_TCHAR));
							}
							m_iCurrentLine = max(0, m_iCurrentLine-1);
							m_iCurrentChar = m_lpLineInfo[m_iCurrentLine].nLen;
						}
						else
						{
							m_iSize--;
							free(m_lpszText);
							m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
							if (m_lpszText != NULL)
							{
								memcpy(m_lpszText, lpszTemp, (iCurrentOffset-1)*sizeof(_TCHAR));
								if (iCurrentOffset <= m_iSize)
									memcpy(m_lpszText+iCurrentOffset-1, lpszTemp+iCurrentOffset, (iOldSize-iCurrentOffset)*sizeof(_TCHAR));
							}
							m_iCurrentChar = max(0, m_iCurrentChar-1);
						}
						delete lpszTemp;

						// Check for listbox visible
						if (m_ListBox.m_hWnd != NULL)
						{
							// Destroy listbox
							m_ListBox.DestroyWindow();

							// Show auto-complete listbox
							ShowAutoComplete();
						}
					}
				}
			}
			break;

		case VK_ESCAPE:
			{
				// Check for listbox visible
				if (m_ListBox.m_hWnd != NULL)
				{
					// Destroy listbox
					m_ListBox.DestroyWindow();
				}
			}
			break;

		case 0x03:
			{
				// Check for valid selection
				if ((m_iLineStartSelection != m_iLineEndSelection) || (m_iCharStartSelection != m_iCharEndSelection))
				{
					// Get selection range
					int iStartChar, iEndChar;
					int iStartLine, iEndLine;
					if (m_iLineStartSelection < m_iLineEndSelection)
					{
						iStartChar = m_iCharStartSelection;
						iEndChar = m_iCharEndSelection;
						iStartLine = m_iLineStartSelection;
						iEndLine = m_iLineEndSelection;
					}
					else if (m_iLineStartSelection > m_iLineEndSelection)
					{
						iStartChar = m_iCharEndSelection;
						iEndChar = m_iCharStartSelection;
						iStartLine = m_iLineEndSelection;
						iEndLine = m_iLineStartSelection;
					}
					else
					{
						iStartChar = min(m_iCharStartSelection, m_iCharEndSelection);
						iEndChar = max(m_iCharStartSelection, m_iCharEndSelection);
						iStartLine = m_iLineStartSelection;
						iEndLine = m_iLineEndSelection;
					}
					int iStartOffset = GetCharOffset(iStartLine, iStartChar);
					if (m_lpszText[iStartOffset] == '\r')
						iStartOffset += 2;
					int iEndOffset = GetCharOffset(iEndLine, iEndChar);
					int iCount = iEndOffset - iStartOffset;

					// Copy selection on the clipboard
					if (::OpenClipboard(m_hWnd))
					{
						::EmptyClipboard();
						HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, (iCount+1)*sizeof(_TCHAR));
						LPTSTR lpszText = (LPTSTR)::GlobalLock(hMem);
						memcpy(lpszText, m_lpszText+iStartOffset, iCount*sizeof(_TCHAR));
						lpszText[iCount] = '\0';
						::SetClipboardData(CF_UNICODETEXT, hMem);
						::GlobalUnlock(hMem);
						::GlobalFree(hMem);
						::CloseClipboard();
					}
				}
			}
			break;

		case 0x16:
			{
				// Check for valid selection
				if ((m_iLineStartSelection != m_iLineEndSelection) || (m_iCharStartSelection != m_iCharEndSelection))
				{
					// Delete selected text
					DeleteText();
				}

				// Paste selection from the clipboard
				if (::OpenClipboard(m_hWnd))
				{
					HGLOBAL hMem = ::GetClipboardData(CF_UNICODETEXT);
					LPTSTR lpszText = (LPTSTR)::GlobalLock(hMem);
					if (lpszText != NULL)
					{
						// Add UNICODE text
						int iLen = (int)_tcslen(lpszText);
						AddText(lpszText, iLen);
					}
					else
					{
						// Add ANSI text
						hMem = ::GetClipboardData(CF_TEXT);
						LPSTR lpszText = (LPSTR)::GlobalLock(hMem);
						if (lpszText != NULL)
						{
							int iLen = (int)strlen(lpszText);
							int iSize = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpszText, iLen, NULL, 0);
							LPTSTR lpszNewText = (LPTSTR)malloc(iSize*sizeof(_TCHAR));
							MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpszText, iLen, lpszNewText, iSize);
							AddText(lpszNewText, iLen);
							free(lpszNewText);
						}
					}
					::GlobalUnlock(hMem);
					::CloseClipboard();
				}
			}
			break;

		case 0x01:
			{
				// Select all text
				m_iCharStartSelection = 0;
				m_iLineStartSelection = 0;
				m_iCharEndSelection = m_lpLineInfo[m_iNumberLines-1].nLen;
				m_iLineEndSelection = m_iNumberLines - 1;
			}
			break;

		default:
			{
				// Check for copying flag
				if (!m_bCopying)
				{
					// Add new char
					int iOldSize = m_iSize;
					int iCurrentOffset = GetCharOffset(m_iCurrentLine, m_iCurrentChar);
					LPTSTR lpszTemp = new _TCHAR[iOldSize];
					memcpy(lpszTemp, m_lpszText, iOldSize*sizeof(_TCHAR));
					m_iSize++;
					free(m_lpszText);
					m_lpszText = (LPTSTR)malloc(m_iSize*sizeof(_TCHAR));
					memcpy(m_lpszText, lpszTemp, iCurrentOffset*sizeof(_TCHAR));
					m_lpszText[iCurrentOffset] = nChar;
					memcpy(m_lpszText+iCurrentOffset+1, lpszTemp+iCurrentOffset, (iOldSize-iCurrentOffset)*sizeof(_TCHAR));
					delete lpszTemp;
					m_iCurrentChar++;

					// Check for listbox visible
					if (m_ListBox.m_hWnd != NULL)
					{
						// Destroy listbox
						m_ListBox.DestroyWindow();

						// Show auto-complete listbox
						ShowAutoComplete();
					}
				}
				else
				{
					// Clear flags
					m_bSelecting = FALSE;
					m_bCopying = FALSE;

					// Show auto-complete listbox
					ShowAutoComplete();
				}
			}
	}

	// Update control
	UpdateControl();

	// Update caret
	UpdateCaret();

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

BOOL CUltraPadWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	// Update cursor
	if ((nHitTest == HTCLIENT) && (m_ListBox.m_hWnd == NULL))
	{
		SetCursor(LoadCursor(NULL, IDC_IBEAM));
		return TRUE;
	}

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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Elektromehanika d.o.o. Nis
Serbia Serbia
He has a master degree in Computer Science at Faculty of Electronics in Nis (Serbia), and works as a C++/C# application developer for Windows platforms since 2001. He likes traveling, reading and meeting new people and cultures.

Comments and Discussions