////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//////////////////////////////////////////////////////////////////////////////
//
// EDIT.CPP
// 
// History:
//		08/15/96 JMI	Started.
//
//		09/24/96	JMI	Changed all BLU_MB?_* macros to RSP_MB?_* macros.
//
//		10/02/96	JMI	No longer checks ms_szText for NULL before calling
//							m_print stuff and updating m_aptTextPos.
//
//		10/27/96 MJR	Fixed "unused variable" warnings.
//
//		10/31/96	JMI	Changed:
//							Old label:		New label:
//							=========		=========
//							CEdit				REdit
//							CTxt				RTxt
//							CImage			RImage
//							CGuiItem			RGuiItem
//							LEFT				RGuiItem::Left
//							MULTILINE		Multiline
//							NUMBERS_ONLY	NumbersOnly
//							POINT				Point
//							EDITNOTIFY		EditNotify
//
//							Moved #include <string.h> to the beginning.
//
//		10/30/96	JMI	Changed:
//							Old label:		New label:
//							=========		=========
//							Rect				RRect
//
//		11/12/96 MJR	Added RSP_ prefix to GK keyboard macros.
//
//		11/14/96	JMI	DrawText() now attempts to clip the string to the edit
//							box.
//
//		11/27/96	JMI	Added initialization of m_type to identify this type
//							of GUI item.
//
//		12/04/96	JMI	Do() can no longer optionally poll for input, does not 
//							call rspGetKey(), and does not return that key.  Instead,
//							you pass the a pointer to the rspGetKeys() formatted key 
//							you want Do() to process.  The key will be zeroed if it
//							is "absorbed" by the edit box.
//
//		12/19/96	JMI	Uses new m_justification (as m_sJustification) and 
//							upgraded to new RFont/RPrint.
//
//		12/31/96	JMI	Do() now calls base implementation in RGuiItem.
//
//		01/01/97	JMI	DrawText() now draws a space with transparent back and
//							foreground colors so that the positions array will be
//							updated so we can use the caret position.
//							Also, now clips caret.
//
//		01/04/97	JMI	Upgraded HotCall() to new CursorEvent().  This upgrade
//							is in response to RGuiItem now using relative RHots.
//							Now m_hot.m_sX/Y is parent item relative just like
//							m_sX/Y.  This should simplify a lot of stuff and even
//							fix some odd features like being able to click a GUI
//							item that exceeds the boundary of its parent.  This fix
//							will be essential for the not-yet-existent RListBox since
//							it will most likely scroll many children through a small
//							client area.
//							Now there are two regions associated with cursor events.
//							The first is the 'hot' area.  This is the area that m_hot
//							is set to include.  Child items can only receive cursor
//							events through this area.  The second is the 'event' area.
//							This is the area where the item really is actually con-
//							cerned with cursor events.  Example:  For a Dlg, the
//							entire window is the 'hot' area and the title bar is the
//							'event' area.
//
//		01/08/97	JMI	There were some typos in Draw() involving clipping.
//							Fixed.
//
//		01/18/97	JMI	Converted Do() to take an RInputEvent* instead of a 
//							long*.
//
//		01/20/97	JMI	Made very few changes for overriding Read/WriteMembers.  
//							Should not change functionality.  Checked in to use at
//							home.
//
//		01/21/97	JMI	Added ReadMembers() and WriteMembers() overloads to read
//							and write members of this class.  Note that they call the
//							base class version to read/write base class members.
//							Support exists for versions 0 and 1.  Version 0 did not
//							contain other than RGuiItem members.
//
//		01/23/97	JMI	Added escape to keys we don't want to process during Do().
//
//		01/23/97	JMI	Now sets m_sShowFocus to FALSE in constructor.  Focus is
//							shown via caret anyhow.
//
//		02/05/97	JMI	Changed position of default: case in ReadMembers().
//
//		03/14/97	JMI	Added ClipCaret().
//
//		03/19/97	JMI	Converted to using the RHot::m_iecUser (was using
//							RHot::m_epcUser) so HotCall and CursorEvent now take
//							RInputEvent ptrs.

//		04/10/97	JMI	DrawText() considers new m_sFontCellHeight member.
//							Now uses m_sFontCellHeight instead of GetPos() to get
//							cell height.
//
//		04/24/97	JMI	Added m_u32TextShadowColor.
//
//		07/07/97	JMI	Now calls SetTextEffects() before drawing text in 
//							DrawText() and Draw().
//
//		08/25/97	JMI	Added m_sFirstVisibleCharIndex which is the first visible
//							character in the field.
//							Now automagically scrolls left or right based on caret
//							positions.
//
//		08/25/97	JMI	Was not handling the case where the text was truncated by
//							an outside source.  Now handled by calling ClipCaret() in
//							DrawText().
//
//		08/25/97	JMI	Added macros to define extra space on each side of text
//							in the edit field to make sure it doesn't butt up against
//							stuff.
//							Also, effects, justification, size, and word-wrappage
//							were not being set before drawing the caret in Draw().
//
//		08/25/97	JMI	Now scrolls half the field to the left if the caret 
//							passes the left edge (used to only scroll one character).
//
//		08/30/97	JMI	Now repaginates if the caret position is less than the
//							first visible character.
//
//		09/01/97	JMI	Now Do() only functions if this is activated.
//
//		01/15/98	JMI	Added numeric keypad support in Do().
//
//////////////////////////////////////////////////////////////////////////////
//
// This a GUI item that is based on the CTxt item. 
// This overrides CursorEvent() to get information about where a click it
// occurred.
// This overrides DrawText() to store the postion of each character.
// This overrides Draw() to add a cursor.
//
// Enhancements/Uses:
// To change the look of the text, you may want to override DrawText()
// in a derived class or see CTxt for other overridables
// To change the background of the text item, see CGuiItem.
// To get a callback on a click/release pair in the text item, see CTxt.
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Headers.
//////////////////////////////////////////////////////////////////////////////

#include <string.h>

#include "Blue.h"

#ifdef PATHS_IN_INCLUDES
	#include "ORANGE/GUI/edit.h"
#else
	#include "edit.h"
#endif // PATHS_IN_INCLUDES

//////////////////////////////////////////////////////////////////////////////
// Module specific macros.
//////////////////////////////////////////////////////////////////////////////

// Sets val to def if val is -1.
#define DEF(val, def)	((val == -1) ? def : val)

// Number of pixels to reserve as non-client on each vertical edge.
#define VERT_EDGE_RESERVED		1

#define EXTRA_NONCLIENT_EDGE	(m_sShowFocus ? m_sBorderThickness + VERT_EDGE_RESERVED : VERT_EDGE_RESERVED)

//////////////////////////////////////////////////////////////////////////////
// Module specific typedefs.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Module specific (static) variables.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Construction/Destruction.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Default constructor.
//
//////////////////////////////////////////////////////////////////////////////
REdit::REdit()
	{
	///////////////////////////////////////////////////////////////////////////
	// Defaults:
	///////////////////////////////////////////////////////////////////////////
	
	m_cCaretChar		= '_';	// Character to use as caret.
	m_u32CaretColor	= 255;	// Color to use for caret.
	m_sCaretPos			= 0;		// Text position of caret.
	m_lCaretBlinkRate	= 500;	// Rate at which character blinks in ms.  Can be
										// 0 indicating no blinkage.
	m_sMaxText			= GUI_MAX_STR;	// Maximum text to allow.  Limited to 
												// GUI_MAX_STR.
	m_sBehavior			= 0;		// Flags.  See enums in header.

	m_encCall			= NULL;	// Callback when a user input notification 
										// should occur such as too much input or 
										// invalid character generated (e.g., alphas 
										// in NUMBERS_ONLY mode).  A good place to 
										// generate a beep or something.

	m_lNextCaretUpdate	= 0;	// Time in ms of next caret update.
	m_sCaretState			= 0;	// Current state the caret is in until 
										// m_lNextCaretUpdate. (0 == hidden, 
										// 1 == shown).

	m_sInvertedBorder		= TRUE;	// Override RGuiItem's default.

	m_type					= Edit;	// Indicates type of GUI item.

	m_sShowFocus			= FALSE;	// Focus is shown via the caret.

	m_sFirstVisibleCharIndex	= 0;	// First visible char in field.
	}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor.
//
//////////////////////////////////////////////////////////////////////////////
REdit::~REdit()
	{
	}

////////////////////////////////////////////////////////////////////////
// Methods.
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
//
// Draw this item and all its subitems into the provided RImage.
// This override adds in the caret.
//
////////////////////////////////////////////////////////////////////////
int16_t REdit::Draw(			// Returns 0 on success.
	RImage* pimDst,			// Destination image.
	int16_t sDstX	/*= 0*/,		// X position in destination.
	int16_t sDstY	/*= 0*/,		// Y position in destination.
	int16_t sSrcX /*= 0*/,		// X position in source.
	int16_t sSrcY /*= 0*/,		// Y position in source.
	int16_t sW /*= 0*/,			// Amount to draw.
	int16_t sH /*= 0*/,			// Amount to draw.
	RRect* prc /*= NULL*/)	// Clip to.
	{
	int16_t	sRes	= 0;	// Assume success.

	// If visible . . .
	if (m_sVisible != FALSE)
		{
		// Call the base class.
		sRes = RGuiItem::Draw(	pimDst, sDstX, sDstY,
										sSrcX, sSrcY, sW, sH, prc);

		// If the caret is shown . . .
		if (m_sCaretState != 0)
			{
			// Make sure the caret is inside the string.
			ClipCaret();

			// If the caret is less than the first visible index (this can happen
			// if an outside party sets the caret position) . . .
			if (m_sCaretPos < m_sFirstVisibleCharIndex)
				{
				// We'll need to 'repaginate' the field.
				Compose();
				}

			// Setup printer ////////////////////////////////

			// Word wrap must be off.
			int16_t	sWordWrapWas	= (m_pprint->m_eModes & RPrint::WORD_WRAP) ? TRUE : FALSE;
			m_pprint->SetWordWrap(FALSE);

			// Text support is currently 8 bit only.
			m_pprint->SetColor((int16_t)m_u32CaretColor, (int16_t)0, (int16_t)m_u32TextShadowColor);

			m_pprint->SetFont(m_sFontCellHeight);

			SetJustification();
			SetTextEffects();		
	
			// Done setting up printer //////////////////////

			int16_t	sClientX, sClientY, sClientW, sClientH;
			GetClient(&sClientX, &sClientY, &sClientW, &sClientH);

			int16_t	sClipW, sClipH;

			if (sW == 0)
				{
				// Use client.
				sClipW	= sClientW;
				}
			else
				{
				// Adjust by blt position.
				sClipW	= sW - (sClientX + m_sX);
				}

			if (sH == 0)
				{
				// Use client.
				sClipH	= sClientH;
				}
			else
				{
				// Adjust by blt position.
				sClipH	= sH - (sClientY + m_sY);
				}

			// Clip.
			m_pprint->SetColumn(
				sDstX + m_sX + sClientX + EXTRA_NONCLIENT_EDGE, 
				sDstY + m_sY + sClientY, 
				sClipW - EXTRA_NONCLIENT_EDGE * 2, 
				sClipH);

			ASSERT(m_sCaretPos - m_sFirstVisibleCharIndex >= 0);

			m_pprint->print(
				sDstX + m_sX + m_aptTextPos[m_sCaretPos - m_sFirstVisibleCharIndex].sX, 
				sDstY + m_sY + m_aptTextPos[m_sCaretPos - m_sFirstVisibleCharIndex].sY,
				"%c", m_cCaretChar);

			// Restore printer //////////////////////////////

			m_pprint->SetWordWrap(sWordWrapWas);
			}
		}

	return sRes;
	}

////////////////////////////////////////////////////////////////////////
//
// Draw text in m_szText in m_u32TextColor with transparent
// background at sX, sY with sW and m_sJustification.
// The only additional functionality this has over RGuiItem::DrawText is
// that it blts the characters one at a time and tracks the position of
// each character in m_aptTextPos.
// If m_szText is empty, a space is printed with the foreground and
// background colors set to transparent.  This way, the array of 
// positions is set for the caret.
//
////////////////////////////////////////////////////////////////////////
int16_t REdit::DrawText(		// Returns 0 on success.
	int16_t sX,					// X position in image.
	int16_t sY,					// Y position in image.
	int16_t sW /*= 0*/,			// Width of text area.
	int16_t	sH /*= 0*/			// Height of test area.
	)	// Destination image.  NULL == use m_im.
	{
	int16_t	sRes	= 0;	// Assume success.
	
	char*	pszText;
	U32	u32ForeColor;

	if (m_szText[0] != '\0')
		{
		// Use user supplied text.
		pszText	= m_szText;
		// Use user supplied color.
		u32ForeColor	= m_u32TextColor;
		}
	else
		{
		// Use fake text and colors so that the positions
		// array is updated.  It would be handy if the
		// print command would update the array even for
		// printing "".  Then this would not be necessary.
		static char szSpace[]	= " ";

		// Display space so that we can guarantee that the
		// character positions array will be updated.
		pszText	= szSpace;

		// Use transparent color (just in case actually has
		// some pixels).
		u32ForeColor	= 0;
		}

	// Text support is currently 8 bit only.
	m_pprint->SetColor(u32ForeColor, 0, m_u32TextShadowColor);
	
	// Set size.  Hopefully this won't do too much scaling for 
	// caching purposes but I'm not sure.
	m_pprint->SetFont(m_sFontCellHeight);

	// Only left justified editting supported . . .
	if (m_justification == RGuiItem::Left)
		{
		// Added in extra space before starting text.
		sX	+= EXTRA_NONCLIENT_EDGE;
		// Remove extra space before starting text.
		sW	-= EXTRA_NONCLIENT_EDGE * 2;

		// Keep the caret within bounds.
		ClipCaret();

		SetJustification();
		SetTextEffects();

		// Word wrap must be off.
		int16_t	sWordWrapWas	= (m_pprint->m_eModes & RPrint::WORD_WRAP) ? TRUE : FALSE;
		m_pprint->SetWordWrap(FALSE);		
		m_pprint->SetColumn(sX, sY, sW, sH);
		m_pprint->GetWidth(pszText);

		int32_t	lLen				= strlen(pszText);

		// Whenever the caret hits or passes the left edge, jump the first visible
		// character back.
		if (m_sCaretPos < m_sFirstVisibleCharIndex)
			{
			int16_t	sHalfWidth	= sW / 2;
			// Go backward until we hit the beginning
			// or we get within half the field of the caret.

			while (m_sFirstVisibleCharIndex > 0)
				{
				if (m_pprint->ms_sCharPosX[m_sCaretPos] - m_pprint->ms_sCharPosX[m_sFirstVisibleCharIndex] >= sHalfWidth)
					{
					break;
					}

				m_sFirstVisibleCharIndex--;
				}
			}

		int16_t	sCaretWidth	= m_pprint->GetBlitW(m_cCaretChar);

		// Whenever the caret hits or passes the right edge, jump the first 
		// visible character forward.
		if (m_pprint->ms_sCharPosX[m_sCaretPos] - m_pprint->ms_sCharPosX[m_sFirstVisibleCharIndex] + sCaretWidth >= sW - EXTRA_NONCLIENT_EDGE)
			{
			int16_t	sHalfWidth	= sW / 2;
			// Go forward until we hit the end
			// or we get within half the field of the caret.

			while (m_sFirstVisibleCharIndex < lLen)
				{
				if (m_pprint->ms_sCharPosX[m_sCaretPos] - m_pprint->ms_sCharPosX[m_sFirstVisibleCharIndex] <= sHalfWidth)
					{
					break;
					}

				m_sFirstVisibleCharIndex++;
				}
			}

		ASSERT(m_sFirstVisibleCharIndex <= lLen);
		ASSERT(m_sFirstVisibleCharIndex >= 0);

		m_pprint->print(sX, sY, "%s", pszText + m_sFirstVisibleCharIndex);
	

		REdit::Point*	ppt	= m_aptTextPos;
		int32_t	lIndex;
		for (lIndex	= 0; lIndex < lLen; lIndex++, ppt++)
			{
			// Store its position in the pim.
			ppt->sX	= m_pprint->ms_sCharPosX[lIndex];
			ppt->sY	= sY;	// Eventually will be available as above.
			}

		// Update last position for cursor.
		ppt->sX	= m_pprint->ms_sCharPosX[lIndex];
		ppt->sY	= sY;	// Eventually will be available as above.

		m_pprint->SetWordWrap(sWordWrapWas);
		}
	else
		{
		TRACE("DrawText(): Only left justification supported by REdit "
			"currently.\n");
		sRes	= -1;
		}

	return sRes;
	}

////////////////////////////////////////////////////////////////////////
//
// Does REdit stuff like check for keys, update caret, and add new 
// text.
//
////////////////////////////////////////////////////////////////////////
void REdit::Do(		// Returns nothing.
	RInputEvent* pie)	// In:  Most recent user input event.
							// Out: pie->sUsed = TRUE, if used.
	{
	// Call base.
	RTxt::Do(pie);

	// Only if currenly active . . .
	if (IsActivated() != FALSE)
		{
		int16_t sRedrawText	= FALSE;	// Redraw text if TRUE.
		int16_t	sRedrawItem	= FALSE;	// Redraw item if TRUE (for event driven 
											// user).
		
		// If unused key event . . .
		if (pie->sUsed == FALSE && pie->type == RInputEvent::Key)
			{
			// Set the caret state to show.
			m_sCaretState	= 1;

			// Get length of current text.  Always handy.
			int32_t	lStrLen	= strlen(m_szText);

			// Remember caret position.
			int16_t	sCaretPosIn	= m_sCaretPos;

			// Char to add or 0.
			int32_t	lNewChar	= 0;

			// Make sure the caret is inside the string.
			ClipCaret();

			// Switch on extended keys.
			switch (pie->lKey & 0x0000FF00)
				{
				case 0x00000000:	// ASCII key.
					// Switch on ASCII key.
					switch (pie->lKey & 0x000000FF)
						{
						case '\b':
							if (m_sCaretPos > 0)
								{
								// Remove character at cursor.
								memmove(	m_szText + m_sCaretPos - 1,
											m_szText + m_sCaretPos,
											lStrLen - m_sCaretPos + 1);
								
								m_sCaretPos--;

								// Need to redraw text.
								sRedrawText	= TRUE;
								}
							else
								{
								NotifyCall();
								}

							// Absorb key.
							pie->sUsed	= TRUE;

							break;

						case '\r':
							if (m_sBehavior & REdit::Multiline)
								{
								TRACE("Do(): REdit::Multiline NYI.\n");
								NotifyCall();

								// Absorb key.
								pie->sUsed	= TRUE;
								}
							break;

						// Keys we don't want to process.
						case '\t':
						case 27:
							break;

						default:							
							lNewChar	= pie->lKey;

							// Absorb key.
							pie->sUsed	= TRUE;

							break;
						}
					break;										
				}

			// If we should add a key . . .
			if (lNewChar)
				{
				if (lStrLen < m_sMaxText - 1)
					{
					// Add character to string.
					// Move existing characters (perhaps only '\0')
					// out of the way.
					memmove(	m_szText + m_sCaretPos + 1, 
								m_szText + m_sCaretPos, 
								lStrLen - m_sCaretPos + 1);
					// Place character and advance caret position.
					m_szText[m_sCaretPos++]	= (char)lNewChar;
					// Need to redraw text.
					sRedrawText	= TRUE;
					}
				else
					{
					NotifyCall();
					}
				}

			// If change in caret pos . . .
			if (m_sCaretPos != sCaretPosIn)
				{
				// We must make sure the caret is visible.
				Compose();
				sRedrawItem	= TRUE;
				}
			}

		if (m_lCaretBlinkRate != 0)
			{
			// If the next blink time has expired . . .
			int32_t	lCurTime	= rspGetMilliseconds();
			if (lCurTime >= m_lNextCaretUpdate)
				{
				// Set next blink time.
				m_lNextCaretUpdate	= lCurTime + m_lCaretBlinkRate;
				// If caret is to be shown . . .
				if (m_sCaretState == 1)
					{
					// Set caret state.
					m_sCaretState	= 0;
					}
				else
					{
					// Set caret state.
					m_sCaretState	= 1;
					}

				// Redraw needed.
				sRedrawItem	= TRUE;
				}
			}
		else
			{
			m_sCaretState	= 1;
			}

		// If we need to redraw the text . . .
		if (sRedrawText != FALSE)
			{
			// Recompose the item.
			Compose();
			sRedrawItem	= TRUE;
			}

		// If a redraw is necessary . . .
		if (sRedrawItem != FALSE)
			{
			// Cause a redraw for the event driven user.
			Redraw();
			}
		}
	}



////////////////////////////////////////////////////////////////////////
// Internal.
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
//
// Read item's members from file.
// (virtual/protected (overriden here)).
//
////////////////////////////////////////////////////////////////////////
int16_t REdit::ReadMembers(			// Returns 0 on success.
	RFile*	pfile,					// File to read from.
	U32		u32Version)				// File format version to use.
	{
	int16_t	sRes	= 0;	// Assume success.

	// Invoke base class to read base members.
	sRes	= RTxt::ReadMembers(pfile, u32Version);

	// If okay so far . . .
	if (sRes == 0)
		{
		ASSERT(pfile != NULL);
		ASSERT(pfile->IsOpen() != FALSE);
		
		// Switch on version.
		switch (u32Version)
			{
			default:
			// Insert additional version numbers here!
			// case 4:	// Version 4 stuff.
			// case 3:	// Version 3 stuff.
			case 2:	// Version 2 stuff.

			case 1:
				// Read this class's members.
				pfile->Read(&m_cCaretChar);
				pfile->Read(&m_u32CaretColor);
				pfile->Read(&m_lCaretBlinkRate);
				pfile->Read(&m_sMaxText);
				pfile->Read(&m_sBehavior);

			case 0:	// In version 0, only base class RGuiItem members were stored.
				// If successful . . .
				if (pfile->Error() == FALSE)
					{
					// Success.
					}
				else
					{
					TRACE("ReadMembers(): Error reading REdit members.\n");
					sRes	= -1;
					}
				break;
			}
		}

	return sRes;
	}

////////////////////////////////////////////////////////////////////////
//
// Write item's members to file.
// (virtual/protected (overriden here)).
//
////////////////////////////////////////////////////////////////////////
int16_t REdit::WriteMembers(			// Returns 0 on success.
	RFile*	pfile)					// File to write to.
	{
	int16_t	sRes	= 0;	// Assume success.

	// Invoke base class to read base members.
	sRes	= RTxt::WriteMembers(pfile);

	// If okay so far . . .
	if (sRes == 0)
		{
		ASSERT(pfile != NULL);
		ASSERT(pfile->IsOpen() != FALSE);
		
		// Write this class's members.
		pfile->Write(&m_cCaretChar);
		pfile->Write(&m_u32CaretColor);
		pfile->Write(&m_lCaretBlinkRate);
		pfile->Write(&m_sMaxText);
		pfile->Write(&m_sBehavior);

		// If successful . . .
		if (pfile->Error() == FALSE)
			{
			// Success.
			}
		else
			{
			TRACE("WriteMembers(): Error writing REdit members.\n");
			sRes	= -1;
			}
		}

	return sRes;
	}

////////////////////////////////////////////////////////////////////////
// Clips the caret to within the string length.
////////////////////////////////////////////////////////////////////////
void REdit::ClipCaret(void)		// Returns nothing.
	{
	int32_t	lLen	= strlen(m_szText);
	if (m_sCaretPos > lLen)
		{
		// After last char.
		m_sCaretPos	= (int16_t)lLen;
		}
	}

////////////////////////////////////////////////////////////////////////
// Querries.
////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////////////
