#include "stdafx.h"
#include "CLCDInput.h"
#include "CLCDOutputManager.h"

//************************************************************************
// CLCDInput::CLCDInput
//************************************************************************
CLCDInput::CLCDInput() 
{
	memset(&m_Marker, 0, sizeof(m_Marker));

	//	SetScrollbarAlignment(TOP);
	Reset();
}


//************************************************************************
// CLCDInput::~CLCDInput
//************************************************************************
CLCDInput::~CLCDInput()
{
	m_vLineOffsets.clear();
}

//************************************************************************
// CLCDInput::Initialize
//************************************************************************
bool CLCDInput::Initialize()
{
	if (!CLCDTextObject::Initialize())
		return false;

	//	m_pParent = pParent;
	return true;
}

//************************************************************************
// CLCDInput::Shutdown
//************************************************************************
bool CLCDInput::Shutdown()
{
	if (!CLCDTextObject::Shutdown())
		return false;

	return true;
}

//************************************************************************
// CLCDInput::Update
//************************************************************************
void CLCDInput::SetScrollbar(CLCDBar *pScrollbar)
{
	m_pScrollbar = pScrollbar;
	if (m_pScrollbar) {
		m_pScrollbar->SetSliderSize(m_iLineCount);
		m_pScrollbar->SetRange(0, (int)m_vLineOffsets.size());
		m_pScrollbar->ScrollTo(m_iLinePosition);
	}
}

//************************************************************************
// CLCDInput::GetLastInputTime
//************************************************************************
long CLCDInput::GetLastInputTime()
{
	return m_lInputTime;
}

//************************************************************************
// CLCDInput::Update
//************************************************************************
bool CLCDInput::Update()
{
	if (!CLCDTextObject::Update())
		return false;

	if (m_lBlinkTimer + 500 <= (long)GetTickCount()) {
		m_bShowMarker = !m_bShowMarker;
		m_lBlinkTimer = GetTickCount();
	}

	return true;
}

//************************************************************************
// CLCDInput::Draw
//************************************************************************
bool CLCDInput::Draw(CLCDGfx *pGfx)
{
	if (!CLCDTextObject::Draw(pGfx))
		return false;

	SelectObject(pGfx->GetHDC(), m_hFont);

	RECT rBoundary = {0, 0,0 + GetWidth(), 0 + GetHeight()};
	int iLine = m_iLinePosition;
	int iEndLine = m_iLinePosition + m_iLineCount;
	int iLen = 0;
	wchar_t *pcOffset = nullptr;
	while (iLine < iEndLine && iLine < m_vLineOffsets.size()) {
		// Calculate the text length
		if (iLine < m_vLineOffsets.size() - 1) {
			iLen = m_vLineOffsets[iLine + 1].iOffset - m_vLineOffsets[iLine].iOffset;
			// Draw the linebreak marker
			if (m_bShowSymbols && m_vLineOffsets[iLine + 1].bLineBreak)
				pGfx->DrawFilledRect(m_vLineOffsets[iLine].iWidth + 1, rBoundary.top + m_iFontHeight / 3, m_iFontHeight / 3, m_iFontHeight / 3);
		}
		else iLen = (int)m_strText.length() - m_vLineOffsets[iLine].iOffset;

		// Draw the text
		pcOffset = (wchar_t*)m_strText.c_str() + m_vLineOffsets[iLine].iOffset;
		DrawTextEx(pGfx->GetHDC(),
			(LPTSTR)pcOffset,
			iLen,
			&rBoundary,
			m_iTextFormat,
			&m_dtp);

		// Draw the input cursor
		if (m_pInput && m_bShowMarker && m_Marker[0].iLine == iLine) {
			// insert-mode cursor
			if (m_bInsert || m_Marker[0].iXWidth == 1) {
				pGfx->DrawFilledRect(m_Marker[0].iXLine,
					m_iFontHeight*(iLine - m_iLinePosition),
					1,
					m_iFontHeight);
			}
			// replace-mode cursor
			else {
				RECT rMarker = {m_Marker[0].iXLine,
								m_iFontHeight*(iLine - m_iLinePosition),
								m_Marker[0].iXLine + m_Marker[0].iXWidth,
								m_iFontHeight*(iLine - m_iLinePosition + 1)};
				InvertRect(pGfx->GetHDC(), &rMarker);
			}
		}

		rBoundary.top += m_iFontHeight;
		rBoundary.bottom += m_iFontHeight;

		iLine++;
	}
	return true;
}

//************************************************************************
// CLCDInput::ShowSymbols
//************************************************************************
void CLCDInput::ShowSymbols(bool bShow)
{
	m_bShowSymbols = bShow;
}

//************************************************************************
// CLCDInput::SetBreakKeys
//************************************************************************
void CLCDInput::SetBreakKeys(int iKeys)
{
	m_iBreakKeys = iKeys != 0;
}

//************************************************************************
// returns wether the input is currently active
//************************************************************************
bool CLCDInput::IsInputActive()
{
	return m_pInput != nullptr;
}


//************************************************************************
// CLCDInput::OnSizeChanged
//************************************************************************
void CLCDInput::OnSizeChanged(SIZE OldSize)
{
	// if the width has changed, offsets need to be recalculated
	if (GetWidth() != OldSize.cx)
		OnFontChanged();
	// otherwise, just update scrollbar & linecount
	else if (m_pScrollbar) {
		m_iLineCount = GetHeight() / m_iFontHeight;
		m_pScrollbar->SetSliderSize(m_iLineCount);
	}
}

//************************************************************************
// CLCDInput::OnFontChanged
//************************************************************************
void CLCDInput::OnFontChanged()
{
	if (m_iFontHeight == 0)
		return;

	if (m_pScrollbar)
		m_pScrollbar->SetSliderSize(m_iLineCount);

	m_iLinePosition = 0;
	m_iLineCount = GetHeight() / m_iFontHeight;

	if (m_pScrollbar)
		m_pScrollbar->SetSliderSize(m_iLineCount);

	m_Marker[0].iLine = 0;
	m_Marker[0].iPosition = (int)m_strText.length();

	// Delete all offsets and recalculate them
	m_vLineOffsets.clear();
	// Create a new offset
	SLineEntry offset;
	offset.bLineBreak = false;
	offset.iOffset = 0;
	m_vLineOffsets.push_back(offset);

	UpdateOffsets(0);

	UpdateMarker();
	if (m_iLineCount > 0)
		ScrollToMarker();
}

//************************************************************************
// CLCDInput::ActivateInput
//************************************************************************
void CLCDInput::ActivateInput()
{
	if (m_pInput)
		return;

	CLCDConnection *pLCDCon = CLCDOutputManager::GetInstance()->GetLCDConnection();
	pLCDCon->SetAsForeground(1);

	m_hKBHook = SetWindowsHookEx(WH_KEYBOARD_LL, CLCDInput::KeyboardHook, GetModuleHandle(nullptr), 0);
	if (!m_hKBHook)
		return;
	m_pInput = this;
	GetKeyboardState(m_acKeyboardState);
}

//************************************************************************
// CLCDInput::DeactivateInput
//************************************************************************
void CLCDInput::DeactivateInput()
{
	if (!m_pInput)
		return;
	UnhookWindowsHookEx(m_hKBHook);
	m_hKBHook = nullptr;

	m_pInput = nullptr;

	CLCDConnection *pLCDCon = CLCDOutputManager::GetInstance()->GetLCDConnection();
	pLCDCon->SetAsForeground(0);
}

//************************************************************************
// CLCDInput::KeyboardHook
//************************************************************************
CLCDInput* CLCDInput::m_pInput = nullptr;

LRESULT CALLBACK CLCDInput::KeyboardHook(int Code, WPARAM wParam, LPARAM lParam)
{
	return m_pInput->ProcessKeyEvent(Code, wParam, lParam);
}

//************************************************************************
// CLCDInput::ProcessKeyEvent
//************************************************************************
LRESULT CLCDInput::ProcessKeyEvent(int Code, WPARAM wParam, LPARAM lParam)
{
	// Event verarbeiten
	if (Code == HC_ACTION) {
		KBDLLHOOKSTRUCT *key = (KBDLLHOOKSTRUCT *)(lParam);

		bool bKeyDown = !(key->flags & LLKHF_UP);
		bool bToggled = (m_acKeyboardState[key->vkCode] & 0x0F) != 0;
		if (bKeyDown)
			bToggled = !bToggled;
		m_acKeyboardState[key->vkCode] = (bKeyDown ? 0x80 : 0x00) | (bToggled ? 0x01 : 0x00);
		if (key->vkCode == VK_LSHIFT || key->vkCode == VK_RSHIFT)
			m_acKeyboardState[VK_SHIFT] = m_acKeyboardState[key->vkCode];
		else if (key->vkCode == VK_LCONTROL || key->vkCode == VK_RCONTROL)
			m_acKeyboardState[VK_CONTROL] = m_acKeyboardState[key->vkCode];
		else if (key->vkCode == VK_LMENU || key->vkCode == VK_RMENU)
			m_acKeyboardState[VK_MENU] = m_acKeyboardState[key->vkCode];

		/*
		if(bKeyDown)
			TRACE(L"Key pressed: %i\n",key->vkCode);
		else
			TRACE(L"Key released: %i\n",key->vkCode);
		*/
		// Only handle Keyup
		if (bKeyDown) {
			// Actions with Control/Menu keys
			if ((m_acKeyboardState[VK_LMENU] & 0x80 || m_acKeyboardState[VK_CONTROL] & 0x80)
				&& m_acKeyboardState[VK_SHIFT] & 0x80) {
				ActivateKeyboardLayout((HKL)HKL_NEXT, 0);//KLF_SETFORPROCESS);
				TRACE(L"Keyboardlayout switched!\n");
				return 1;
			}

			int res = 0, size = 0, dir = MARKER_HORIZONTAL, scroll = 0;
			/*
						if(key->vkCode == VK_DELETE) {
							dir = MARKER_HOLD;
							res = -1;
							if(m_strText[m_Marker[0].iPosition] == '\r')
								res = -2;
							if(m_strText.length() >= m_Marker[0].iPosition + -res) {
								m_strText.erase(m_Marker[0].iPosition,-res);
								scroll = 1;
								size = 1;
							} else {
								res = 0;
							}
						}
						else */if (key->vkCode == VK_BACK) {
							if (m_Marker[0].iPosition != 0) {
								res = -1;
								if (m_strText[m_Marker[0].iPosition + res] == '\n')
									res = -2;

								m_strText.erase(m_Marker[0].iPosition + res, -res);
								scroll = 1;
								size = res;
							}
						}
			// Marker navigation
						else if (key->vkCode == VK_INSERT) {
							m_bInsert = !m_bInsert;
						}
						else if (key->vkCode == VK_HOME) {
							res = m_vLineOffsets[m_Marker[0].iLine].iOffset - m_Marker[0].iPosition;
							scroll = 1;
						}
						else if (key->vkCode == VK_END) {
							if (m_vLineOffsets.size() - 1 == m_Marker[0].iLine)
								res = (int)m_strText.length() - m_Marker[0].iPosition;
							else
								res = (m_vLineOffsets[m_Marker[0].iLine + 1].iOffset - 1 - m_vLineOffsets[m_Marker[0].iLine + 1].bLineBreak) - m_Marker[0].iPosition;

							scroll = 1;
						}
						else if (key->vkCode == VK_UP) {
							res = -1;
							dir = MARKER_VERTICAL;
						}
						else if (key->vkCode == VK_DOWN) {
							res = 1;
							dir = MARKER_VERTICAL;
						}
						else if (key->vkCode == VK_LEFT)
	res = -1;
						else if (key->vkCode == VK_RIGHT)
	res = 1;

						else {

						#ifdef _UNICODE
							wchar_t output[4];
						#else
							unsigned char output[2];
						#endif

							if (key->vkCode == VK_RETURN) {
								bool bCtrlDown = (m_acKeyboardState[VK_CONTROL] & 0x80) != 0;
								if (bCtrlDown != (m_iBreakKeys == KEYS_RETURN)) {
									DeactivateInput();
									//m_pParent->OnInputFinished();
									return 1;
								}
								else {
									res = 2;
									output[0] = '\r';
									output[1] = '\n';
									output[2] = 0;
								}
							}
							else {
							#ifdef _UNICODE
								res = ToUnicode(key->vkCode, key->scanCode, m_acKeyboardState, output, 4, 0);
							#else
								res = ToAscii(key->vkCode, key->scanCode, m_acKeyboardState, (uint16_t*)output, 0);
							#endif
							}

							if (res <= 0)
								res = 0;
							else {
								if (output[0] != '\r' && output[0] <= 0x001F)
									return 1;

								if (m_bInsert || m_strText[m_Marker[0].iPosition] == '\r')
									m_strText.insert(m_Marker[0].iPosition, (wchar_t*)output, res);
								else
									m_strText.replace(m_Marker[0].iPosition, res, (wchar_t*)output);

								scroll = 1;
								size = res;
							}
						}
			if (res != 0) {
				UpdateOffsets(size);
				UpdateMarker();
				ScrollToMarker();
				m_lInputTime = GetTickCount();
			}
			//WrapLine();
			// ----



			// Block this KeyEvent
		}
		return 1;
	}
	return CallNextHookEx(m_hKBHook, Code, wParam, lParam);
}

//************************************************************************
// CLCDInput::MoveMarker
//************************************************************************
void CLCDInput::MoveMarker(int iDir, int iMove, bool bShift)
{
	// Just cursor
	if (!bShift) {
		m_lBlinkTimer = GetTickCount();
		m_bShowMarker = true;

		if (iDir == MARKER_HORIZONTAL) {
			m_Marker[0].iPosition += iMove;

		}
		if (iDir == MARKER_VERTICAL) {
			if (iMove < 0 && m_Marker[0].iLine == 0)
				return;
			if (iMove > 0 && m_Marker[0].iLine == m_vLineOffsets.size() - 1)
				return;

			m_Marker[0].iLine += iMove;

			int iX = 0, iX2 = 0;

			SIZE sizeChar = {0,0};
			int iBegin = m_vLineOffsets[m_Marker[0].iLine].iOffset;
			int iLen = 0;
			if (m_Marker[0].iLine < m_vLineOffsets.size() - 1)
				iLen = m_vLineOffsets[m_Marker[0].iLine + 1].iOffset - m_vLineOffsets[m_Marker[0].iLine].iOffset;
			else
				iLen = (int)m_strText.length() - m_vLineOffsets[m_Marker[0].iLine].iOffset;

			HDC hDC = CreateCompatibleDC(nullptr);
			if (nullptr == hDC)
				return;
			SelectObject(hDC, m_hFont);
			m_Marker[0].iXWidth = 1;
			m_Marker[0].iPosition = -1;

			int *piWidths = new int[iLen];
			int iMaxChars;
			int iChar = iBegin;

			GetTextExtentExPoint(hDC, m_strText.c_str() + iBegin, iLen, GetWidth(), &iMaxChars, piWidths, &sizeChar);
			for (; iChar < iBegin + iMaxChars; iChar++) {
				iX2 = iX;
				iX = piWidths[iChar - iBegin];

				if (m_Marker[0].iPosition >= 0 &&
					iChar >= m_Marker[0].iPosition) {
					m_Marker[0].iXWidth = sizeChar.cx;
					break;
				}

				if (iX >= m_Marker[0].iXLine || (iChar < iBegin + iLen - 1 && m_strText[iChar + 1] == 10)) {
					if (m_Marker[0].iXLine - iX2 <= iX - m_Marker[0].iXLine) {
						m_Marker[0].iPosition = iChar;
						m_Marker[0].iXLine = iX2;
						m_Marker[0].iXWidth = sizeChar.cx;
						break;
					}
					else {
						m_Marker[0].iPosition = iChar + 1;
						m_Marker[0].iXLine = iX;
					}
				}
			}

			delete[] piWidths;

			if (m_Marker[0].iPosition == -1) {
				m_Marker[0].iPosition = iChar;
				m_Marker[0].iXLine = iX;
			}
			DeleteObject(hDC);
		}

		for (int i = 0; i < 2; i++) {
			if (m_Marker[i].iPosition < 0)
				m_Marker[i].iPosition = 0;
			else if (m_Marker[i].iPosition > m_strText.length())
				m_Marker[i].iPosition = (int)m_strText.length();
		}
		if (m_Marker[0].iPosition > 0 && m_strText[m_Marker[0].iPosition - 1] == '\r')
			m_Marker[0].iPosition += (iDir == MARKER_HORIZONTAL && iMove > 0) ? 1 : -1;

	}
}

//************************************************************************
// CLCDInput::GetText
//************************************************************************
tstring CLCDInput::GetText()
{
	return m_strText;
}

//************************************************************************
// CLCDInput::Reset
//************************************************************************
void CLCDInput::Reset()
{
	m_lInputTime = 0;
	m_bInsert = true;

	memset(&m_Marker[0], 0, sizeof(SMarker));

	m_strText = L"";
	m_vLineOffsets.clear();
	m_iLinePosition = 0;
	SLineEntry offset;
	offset.bLineBreak = false;
	offset.iOffset = 0;
	m_vLineOffsets.push_back(offset);

	if (m_pScrollbar) {
		m_pScrollbar->ScrollTo(0);
		m_pScrollbar->SetRange(0, 0);
		m_pScrollbar->SetSliderSize(m_iLineCount);
	}
}

//************************************************************************
// CLCDInput::UpdateOffsets
//************************************************************************
void CLCDInput::UpdateOffsets(int iModified)
{
	if (m_vLineOffsets.size() == 0 && m_strText.empty())
		return;

	HDC hDC = CreateCompatibleDC(nullptr);
	if (nullptr == hDC)
		return;

	// Reset the marker
	m_Marker[0].iXLine = 0;
	m_Marker[0].iXWidth = 1;

	// Initialize variables
	int iLen = (int)m_strText.length();
	int *piWidths = new int[iLen];
	wchar_t *pszText = (wchar_t*)m_strText.c_str();
	tstring::size_type pos = 0;
	int iMaxChars = 0;

	SIZE sizeWord = {0, 0};
	SIZE sizeLine = {0, 0};
	SelectObject(hDC, m_hFont);

	int iLine = -1;
	// Find the first line to update
	for (int i = (int)m_vLineOffsets.size() - 1; i >= 0; i--)
		if (m_vLineOffsets[i].iOffset <= m_Marker[0].iPosition) {
			iLine = i;
			break;
		}

	if (iModified < 0 && iLine - 1 >= 0)
		iLine--;

	bool bLineClosed = false;

	// TRACE(L"InputText: Begin Update at #%i\n",iLine);
	for (; iLine < m_vLineOffsets.size(); iLine++) {
		bLineClosed = false;

		int iChar = m_vLineOffsets[iLine].iOffset;
		int iWordOffset = iChar;

		if (!(iLen == 0) && iChar > iLen) {
			// TRACE(L"InputText: Deleted offset #%i\n",iLine);	
			m_vLineOffsets.erase(m_vLineOffsets.begin() + iLine);
			continue;
		}

		sizeLine.cx = 0;
		sizeWord.cx = 0;
		iWordOffset = iChar + 1;

		while (iChar < iLen) {
			iWordOffset = iChar;

			GetTextExtentExPoint(hDC, pszText + iChar, iLen - iChar, GetWidth(), &iMaxChars, piWidths, &sizeLine);
			pos = m_strText.find(L"\n", iChar);
			// check for linebreaks
			if (pos != tstring::npos && pos >= iChar && pos <= iChar + iMaxChars) {
				iWordOffset = (int)pos + 1;
				iMaxChars = (int)pos - iChar;
			}
			// if the string doesnt fit, try to wrap the last word to the next line
			else {
				// find the last space in the line
				pos = m_strText.rfind(L" ", iChar + iMaxChars);
				if (pos != tstring::npos && pos >= iChar)
					iWordOffset = (int)pos + 1;
				else
					iWordOffset = iChar + iMaxChars;
			}

			if (m_Marker[0].iPosition >= iChar && m_Marker[0].iPosition <= iChar + iMaxChars) {
				if (m_Marker[0].iPosition > iChar) {
					m_Marker[0].iXLine = piWidths[m_Marker[0].iPosition - 1 - iChar];
					if (m_strText[m_Marker[0].iPosition - 1] == '\n')
						m_Marker[0].iXLine = 0;
				}
				if (m_Marker[0].iPosition < iChar + iMaxChars) {
					if (m_strText[m_Marker[0].iPosition] > 0x001F)
						m_Marker[0].iXWidth = piWidths[m_Marker[0].iPosition - iChar] - m_Marker[0].iXLine;
				}
			}

			//iChar += iMaxChars;

			if (m_strText[iChar] == '\n' || sizeLine.cx > GetWidth()) {

				bLineClosed = true;

				int iDistance = INFINITE;


				// Check if a matching offset already exists
				for (int iLine2 = iLine + 1; iLine2 < m_vLineOffsets.size(); iLine2++) {
					if (m_vLineOffsets[iLine2].bLineBreak == (m_strText[iChar] == '\n')) {
						iDistance = iChar - (m_vLineOffsets[iLine2].iOffset - 1);
						if (m_vLineOffsets[iLine2].iOffset == iWordOffset || iDistance == iModified) {
							// if there are other offsets in front of this one, delete them
							if (iLine2 != iLine + 1) {
								// TRACE(L"InputText: Deleted offsets #%i to #%i\n",iLine+1,iLine2-1);	
								m_vLineOffsets.erase(m_vLineOffsets.begin() + iLine + 1, m_vLineOffsets.begin() + iLine2);
							}
							break;
						}
					}
				}
				// A matching offset was found
				if (iDistance == iModified) {
					if (iModified != 0) {
						// Update line's width
						if (iMaxChars > 0) {
							if (m_strText[iChar] == '\n' && iMaxChars >= 2)
								m_vLineOffsets[iLine].iWidth = piWidths[iMaxChars - 2];
							else
								m_vLineOffsets[iLine].iWidth = piWidths[iMaxChars - 1];
						}
						else
							m_vLineOffsets[iLine].iWidth = 0;

						// TRACE(L"InputText: shifted offsets #%i to end %i position(s)\n",iLine+1,iDistance);
						for (iLine++; iLine < m_vLineOffsets.size(); iLine++)
							m_vLineOffsets[iLine].iOffset += iDistance;

						goto finished;
					}
				}
				// if no matching offset was found, a new one has to be created
				else if (iDistance != 0) {
					SLineEntry offset;
					offset.bLineBreak = (m_strText[iChar] == '\n');
					offset.iOffset = iWordOffset;
					if (iLine == m_vLineOffsets.size() - 1)
						m_vLineOffsets.push_back(offset);
					else
						m_vLineOffsets.insert(m_vLineOffsets.begin() + iLine + 1, offset);

					// TRACE(L"InputText: Inserted new  %soffset at #%i\n",m_strText[iChar] == '\n'?L"linebreak ":L"",iLine+1);	
				}
				break;
			}

			iChar += iMaxChars;
		}
		// Update line's width
		if (iMaxChars > 0) {
			if (m_strText[iChar - 1] == '\n' && iMaxChars >= 2)
				m_vLineOffsets[iLine].iWidth = piWidths[iMaxChars - 2];
			else
				m_vLineOffsets[iLine].iWidth = piWidths[iMaxChars - 1];
		}
		else
			m_vLineOffsets[iLine].iWidth = 0;

		if (iLine != m_vLineOffsets.size() - 1 && !bLineClosed) {
			// TRACE(L"InputText: Deleted offsets #%i to #%i\n",iLine+1,m_vLineOffsets.size()-1);	
			m_vLineOffsets.erase(m_vLineOffsets.begin() + iLine + 1, m_vLineOffsets.end());
		}

	}

finished:
	delete[] piWidths;
	DeleteObject(hDC);

	if (m_pScrollbar)
		m_pScrollbar->SetRange(0, (int)m_vLineOffsets.size() - 1);
}

//************************************************************************
// CLCDInput::UpdateMarker
//************************************************************************
void CLCDInput::UpdateMarker()
{
	// Adjust the markers propertys
	for (int i = (int)m_vLineOffsets.size() - 1; i >= 0; i--)
		if (m_Marker[0].iPosition >= m_vLineOffsets[i].iOffset) {
			if (m_Marker[0].iPosition == m_vLineOffsets[i].iOffset)
				m_Marker[0].iXLine = 0;
			m_Marker[0].iLine = i;
			break;
		}
}

//************************************************************************
// CLCDInput::ScrollLine
//************************************************************************
bool CLCDInput::ScrollLine(bool bDown)
{
	if (bDown && m_iLinePosition + (m_iLineCount - 1) < m_vLineOffsets.size() - 1)
		m_iLinePosition++;
	else if (!bDown && m_iLinePosition > 0)
		m_iLinePosition--;
	else
		return false;

	if (m_pScrollbar)
		m_pScrollbar->ScrollTo(m_iLinePosition);
	return true;
}


//************************************************************************
// CLCDInput::ScrollToMarker
//************************************************************************
void CLCDInput::ScrollToMarker()
{
	if (m_Marker[0].iLine < m_iLinePosition)
		m_iLinePosition = m_Marker[0].iLine;

	if (m_Marker[0].iLine > m_iLinePosition + (m_iLineCount - 1)) {
		ScrollLine();
		if (m_Marker[0].iLine > m_iLinePosition + (m_iLineCount - 1))
			m_iLinePosition = (m_Marker[0].iLine / m_iLineCount)*m_iLineCount;
	}

	if (m_iLinePosition > m_vLineOffsets.size() - 1)
		m_iLinePosition = (int)m_vLineOffsets.size() - 1;
	if (m_iLinePosition < 0)
		m_iLinePosition = 0;

	if (m_pScrollbar)
		m_pScrollbar->ScrollTo(m_iLinePosition);
}

//************************************************************************
// CLCDInput::GetLineCount
//************************************************************************
int CLCDInput::GetLineCount()
{
	return (int)m_vLineOffsets.size();
}

//************************************************************************
// CLCDInput::GetLine
//************************************************************************
int CLCDInput::GetLine()
{
	return m_iLinePosition;
}